removed unneeded runtime requires
[profile/ivi/GhostCluster.git] / api.js
1 /*
2  * Copyright (c) 2012, Intel Corporation.
3  *
4  * This program is licensed under the terms and conditions of the
5  * Apache License, version 2.0.  The full text of the Apache License is at
6  * http://www.apache.org/licenses/LICENSE-2.0
7  *
8  */
9
10 /*****************************************************************************
11 * Class name: Vehicle
12 * Description:
13 *    A javascript implementation of the IVI vehicle API that communicates
14 *    to the automotive message broker through a websocket
15 * Optional constructor arguments:
16 *    sCB: success callback, called when socket is connected, argument is 
17 *         success message string
18 *    eCB: error callback, called on socket close or error, argument is error
19 *         message string
20 *    url: the URL to use for the websocket, in the form "ws://host:port/script"
21 *    protocol: the protocol to use for the websocket, default is "http-only"
22 *
23 * [Public Member functions]
24 *  Function name: getSupportedEventTypes(type, writeable, successCB, errorCB)
25 *    Description:
26 *        Retrieves a list of vehicle events for the requested type
27 *    Required arguments:
28 *        type: target event or group to query (use empty string for all events)
29 *        writeable: if true, return only writeable events, otherwise get all
30 *        successCB: success callback, gets called with a string list of names
31 *              for all the events and event groups that are children of the 
32 *              target. e.g. "vehicle_info" returns all events/groups with the 
33 *              vehicle_info prefix. If the target is an event group, it's
34 *              omitted from the returned list
35 *        errorCB: error callback, called with error message string
36 *
37 *  Function name: get(eventlist, successCB, errorCB)
38 *    Description:
39 *        Retrieves a list of event/value pairs for a target list of event names
40 *    Required arguments:
41 *        eventlist[]: list of events to read (use empty string for all events)
42 *        successCB: success callback, gets called with the event/value pair list
43 *                   for all requested events. The list is the in the 
44 *                   form of data[n].name/data[n].value
45 *        errorCB: error callback, called with error message string
46 *
47 *  Function name: set(eventlist, valuelist, successCB, errorCB)
48 *    Description:
49 *        Sets a gourp of event's values (triggers error on read-only events)
50 *    Required arguments:
51 *        eventlist: target events to set
52 *        valuelist: target event values
53 *        successCB: success callback, gets called with the eventlist
54 *                   that was successfully set
55 *        errorCB: error callback, called with error message string
56 *
57 *  Function name: subscribe(eventlist, successCB, errorCB)
58 *    Description:
59 *        Subscribe to a list of events so you can listen to value changes, they
60 *        can be monitored with document.addEventListener(eventname, callback, false);
61 *        The Event object passed to the callback has two parameters, e.name and 
62 *        e.value. Events are sent to the handler individually.
63 *    Required arguments:
64 *        eventlist: target events to listen to
65 *        successCB: success callback, gets called with the eventlist
66 *                   that was successfully subscribed
67 *        errorCB: error callback, called with the eventlist that failed to subscribe
68 *
69 *  Function name: unsubscribe(eventlist, successCB, errorCB)
70 *    Description:
71 *        Unsubscribe to a list of events to let the server know you're not listening, 
72 *        they should stop being sent from the server if no other clients are using them,
73 *        but will at least stop being triggered in your app.
74 *    Required arguments:
75 *        eventlist: target events to stop listening to
76 *        successCB: success callback, gets called with the eventlist
77 *                   that was successfully unsubscribed
78 *        errorCB: error callback, called with the eventlist that failed to unsubscribe
79 *
80 ******************************************************************************/
81
82 function Vehicle(sCB, eCB, url, protocol)
83 {
84     /* store a copy of Vehicle this for reference in callbacks */
85     var self = this;
86
87     this.iSuccessCB = sCB;
88     this.iErrorCB = eCB;
89
90     /* variables for call management, supports up to 100 simultaneously */
91     this.methodIdx = 0;
92     this.methodCalls = [];
93     for(var i = 0; i < 100; i++)
94     {
95         this.methodCalls[i] = null;
96     }
97
98     /* number of connection retries to attempt if the socket closes */
99     this.retries = 5;
100     this.connected = false;
101
102     /* timeout for method calls in milliseconds */
103     this.timeouttime = 5000;
104
105     /* default values for WebSocket */
106     this.socketUrl = "ws://localhost:23000/vehicle";
107     this.socketProtocol = "http-only";
108
109     /* override the websocket address if parameters are given */
110     if(url !== undefined) this.socketUrl = url;
111     if(protocol !== undefined) this.socketProtocol = protocol;
112
113     this.VehicleMethodCall = function(id, name, successCB, errorCB)
114     {
115         var me = this;
116         this.successCB = successCB;
117         this.errorCB = errorCB;
118         this.transactionid = id;
119         this.name = name;
120         this.done = false;
121         this.start = function()
122         {
123             me.timeout = setTimeout(function(){
124                 if(me.errorCB !== undefined)
125                 {
126                     me.errorCB("\""+me.name+"\" method timed out after "+self.timeouttime+"ms");
127                 }
128                 me.finish();
129             }, self.timeouttime);
130         }
131         this.finish = function()
132         {
133             if(me.timeout !== undefined)
134             {
135                 clearTimeout(me.timeout);
136             }
137             me.done = true;
138         }
139     }
140
141     function init() {
142         if ("WebSocket" in window)
143         {
144             if(self.socketProtocol.length > 0)
145             {
146                 self.socket = new WebSocket(self.socketUrl, self.socketProtocol);
147             }
148             else
149             {
150                 self.socket = new WebSocket(self.socketUrl);
151             }
152             self.socket.onopen = function()
153             {
154                 self.connected = true;
155                 self.iSuccessCB((self.retries < 5)?"(RECONNECTED)":"");
156                 self.retries = 5;
157             };
158             self.socket.onclose = function()
159             {
160                 self.connected = false;
161                 self.iErrorCB("socket closed "+((self.retries > 0)?"retrying in 5 seconds ...":""));
162                 if(self.retries > 0)
163                 {
164                     setTimeout(function(){
165                         self.retries--;
166                         init();
167                     }, 5000);
168                 }
169             };
170             self.socket.onerror = function(e)
171             {
172                 self.iErrorCB(e.data);
173             };
174             self.socket.onmessage = function (e) 
175             {
176                 self.receive(e.data);
177             };
178         }
179         else
180         {
181             console.log("This browser doesn't appear to support websockets!");
182         }
183     }
184     init();
185 }
186
187 Vehicle.prototype.generateTransactionId = function()
188 {
189     var i, val = [];
190     for(i = 0; i < 8; i++)
191     {
192        var num = Math.floor((Math.random()+1)*65536);
193        val[i] = num.toString(16).substring(1);
194     }
195     var uuid = val[0]+val[1]+"-"+
196                val[2]+"-"+val[3]+"-"+val[4]+"-"+
197                val[5]+val[6]+val[7];
198     return uuid;
199 }
200
201 Vehicle.prototype.send = function(obj, successCB, errorCB)
202 {
203     if(!this.connected)
204     {
205         if(errorCB !== undefined)
206         {
207             errorCB("\""+obj.name+"\" method failed because socket is closed");
208         }
209         return;
210     }
211     var i = this.methodIdx;
212     this.methodIdx = (this.methodIdx + 1)%100;
213     this.methodCalls[i] = new this.VehicleMethodCall(obj.transactionid, 
214         obj.name, successCB, errorCB);
215     this.socket.send(JSON.stringify(obj));
216     this.methodCalls[i].start();
217 }
218
219
220 Vehicle.prototype.getSupportedEventTypes = function(type, writeable, successCB, errorCB)
221 {
222     var obj = {
223         "type" : "method",
224         "name" : "getSupportedEventTypes",
225         "writeable" : writeable,
226         "transactionid" : this.generateTransactionId(),
227         "data" : type
228     };
229     this.send(obj, successCB, errorCB);
230 }
231
232 Vehicle.prototype.get = function(namelist, successCB, errorCB)
233 {
234     if(namelist.length <= 0)
235     {
236         return;
237     }
238
239     var obj = {
240         "type" : "method",
241         "name": "get",
242         "transactionid" : this.generateTransactionId(),
243         "data" : namelist
244     };
245     this.send(obj, successCB, errorCB);
246 }
247
248 Vehicle.prototype.getHistory = function(namelist, beginDate, endDate, successCB, errorCB)
249 {
250     if(namelist.length <= 0)
251     {
252         return;
253     }
254
255     var dataobj = {
256                 "property" : namelist,
257                 "timeBegin" : (beginDate.getTime() / 1000).toString(),
258                 "timeEnd" : (endDate.getTime() / 1000).toString(),
259                 "sequenceBegin" : "-1",
260                 "sequenceEnd" : "-1"
261         }
262     
263     var obj = {
264         "type" : "method",
265         "name": "getRanged",
266         "transactionid" : this.generateTransactionId(),
267         "data" : dataobj                
268     };
269     this.send(obj, successCB, errorCB);
270 }
271
272 Vehicle.prototype.set = function(namelist, valuelist, successCB, errorCB)
273 {
274     if((namelist.length != valuelist.length)||(namelist.length <= 0))
275     {
276         return;
277     }
278
279     var obj = {
280         "type" : "method",
281         "name": "set",
282         "transactionid" : this.generateTransactionId(),
283         "data" : []
284     };
285     var list = [];
286     for(var i = 0; i < namelist.length; i++)
287     {
288         var val = {"property" : namelist[i], "value" : valuelist[i]};
289         list[list.length] = val;
290     }
291     obj.data = list;
292     this.send(obj, successCB, errorCB);
293 }
294
295 Vehicle.prototype.subscribe = function(namelist, successCB, errorCB)
296 {
297     var obj = {
298         "type" : "method",
299         "name": "subscribe",
300         "transactionid" : this.generateTransactionId(),
301         "data" : namelist
302     };
303     this.send(obj, successCB, errorCB);
304 }
305
306 Vehicle.prototype.unsubscribe = function(namelist, successCB, errorCB)
307 {
308     var obj = {
309         "type" : "method",
310         "name": "unsubscribe",
311         "transactionid" : this.generateTransactionId(),
312         "data" : namelist
313     };
314     this.send(obj, successCB, errorCB);
315 }
316
317 Vehicle.prototype.sendEvent = function(name, value)
318 {
319     var evt = document.createEvent("Event");
320     evt.initEvent(name, true, true);
321     evt.name = name;
322     evt.value = value;
323     document.dispatchEvent(evt);
324     console.log(evt);
325 }
326
327 Vehicle.prototype.receive = function(msg)
328 {
329     var self = this;
330     var event;
331     try {
332         event = JSON.parse(msg);
333     }
334     catch(e) {
335         self.iErrorCB("GARBAGE MESSAGE: "+msg);
336         return;
337     }
338
339     if((event === undefined)||(event.type === undefined)||
340        (event.name === undefined))
341     {
342         self.iErrorCB("BADLY FORMED MESSAGE: "+msg);
343         return;
344     }
345     else
346     {
347         if(event.type === "methodReply")
348         {
349             var calls = this.methodCalls;
350             for(var i = 0; i < calls.length; i++)
351             {
352                 var call = calls[i];
353                 if(call&&(!call.done)&&(call.transactionid === event.transactionid))
354                 {
355                     call.finish();
356                     if(event.error !== undefined)
357                     {
358                         call.errorCB(event.error);
359                     }
360                     if(event.data !== undefined && call.successCB !== undefined)
361                     {
362                         call.successCB(event.data);
363                     }
364                     return;
365                 }
366             }
367         }
368         else if(event.type === "valuechanged")
369         {
370             self.sendEvent(event.name, event.data);
371         }
372     }
373 }