6b8e60099c52742a5efc87630e6f0a175c618721
[profile/ivi/automotive-message-broker.git] / plugins / websocketsink / test / vehicle.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: getHistory(event, startTime, endTime, successCB, errorCB)
48 *    Description:
49 *        Retrieves a list of event/value pairs for a target list of event names
50 *    Required arguments:
51 *        event: event to read
52 *        startTime: start date/time
53 *        endTime: end date/time
54 *        successCB: success callback, gets called with the event/value pair list
55 *                   for all requested events. The list is the in the
56 *                   form of data[n].name/data[n].value
57 *        errorCB: error callback, called with error message string
58 *
59 *
60 *  Function name: set(eventlist, valuelist, successCB, errorCB)
61 *    Description:
62 *        Sets a gourp of event's values (triggers error on read-only events)
63 *    Required arguments:
64 *        eventlist: target events to set
65 *        valuelist: target event values
66 *        successCB: success callback, gets called with the eventlist
67 *                   that was successfully set
68 *        errorCB: error callback, called with error message string
69 *
70 *  Function name: subscribe(eventlist, successCB, errorCB)
71 *    Description:
72 *        Subscribe to a list of events so you can listen to value changes, they
73 *        can be monitored with document.addEventListener(eventname, callback, false);
74 *        The Event object passed to the callback has two parameters, e.name and 
75 *        e.value. Events are sent to the handler individually.
76 *    Required arguments:
77 *        eventlist: target events to listen to
78 *        successCB: success callback, gets called with the eventlist
79 *                   that was successfully subscribed
80 *        errorCB: error callback, called with the eventlist that failed to subscribe
81 *
82 *  Function name: unsubscribe(eventlist, successCB, errorCB)
83 *    Description:
84 *        Unsubscribe to a list of events to let the server know you're not listening, 
85 *        they should stop being sent from the server if no other clients are using them,
86 *        but will at least stop being triggered in your app.
87 *    Required arguments:
88 *        eventlist: target events to stop listening to
89 *        successCB: success callback, gets called with the eventlist
90 *                   that was successfully unsubscribed
91 *        errorCB: error callback, called with the eventlist that failed to unsubscribe
92 *
93 ******************************************************************************/
94 /*
95 (function () {
96 */
97 function Vehicle(sCB, eCB, url, protocol)
98 {
99     /* store a copy of Vehicle this for reference in callbacks */
100     var self = this;
101
102     this.iSuccessCB = sCB;
103     this.iErrorCB = eCB;
104
105     /* variables for call management, supports up to 100 simultaneously */
106     this.methodIdx = 0;
107     this.methodCalls = [];
108     for(var i = 0; i < 100; i++)
109     {
110         this.methodCalls[i] = null;
111     }
112
113     /* number of connection retries to attempt if the socket closes */
114     this.retries = 5;
115     this.connected = false;
116
117     /* timeout for method calls in milliseconds */
118     this.timeouttime = 5000;
119
120     /* default values for WebSocket */
121     this.socketUrl = "ws://localhost:23000/vehicle";
122     this.socketProtocol = "http-only";
123
124     /* override the websocket address if parameters are given */
125     if(url !== undefined) this.socketUrl = url;
126     if(protocol !== undefined) this.socketProtocol = protocol;
127
128     this.VehicleMethodCall = function(id, name, successCB, errorCB)
129     {
130         var me = this;
131         this.successCB = successCB;
132         this.errorCB = errorCB;
133         this.transactionid = id;
134         this.name = name;
135         this.done = false;
136         this.start = function()
137         {
138             me.timeout = setTimeout(function(){
139                 if(me.errorCB !== undefined)
140                 {
141                     me.errorCB("\""+me.name+"\" method timed out after "+self.timeouttime+"ms");
142                 }
143                 me.finish();
144             }, self.timeouttime);
145         }
146         this.finish = function()
147         {
148             if(me.timeout !== undefined)
149             {
150                 clearTimeout(me.timeout);
151             }
152             me.done = true;
153         }
154     }
155
156     function init() {
157         if ("WebSocket" in window)
158         {
159             if(self.socketProtocol.length > 0)
160             {
161                 self.socket = new WebSocket(self.socketUrl, self.socketProtocol);
162             }
163             else
164             {
165                 self.socket = new WebSocket(self.socketUrl);
166             }
167             self.socket.onopen = function()
168             {
169                 self.connected = true;
170                 self.iSuccessCB((self.retries < 5)?"(RECONNECTED)":"");
171                 self.retries = 5;
172             };
173             self.socket.onclose = function()
174             {
175                 self.connected = false;
176                 self.iErrorCB("socket closed "+((self.retries > 0)?"retrying in 5 seconds ...":""));
177                 if(self.retries > 0)
178                 {
179                     setTimeout(function(){
180                         self.retries--;
181                         init();
182                     }, 5000);
183                 }
184             };
185             self.socket.onerror = function(e)
186             {
187                 self.iErrorCB(e.data);
188             };
189             self.socket.onmessage = function (e) 
190             {
191                 self.receive(e.data);
192             };
193         }
194         else
195         {
196             console.log("This browser doesn't appear to support websockets!");
197         }
198     }
199     init();
200 }
201
202 Vehicle.prototype.generateTransactionId = function()
203 {
204     var i, val = [];
205     for(i = 0; i < 8; i++)
206     {
207        var num = Math.floor((Math.random()+1)*65536);
208        val[i] = num.toString(16).substring(1);
209     }
210     var uuid = val[0]+val[1]+"-"+
211                val[2]+"-"+val[3]+"-"+val[4]+"-"+
212                val[5]+val[6]+val[7];
213     return uuid;
214 }
215
216 Vehicle.prototype.send = function(obj, successCB, errorCB)
217 {
218     if(!this.connected)
219     {
220         if(errorCB !== undefined)
221         {
222             errorCB("\""+obj.name+"\" method failed because socket is closed");
223         }
224         return;
225     }
226     var i = this.methodIdx;
227     this.methodIdx = (this.methodIdx + 1)%100;
228     this.methodCalls[i] = new this.VehicleMethodCall(obj.transactionid, 
229         obj.name, successCB, errorCB);
230     this.socket.send(JSON.stringify(obj));
231     this.methodCalls[i].start();
232 }
233
234
235 Vehicle.prototype.getSupportedEventTypes = function(type, writeable, successCB, errorCB)
236 {
237     var obj = {
238         "type" : "method",
239         "name" : "getSupportedEventTypes",
240         "writeable" : writeable,
241         "transactionid" : this.generateTransactionId(),
242         "data" : type
243     };
244     this.send(obj, successCB, errorCB);
245 }
246
247 Vehicle.prototype.get = function(namelist, zone, successCB, errorCB)
248 {
249     if(namelist.length <= 0)
250     {
251         return;
252     }
253
254         var properties = [];
255
256     for(var i = 0; i < namelist.length; i++)
257     {
258         properties[i] = {"property" : namelist[i], "zone" : zone};
259     }
260     var obj = {
261         "type" : "method",
262         "name": "get",
263         "transactionid" : this.generateTransactionId(),
264         "data" : properties
265     };
266     this.send(obj, successCB, errorCB);
267 }
268
269 Vehicle.prototype.getHistory = function(event, startTime, endTime, successCB, errorCB)
270 {
271     var obj = {
272         "type" : "method",
273         "name": "getHistory",
274         "transactionid" : this.generateTransactionId(),
275         "data" : [event, (startTime.getTime()/1000).toString(), (endTime.getTime()/1000).toString()]
276     };
277
278     this.send(obj, successCB, errorCB);
279
280 }
281
282 Vehicle.prototype.set = function(namelist, valuelist, zoneList, successCB, errorCB)
283 {
284     if((namelist.length != valuelist.length)||(namelist.length <= 0))
285     {
286         return;
287     }
288
289     var obj = {
290         "type" : "method",
291         "name": "set",
292         "transactionid" : this.generateTransactionId(),
293         "data" : []
294     };
295     var list = [];
296     for(var i = 0; i < namelist.length; i++)
297     {
298         var val = {"property" : namelist[i], "value" : valuelist[i],"zone" : zoneList[i]};
299         list[list.length] = val;
300     }
301     obj.data = list;
302     this.send(obj, successCB, errorCB);
303 }
304
305 Vehicle.prototype.subscribe = function(namelist, zoneList, successCB, errorCB)
306 {
307     var obj = {
308         "type" : "method",
309         "name": "subscribe",
310         "transactionid" : this.generateTransactionId(),
311         "data" : namelist,
312         "zone" : zoneList
313     };
314     this.send(obj, successCB, errorCB);
315 }
316
317 Vehicle.prototype.unsubscribe = function(namelist, zoneList, successCB, errorCB)
318 {
319     var obj = {
320         "type" : "method",
321         "name": "unsubscribe",
322         "transactionid" : this.generateTransactionId(),
323         "data" : namelist,
324         "zone" : zoneList
325     };
326     this.send(obj, successCB, errorCB);
327 }
328
329 Vehicle.prototype.sendEvent = function(name, value)
330 {
331     var evt = document.createEvent("Event");
332     evt.initEvent(name, true, true);
333     evt.name = name;
334     evt.value = value;
335     document.dispatchEvent(evt);
336     console.log(evt);
337 }
338
339 Vehicle.prototype.receive = function(msg)
340 {
341     var self = this;
342     var event;
343     try {
344         event = JSON.parse(msg);
345     }
346     catch(e) {
347         self.iErrorCB("GARBAGE MESSAGE: "+msg);
348         return;
349     }
350
351     if((event === undefined)||(event.type === undefined)||
352        (event.name === undefined))
353     {
354         self.iErrorCB("BADLY FORMED MESSAGE: "+msg);
355         return;
356     }
357     else
358     {
359         if(event.type === "methodReply")
360         {
361             var calls = this.methodCalls;
362             for(var i = 0; i < calls.length; i++)
363             {
364                 var call = calls[i];
365                 if(call&&(!call.done)&&(call.transactionid === event.transactionid))
366                 {
367                     call.finish();
368                     if(event.error !== undefined)
369                     {
370                         call.errorCB(event.error);
371                     }
372                     else if(event.data !== undefined && call.successCB !== undefined)
373                     {
374                         call.successCB(event.data);
375                     }
376                     return;
377                 }
378             }
379         }
380         else if(event.type === "valuechanged")
381         {
382             self.sendEvent(event.name, event.data);
383         }
384     }
385 }
386
387 /*
388     // AMD / RequireJS
389     if (typeof define !== 'undefined' && define.amd) {
390         define([], function () {
391             return {
392                 Vehicle: Vehicle
393             };
394         });
395     }
396     // Node.js
397     else if (typeof module !== 'undefined' && module.exports) {
398         module.exports = {
399                 Vehicle: Vehicle
400             };
401     }
402     // included directly via <script> tag
403     else {
404         root.vehicle = {
405                 Vehicle: Vehicle
406             };
407     }
408 })();
409 */