[15] | 1 | /* A simple javascript websockets client for the rosbridge v2.0 protocol. |
---|
| 2 | * |
---|
| 3 | * An AMD-compatible version of ros.js is provided as ros_amd.js |
---|
| 4 | * |
---|
| 5 | * A backwards-compatible ros.js is provided as ros_compatible.js |
---|
| 6 | * |
---|
| 7 | * Recommended practice is to use ros.js |
---|
| 8 | * |
---|
| 9 | * **/ |
---|
| 10 | |
---|
| 11 | var Bridge = function(url) { |
---|
| 12 | // Initialize internal variables |
---|
| 13 | this.service_handlers = {}; |
---|
| 14 | this.service_seed = 0; |
---|
| 15 | this.subscription_handlers = {}; |
---|
| 16 | |
---|
| 17 | // Ensure that JSON and WebSocket are available. Thrown an exception if not |
---|
| 18 | if (!WebSocket && MozWebSocket) { |
---|
| 19 | WebSocket = MozWebSocket; |
---|
| 20 | } |
---|
| 21 | if (!WebSocket) { |
---|
| 22 | throw "Browser does not support WebSockets"; |
---|
| 23 | } |
---|
| 24 | if (!JSON) { |
---|
| 25 | throw "Browser does not support JSON"; |
---|
| 26 | } |
---|
| 27 | |
---|
| 28 | // Create the connection |
---|
| 29 | this.socket = new WebSocket(url); |
---|
| 30 | |
---|
| 31 | var self = this |
---|
| 32 | this.socket.onmessage = function() { |
---|
| 33 | self.receiveMessage.apply(self, arguments); |
---|
| 34 | self.onMessage.apply(self, arguments); |
---|
| 35 | } |
---|
| 36 | this.socket.onerror = function() { |
---|
| 37 | self.onError.apply(self, arguments); |
---|
| 38 | } |
---|
| 39 | this.socket.onopen = function() { |
---|
| 40 | self.onOpen.apply(self, arguments); |
---|
| 41 | } |
---|
| 42 | this.socket.onclose = function() { |
---|
| 43 | self.onClose.apply(self, arguments); |
---|
| 44 | } |
---|
| 45 | } |
---|
| 46 | |
---|
| 47 | Bridge.prototype.send = function(op, id, msg) { |
---|
| 48 | msg.op = op; |
---|
| 49 | if (id != null) { |
---|
| 50 | msg.id = id; |
---|
| 51 | } |
---|
| 52 | this.socket.send(JSON.stringify(msg)); |
---|
| 53 | } |
---|
| 54 | |
---|
| 55 | Bridge.prototype.advertise = function(topic, type, /*optional*/ id) { |
---|
| 56 | this.send("advertise", id, {topic: topic, type: type}); |
---|
| 57 | } |
---|
| 58 | |
---|
| 59 | Bridge.prototype.unadvertise = function(topic, /*optional*/ id) { |
---|
| 60 | this.send("unadvertise", id, {topic: topic}); |
---|
| 61 | } |
---|
| 62 | |
---|
| 63 | Bridge.prototype.publish = function(topic, msg, /*optional*/ id) { |
---|
| 64 | this.send("publish", id, {topic: topic, msg: msg}); |
---|
| 65 | } |
---|
| 66 | |
---|
| 67 | Bridge.prototype.subscribe = function(callback, topic, /*optional*/ type, /*optional*/ throttle_rate, |
---|
| 68 | /*optional*/ queue_length, /*optional*/ fragment_size, /*optional*/ compression, /*optional*/ id) { |
---|
| 69 | // Construct the message |
---|
| 70 | msg = {topic: topic}; |
---|
| 71 | if (type != null) msg.type = type; |
---|
| 72 | if (throttle_rate != null) msg.throttle_rate = throttle_rate; |
---|
| 73 | if (queue_length != null) msg.queue_length = queue_length; |
---|
| 74 | if (fragment_size != null) msg.fragment_size = fragment_size; |
---|
| 75 | if (compression != null) msg.compression = compression; |
---|
| 76 | |
---|
| 77 | // Send the message |
---|
| 78 | this.send("subscribe", id, msg); |
---|
| 79 | |
---|
| 80 | // Save the callback |
---|
| 81 | if (this.subscription_handlers[topic] == null) { |
---|
| 82 | this.subscription_handlers[topic] = {}; |
---|
| 83 | } |
---|
| 84 | if (this.subscription_handlers[topic][id] == null) { |
---|
| 85 | this.subscription_handlers[topic][id] = []; |
---|
| 86 | } |
---|
| 87 | this.subscription_handlers[topic][id].push(callback); |
---|
| 88 | } |
---|
| 89 | |
---|
| 90 | Bridge.prototype.unsubscribe = function(topic, /*optional*/ id) { |
---|
| 91 | // Send the message |
---|
| 92 | this.send("unsubscribe", id, {topic: topic}); |
---|
| 93 | |
---|
| 94 | // Delete callbacks |
---|
| 95 | if (this.subscription_handlers[topic] && this.subscription_handlers[topic][id]) { |
---|
| 96 | delete this.subscription_handlers[topic][id]; |
---|
| 97 | } |
---|
| 98 | if (id==null || Object.keys(this.subscription_handlers[topic]).length == 0) { |
---|
| 99 | delete this.subscription_handlers[topic]; |
---|
| 100 | } |
---|
| 101 | } |
---|
| 102 | |
---|
| 103 | Bridge.prototype.callService = function(callback, service, /*optional*/ args, |
---|
| 104 | /*optional*/ fragment_size, /*optional*/ compression, /*optional*/ id) { |
---|
| 105 | // Construct the message |
---|
| 106 | msg = {service: service}; |
---|
| 107 | if (args != null) msg.args = args; |
---|
| 108 | if (fragment_size != null) msg.fragment_size = fragment_size; |
---|
| 109 | if (compression != null) msg.compression = compression; |
---|
| 110 | |
---|
| 111 | // Generate an ID for service calls |
---|
| 112 | if (id == null) { |
---|
| 113 | id = this.service_seed; |
---|
| 114 | this.service_seed++; |
---|
| 115 | } |
---|
| 116 | |
---|
| 117 | // Send the message |
---|
| 118 | this.send("call_service", id, msg); |
---|
| 119 | |
---|
| 120 | // Save the callback |
---|
| 121 | if (this.service_handlers[service] == null) { |
---|
| 122 | this.service_handlers[service] = {}; |
---|
| 123 | } |
---|
| 124 | this.service_handlers[service][id] = callback; |
---|
| 125 | } |
---|
| 126 | |
---|
| 127 | Bridge.prototype.receiveMessage = function(event) { |
---|
| 128 | msg = JSON.parse(event.data); |
---|
| 129 | |
---|
| 130 | switch(msg.op) { |
---|
| 131 | case "publish": this.onPublish(msg); break; |
---|
| 132 | case "service_response": this.onServiceResponse(msg); break; |
---|
| 133 | } |
---|
| 134 | } |
---|
| 135 | |
---|
| 136 | Bridge.prototype.onOpen = function(event) {} |
---|
| 137 | Bridge.prototype.onClose = function(event) {} |
---|
| 138 | Bridge.prototype.onError = function(event) {} |
---|
| 139 | Bridge.prototype.onMessage = function(event) {} |
---|
| 140 | |
---|
| 141 | Bridge.prototype.onPublish = function(message) { |
---|
| 142 | // Extract message details |
---|
| 143 | topic = message.topic; |
---|
| 144 | msg = message.msg; |
---|
| 145 | |
---|
| 146 | // Copy the callbacks - in case the callback modifies the subscription |
---|
| 147 | var callbacks = []; |
---|
| 148 | for (var id in this.subscription_handlers[topic]) { |
---|
| 149 | callbacks = callbacks.concat(this.subscription_handlers[topic][id]); |
---|
| 150 | } |
---|
| 151 | |
---|
| 152 | // Call all the callbacks |
---|
| 153 | for (var i = 0; i < callbacks.length; i++) { |
---|
| 154 | try { |
---|
| 155 | callbacks[i](msg); |
---|
| 156 | } catch (err) { |
---|
| 157 | // Best we can do is print the error |
---|
| 158 | console.error(err); |
---|
| 159 | } |
---|
| 160 | } |
---|
| 161 | } |
---|
| 162 | |
---|
| 163 | Bridge.prototype.onServiceResponse = function(response) { |
---|
| 164 | // Extract message details |
---|
| 165 | service = response.service; |
---|
| 166 | values = response.values; |
---|
| 167 | id = response.id; |
---|
| 168 | |
---|
| 169 | // Call the callback and remove it |
---|
| 170 | if (this.service_handlers[service] && this.service_handlers[service][id]) { |
---|
| 171 | callback = this.service_handlers[service][id]; |
---|
| 172 | delete this.service_handlers[service][id]; |
---|
| 173 | if (Object.keys(this.service_handlers[service]).length == 0) { |
---|
| 174 | delete this.service_handlers[service]; |
---|
| 175 | } |
---|
| 176 | callback(values); |
---|
| 177 | } |
---|
| 178 | } |
---|
| 179 | |
---|
| 180 | var ros = ros || {}; |
---|
| 181 | ros.Bridge = Bridge; |
---|