From: Kevron Rees Date: Mon, 6 Jan 2014 21:45:24 +0000 (-0800) Subject: readded old websocket sink plugin X-Git-Tag: 0.11.800~14 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=94a0055c0c746d3da709de3390da588646797d74;p=profile%2Fivi%2Fautomotive-message-broker.git readded old websocket sink plugin --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 49db618..f7e20cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ set (DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/packages/${PROJECT_NAME} option(qtmainloop "Use QCoreApplication mainloop " OFF) option(websocket_plugin "websocket source and sink plugins" OFF) +option(websocketsink_plugin "old websocket sink plugin" OFF) option(tpms_plugin "TPMS plugin " OFF) option(obd2_plugin "OBD-II plugin" OFF) option(database_plugin "Database plugins" OFF) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1c552ea..3dcbbe4 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -24,6 +24,7 @@ add_subdirectory(common) add_subdirectory(wheel) add_subdirectory(dbus) add_subdirectory(websocket) +add_subdirectory(websocketsink) add_subdirectory(obd2plugin) add_subdirectory(demosink) add_subdirectory(tpms) diff --git a/plugins/websocket/CMakeLists.txt b/plugins/websocket/CMakeLists.txt index cf19170..bb66b8a 100644 --- a/plugins/websocket/CMakeLists.txt +++ b/plugins/websocket/CMakeLists.txt @@ -13,7 +13,7 @@ if(Qt5Core_FOUND) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}") message(STATUS "size of void_p: ${CMAKE_SIZEOF_VOID_P}") if(CMAKE_SIZEOF_VOID_P MATCHES "8") - message(STATUS "can has 64bits") + message(STATUS "64bit enabled") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcmodel=large") endif(CMAKE_SIZEOF_VOID_P MATCHES "8") add_definitions(${Qt5Core_DEFINITIONS} -DQTBINARY_DATA) @@ -26,19 +26,19 @@ include_directories(${CMAKE_SOURCE_DIR}/lib ${include_dirs} ${QT_INCLUDE_DIRS}) set(websocketsinkplugin_headers websocketsink.h websocketmanager.h) set(websocketsinkplugin_sources websocketsinkmanager.cpp websocketsink.cpp) -add_library(websocketsinkplugin MODULE ${websocketsinkplugin_sources}) -set_target_properties(websocketsinkplugin PROPERTIES PREFIX "") -target_link_libraries(websocketsinkplugin amb ${websockets_LIBRARIES} -L${CMAKE_CURRENT_BINARY_DIR}/lib ${link_libraries} ${QT_LIBRARIES}) +add_library(websocketsink MODULE ${websocketsinkplugin_sources}) +set_target_properties(websocketsink PROPERTIES PREFIX "") +target_link_libraries(websocketsink amb ${websockets_LIBRARIES} -L${CMAKE_CURRENT_BINARY_DIR}/lib ${link_libraries} ${QT_LIBRARIES}) -install(TARGETS websocketsinkplugin LIBRARY DESTINATION lib${LIB_SUFFIX}/automotive-message-broker) +install(TARGETS websocketsink LIBRARY DESTINATION lib${LIB_SUFFIX}/automotive-message-broker) set(websocketsourceplugin_headers websocketsource.h) set(websocketsourceplugin_sources websocketsource.cpp) -add_library(websocketsourceplugin MODULE ${websocketsourceplugin_sources}) -set_target_properties(websocketsourceplugin PROPERTIES PREFIX "") -target_link_libraries(websocketsourceplugin amb ${websockets_LIBRARIES} -L${CMAKE_CURRENT_BINARY_DIR}/lib ${link_libraries} ${QT_LIBRARIES}) +add_library(websocketsource MODULE ${websocketsourceplugin_sources}) +set_target_properties(websocketsource PROPERTIES PREFIX "") +target_link_libraries(websocketsource amb ${websockets_LIBRARIES} -L${CMAKE_CURRENT_BINARY_DIR}/lib ${link_libraries} ${QT_LIBRARIES}) -install(TARGETS websocketsourceplugin LIBRARY DESTINATION lib${LIB_SUFFIX}/automotive-message-broker) +install(TARGETS websocketsource LIBRARY DESTINATION lib${LIB_SUFFIX}/automotive-message-broker) endif(websocket_plugin) diff --git a/plugins/websocketsink/CMakeLists.txt b/plugins/websocketsink/CMakeLists.txt new file mode 100644 index 0000000..998340c --- /dev/null +++ b/plugins/websocketsink/CMakeLists.txt @@ -0,0 +1,16 @@ +if(websocketsink_plugin) + +include(CheckIncludeFiles) +include_directories(${CMAKE_SOURCE_DIR}/lib ${include_dirs}) + +pkg_check_modules(websockets REQUIRED libwebsockets) + +set(websocketsinkplugin_headers websocketsink.h websocketmanager.h) +set(websocketsinkplugin_sources websocketsinkmanager.cpp websocketsink.cpp) +add_library(websocketsinkplugin MODULE ${websocketsinkplugin_sources}) +set_target_properties(websocketsinkplugin PROPERTIES PREFIX "") +target_link_libraries(websocketsinkplugin amb ${websockets_LIBRARIES} -L${CMAKE_CURRENT_BINARY_DIR}/lib ${link_libraries}) + +install(TARGETS websocketsinkplugin LIBRARY DESTINATION lib${LIB_SUFFIX}/automotive-message-broker) + +endif(websocketsink_plugin) diff --git a/plugins/websocketsink/protocol b/plugins/websocketsink/protocol new file mode 100644 index 0000000..5250b11 --- /dev/null +++ b/plugins/websocketsink/protocol @@ -0,0 +1,25 @@ +Example protocol messages + +Property changed event: +{"type":"valuechanged","name":"VehicleSpeed","data":"217","transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66", "timestamp":"1354521964.60253","sequence":"0"} + +Get property request: +{"type":"method","name":"get","data":["VehicleSpeed"],"transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} + +Get property reply: +{"type":"methodReply","name":"get","data":[{"property":"VehicleSpeed","value":"17"}],"transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66", "timestamp" : "1354521964.24962", "sequence": "0" } + +Get supported request: +{"type":"method","name":"getSupportedEventTypes","data":[],"transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} + +Get supported reply: +{"type":"methodReply","name":"getSupportedEventTypes","data":["running_status_speedometer","running_status_engine_speed","running_status_steering_wheel_angle","running_status_transmission_gear_status","EngineSpeed","VehicleSpeed","AccelerationX","TransmissionShiftPosition","SteeringWheelAngle","ThrottlePosition","EngineCoolantTemperature","VIN","WMI","BatteryVoltage","MachineGunTurretStatus"],"transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} + +Subscribe to data: +{"type":"method","name":"subscribe","data":["EngineSpeed"],"transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} + +Subscribe to data reply: +{"type":"methodReply","name":"subscribe","data":["EngineSpeed"],"transactionid":"d293f670-f0b3-11e1-aff1-0800200c9a66"} + +Get History request: +{"type":"method","name":"getRange","data": {"timeBegin":"1368825008.35948","timeEnd":"1368825018.35948","sequenceBegin":"-1","sequenceEnd":"-1"},"transactionid":"b07589ba-417c-4604-80c6-01c0dcbd524d"} diff --git a/plugins/websocketsink/test/events.js b/plugins/websocketsink/test/events.js new file mode 100644 index 0000000..cb0cfd5 --- /dev/null +++ b/plugins/websocketsink/test/events.js @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +/* ---------------------- vehicle event typedef --------------------------- */ + +function VehicleEventType() +{ + this.event = [ +"Randomize", +"AirConditioning", +"AirRecirculation", +"AirflowDirection", +"AvgKW", +"BatteryStatus", +"ChildLock", +"Defrost", +"ExteriorBrightness", +"ExteriorTemperature", +"FanSpeed", +"FrontWheelRadius", +"FullBatteryRange", +"InteriorTemperature", +"LightHazard", +"LightHead", +"LightParking", +"NightMode", +"Odometer", +"SeatHeater", +"TargetTemperature", +"TransmissionShiftPosition", +"VehicleSpeed", +"Weather" + ]; + this.value = []; + + /* set random initial values for all the props */ + for(i in this.event) + { + var prop = this.event[i]; + this.value[prop] = Math.floor(Math.random() * 1000000); + } +} + +VehicleEventType.prototype.getSupportedEventList = function(val) +{ + /* for undefined just assume everything */ + if((val == undefined)||(val === "")) + return this.event; + + /* grab every event with case insensitive prefix of val */ + var value = val.toLowerCase(); + var list = []; + for(i in this.event) + { + var prop = this.event[i].toLowerCase(); + if(prop.indexOf(value) === 0) + { + list[list.length] = prop; + } + } + + /* if the target val isn't alone, remove it, it's a grouping */ + var idx = list.indexOf(value); + if((idx >= 0)&&(list.length > 1)) + { + list.splice(idx, 1); + } + return list; +} + +VehicleEventType.prototype.getValueEventList = function(val) +{ + var i, j, list = this.getSupportedEventList(val); + for(i = 0; i < list.length; i++) + { + for(j = i + 1; j < list.length; j++) + { + if(list[j].indexOf(list[i]) === 0) + { + list.splice(i, 1); + i--; + } + } + } + return list; +} + +VehicleEventType.prototype.getValuesEventList = function(vals) +{ + var i, j, list = []; + for(i = 0; i < vals.length; i++) + { + var sublist = this.getValueEventList(vals[i]); + for(j = 0; j < sublist.length; j++) + { + if(list.indexOf(sublist[j]) < 0) + { + list[list.length] = sublist[j]; + } + } + } + return list; +} + +VehicleEventType.prototype.isValueEvent = function(val) +{ + var list = this.getValueEventList(val); + return(list.length === 1); +} + +VehicleEventType.prototype.getValue = function(prop) +{ + return this.value[prop]; +} + +VehicleEventType.prototype.isValid = function(prop) +{ + return (this.event.indexOf(prop) >= 0); +} + +VehicleEventType.prototype.setValue = function(prop, newval) +{ + this.value[prop] = newval; +} diff --git a/plugins/websocketsink/test/index.html b/plugins/websocketsink/test/index.html new file mode 100644 index 0000000..9a5daef --- /dev/null +++ b/plugins/websocketsink/test/index.html @@ -0,0 +1,17 @@ + + + + IVI API Tester + + + + + +
+
+
+
+ + + + diff --git a/plugins/websocketsink/test/servertest/client.html b/plugins/websocketsink/test/servertest/client.html new file mode 100644 index 0000000..9ef2ee3 --- /dev/null +++ b/plugins/websocketsink/test/servertest/client.html @@ -0,0 +1,17 @@ + + + + IVI API Tester + + + + + +
+
+
+
+ + + + diff --git a/plugins/websocketsink/test/servertest/server.html b/plugins/websocketsink/test/servertest/server.html new file mode 100644 index 0000000..43dc72a --- /dev/null +++ b/plugins/websocketsink/test/servertest/server.html @@ -0,0 +1,22 @@ + + + + IVI API Server Test + + + + + + +
+
+ + diff --git a/plugins/websocketsink/test/servertest/server.js b/plugins/websocketsink/test/servertest/server.js new file mode 100644 index 0000000..3fcda40 --- /dev/null +++ b/plugins/websocketsink/test/servertest/server.js @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +/* --------------------------- utility code ------------------------------- */ + +var PRINT = { + logElement : null, + init : function(log_id) { + this.logElement = document.getElementById(log_id); + }, + + scrollToBottom : function() { + this.logElement.scrollTop = this.logElement.scrollHeight; + }, + + incoming : function(msg) { + this.logElement.innerHTML += "
REQUEST: " + msg + "
"; + this.scrollToBottom(); + }, + + outgoing : function(msg) { + this.logElement.innerHTML += "
RESPONSE: " + msg + "
"; + this.scrollToBottom(); + }, + + pass : function(msg) { + this.logElement.innerHTML += "
SUCCESS: " + msg + "
"; + this.scrollToBottom(); + }, + + fail : function(msg) { + this.logElement.innerHTML += "
FAIL: " + msg + "
"; + this.scrollToBottom(); + }, + + log : function(msg) { + this.logElement.innerHTML += "
" + msg + "
"; + this.scrollToBottom(); + }, +} + +/* ----------------------------- test code --------------------------------- */ + +function VehicleServer(socketUrl) +{ + var self = this; + this.vehicleEventType = new VehicleEventType(); + this.subscriptions = []; + + this.Signal = function(name) + { + var me = this; + this.users = 0; + this.name = name; + this.start = function() { + if(me.users <= 0) + { + var interval = Math.floor(Math.random()*5000) + 1000; + me.timer = setInterval(function() { + var value = parseInt(self.vehicleEventType.getValue(me.name)) + 1; + self.vehicleEventType.setValue(me.name, value); + var obj = { + "type" : "valuechanged", + "name": me.name, + "data" : value + }; + self.socket.send(JSON.stringify(obj)); + }, interval); + } + me.users = 1; + } + this.stop = function() { + me.users--; + if((me.users <= 0)&&(me.timer != undefined)) + { + clearInterval(me.timer); + } + } + } + + function init() { + if ("WebSocket" in window) + { + var list = self.vehicleEventType.getValueEventList(); + for(var i = 0; i < list.length; i++) + { + self.subscriptions[i] = new self.Signal(list[i]); + } + + self.socket = new WebSocket(socketUrl); + self.socket.onopen = function() + { + PRINT.pass("Server READY"); + }; + self.socket.onclose = function() + { + PRINT.fail("Server CLOSED"); + }; + self.socket.onerror = function(e) + { + PRINT.fail("Server ERROR: "+e.data); + }; + self.socket.onmessage = function (e) + { + self.receive(e.data); + }; + } + else + { + PRINT.fail("This browser doesn't appear to support websockets!"); + } + } + init(); +} + +VehicleServer.prototype.subscribe = function(list) +{ + for(var i = 0; i < this.subscriptions.length; i++) + { + if(list.indexOf(this.subscriptions[i].name) >= 0) + { + this.subscriptions[i].start(); + } + } +} + +VehicleServer.prototype.unsubscribe = function(list) +{ + for(var i = 0; i < this.subscriptions.length; i++) + { + if(list.indexOf(this.subscriptions[i].name) >= 0) + { + this.subscriptions[i].stop(); + } + } +} + +VehicleServer.prototype.receive = function(msg) +{ + var event = JSON.parse(msg); + /* accept only methods with transaction ids */ + if((event == undefined)||(event.transactionid == undefined)|| + (event.type != "method")) + { + return; + } + + var obj; + PRINT.incoming(msg); + if(event.name === "getSupportedEventTypes") + { + var data; + if(event.writeable) + { + data = this.vehicleEventType.getValueEventList(event.data); + } + else + { + data = this.vehicleEventType.getSupportedEventList(event.data); + } + obj = { + "type" : "methodReply", + "name": event.name, + "transactionid" : event.transactionid, + "data" : data + }; + } + else if(event.name === "get") + { + var names = this.vehicleEventType.getValuesEventList(event.data); + if(names.length > 0) + { + obj = { + "type" : "methodReply", + "name": event.name, + "transactionid" : event.transactionid, + "data" : [] + }; + for(i in names) + { + var value = this.vehicleEventType.getValue(names[i]); + obj.data.push({"name" : names[i], "value" : value}); + } + } + else + { + obj = { + "type" : "methodReply", + "name": event.name, + "transactionid" : event.transactionid, + "error" : event.data + " is not a valid event" + }; + } + } + else if(event.name === "set") + { + var bad = []; + var good = []; + for(var i = 0; i < event.data.length; i++) + { + if((event.data[i].value != undefined) && + this.vehicleEventType.isValueEvent(event.data[i].property)) + { + this.vehicleEventType.setValue(event.data[i].property, parseInt(event.data[i].value)); + good[good.length] = event.data[i].property; + } + else + { + bad[bad.length] = event.data[i].property; + } + } + + obj = { + "type" : "methodReply", + "name": event.name, + "transactionid" : event.transactionid + }; + + if(bad.length > 0) + { + obj.error = "Failed to set:"; + for(var i = 0; i < bad.length; i++) + { + obj.error += " "+bad[i]; + } + } + + if(good.length > 0) + { + obj.data = "Successfully set:"; + for(var i = 0; i < good.length; i++) + { + obj.data += " "+good[i]; + } + } + } + else if(event.name === "subscribe") + { + var names = this.vehicleEventType.getValuesEventList(event.data); + if(names.length > 0) + { + obj = { + "type" : "methodReply", + "name": event.name, + "transactionid" : event.transactionid, + "data" : names + }; + for(i in names) + { + this.subscribe(names[i]); + } + } + else + { + obj = { + "type" : "methodReply", + "name": event.name, + "transactionid" : event.transactionid, + "error" : "no valid events provided" + }; + } + } + else if(event.name === "unsubscribe") + { + var names = this.vehicleEventType.getValuesEventList(event.data); + if(names.length > 0) + { + obj = { + "type" : "methodReply", + "name": event.name, + "transactionid" : event.transactionid, + "data" : names + }; + for(i in names) + { + this.unsubscribe(names[i]); + } + } + else + { + obj = { + "type" : "methodReply", + "name": event.name, + "transactionid" : event.transactionid, + "error" : "no valid events provided" + }; + } + } + else + { + obj = { + "type" : "methodReply", + "name": event.name, + "transactionid" : event.transactionid, + "error" : event.name + " is not a valid method" + }; + } + PRINT.outgoing(JSON.stringify(obj)); + this.socket.send(JSON.stringify(obj)); +} + +window.addEventListener('load', function () { + "use strict"; + PRINT.init("result"); + var server = new VehicleServer("ws://localhost:23023/vehicle?server"); +}); diff --git a/plugins/websocketsink/test/style.css b/plugins/websocketsink/test/style.css new file mode 100644 index 0000000..8456fd1 --- /dev/null +++ b/plugins/websocketsink/test/style.css @@ -0,0 +1,182 @@ +.PassClass { + font: bold 16px Arial; + color: green; +} + +.FailClass { + font: bold 16px Arial; + color: red; +} + +.LogClass { + font: 16px Arial; + color: black; +} + +#tester { + position: absolute; + -webkit-user-select: none; + top: 0px; + left: 0%; + height: 100%; + width: 620px; + overflow-y: auto; +} + +#result { + position: absolute; + top: 0px; + left: 620px; + height: 98%; + width: 1380px; + padding-top: 1%; + background-color: #eeeeee; + padding-left: 1%; + word-wrap:break-word; + overflow-y: auto; +} + +.proptest { + position: relative; + left: 0px; + height: 67px; + width: 700px; + overflow: hidden; +} + +.proptest .buttons { + position: absolute; + top: 32px; + left: 0px; + height: 35px; + width: 630px; +} + +.smallText { + text-align: left; + color: #FFFFFF; + font: 18px Arial; + display: inline; +} +.propinfo { + position: absolute; + top: 0px; + left: 0px; + height: 67px; + width: 600px; + text-align: left; + color: #FFFFFF; + font: 18px Arial; + line-height: 30px; + padding-left: 5px; + background: -webkit-gradient(linear, left top, left bottom, from(#444), to(#000)); + cursor: pointer; +} + +.propinfo.select { + background: -webkit-gradient(linear, left top, left bottom, from(#444), to(#aaa)); +} + +.propinfo.unselectable { + cursor: auto; + background: #000000; + border-bottom: solid 1px #444; +} + +input[type='text'] { + margin-top: 5px; + color: black; + font: bold 18px Arial; + height: 19px; + width: 100px; + background: -webkit-gradient(linear, left top, left bottom, from(#aaa), to(#fff)); + -webkit-transition: all 2s linear; +} + +input[type='text'].change { + -webkit-transition: all 0.1s linear; + color: red; +} + +.testbutton { + position: relative; + float: left; + color: #d7d7d7; + border: solid 1px #333; + text-align: center; + text-decoration: none; + font: 16px/100% Arial, Helvetica, sans-serif; + text-shadow: 0 1px 1px rgba(0,0,0,.3); + -webkit-border-radius: 12px; + border-radius: 12px; + background: -webkit-gradient(linear, left top, left bottom, from(#666), to(#000)); + height: 25px; + line-height: 24px; + cursor: pointer; + box-shadow: 2px 2px 14px #000; + margin-top: 5px; + margin-bottom: 5px; + margin-right: 2px; +} + +.testbutton.types { + width: 50px; + background: -webkit-gradient(linear, left top, left bottom, from(blue), to(#000)); +} + +.testbutton.types:after { + content: 'types'; +} + +.testbutton.get { + width: 35px; + background: -webkit-gradient(linear, left top, left bottom, from(green), to(#000)); +} + +.testbutton.get:after { + content: 'get'; +} + +.testbutton.set { + width: 35px; + background: -webkit-gradient(linear, left top, left bottom, from(green), to(#000)); +} + +.testbutton.set:after { + content: 'set'; +} + +.testbutton.subscribe { + width: 80px; + background: -webkit-gradient(linear, left top, left bottom, from(purple), to(#000)); +} + +.testbutton.subscribe.disable { + pointer-events: none; + color: #999999; + background: -webkit-gradient(linear, left top, left bottom, from(#000), to(#111)); +} + +.testbutton.subscribe:after { + content: 'subscribe'; +} + +.testbutton.unsubscribe { + width: 100px; + background: -webkit-gradient(linear, left top, left bottom, from(purple), to(#000)); +} + +.testbutton.unsubscribe.disable { + pointer-events: none; + color: #999999; + background: -webkit-gradient(linear, left top, left bottom, from(#000), to(#111)); +} + +.testbutton.unsubscribe:after { + content: 'unsubscribe'; +} + +.testbutton:active { + color: #666; + background: -webkit-gradient(linear, left top, left bottom, from(#000), to(#444)); +} diff --git a/plugins/websocketsink/test/test.js b/plugins/websocketsink/test/test.js new file mode 100644 index 0000000..90106f2 --- /dev/null +++ b/plugins/websocketsink/test/test.js @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +/* --------------------------- utility code ------------------------------- */ + +var PRINT = { + logElement: null, + init: function(log_id) { + this.logElement = document.getElementById(log_id); + }, + + scrollToBottom: function() { + this.logElement.scrollTop = this.logElement.scrollHeight; + }, + + clear: function() { + this.logElement.innerHTML = ""; + }, + + pass: function(msg) { + this.logElement.innerHTML += "
PASS: " + msg + "
"; + this.scrollToBottom(); + }, + + fail: function(msg) { + this.logElement.innerHTML += "
FAIL: " + msg + "
"; + this.scrollToBottom(); + }, + + log: function(msg) { + this.logElement.innerHTML += "
" + msg + "
"; + this.scrollToBottom(); + }, +} + +/* ----------------------------- test code --------------------------------- */ + +var vehicleEventType = new VehicleEventType(); +var selected = []; + +function getTypes(event) { + var types = window.vehicle.getSupportedEventTypes(event, false, + function(data) { + if (data && data.length > 1) { + PRINT.pass(event + " is a set of " + data.length + " events:"); + for (i in data) { + PRINT.log(data[i]); + } + } else if (data && data.length > 0) { + PRINT.pass(event + " is a single event:"); + for (i in data) { + PRINT.log(data[i]); + } + } else { + PRINT.fail(event + " unexcepted empty data field"); + } + }, + function(msg) { + PRINT.fail(((event === "") ? "all events" : event) + ":
" + msg); + } + ); +} + +function updateInput(input, value) { + input.value = value; + input.className = "change"; + input.addEventListener('webkitTransitionEnd', function callback(e) { + e.target.removeEventListener('webkitTransitionEnd', callback, false); + e.target.className = ""; + }, false); +} + +function getValue(eventlist) { + var zoneList = getZone(eventlist); + var types = window.vehicle.get(eventlist, zoneList, + function(data) { + if (data) { + PRINT.pass("values received:"); + if (eventlist.length > 1 && !! data.length && data.length > 0) { + var list = []; + for (var i = 0; i < data.length; i++) { + list[i] = data[i].property; + //list[i] = data[i].name; ??? + PRINT.log(data[i].property + ": " + data[i].value + ", zone: " + data[i].zone); + //PRINT.log(data[i].name+": "+data[i].value); ??? + } + + var elements = document.getElementsByClassName('proptest'); + for (var i = 0; i < elements.length; i++) { + var propinfo = elements[i].getElementsByClassName('propinfo')[0]; + var name = propinfo.innerHTML; + var idx = list.indexOf(name); + if (idx >= 0) { + var zone = elements[i].getElementsByTagName('input')[1]; + updateInput(zone, zone.value); + var input = elements[i].getElementsByTagName('input')[0]; + updateInput(input, data[idx].value); + } + } + } else { + PRINT.log(JSON.stringify(data)); + var elements = document.getElementsByClassName('proptest'); + for (var i = 0; i < elements.length; i++) { + var propinfo = elements[i].getElementsByClassName('propinfo')[0]; + if (data.property == propinfo.innerHTML) { + var zone = elements[i].getElementsByTagName('input')[1]; + updateInput(zone, zone.value); + var input = elements[i].getElementsByTagName('input')[0]; + updateInput(input, data.value); + } + } + } + } else { + PRINT.fail("no values retrieved for " + eventlist); + } + }, + function(msg) { + PRINT.fail(msg); + } + ); +} + +function setValue(eventlist) { + var zoneList = getZone(eventlist); + var elements = document.getElementsByClassName('proptest'); + var i, valuelist = [] ; + + /* initialize the value list */ + for (i = 0; i < eventlist.length; i++) { + valuelist[i] = 0; + } + + for (var i = 0; i < elements.length; i++) { + var propinfo = elements[i].getElementsByClassName('propinfo')[0]; + var name = propinfo.innerHTML; + var idx = eventlist.indexOf(name); + if (idx >= 0) { + + var input = elements[i].getElementsByTagName('input')[0]; + valuelist[idx] = input.value; + } + } + + var types = window.vehicle.set(eventlist, valuelist, zoneList, + function(msg) { + PRINT.pass("Set success for: " + JSON.stringify(msg)); + }, + function(msg) { + PRINT.fail("Set error: " + JSON.stringify(msg)); + } + ); +} + +function eventListener(e) { + PRINT.log(e.name + " update: " + JSON.stringify(e.value)); + var elements = document.getElementsByClassName('proptest'); + for (var i = 0; i < elements.length; i++) { + var propinfo = elements[i].getElementsByClassName('propinfo')[0]; + var name = propinfo.innerHTML; + if (name === e.name) { + var input = elements[i].getElementsByTagName('input')[0]; + updateInput(input, e.value.value); + var zone = elements[i].getElementsByTagName('input')[1]; + updateInput(zone, e.value.zone); + } + } +} + +function subscribe(eventlist) { + var zoneList = getZone(eventlist); + window.vehicle.subscribe(eventlist, zoneList, + function(data) { + PRINT.pass("Subscribe success for: " + data); + for (var i = 0; i < data.length; i++) { + var sub = data[i] + "_subscribe"; + var unsub = data[i] + "_unsubscribe"; + // document.getElementById(sub).className = "testbutton subscribe disable" + // document.getElementById(unsub).className = "testbutton unsubscribe"; + document.addEventListener(data[i], eventListener, false); + } + }, + function(msg) { + PRINT.fail("Subscribe failed for: " + msg); + } + ); +} + +function unsubscribe(eventlist, zoneList) { + zoneList = getZone(eventlist); + /* kill the handers first, so even if the service fails to acknowledge */ + /* we've stopped listening */ + for (var i = 0; i < eventlist.length; i++) { + document.removeEventListener(eventlist[i], eventListener, false); + } + window.vehicle.unsubscribe(eventlist, zoneList, + function(data) { + PRINT.pass("Unsubscribe success for: " + data); + for (var i = 0; i < data.length; i++) { + var sub = data[i] + "_subscribe"; + var unsub = data[i] + "_unsubscribe"; + // document.getElementById(unsub).className = "testbutton unsubscribe disable"; + // document.getElementById(sub).className = "testbutton subscribe"; + } + }, + function(msg) { + PRINT.fail("Unsubscribe failed for: " + msg); + } + ); +} + +function getZone(eventlist) { + var list = []; + if (eventlist.length > 1) { + + // for (var i = 0; i < data.length; i++) { + // list[i] = data[i].property; + // //list[i] = data[i].name; ??? + // PRINT.log(data[i].property + ": " + data[i].value); + // //PRINT.log(data[i].name+": "+data[i].value); ??? + // } + + var elements = document.getElementsByClassName('proptest'); + for (var i = 0; i < elements.length; i++) { + var propinfo = elements[i].getElementsByClassName('propinfo')[0]; + var name = propinfo.innerHTML; + var idx = eventlist.indexOf(name); + if (idx >= 0) { + var zone = elements[i].getElementsByTagName('input')[1]; + list.push(zone.value); + } + } + } else { + var elements = document.getElementsByClassName('proptest'); + for (var i = 0; i < elements.length; i++) { + var propinfo = elements[i].getElementsByClassName('propinfo')[0]; + if (eventlist[0] == propinfo.innerHTML) { + var zone = elements[i].getElementsByTagName('input')[1]; + list.push(zone.value); + } + } + } + return list.join(); +} + +function select(elem) { + var name = elem.innerHTML; + if (!vehicleEventType.isValid(name)) + return; + + var idx = selected.indexOf(name); + if (elem.className == "propinfo") { + if (idx < 0) { + selected[selected.length] = name; + } + elem.className = "propinfo select"; + } else if (elem.className == "propinfo select") { + if (idx >= 0) { + selected.splice(idx, 1); + } + elem.className = "propinfo"; + } +} + +function start(msg) { + if (window.vehicle && window.vehicle.getSupportedEventTypes) { + PRINT.pass("vehicle interface online " + msg); + } else { + PRINT.fail("vehicle interface not found"); + return; + } + + var tester = document.getElementById("tester"); + var part = ['
', + '
Zone:
' + ]; + var events = vehicleEventType.event; + + /* apply on all selected events */ + var html = '
apply on all selected events' + + '
' + + '' + + '
' + + '
' + + '
'; + + /* all events */ + html += '
all events'; + html += '
'; + + /* events */ + for (var i = 0; i < events.length; i++) { + var piece = ""; + for (var j = 0; j < part.length - 1; j++) { + piece += part[j] + events[i]; + } + html += piece + part[j]; + } + tester.innerHTML = html; +} + +function error(msg) { + PRINT.fail(msg); +} + +function init(url, protocol) { + PRINT.init("result"); + window.vehicle = new Vehicle(start, error, url, protocol); +} diff --git a/plugins/websocketsink/test/vehicle.js b/plugins/websocketsink/test/vehicle.js new file mode 100644 index 0000000..6b8e600 --- /dev/null +++ b/plugins/websocketsink/test/vehicle.js @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +/***************************************************************************** +* Class name: Vehicle +* Description: +* A javascript implementation of the IVI vehicle API that communicates +* to the automotive message broker through a websocket +* Optional constructor arguments: +* sCB: success callback, called when socket is connected, argument is +* success message string +* eCB: error callback, called on socket close or error, argument is error +* message string +* url: the URL to use for the websocket, in the form "ws://host:port/script" +* protocol: the protocol to use for the websocket, default is "http-only" +* +* [Public Member functions] +* Function name: getSupportedEventTypes(type, writeable, successCB, errorCB) +* Description: +* Retrieves a list of vehicle events for the requested type +* Required arguments: +* type: target event or group to query (use empty string for all events) +* writeable: if true, return only writeable events, otherwise get all +* successCB: success callback, gets called with a string list of names +* for all the events and event groups that are children of the +* target. e.g. "vehicle_info" returns all events/groups with the +* vehicle_info prefix. If the target is an event group, it's +* omitted from the returned list +* errorCB: error callback, called with error message string +* +* Function name: get(eventlist, successCB, errorCB) +* Description: +* Retrieves a list of event/value pairs for a target list of event names +* Required arguments: +* eventlist[]: list of events to read (use empty string for all events) +* successCB: success callback, gets called with the event/value pair list +* for all requested events. The list is the in the +* form of data[n].name/data[n].value +* errorCB: error callback, called with error message string +* +* Function name: getHistory(event, startTime, endTime, successCB, errorCB) +* Description: +* Retrieves a list of event/value pairs for a target list of event names +* Required arguments: +* event: event to read +* startTime: start date/time +* endTime: end date/time +* successCB: success callback, gets called with the event/value pair list +* for all requested events. The list is the in the +* form of data[n].name/data[n].value +* errorCB: error callback, called with error message string +* +* +* Function name: set(eventlist, valuelist, successCB, errorCB) +* Description: +* Sets a gourp of event's values (triggers error on read-only events) +* Required arguments: +* eventlist: target events to set +* valuelist: target event values +* successCB: success callback, gets called with the eventlist +* that was successfully set +* errorCB: error callback, called with error message string +* +* Function name: subscribe(eventlist, successCB, errorCB) +* Description: +* Subscribe to a list of events so you can listen to value changes, they +* can be monitored with document.addEventListener(eventname, callback, false); +* The Event object passed to the callback has two parameters, e.name and +* e.value. Events are sent to the handler individually. +* Required arguments: +* eventlist: target events to listen to +* successCB: success callback, gets called with the eventlist +* that was successfully subscribed +* errorCB: error callback, called with the eventlist that failed to subscribe +* +* Function name: unsubscribe(eventlist, successCB, errorCB) +* Description: +* Unsubscribe to a list of events to let the server know you're not listening, +* they should stop being sent from the server if no other clients are using them, +* but will at least stop being triggered in your app. +* Required arguments: +* eventlist: target events to stop listening to +* successCB: success callback, gets called with the eventlist +* that was successfully unsubscribed +* errorCB: error callback, called with the eventlist that failed to unsubscribe +* +******************************************************************************/ +/* +(function () { +*/ +function Vehicle(sCB, eCB, url, protocol) +{ + /* store a copy of Vehicle this for reference in callbacks */ + var self = this; + + this.iSuccessCB = sCB; + this.iErrorCB = eCB; + + /* variables for call management, supports up to 100 simultaneously */ + this.methodIdx = 0; + this.methodCalls = []; + for(var i = 0; i < 100; i++) + { + this.methodCalls[i] = null; + } + + /* number of connection retries to attempt if the socket closes */ + this.retries = 5; + this.connected = false; + + /* timeout for method calls in milliseconds */ + this.timeouttime = 5000; + + /* default values for WebSocket */ + this.socketUrl = "ws://localhost:23000/vehicle"; + this.socketProtocol = "http-only"; + + /* override the websocket address if parameters are given */ + if(url !== undefined) this.socketUrl = url; + if(protocol !== undefined) this.socketProtocol = protocol; + + this.VehicleMethodCall = function(id, name, successCB, errorCB) + { + var me = this; + this.successCB = successCB; + this.errorCB = errorCB; + this.transactionid = id; + this.name = name; + this.done = false; + this.start = function() + { + me.timeout = setTimeout(function(){ + if(me.errorCB !== undefined) + { + me.errorCB("\""+me.name+"\" method timed out after "+self.timeouttime+"ms"); + } + me.finish(); + }, self.timeouttime); + } + this.finish = function() + { + if(me.timeout !== undefined) + { + clearTimeout(me.timeout); + } + me.done = true; + } + } + + function init() { + if ("WebSocket" in window) + { + if(self.socketProtocol.length > 0) + { + self.socket = new WebSocket(self.socketUrl, self.socketProtocol); + } + else + { + self.socket = new WebSocket(self.socketUrl); + } + self.socket.onopen = function() + { + self.connected = true; + self.iSuccessCB((self.retries < 5)?"(RECONNECTED)":""); + self.retries = 5; + }; + self.socket.onclose = function() + { + self.connected = false; + self.iErrorCB("socket closed "+((self.retries > 0)?"retrying in 5 seconds ...":"")); + if(self.retries > 0) + { + setTimeout(function(){ + self.retries--; + init(); + }, 5000); + } + }; + self.socket.onerror = function(e) + { + self.iErrorCB(e.data); + }; + self.socket.onmessage = function (e) + { + self.receive(e.data); + }; + } + else + { + console.log("This browser doesn't appear to support websockets!"); + } + } + init(); +} + +Vehicle.prototype.generateTransactionId = function() +{ + var i, val = []; + for(i = 0; i < 8; i++) + { + var num = Math.floor((Math.random()+1)*65536); + val[i] = num.toString(16).substring(1); + } + var uuid = val[0]+val[1]+"-"+ + val[2]+"-"+val[3]+"-"+val[4]+"-"+ + val[5]+val[6]+val[7]; + return uuid; +} + +Vehicle.prototype.send = function(obj, successCB, errorCB) +{ + if(!this.connected) + { + if(errorCB !== undefined) + { + errorCB("\""+obj.name+"\" method failed because socket is closed"); + } + return; + } + var i = this.methodIdx; + this.methodIdx = (this.methodIdx + 1)%100; + this.methodCalls[i] = new this.VehicleMethodCall(obj.transactionid, + obj.name, successCB, errorCB); + this.socket.send(JSON.stringify(obj)); + this.methodCalls[i].start(); +} + + +Vehicle.prototype.getSupportedEventTypes = function(type, writeable, successCB, errorCB) +{ + var obj = { + "type" : "method", + "name" : "getSupportedEventTypes", + "writeable" : writeable, + "transactionid" : this.generateTransactionId(), + "data" : type + }; + this.send(obj, successCB, errorCB); +} + +Vehicle.prototype.get = function(namelist, zone, successCB, errorCB) +{ + if(namelist.length <= 0) + { + return; + } + + var properties = []; + + for(var i = 0; i < namelist.length; i++) + { + properties[i] = {"property" : namelist[i], "zone" : zone}; + } + var obj = { + "type" : "method", + "name": "get", + "transactionid" : this.generateTransactionId(), + "data" : properties + }; + this.send(obj, successCB, errorCB); +} + +Vehicle.prototype.getHistory = function(event, startTime, endTime, successCB, errorCB) +{ + var obj = { + "type" : "method", + "name": "getHistory", + "transactionid" : this.generateTransactionId(), + "data" : [event, (startTime.getTime()/1000).toString(), (endTime.getTime()/1000).toString()] + }; + + this.send(obj, successCB, errorCB); + +} + +Vehicle.prototype.set = function(namelist, valuelist, zoneList, successCB, errorCB) +{ + if((namelist.length != valuelist.length)||(namelist.length <= 0)) + { + return; + } + + var obj = { + "type" : "method", + "name": "set", + "transactionid" : this.generateTransactionId(), + "data" : [] + }; + var list = []; + for(var i = 0; i < namelist.length; i++) + { + var val = {"property" : namelist[i], "value" : valuelist[i],"zone" : zoneList[i]}; + list[list.length] = val; + } + obj.data = list; + this.send(obj, successCB, errorCB); +} + +Vehicle.prototype.subscribe = function(namelist, zoneList, successCB, errorCB) +{ + var obj = { + "type" : "method", + "name": "subscribe", + "transactionid" : this.generateTransactionId(), + "data" : namelist, + "zone" : zoneList + }; + this.send(obj, successCB, errorCB); +} + +Vehicle.prototype.unsubscribe = function(namelist, zoneList, successCB, errorCB) +{ + var obj = { + "type" : "method", + "name": "unsubscribe", + "transactionid" : this.generateTransactionId(), + "data" : namelist, + "zone" : zoneList + }; + this.send(obj, successCB, errorCB); +} + +Vehicle.prototype.sendEvent = function(name, value) +{ + var evt = document.createEvent("Event"); + evt.initEvent(name, true, true); + evt.name = name; + evt.value = value; + document.dispatchEvent(evt); + console.log(evt); +} + +Vehicle.prototype.receive = function(msg) +{ + var self = this; + var event; + try { + event = JSON.parse(msg); + } + catch(e) { + self.iErrorCB("GARBAGE MESSAGE: "+msg); + return; + } + + if((event === undefined)||(event.type === undefined)|| + (event.name === undefined)) + { + self.iErrorCB("BADLY FORMED MESSAGE: "+msg); + return; + } + else + { + if(event.type === "methodReply") + { + var calls = this.methodCalls; + for(var i = 0; i < calls.length; i++) + { + var call = calls[i]; + if(call&&(!call.done)&&(call.transactionid === event.transactionid)) + { + call.finish(); + if(event.error !== undefined) + { + call.errorCB(event.error); + } + else if(event.data !== undefined && call.successCB !== undefined) + { + call.successCB(event.data); + } + return; + } + } + } + else if(event.type === "valuechanged") + { + self.sendEvent(event.name, event.data); + } + } +} + +/* + // AMD / RequireJS + if (typeof define !== 'undefined' && define.amd) { + define([], function () { + return { + Vehicle: Vehicle + }; + }); + } + // Node.js + else if (typeof module !== 'undefined' && module.exports) { + module.exports = { + Vehicle: Vehicle + }; + } + // included directly via