Merge remote-tracking branch 'origin/master' into wheelsource
[profile/ivi/automotive-message-broker.git] / plugins / websocketsink / test / 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(type, successCB, errorCB)
38 *    Description:
39 *        Retrieves a list of event/value pairs for a target event or event group
40 *    Required arguments:
41 *        type: target event group to query (use empty string for all events)
42 *        successCB: success callback, gets called with the event/value pair list
43 *                   for all event children of the target. 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(type, value, successCB, errorCB)
48 *    Description:
49 *        Sets a single event's value (triggers error if it's read-only)
50 *    Required arguments:
51 *        type: target event to set (an event group will trigger an error)
52 *        successCB: success callback, gets called with the event/value pair
53 *                   that was successfully set in the form data.name/data.value
54 *        errorCB: error callback, called with error message string
55 *
56 ******************************************************************************/
57
58 function Vehicle(sCB, eCB, url, protocol)
59 {
60     /* store a copy of Vehicle this for reference in callbacks */
61     var self = this;
62
63     this.iSuccessCB = sCB;
64     this.iErrorCB = eCB;
65
66     /* variables for call management, supports up to 100 simultaneously */
67     this.methodIdx = 0;
68     this.methodCalls = [];
69     for(var i = 0; i < 100; i++)
70     {
71         this.methodCalls[i] = null;
72     }
73
74     /* number of connection retries to attempt if the socket closes */
75     this.retries = 5;
76     this.connected = false;
77
78     /* timeout for method calls in milliseconds */
79     this.timeouttime = 5000;
80
81     /* default values for WebSocket */
82     this.socketUrl = "ws://localhost:23000/vehicle";
83     this.socketProtocol = "http-only";
84
85     /* override the websocket address if parameters are given */
86     if(url != undefined) this.socketUrl = url;
87     if(protocol != undefined) this.socketProtocol = protocol;
88
89     this.VehicleMethodCall = function(id, name, successCB, errorCB)
90     {
91         var me = this;
92         this.successCB = successCB;
93         this.errorCB = errorCB;
94         this.transactionid = id;
95         this.name = name;
96         this.done = false;
97         this.start = function()
98         {
99             me.timeout = setTimeout(function(){
100                 if(me.errorCB != undefined)
101                 {
102                     me.errorCB("\""+me.name+"\" method timed out after "+self.timeouttime+"ms");
103                 }
104                 me.finish();
105             }, self.timeouttime);
106         }
107         this.finish = function()
108         {
109             if(me.timeout != undefined)
110             {
111                 clearTimeout(me.timeout);
112             }
113             me.done = true;
114         }
115     }
116
117     function init() {
118         if ("WebSocket" in window)
119         {
120             if(self.socketProtocol.length > 0)
121             {
122                 self.socket = new WebSocket(self.socketUrl, self.socketProtocol);
123             }
124             else
125             {
126                 self.socket = new WebSocket(self.socketUrl);
127             }
128             self.socket.onopen = function()
129             {
130                 self.connected = true;
131                 self.iSuccessCB((self.retries < 5)?"(RECONNECTED)":"");
132                 self.retries = 5;
133             };
134             self.socket.onclose = function()
135             {
136                 self.connected = false;
137                 self.iErrorCB("socket closed "+((self.retries > 0)?"retrying in 5 seconds ...":""));
138                 if(self.retries > 0)
139                 {
140                     setTimeout(function(){
141                         self.retries--;
142                         init();
143                     }, 5000);
144                 }
145             };
146             self.socket.onerror = function(e)
147             {
148                 self.iErrorCB(e.data);
149             };
150             self.socket.onmessage = function (e) 
151             {
152                 self.receive(e.data);
153             };
154         }
155         else
156         {
157             console.log("This browser doesn't appear to support websockets!");
158         }
159     }
160     init();
161 }
162
163 Vehicle.prototype.generateTransactionId = function()
164 {
165     var i, val = [];
166     for(i = 0; i < 8; i++)
167     {
168        var num = Math.floor((Math.random()+1)*65536);
169        val[i] = num.toString(16).substring(1);
170     }
171     var uuid = val[0]+val[1]+"-"+
172                val[2]+"-"+val[3]+"-"+val[4]+"-"+
173                val[5]+val[6]+val[7];
174     return uuid;
175 }
176
177 Vehicle.prototype.send = function(obj, successCB, errorCB)
178 {
179     if(!this.connected)
180     {
181         if(errorCB != undefined)
182         {
183             errorCB("\""+obj.name+"\" method failed because socket is closed");
184         }
185         return;
186     }
187     var i = this.methodIdx;
188     this.methodIdx = (this.methodIdx + 1)%100;
189     this.methodCalls[i] = new this.VehicleMethodCall(obj.transactionid, 
190         obj.name, successCB, errorCB);
191     this.socket.send(JSON.stringify(obj));
192     this.methodCalls[i].start();
193 }
194
195
196 Vehicle.prototype.getSupportedEventTypes = function(type, writeable, successCB, errorCB)
197 {
198     var obj = {
199         "type" : "method",
200         "name" : "getSupportedEventTypes",
201         "writeable" : writeable,
202         "transactionid" : this.generateTransactionId(),
203         "data" : type
204     };
205     this.send(obj, successCB, errorCB);
206 }
207
208 Vehicle.prototype.get = function(type, successCB, errorCB)
209 {
210     var obj = {
211         "type" : "method",
212         "name": "get",
213         "transactionid" : this.generateTransactionId(),
214         "data" : type
215     };
216     this.send(obj, successCB, errorCB);
217 }
218
219 Vehicle.prototype.set = function(type, value, successCB, errorCB)
220 {
221     var obj = {
222         "type" : "method",
223         "name": "set",
224         "transactionid" : this.generateTransactionId(),
225         "data" : {"property" : type, "value" : value}
226     };
227     this.send(obj, successCB, errorCB);
228 }
229
230 Vehicle.prototype.receive = function(msg)
231 {
232     var self = this;
233     var event;
234     try {
235         event = JSON.parse(msg);
236     }
237     catch(e) {
238         self.iErrorCB("GARBAGE MESSAGE: "+msg);
239         return;
240     }
241
242     if((event == undefined)||(event.type == undefined)||
243        (event.name == undefined)||(event.transactionid == undefined))
244     {
245         self.iErrorCB("BADLY FORMED MESSAGE: "+msg);
246         return;
247     }
248     else
249     {
250         if(event.type === "methodReply")
251         {
252             var calls = this.methodCalls;
253             for(var i = 0; i < calls.length; i++)
254             {
255                 var call = calls[i];
256                 if(call&&(!call.done)&&(call.transactionid === event.transactionid))
257                 {
258                     call.finish();
259                     if(event.error != undefined)
260                     {
261                         call.errorCB(event.error);
262                     }
263                     else
264                     {
265                         call.successCB(event.data);
266                     }
267                     return;
268                 }
269             }
270         }
271     }
272 }