diff --git a/backend/js/ros_scripts.js b/backend/js/ros_scripts.js index 86750f8..59078dc 100644 --- a/backend/js/ros_scripts.js +++ b/backend/js/ros_scripts.js @@ -30,7 +30,7 @@ ros.on('connection', function() { document.getElementById("ConnectionIPInput").value = ros.connectionName; document.getElementById("ConnectionIPLabel").innerHTML = 'Connection made at:'; document.getElementById("rosbridgeconnection_badge").innerHTML = 'Connected'; - document.getElementById("ConnectionButton").className = "btn btn-success" + document.getElementById("ConnectionButton").className = "btn btn-success"; ros.nodes = Array(); ros.topics = Array(); @@ -48,7 +48,7 @@ ros.on('close', function() { ros.topics = Array(); }); // Create a connection to the rosbridge WebSocket server. -ros.connect(ros.connectionName); +//ros.connect(ros.connectionName); /////////////////////////////////////////////////////////////////////////////////// diff --git a/backend/js/update_guis.js b/backend/js/update_guis.js index ae3549d..747b502 100644 --- a/backend/js/update_guis.js +++ b/backend/js/update_guis.js @@ -66,7 +66,6 @@ function updateTopicsGUI() }); var topics = ros.topics; - topics.sort() if(topics != null){ var innerHTML = ""; diff --git a/coordinator/coordinator.html b/coordinator/coordinator.html index cffdcda..75e0190 100644 --- a/coordinator/coordinator.html +++ b/coordinator/coordinator.html @@ -8,7 +8,7 @@ Teaching Mode - Execution Mode + Massage Mode Standby Mode diff --git a/include/js/roslib.js b/include/js/roslib.js index 645e59f..b7ce072 100644 --- a/include/js/roslib.js +++ b/include/js/roslib.js @@ -1,43 +1,1564 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +(function(global, undefined) { "use strict"; +var POW_2_24 = Math.pow(2, -24), + POW_2_32 = Math.pow(2, 32), + POW_2_53 = Math.pow(2, 53); + +function encode(value) { + var data = new ArrayBuffer(256); + var dataView = new DataView(data); + var lastLength; + var offset = 0; + + function ensureSpace(length) { + var newByteLength = data.byteLength; + var requiredLength = offset + length; + while (newByteLength < requiredLength) + newByteLength *= 2; + if (newByteLength !== data.byteLength) { + var oldDataView = dataView; + data = new ArrayBuffer(newByteLength); + dataView = new DataView(data); + var uint32count = (offset + 3) >> 2; + for (var i = 0; i < uint32count; ++i) + dataView.setUint32(i * 4, oldDataView.getUint32(i * 4)); + } + + lastLength = length; + return dataView; + } + function write() { + offset += lastLength; + } + function writeFloat64(value) { + write(ensureSpace(8).setFloat64(offset, value)); + } + function writeUint8(value) { + write(ensureSpace(1).setUint8(offset, value)); + } + function writeUint8Array(value) { + var dataView = ensureSpace(value.length); + for (var i = 0; i < value.length; ++i) + dataView.setUint8(offset + i, value[i]); + write(); + } + function writeUint16(value) { + write(ensureSpace(2).setUint16(offset, value)); + } + function writeUint32(value) { + write(ensureSpace(4).setUint32(offset, value)); + } + function writeUint64(value) { + var low = value % POW_2_32; + var high = (value - low) / POW_2_32; + var dataView = ensureSpace(8); + dataView.setUint32(offset, high); + dataView.setUint32(offset + 4, low); + write(); + } + function writeTypeAndLength(type, length) { + if (length < 24) { + writeUint8(type << 5 | length); + } else if (length < 0x100) { + writeUint8(type << 5 | 24); + writeUint8(length); + } else if (length < 0x10000) { + writeUint8(type << 5 | 25); + writeUint16(length); + } else if (length < 0x100000000) { + writeUint8(type << 5 | 26); + writeUint32(length); + } else { + writeUint8(type << 5 | 27); + writeUint64(length); + } + } + + function encodeItem(value) { + var i; + + if (value === false) + return writeUint8(0xf4); + if (value === true) + return writeUint8(0xf5); + if (value === null) + return writeUint8(0xf6); + if (value === undefined) + return writeUint8(0xf7); + + switch (typeof value) { + case "number": + if (Math.floor(value) === value) { + if (0 <= value && value <= POW_2_53) + return writeTypeAndLength(0, value); + if (-POW_2_53 <= value && value < 0) + return writeTypeAndLength(1, -(value + 1)); + } + writeUint8(0xfb); + return writeFloat64(value); + + case "string": + var utf8data = []; + for (i = 0; i < value.length; ++i) { + var charCode = value.charCodeAt(i); + if (charCode < 0x80) { + utf8data.push(charCode); + } else if (charCode < 0x800) { + utf8data.push(0xc0 | charCode >> 6); + utf8data.push(0x80 | charCode & 0x3f); + } else if (charCode < 0xd800) { + utf8data.push(0xe0 | charCode >> 12); + utf8data.push(0x80 | (charCode >> 6) & 0x3f); + utf8data.push(0x80 | charCode & 0x3f); + } else { + charCode = (charCode & 0x3ff) << 10; + charCode |= value.charCodeAt(++i) & 0x3ff; + charCode += 0x10000; + + utf8data.push(0xf0 | charCode >> 18); + utf8data.push(0x80 | (charCode >> 12) & 0x3f); + utf8data.push(0x80 | (charCode >> 6) & 0x3f); + utf8data.push(0x80 | charCode & 0x3f); + } + } + + writeTypeAndLength(3, utf8data.length); + return writeUint8Array(utf8data); + + default: + var length; + if (Array.isArray(value)) { + length = value.length; + writeTypeAndLength(4, length); + for (i = 0; i < length; ++i) + encodeItem(value[i]); + } else if (value instanceof Uint8Array) { + writeTypeAndLength(2, value.length); + writeUint8Array(value); + } else { + var keys = Object.keys(value); + length = keys.length; + writeTypeAndLength(5, length); + for (i = 0; i < length; ++i) { + var key = keys[i]; + encodeItem(key); + encodeItem(value[key]); + } + } + } + } + + encodeItem(value); + + if ("slice" in data) + return data.slice(0, offset); + + var ret = new ArrayBuffer(offset); + var retView = new DataView(ret); + for (var i = 0; i < offset; ++i) + retView.setUint8(i, dataView.getUint8(i)); + return ret; +} + +function decode(data, tagger, simpleValue) { + var dataView = new DataView(data); + var offset = 0; + + if (typeof tagger !== "function") + tagger = function(value) { return value; }; + if (typeof simpleValue !== "function") + simpleValue = function() { return undefined; }; + + function read(value, length) { + offset += length; + return value; + } + function readArrayBuffer(length) { + return read(new Uint8Array(data, offset, length), length); + } + function readFloat16() { + var tempArrayBuffer = new ArrayBuffer(4); + var tempDataView = new DataView(tempArrayBuffer); + var value = readUint16(); + + var sign = value & 0x8000; + var exponent = value & 0x7c00; + var fraction = value & 0x03ff; + + if (exponent === 0x7c00) + exponent = 0xff << 10; + else if (exponent !== 0) + exponent += (127 - 15) << 10; + else if (fraction !== 0) + return fraction * POW_2_24; + + tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13); + return tempDataView.getFloat32(0); + } + function readFloat32() { + return read(dataView.getFloat32(offset), 4); + } + function readFloat64() { + return read(dataView.getFloat64(offset), 8); + } + function readUint8() { + return read(dataView.getUint8(offset), 1); + } + function readUint16() { + return read(dataView.getUint16(offset), 2); + } + function readUint32() { + return read(dataView.getUint32(offset), 4); + } + function readUint64() { + return readUint32() * POW_2_32 + readUint32(); + } + function readBreak() { + if (dataView.getUint8(offset) !== 0xff) + return false; + offset += 1; + return true; + } + function readLength(additionalInformation) { + if (additionalInformation < 24) + return additionalInformation; + if (additionalInformation === 24) + return readUint8(); + if (additionalInformation === 25) + return readUint16(); + if (additionalInformation === 26) + return readUint32(); + if (additionalInformation === 27) + return readUint64(); + if (additionalInformation === 31) + return -1; + throw "Invalid length encoding"; + } + function readIndefiniteStringLength(majorType) { + var initialByte = readUint8(); + if (initialByte === 0xff) + return -1; + var length = readLength(initialByte & 0x1f); + if (length < 0 || (initialByte >> 5) !== majorType) + throw "Invalid indefinite length element"; + return length; + } + + function appendUtf16data(utf16data, length) { + for (var i = 0; i < length; ++i) { + var value = readUint8(); + if (value & 0x80) { + if (value < 0xe0) { + value = (value & 0x1f) << 6 + | (readUint8() & 0x3f); + length -= 1; + } else if (value < 0xf0) { + value = (value & 0x0f) << 12 + | (readUint8() & 0x3f) << 6 + | (readUint8() & 0x3f); + length -= 2; + } else { + value = (value & 0x0f) << 18 + | (readUint8() & 0x3f) << 12 + | (readUint8() & 0x3f) << 6 + | (readUint8() & 0x3f); + length -= 3; + } + } + + if (value < 0x10000) { + utf16data.push(value); + } else { + value -= 0x10000; + utf16data.push(0xd800 | (value >> 10)); + utf16data.push(0xdc00 | (value & 0x3ff)); + } + } + } + + function decodeItem() { + var initialByte = readUint8(); + var majorType = initialByte >> 5; + var additionalInformation = initialByte & 0x1f; + var i; + var length; + + if (majorType === 7) { + switch (additionalInformation) { + case 25: + return readFloat16(); + case 26: + return readFloat32(); + case 27: + return readFloat64(); + } + } + + length = readLength(additionalInformation); + if (length < 0 && (majorType < 2 || 6 < majorType)) + throw "Invalid length"; + + switch (majorType) { + case 0: + return length; + case 1: + return -1 - length; + case 2: + if (length < 0) { + var elements = []; + var fullArrayLength = 0; + while ((length = readIndefiniteStringLength(majorType)) >= 0) { + fullArrayLength += length; + elements.push(readArrayBuffer(length)); + } + var fullArray = new Uint8Array(fullArrayLength); + var fullArrayOffset = 0; + for (i = 0; i < elements.length; ++i) { + fullArray.set(elements[i], fullArrayOffset); + fullArrayOffset += elements[i].length; + } + return fullArray; + } + return readArrayBuffer(length); + case 3: + var utf16data = []; + if (length < 0) { + while ((length = readIndefiniteStringLength(majorType)) >= 0) + appendUtf16data(utf16data, length); + } else + appendUtf16data(utf16data, length); + return String.fromCharCode.apply(null, utf16data); + case 4: + var retArray; + if (length < 0) { + retArray = []; + while (!readBreak()) + retArray.push(decodeItem()); + } else { + retArray = new Array(length); + for (i = 0; i < length; ++i) + retArray[i] = decodeItem(); + } + return retArray; + case 5: + var retObject = {}; + for (i = 0; i < length || length < 0 && !readBreak(); ++i) { + var key = decodeItem(); + retObject[key] = decodeItem(); + } + return retObject; + case 6: + return tagger(decodeItem(), length); + case 7: + switch (length) { + case 20: + return false; + case 21: + return true; + case 22: + return null; + case 23: + return undefined; + default: + return simpleValue(length); + } + } + } + + var ret = decodeItem(); + if (offset !== data.byteLength) + throw "Remaining bytes"; + return ret; +} + +var obj = { encode: encode, decode: decode }; + +if (typeof define === "function" && define.amd) + define("cbor/cbor", obj); +else if (typeof module !== 'undefined' && module.exports) + module.exports = obj; +else if (!global.CBOR) + global.CBOR = obj; + +})(this); -function ToObject(val) { - if (val == null) { +},{}],2:[function(require,module,exports){ +(function (process){ +/*! + * EventEmitter2 + * https://github.com/hij1nx/EventEmitter2 + * + * Copyright (c) 2013 hij1nx + * Licensed under the MIT license. + */ +;!function(undefined) { + + var isArray = Array.isArray ? Array.isArray : function _isArray(obj) { + return Object.prototype.toString.call(obj) === "[object Array]"; + }; + var defaultMaxListeners = 10; + + function init() { + this._events = {}; + if (this._conf) { + configure.call(this, this._conf); + } + } + + function configure(conf) { + if (conf) { + this._conf = conf; + + conf.delimiter && (this.delimiter = conf.delimiter); + this._maxListeners = conf.maxListeners !== undefined ? conf.maxListeners : defaultMaxListeners; + + conf.wildcard && (this.wildcard = conf.wildcard); + conf.newListener && (this.newListener = conf.newListener); + conf.verboseMemoryLeak && (this.verboseMemoryLeak = conf.verboseMemoryLeak); + + if (this.wildcard) { + this.listenerTree = {}; + } + } else { + this._maxListeners = defaultMaxListeners; + } + } + + function logPossibleMemoryLeak(count, eventName) { + var errorMsg = '(node) warning: possible EventEmitter memory ' + + 'leak detected. ' + count + ' listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.'; + + if(this.verboseMemoryLeak){ + errorMsg += ' Event name: ' + eventName + '.'; + } + + if(typeof process !== 'undefined' && process.emitWarning){ + var e = new Error(errorMsg); + e.name = 'MaxListenersExceededWarning'; + e.emitter = this; + e.count = count; + process.emitWarning(e); + } else { + console.error(errorMsg); + + if (console.trace){ + console.trace(); + } + } + } + + function EventEmitter(conf) { + this._events = {}; + this.newListener = false; + this.verboseMemoryLeak = false; + configure.call(this, conf); + } + EventEmitter.EventEmitter2 = EventEmitter; // backwards compatibility for exporting EventEmitter property + + // + // Attention, function return type now is array, always ! + // It has zero elements if no any matches found and one or more + // elements (leafs) if there are matches + // + function searchListenerTree(handlers, type, tree, i) { + if (!tree) { + return []; + } + var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached, + typeLength = type.length, currentType = type[i], nextType = type[i+1]; + if (i === typeLength && tree._listeners) { + // + // If at the end of the event(s) list and the tree has listeners + // invoke those listeners. + // + if (typeof tree._listeners === 'function') { + handlers && handlers.push(tree._listeners); + return [tree]; + } else { + for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) { + handlers && handlers.push(tree._listeners[leaf]); + } + return [tree]; + } + } + + if ((currentType === '*' || currentType === '**') || tree[currentType]) { + // + // If the event emitted is '*' at this part + // or there is a concrete match at this patch + // + if (currentType === '*') { + for (branch in tree) { + if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { + listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1)); + } + } + return listeners; + } else if(currentType === '**') { + endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*')); + if(endReached && tree._listeners) { + // The next element has a _listeners, add it to the handlers. + listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength)); + } + + for (branch in tree) { + if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { + if(branch === '*' || branch === '**') { + if(tree[branch]._listeners && !endReached) { + listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength)); + } + listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); + } else if(branch === nextType) { + listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2)); + } else { + // No match on this one, shift into the tree but not in the type array. + listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); + } + } + } + return listeners; + } + + listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1)); + } + + xTree = tree['*']; + if (xTree) { + // + // If the listener tree will allow any match for this part, + // then recursively explore all branches of the tree + // + searchListenerTree(handlers, type, xTree, i+1); + } + + xxTree = tree['**']; + if(xxTree) { + if(i < typeLength) { + if(xxTree._listeners) { + // If we have a listener on a '**', it will catch all, so add its handler. + searchListenerTree(handlers, type, xxTree, typeLength); + } + + // Build arrays of matching next branches and others. + for(branch in xxTree) { + if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) { + if(branch === nextType) { + // We know the next element will match, so jump twice. + searchListenerTree(handlers, type, xxTree[branch], i+2); + } else if(branch === currentType) { + // Current node matches, move into the tree. + searchListenerTree(handlers, type, xxTree[branch], i+1); + } else { + isolatedBranch = {}; + isolatedBranch[branch] = xxTree[branch]; + searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1); + } + } + } + } else if(xxTree._listeners) { + // We have reached the end and still on a '**' + searchListenerTree(handlers, type, xxTree, typeLength); + } else if(xxTree['*'] && xxTree['*']._listeners) { + searchListenerTree(handlers, type, xxTree['*'], typeLength); + } + } + + return listeners; + } + + function growListenerTree(type, listener) { + + type = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); + + // + // Looks for two consecutive '**', if so, don't add the event at all. + // + for(var i = 0, len = type.length; i+1 < len; i++) { + if(type[i] === '**' && type[i+1] === '**') { + return; + } + } + + var tree = this.listenerTree; + var name = type.shift(); + + while (name !== undefined) { + + if (!tree[name]) { + tree[name] = {}; + } + + tree = tree[name]; + + if (type.length === 0) { + + if (!tree._listeners) { + tree._listeners = listener; + } + else { + if (typeof tree._listeners === 'function') { + tree._listeners = [tree._listeners]; + } + + tree._listeners.push(listener); + + if ( + !tree._listeners.warned && + this._maxListeners > 0 && + tree._listeners.length > this._maxListeners + ) { + tree._listeners.warned = true; + logPossibleMemoryLeak.call(this, tree._listeners.length, name); + } + } + return true; + } + name = type.shift(); + } + return true; + } + + // By default EventEmitters will print a warning if more than + // 10 listeners are added to it. This is a useful default which + // helps finding memory leaks. + // + // Obviously not all Emitters should be limited to 10. This function allows + // that to be increased. Set to zero for unlimited. + + EventEmitter.prototype.delimiter = '.'; + + EventEmitter.prototype.setMaxListeners = function(n) { + if (n !== undefined) { + this._maxListeners = n; + if (!this._conf) this._conf = {}; + this._conf.maxListeners = n; + } + }; + + EventEmitter.prototype.event = ''; + + + EventEmitter.prototype.once = function(event, fn) { + return this._once(event, fn, false); + }; + + EventEmitter.prototype.prependOnceListener = function(event, fn) { + return this._once(event, fn, true); + }; + + EventEmitter.prototype._once = function(event, fn, prepend) { + this._many(event, 1, fn, prepend); + return this; + }; + + EventEmitter.prototype.many = function(event, ttl, fn) { + return this._many(event, ttl, fn, false); + } + + EventEmitter.prototype.prependMany = function(event, ttl, fn) { + return this._many(event, ttl, fn, true); + } + + EventEmitter.prototype._many = function(event, ttl, fn, prepend) { + var self = this; + + if (typeof fn !== 'function') { + throw new Error('many only accepts instances of Function'); + } + + function listener() { + if (--ttl === 0) { + self.off(event, listener); + } + return fn.apply(this, arguments); + } + + listener._origin = fn; + + this._on(event, listener, prepend); + + return self; + }; + + EventEmitter.prototype.emit = function() { + + this._events || init.call(this); + + var type = arguments[0]; + + if (type === 'newListener' && !this.newListener) { + if (!this._events.newListener) { + return false; + } + } + + var al = arguments.length; + var args,l,i,j; + var handler; + + if (this._all && this._all.length) { + handler = this._all.slice(); + if (al > 3) { + args = new Array(al); + for (j = 0; j < al; j++) args[j] = arguments[j]; + } + + for (i = 0, l = handler.length; i < l; i++) { + this.event = type; + switch (al) { + case 1: + handler[i].call(this, type); + break; + case 2: + handler[i].call(this, type, arguments[1]); + break; + case 3: + handler[i].call(this, type, arguments[1], arguments[2]); + break; + default: + handler[i].apply(this, args); + } + } + } + + if (this.wildcard) { + handler = []; + var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); + searchListenerTree.call(this, handler, ns, this.listenerTree, 0); + } else { + handler = this._events[type]; + if (typeof handler === 'function') { + this.event = type; + switch (al) { + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + default: + args = new Array(al - 1); + for (j = 1; j < al; j++) args[j - 1] = arguments[j]; + handler.apply(this, args); + } + return true; + } else if (handler) { + // need to make copy of handlers because list can change in the middle + // of emit call + handler = handler.slice(); + } + } + + if (handler && handler.length) { + if (al > 3) { + args = new Array(al - 1); + for (j = 1; j < al; j++) args[j - 1] = arguments[j]; + } + for (i = 0, l = handler.length; i < l; i++) { + this.event = type; + switch (al) { + case 1: + handler[i].call(this); + break; + case 2: + handler[i].call(this, arguments[1]); + break; + case 3: + handler[i].call(this, arguments[1], arguments[2]); + break; + default: + handler[i].apply(this, args); + } + } + return true; + } else if (!this._all && type === 'error') { + if (arguments[1] instanceof Error) { + throw arguments[1]; // Unhandled 'error' event + } else { + throw new Error("Uncaught, unspecified 'error' event."); + } + return false; + } + + return !!this._all; + }; + + EventEmitter.prototype.emitAsync = function() { + + this._events || init.call(this); + + var type = arguments[0]; + + if (type === 'newListener' && !this.newListener) { + if (!this._events.newListener) { return Promise.resolve([false]); } + } + + var promises= []; + + var al = arguments.length; + var args,l,i,j; + var handler; + + if (this._all) { + if (al > 3) { + args = new Array(al); + for (j = 1; j < al; j++) args[j] = arguments[j]; + } + for (i = 0, l = this._all.length; i < l; i++) { + this.event = type; + switch (al) { + case 1: + promises.push(this._all[i].call(this, type)); + break; + case 2: + promises.push(this._all[i].call(this, type, arguments[1])); + break; + case 3: + promises.push(this._all[i].call(this, type, arguments[1], arguments[2])); + break; + default: + promises.push(this._all[i].apply(this, args)); + } + } + } + + if (this.wildcard) { + handler = []; + var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); + searchListenerTree.call(this, handler, ns, this.listenerTree, 0); + } else { + handler = this._events[type]; + } + + if (typeof handler === 'function') { + this.event = type; + switch (al) { + case 1: + promises.push(handler.call(this)); + break; + case 2: + promises.push(handler.call(this, arguments[1])); + break; + case 3: + promises.push(handler.call(this, arguments[1], arguments[2])); + break; + default: + args = new Array(al - 1); + for (j = 1; j < al; j++) args[j - 1] = arguments[j]; + promises.push(handler.apply(this, args)); + } + } else if (handler && handler.length) { + handler = handler.slice(); + if (al > 3) { + args = new Array(al - 1); + for (j = 1; j < al; j++) args[j - 1] = arguments[j]; + } + for (i = 0, l = handler.length; i < l; i++) { + this.event = type; + switch (al) { + case 1: + promises.push(handler[i].call(this)); + break; + case 2: + promises.push(handler[i].call(this, arguments[1])); + break; + case 3: + promises.push(handler[i].call(this, arguments[1], arguments[2])); + break; + default: + promises.push(handler[i].apply(this, args)); + } + } + } else if (!this._all && type === 'error') { + if (arguments[1] instanceof Error) { + return Promise.reject(arguments[1]); // Unhandled 'error' event + } else { + return Promise.reject("Uncaught, unspecified 'error' event."); + } + } + + return Promise.all(promises); + }; + + EventEmitter.prototype.on = function(type, listener) { + return this._on(type, listener, false); + }; + + EventEmitter.prototype.prependListener = function(type, listener) { + return this._on(type, listener, true); + }; + + EventEmitter.prototype.onAny = function(fn) { + return this._onAny(fn, false); + }; + + EventEmitter.prototype.prependAny = function(fn) { + return this._onAny(fn, true); + }; + + EventEmitter.prototype.addListener = EventEmitter.prototype.on; + + EventEmitter.prototype._onAny = function(fn, prepend){ + if (typeof fn !== 'function') { + throw new Error('onAny only accepts instances of Function'); + } + + if (!this._all) { + this._all = []; + } + + // Add the function to the event listener collection. + if(prepend){ + this._all.unshift(fn); + }else{ + this._all.push(fn); + } + + return this; + } + + EventEmitter.prototype._on = function(type, listener, prepend) { + if (typeof type === 'function') { + this._onAny(type, listener); + return this; + } + + if (typeof listener !== 'function') { + throw new Error('on only accepts instances of Function'); + } + this._events || init.call(this); + + // To avoid recursion in the case that type == "newListeners"! Before + // adding it to the listeners, first emit "newListeners". + this.emit('newListener', type, listener); + + if (this.wildcard) { + growListenerTree.call(this, type, listener); + return this; + } + + if (!this._events[type]) { + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + } + else { + if (typeof this._events[type] === 'function') { + // Change to array. + this._events[type] = [this._events[type]]; + } + + // If we've already got an array, just add + if(prepend){ + this._events[type].unshift(listener); + }else{ + this._events[type].push(listener); + } + + // Check for listener leak + if ( + !this._events[type].warned && + this._maxListeners > 0 && + this._events[type].length > this._maxListeners + ) { + this._events[type].warned = true; + logPossibleMemoryLeak.call(this, this._events[type].length, type); + } + } + + return this; + } + + EventEmitter.prototype.off = function(type, listener) { + if (typeof listener !== 'function') { + throw new Error('removeListener only takes instances of Function'); + } + + var handlers,leafs=[]; + + if(this.wildcard) { + var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); + leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); + } + else { + // does not use listeners(), so no side effect of creating _events[type] + if (!this._events[type]) return this; + handlers = this._events[type]; + leafs.push({_listeners:handlers}); + } + + for (var iLeaf=0; iLeaf 0) { + recursivelyGarbageCollect(root[key]); + } + if (Object.keys(obj).length === 0) { + delete root[key]; + } + } + } + recursivelyGarbageCollect(this.listenerTree); + + return this; + }; + + EventEmitter.prototype.offAny = function(fn) { + var i = 0, l = 0, fns; + if (fn && this._all && this._all.length > 0) { + fns = this._all; + for(i = 0, l = fns.length; i < l; i++) { + if(fn === fns[i]) { + fns.splice(i, 1); + this.emit("removeListenerAny", fn); + return this; + } + } + } else { + fns = this._all; + for(i = 0, l = fns.length; i < l; i++) + this.emit("removeListenerAny", fns[i]); + this._all = []; + } + return this; + }; + + EventEmitter.prototype.removeListener = EventEmitter.prototype.off; + + EventEmitter.prototype.removeAllListeners = function(type) { + if (arguments.length === 0) { + !this._events || init.call(this); + return this; + } + + if (this.wildcard) { + var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); + var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); + + for (var iLeaf=0; iLeaf 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],5:[function(require,module,exports){ +var bundleFn = arguments[3]; +var sources = arguments[4]; +var cache = arguments[5]; + +var stringify = JSON.stringify; + +module.exports = function (fn, options) { + var wkey; + var cacheKeys = Object.keys(cache); + + for (var i = 0, l = cacheKeys.length; i < l; i++) { + var key = cacheKeys[i]; + var exp = cache[key].exports; + // Using babel as a transpiler to use esmodule, the export will always + // be an object with the default export as a property of it. To ensure + // the existing api and babel esmodule exports are both supported we + // check for both + if (exp === fn || exp && exp.default === fn) { + wkey = key; + break; + } + } + + if (!wkey) { + wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16); + var wcache = {}; + for (var i = 0, l = cacheKeys.length; i < l; i++) { + var key = cacheKeys[i]; + wcache[key] = key; + } + sources[wkey] = [ + 'function(require,module,exports){' + fn + '(self); }', + wcache + ]; + } + var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16); + + var scache = {}; scache[wkey] = wkey; + sources[skey] = [ + 'function(require,module,exports){' + + // try to call default if defined to also support babel esmodule exports + 'var f = require(' + stringify(wkey) + ');' + + '(f.default ? f.default : f)(self);' + + '}', + scache + ]; + + var workerSources = {}; + resolveSources(skey); + + function resolveSources(key) { + workerSources[key] = true; + + for (var depPath in sources[key][1]) { + var depKey = sources[key][1][depPath]; + if (!workerSources[depKey]) { + resolveSources(depKey); + } + } + } + + var src = '(' + bundleFn + ')({' + + Object.keys(workerSources).map(function (key) { + return stringify(key) + ':[' + + sources[key][0] + + ',' + stringify(sources[key][1]) + ']' + ; + }).join(',') + + '},{},[' + stringify(skey) + '])' + ; + + var URL = window.URL || window.webkitURL || window.mozURL || window.msURL; + + var blob = new Blob([src], { type: 'text/javascript' }); + if (options && options.bare) { return blob; } + var workerUrl = URL.createObjectURL(blob); + var worker = new Worker(workerUrl); + worker.objectURL = workerUrl; + return worker; +}; + +},{}],6:[function(require,module,exports){ /** + * @fileOverview * @author Russell Toris - rctoris@wpi.edu */ +/** + * If you use roslib in a browser, all the classes will be exported to a global variable called ROSLIB. + * + * If you use nodejs, this is the variable you get when you require('roslib') + */ var ROSLIB = this.ROSLIB || { - REVISION : '0.15.0' + REVISION : '1.0.1' }; var assign = require('object-assign'); @@ -55,18 +1576,19 @@ assign(ROSLIB, require('./urdf')); module.exports = ROSLIB; -},{"./actionlib":8,"./core":17,"./math":22,"./tf":25,"./urdf":37,"object-assign":1}],4:[function(require,module,exports){ +},{"./actionlib":12,"./core":21,"./math":26,"./tf":29,"./urdf":41,"object-assign":3}],7:[function(require,module,exports){ (function (global){ global.ROSLIB = require('./RosLib'); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./RosLib":3}],5:[function(require,module,exports){ +},{"./RosLib":6}],8:[function(require,module,exports){ /** + * @fileOverview * @author Russell Toris - rctoris@wpi.edu */ var Topic = require('../core/Topic'); var Message = require('../core/Message'); -var EventEmitter2 = require('./../util/shim/EventEmitter2.js').EventEmitter2; +var EventEmitter2 = require('eventemitter2').EventEmitter2; /** * An actionlib action client. @@ -91,25 +1613,28 @@ function ActionClient(options) { this.serverName = options.serverName; this.actionName = options.actionName; this.timeout = options.timeout; + this.omitFeedback = options.omitFeedback; + this.omitStatus = options.omitStatus; + this.omitResult = options.omitResult; this.goals = {}; // flag to check if a status has been received var receivedStatus = false; // create the topics associated with actionlib - var feedbackListener = new Topic({ + this.feedbackListener = new Topic({ ros : this.ros, name : this.serverName + '/feedback', messageType : this.actionName + 'Feedback' }); - var statusListener = new Topic({ + this.statusListener = new Topic({ ros : this.ros, name : this.serverName + '/status', messageType : 'actionlib_msgs/GoalStatusArray' }); - var resultListener = new Topic({ + this.resultListener = new Topic({ ros : this.ros, name : this.serverName + '/result', messageType : this.actionName + 'Result' @@ -132,34 +1657,40 @@ function ActionClient(options) { this.cancelTopic.advertise(); // subscribe to the status topic - statusListener.subscribe(function(statusMessage) { - receivedStatus = true; - statusMessage.status_list.forEach(function(status) { - var goal = that.goals[status.goal_id.id]; - if (goal) { - goal.emit('status', status); - } + if (!this.omitStatus) { + this.statusListener.subscribe(function(statusMessage) { + receivedStatus = true; + statusMessage.status_list.forEach(function(status) { + var goal = that.goals[status.goal_id.id]; + if (goal) { + goal.emit('status', status); + } + }); }); - }); + } // subscribe the the feedback topic - feedbackListener.subscribe(function(feedbackMessage) { - var goal = that.goals[feedbackMessage.status.goal_id.id]; - if (goal) { - goal.emit('status', feedbackMessage.status); - goal.emit('feedback', feedbackMessage.feedback); - } - }); + if (!this.omitFeedback) { + this.feedbackListener.subscribe(function(feedbackMessage) { + var goal = that.goals[feedbackMessage.status.goal_id.id]; + if (goal) { + goal.emit('status', feedbackMessage.status); + goal.emit('feedback', feedbackMessage.feedback); + } + }); + } // subscribe to the result topic - resultListener.subscribe(function(resultMessage) { - var goal = that.goals[resultMessage.status.goal_id.id]; + if (!this.omitResult) { + this.resultListener.subscribe(function(resultMessage) { + var goal = that.goals[resultMessage.status.goal_id.id]; - if (goal) { - goal.emit('status', resultMessage.status); - goal.emit('result', resultMessage.result); - } - }); + if (goal) { + goal.emit('status', resultMessage.status); + goal.emit('result', resultMessage.result); + } + }); + } // If timeout specified, emit a 'timeout' event if the action server does not respond if (this.timeout) { @@ -181,14 +1712,116 @@ ActionClient.prototype.cancel = function() { this.cancelTopic.publish(cancelMessage); }; -module.exports = ActionClient; -},{"../core/Message":9,"../core/Topic":16,"./../util/shim/EventEmitter2.js":38}],6:[function(require,module,exports){ /** + * Unsubscribe and unadvertise all topics associated with this ActionClient. + */ +ActionClient.prototype.dispose = function() { + this.goalTopic.unadvertise(); + this.cancelTopic.unadvertise(); + if (!this.omitStatus) {this.statusListener.unsubscribe();} + if (!this.omitFeedback) {this.feedbackListener.unsubscribe();} + if (!this.omitResult) {this.resultListener.unsubscribe();} +}; + +module.exports = ActionClient; + +},{"../core/Message":13,"../core/Topic":20,"eventemitter2":2}],9:[function(require,module,exports){ +/** + * @fileOverview + * @author Justin Young - justin@oodar.com.au + * @author Russell Toris - rctoris@wpi.edu + */ + +var Topic = require('../core/Topic'); +var Message = require('../core/Message'); +var EventEmitter2 = require('eventemitter2').EventEmitter2; + +/** + * An actionlib action listener + * + * Emits the following events: + * * 'status' - the status messages received from the action server + * * 'feedback' - the feedback messages received from the action server + * * 'result' - the result returned from the action server + * + * @constructor + * @param options - object with following keys: + * * ros - the ROSLIB.Ros connection handle + * * serverName - the action server name, like /fibonacci + * * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction' + */ +function ActionListener(options) { + var that = this; + options = options || {}; + this.ros = options.ros; + this.serverName = options.serverName; + this.actionName = options.actionName; + this.timeout = options.timeout; + this.omitFeedback = options.omitFeedback; + this.omitStatus = options.omitStatus; + this.omitResult = options.omitResult; + + + // create the topics associated with actionlib + var goalListener = new Topic({ + ros : this.ros, + name : this.serverName + '/goal', + messageType : this.actionName + 'Goal' + }); + + var feedbackListener = new Topic({ + ros : this.ros, + name : this.serverName + '/feedback', + messageType : this.actionName + 'Feedback' + }); + + var statusListener = new Topic({ + ros : this.ros, + name : this.serverName + '/status', + messageType : 'actionlib_msgs/GoalStatusArray' + }); + + var resultListener = new Topic({ + ros : this.ros, + name : this.serverName + '/result', + messageType : this.actionName + 'Result' + }); + + goalListener.subscribe(function(goalMessage) { + that.emit('goal', goalMessage); + }); + + statusListener.subscribe(function(statusMessage) { + statusMessage.status_list.forEach(function(status) { + that.emit('status', status); + }); + }); + + feedbackListener.subscribe(function(feedbackMessage) { + that.emit('status', feedbackMessage.status); + that.emit('feedback', feedbackMessage.feedback); + }); + + // subscribe to the result topic + resultListener.subscribe(function(resultMessage) { + that.emit('status', resultMessage.status); + that.emit('result', resultMessage.result); + }); + +} + +ActionListener.prototype.__proto__ = EventEmitter2.prototype; + +module.exports = ActionListener; + +},{"../core/Message":13,"../core/Topic":20,"eventemitter2":2}],10:[function(require,module,exports){ +/** + * @fileOverview * @author Russell Toris - rctoris@wpi.edu */ var Message = require('../core/Message'); -var EventEmitter2 = require('./../util/shim/EventEmitter2.js').EventEmitter2; +var EventEmitter2 = require('eventemitter2').EventEmitter2; /** * An actionlib goal goal is associated with an action server. @@ -271,14 +1904,15 @@ Goal.prototype.cancel = function() { }; module.exports = Goal; -},{"../core/Message":9,"./../util/shim/EventEmitter2.js":38}],7:[function(require,module,exports){ +},{"../core/Message":13,"eventemitter2":2}],11:[function(require,module,exports){ /** + * @fileOverview * @author Laura Lindzey - lindzey@gmail.com */ var Topic = require('../core/Topic'); var Message = require('../core/Message'); -var EventEmitter2 = require('./../util/shim/EventEmitter2.js').EventEmitter2; +var EventEmitter2 = require('eventemitter2').EventEmitter2; /** * An actionlib action server client. @@ -479,19 +2113,22 @@ SimpleActionServer.prototype.setPreempted = function() { }; module.exports = SimpleActionServer; -},{"../core/Message":9,"../core/Topic":16,"./../util/shim/EventEmitter2.js":38}],8:[function(require,module,exports){ +},{"../core/Message":13,"../core/Topic":20,"eventemitter2":2}],12:[function(require,module,exports){ var Ros = require('../core/Ros'); var mixin = require('../mixin'); var action = module.exports = { ActionClient: require('./ActionClient'), + ActionListener: require('./ActionListener'), Goal: require('./Goal'), SimpleActionServer: require('./SimpleActionServer') }; mixin(Ros, ['ActionClient', 'SimpleActionServer'], action); -},{"../core/Ros":11,"../mixin":23,"./ActionClient":5,"./Goal":6,"./SimpleActionServer":7}],9:[function(require,module,exports){ + +},{"../core/Ros":15,"../mixin":27,"./ActionClient":8,"./ActionListener":9,"./Goal":10,"./SimpleActionServer":11}],13:[function(require,module,exports){ /** + * @fileoverview * @author Brandon Alexander - baalexander@gmail.com */ @@ -508,8 +2145,9 @@ function Message(values) { } module.exports = Message; -},{"object-assign":1}],10:[function(require,module,exports){ +},{"object-assign":3}],14:[function(require,module,exports){ /** + * @fileoverview * @author Brandon Alexander - baalexander@gmail.com */ @@ -591,19 +2229,21 @@ Param.prototype.delete = function(callback) { }; module.exports = Param; -},{"./Service":12,"./ServiceRequest":13}],11:[function(require,module,exports){ +},{"./Service":16,"./ServiceRequest":17}],15:[function(require,module,exports){ /** + * @fileoverview * @author Brandon Alexander - baalexander@gmail.com */ -var WebSocket = require('./../util/shim/WebSocket.js'); +var WebSocket = require('ws'); +var WorkerSocket = require('../util/workerSocket'); var socketAdapter = require('./SocketAdapter.js'); var Service = require('./Service'); var ServiceRequest = require('./ServiceRequest'); var assign = require('object-assign'); -var EventEmitter2 = require('./../util/shim/EventEmitter2.js').EventEmitter2; +var EventEmitter2 = require('eventemitter2').EventEmitter2; /** * Manages connection to the server and all interactions with ROS. @@ -616,14 +2256,19 @@ var EventEmitter2 = require('./../util/shim/EventEmitter2.js').EventEmitter2; * * - a service response came from rosbridge with the given ID * * @constructor - * @param options - possible keys include: - * * url (optional) - the WebSocket URL for rosbridge (can be specified later with `connect`) + * @param options - possible keys include: + * * url (optional) - (can be specified later with `connect`) the WebSocket URL for rosbridge or the node server url to connect using socket.io (if socket.io exists in the page) + * * groovyCompatibility - don't use interfaces that changed after the last groovy release or rosbridge_suite and related tools (defaults to true) + * * transportLibrary (optional) - one of 'websocket', 'workersocket' (default), 'socket.io' or RTCPeerConnection instance controlling how the connection is created in `connect`. + * * transportOptions (optional) - the options to use use when creating a connection. Currently only used if `transportLibrary` is RTCPeerConnection. */ function Ros(options) { options = options || {}; this.socket = null; this.idCounter = 0; this.isConnected = false; + this.transportLibrary = options.transportLibrary || 'websocket'; + this.transportOptions = options.transportOptions || {}; if (typeof options.groovyCompatibility === 'undefined') { this.groovyCompatibility = true; @@ -646,10 +2291,29 @@ Ros.prototype.__proto__ = EventEmitter2.prototype; /** * Connect to the specified WebSocket. * - * @param url - WebSocket URL for Rosbridge + * @param url - WebSocket URL or RTCDataChannel label for Rosbridge */ Ros.prototype.connect = function(url) { - this.socket = assign(new WebSocket(url), socketAdapter(this)); + if (this.transportLibrary === 'socket.io') { + this.socket = assign(io(url, {'force new connection': true}), socketAdapter(this)); + this.socket.on('connect', this.socket.onopen); + this.socket.on('data', this.socket.onmessage); + this.socket.on('close', this.socket.onclose); + this.socket.on('error', this.socket.onerror); + } else if (this.transportLibrary.constructor.name === 'RTCPeerConnection') { + this.socket = assign(this.transportLibrary.createDataChannel(url, this.transportOptions), socketAdapter(this)); + } else if (this.transportLibrary === 'websocket') { + if (!this.socket || this.socket.readyState === WebSocket.CLOSED) { + var sock = new WebSocket(url); + sock.binaryType = 'arraybuffer'; + this.socket = assign(sock, socketAdapter(this)); + } + } else if (this.transportLibrary === 'workersocket') { + this.socket = assign(new WorkerSocket(url), socketAdapter(this)); + } else { + throw 'Unknown transportLibrary: ' + this.transportLibrary.toString(); + } + }; /** @@ -695,13 +2359,64 @@ Ros.prototype.authenticate = function(mac, client, dest, rand, t, level, end) { Ros.prototype.callOnConnection = function(message) { var that = this; var messageJson = JSON.stringify(message); + var emitter = null; + if (this.transportLibrary === 'socket.io') { + emitter = function(msg){that.socket.emit('operation', msg);}; + } else { + emitter = function(msg){that.socket.send(msg);}; + } if (!this.isConnected) { that.once('connection', function() { - that.socket.send(messageJson); + emitter(messageJson); }); } else { - that.socket.send(messageJson); + emitter(messageJson); + } +}; + +/** + * Sends a set_level request to the server + * + * @param level - Status level (none, error, warning, info) + * @param id - Optional: Operation ID to change status level on + */ +Ros.prototype.setStatusLevel = function(level, id){ + var levelMsg = { + op: 'set_level', + level: level, + id: id + }; + + this.callOnConnection(levelMsg); +}; + +/** + * Retrieves Action Servers in ROS as an array of string + * + * * actionservers - Array of action server names + */ +Ros.prototype.getActionServers = function(callback, failedCallback) { + var getActionServers = new Service({ + ros : this, + name : '/rosapi/action_servers', + serviceType : 'rosapi/GetActionServers' + }); + + var request = new ServiceRequest({}); + if (typeof failedCallback === 'function'){ + getActionServers.callService(request, + function(result) { + callback(result.action_servers); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + getActionServers.callService(request, function(result) { + callback(result.action_servers); + }); } }; @@ -711,7 +2426,7 @@ Ros.prototype.callOnConnection = function(message) { * @param callback function with params: * * topics - Array of topic names */ -Ros.prototype.getTopics = function(callback) { +Ros.prototype.getTopics = function(callback, failedCallback) { var topicsClient = new Service({ ros : this, name : '/rosapi/topics', @@ -719,10 +2434,20 @@ Ros.prototype.getTopics = function(callback) { }); var request = new ServiceRequest(); - - topicsClient.callService(request, function(result) { - callback(result.topics); - }); + if (typeof failedCallback === 'function'){ + topicsClient.callService(request, + function(result) { + callback(result); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + topicsClient.callService(request, function(result) { + callback(result); + }); + } }; /** @@ -732,7 +2457,7 @@ Ros.prototype.getTopics = function(callback) { * @param callback function with params: * * topics - Array of topic names */ -Ros.prototype.getTopicsForType = function(topicType, callback) { +Ros.prototype.getTopicsForType = function(topicType, callback, failedCallback) { var topicsForTypeClient = new Service({ ros : this, name : '/rosapi/topics_for_type', @@ -742,10 +2467,20 @@ Ros.prototype.getTopicsForType = function(topicType, callback) { var request = new ServiceRequest({ type: topicType }); - - topicsForTypeClient.callService(request, function(result) { - callback(result.topics); - }); + if (typeof failedCallback === 'function'){ + topicsForTypeClient.callService(request, + function(result) { + callback(result.topics); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + topicsForTypeClient.callService(request, function(result) { + callback(result.topics); + }); + } }; /** @@ -754,7 +2489,7 @@ Ros.prototype.getTopicsForType = function(topicType, callback) { * @param callback - function with the following params: * * services - array of service names */ -Ros.prototype.getServices = function(callback) { +Ros.prototype.getServices = function(callback, failedCallback) { var servicesClient = new Service({ ros : this, name : '/rosapi/services', @@ -762,10 +2497,20 @@ Ros.prototype.getServices = function(callback) { }); var request = new ServiceRequest(); - - servicesClient.callService(request, function(result) { - callback(result.services); - }); + if (typeof failedCallback === 'function'){ + servicesClient.callService(request, + function(result) { + callback(result.services); + }, + function(message) { + failedCallback(message); + } + ); + }else{ + servicesClient.callService(request, function(result) { + callback(result.services); + }); + } }; /** @@ -775,7 +2520,7 @@ Ros.prototype.getServices = function(callback) { * @param callback function with params: * * topics - Array of service names */ -Ros.prototype.getServicesForType = function(serviceType, callback) { +Ros.prototype.getServicesForType = function(serviceType, callback, failedCallback) { var servicesForTypeClient = new Service({ ros : this, name : '/rosapi/services_for_type', @@ -785,10 +2530,86 @@ Ros.prototype.getServicesForType = function(serviceType, callback) { var request = new ServiceRequest({ type: serviceType }); + if (typeof failedCallback === 'function'){ + servicesForTypeClient.callService(request, + function(result) { + callback(result.services); + }, + function(message) { + failedCallback(message); + } + ); + }else{ + servicesForTypeClient.callService(request, function(result) { + callback(result.services); + }); + } +}; - servicesForTypeClient.callService(request, function(result) { - callback(result.services); +/** + * Retrieves a detail of ROS service request. + * + * @param service name of service: + * @param callback - function with params: + * * type - String of the service type + */ +Ros.prototype.getServiceRequestDetails = function(type, callback, failedCallback) { + var serviceTypeClient = new Service({ + ros : this, + name : '/rosapi/service_request_details', + serviceType : 'rosapi/ServiceRequestDetails' }); + var request = new ServiceRequest({ + type: type + }); + + if (typeof failedCallback === 'function'){ + serviceTypeClient.callService(request, + function(result) { + callback(result); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + serviceTypeClient.callService(request, function(result) { + callback(result); + }); + } +}; + +/** + * Retrieves a detail of ROS service request. + * + * @param service name of service: + * @param callback - function with params: + * * type - String of the service type + */ +Ros.prototype.getServiceResponseDetails = function(type, callback, failedCallback) { + var serviceTypeClient = new Service({ + ros : this, + name : '/rosapi/service_response_details', + serviceType : 'rosapi/ServiceResponseDetails' + }); + var request = new ServiceRequest({ + type: type + }); + + if (typeof failedCallback === 'function'){ + serviceTypeClient.callService(request, + function(result) { + callback(result); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + serviceTypeClient.callService(request, function(result) { + callback(result); + }); + } }; /** @@ -797,7 +2618,7 @@ Ros.prototype.getServicesForType = function(serviceType, callback) { * @param callback - function with the following params: * * nodes - array of node names */ -Ros.prototype.getNodes = function(callback) { +Ros.prototype.getNodes = function(callback, failedCallback) { var nodesClient = new Service({ ros : this, name : '/rosapi/nodes', @@ -805,10 +2626,55 @@ Ros.prototype.getNodes = function(callback) { }); var request = new ServiceRequest(); + if (typeof failedCallback === 'function'){ + nodesClient.callService(request, + function(result) { + callback(result.nodes); + }, + function(message) { + failedCallback(message); + } + ); + }else{ + nodesClient.callService(request, function(result) { + callback(result.nodes); + }); + } +}; - nodesClient.callService(request, function(result) { - callback(result.nodes); +/** + * Retrieves list subscribed topics, publishing topics and services of a specific node + * + * @param node name of the node: + * @param callback - function with params: + * * publications - array of published topic names + * * subscriptions - array of subscribed topic names + * * services - array of service names hosted + */ +Ros.prototype.getNodeDetails = function(node, callback, failedCallback) { + var nodesClient = new Service({ + ros : this, + name : '/rosapi/node_details', + serviceType : 'rosapi/NodeDetails' }); + + var request = new ServiceRequest({ + node: node + }); + if (typeof failedCallback === 'function'){ + nodesClient.callService(request, + function(result) { + callback(result.subscribing, result.publishing, result.services); + }, + function(message) { + failedCallback(message); + } + ); + } else { + nodesClient.callService(request, function(result) { + callback(result); + }); + } }; /** @@ -817,26 +2683,37 @@ Ros.prototype.getNodes = function(callback) { * @param callback function with params: * * params - array of param names. */ -Ros.prototype.getParams = function(callback) { +Ros.prototype.getParams = function(callback, failedCallback) { var paramsClient = new Service({ ros : this, name : '/rosapi/get_param_names', serviceType : 'rosapi/GetParamNames' }); - var request = new ServiceRequest(); - paramsClient.callService(request, function(result) { - callback(result.names); - }); + if (typeof failedCallback === 'function'){ + paramsClient.callService(request, + function(result) { + callback(result.names); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + paramsClient.callService(request, function(result) { + callback(result.names); + }); + } }; /** * Retrieves a type of ROS topic. * + * @param topic name of the topic: * @param callback - function with params: * * type - String of the topic type */ -Ros.prototype.getTopicType = function(topic, callback) { +Ros.prototype.getTopicType = function(topic, callback, failedCallback) { var topicTypeClient = new Service({ ros : this, name : '/rosapi/topic_type', @@ -845,9 +2722,54 @@ Ros.prototype.getTopicType = function(topic, callback) { var request = new ServiceRequest({ topic: topic }); - topicTypeClient.callService(request, function(result) { - callback(result.type); + + if (typeof failedCallback === 'function'){ + topicTypeClient.callService(request, + function(result) { + callback(result.type); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + topicTypeClient.callService(request, function(result) { + callback(result.type); + }); + } +}; + +/** + * Retrieves a type of ROS service. + * + * @param service name of service: + * @param callback - function with params: + * * type - String of the service type + */ +Ros.prototype.getServiceType = function(service, callback, failedCallback) { + var serviceTypeClient = new Service({ + ros : this, + name : '/rosapi/service_type', + serviceType : 'rosapi/ServiceType' }); + var request = new ServiceRequest({ + service: service + }); + + if (typeof failedCallback === 'function'){ + serviceTypeClient.callService(request, + function(result) { + callback(result.type); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + serviceTypeClient.callService(request, function(result) { + callback(result.type); + }); + } }; /** @@ -857,7 +2779,7 @@ Ros.prototype.getTopicType = function(topic, callback) { * * details - Array of the message detail * @param message - String of a topic type */ -Ros.prototype.getMessageDetails = function(message, callback) { +Ros.prototype.getMessageDetails = function(message, callback, failedCallback) { var messageDetailClient = new Service({ ros : this, name : '/rosapi/message_details', @@ -866,9 +2788,21 @@ Ros.prototype.getMessageDetails = function(message, callback) { var request = new ServiceRequest({ type: message }); - messageDetailClient.callService(request, function(result) { - callback(result.typedefs); - }); + + if (typeof failedCallback === 'function'){ + messageDetailClient.callService(request, + function(result) { + callback(result.typedefs); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + messageDetailClient.callService(request, function(result) { + callback(result.typedefs); + }); + } }; /** @@ -906,7 +2840,6 @@ Ros.prototype.decodeTypeDefs = function(defs) { if (sub) { var subResult = decodeTypeDefsRec(sub, hints); if (arrayLen === -1) { - typeDefDict[fieldName] = subResult; } else { typeDefDict[fieldName] = [subResult]; @@ -919,19 +2852,22 @@ Ros.prototype.decodeTypeDefs = function(defs) { } return typeDefDict; }; - + return decodeTypeDefsRec(defs[0], defs); }; module.exports = Ros; -},{"./../util/shim/EventEmitter2.js":38,"./../util/shim/WebSocket.js":39,"./Service":12,"./ServiceRequest":13,"./SocketAdapter.js":15,"object-assign":1}],12:[function(require,module,exports){ +},{"../util/workerSocket":47,"./Service":16,"./ServiceRequest":17,"./SocketAdapter.js":19,"eventemitter2":2,"object-assign":3,"ws":43}],16:[function(require,module,exports){ /** + * @fileoverview * @author Brandon Alexander - baalexander@gmail.com */ var ServiceResponse = require('./ServiceResponse'); +var ServiceRequest = require('./ServiceRequest'); +var EventEmitter2 = require('eventemitter2').EventEmitter2; /** * A ROS service client. @@ -947,10 +2883,14 @@ function Service(options) { this.ros = options.ros; this.name = options.name; this.serviceType = options.serviceType; -} + this.isAdvertised = false; + this._serviceCallback = null; +} +Service.prototype.__proto__ = EventEmitter2.prototype; /** - * Calls the service. Returns the service response in the callback. + * Calls the service. Returns the service response in the + * callback. Does nothing if this service is currently advertised. * * @param request - the ROSLIB.ServiceRequest to send * @param callback - function with params: @@ -959,6 +2899,10 @@ function Service(options) { * * error - the error message reported by ROS */ Service.prototype.callService = function(request, callback, failedCallback) { + if (this.isAdvertised) { + return; + } + var serviceCallId = 'call_service:' + this.name + ':' + (++this.ros.idCounter); if (callback || failedCallback) { @@ -977,14 +2921,72 @@ Service.prototype.callService = function(request, callback, failedCallback) { op : 'call_service', id : serviceCallId, service : this.name, + type: this.serviceType, args : request }; this.ros.callOnConnection(call); }; -module.exports = Service; -},{"./ServiceResponse":14}],13:[function(require,module,exports){ /** + * Advertise the service. This turns the Service object from a client + * into a server. The callback will be called with every request + * that's made on this service. + * + * @param callback - This works similarly to the callback for a C++ service and should take the following params: + * * request - the service request + * * response - an empty dictionary. Take care not to overwrite this. Instead, only modify the values within. + * It should return true if the service has finished successfully, + * i.e. without any fatal errors. + */ +Service.prototype.advertise = function(callback) { + if (this.isAdvertised || typeof callback !== 'function') { + return; + } + + this._serviceCallback = callback; + this.ros.on(this.name, this._serviceResponse.bind(this)); + this.ros.callOnConnection({ + op: 'advertise_service', + type: this.serviceType, + service: this.name + }); + this.isAdvertised = true; +}; + +Service.prototype.unadvertise = function() { + if (!this.isAdvertised) { + return; + } + this.ros.callOnConnection({ + op: 'unadvertise_service', + service: this.name + }); + this.isAdvertised = false; +}; + +Service.prototype._serviceResponse = function(rosbridgeRequest) { + var response = {}; + var success = this._serviceCallback(rosbridgeRequest.args, response); + + var call = { + op: 'service_response', + service: this.name, + values: new ServiceResponse(response), + result: success + }; + + if (rosbridgeRequest.id) { + call.id = rosbridgeRequest.id; + } + + this.ros.callOnConnection(call); +}; + +module.exports = Service; + +},{"./ServiceRequest":17,"./ServiceResponse":18,"eventemitter2":2}],17:[function(require,module,exports){ +/** + * @fileoverview * @author Brandon Alexander - balexander@willowgarage.com */ @@ -1001,8 +3003,9 @@ function ServiceRequest(values) { } module.exports = ServiceRequest; -},{"object-assign":1}],14:[function(require,module,exports){ +},{"object-assign":3}],18:[function(require,module,exports){ /** + * @fileoverview * @author Brandon Alexander - balexander@willowgarage.com */ @@ -1019,69 +3022,32 @@ function ServiceResponse(values) { } module.exports = ServiceResponse; -},{"object-assign":1}],15:[function(require,module,exports){ -(function (global){ +},{"object-assign":3}],19:[function(require,module,exports){ /** * Socket event handling utilities for handling events on either * WebSocket and TCP sockets * * Note to anyone reviewing this code: these functions are called * in the context of their parent object, unless bound + * @fileOverview */ 'use strict'; -var Canvas = require('./../util/shim/canvas.js'); -var Image = Canvas.Image || global.Image; -var WebSocket = require('./../util/shim/WebSocket.js'); - -/** - * If a message was compressed as a PNG image (a compression hack since - * gzipping over WebSockets * is not supported yet), this function places the - * "image" in a canvas element then decodes the * "image" as a Base64 string. - * - * @param data - object containing the PNG data. - * @param callback - function with params: - * * data - the uncompressed data - */ -function decompressPng(data, callback) { - // Uncompresses the data before sending it through (use image/canvas to do so). - var image = new Image(); - // When the image loads, extracts the raw data (JSON message). - image.onload = function() { - // Creates a local canvas to draw on. - var canvas = new Canvas(); - var context = canvas.getContext('2d'); - - // Sets width and height. - canvas.width = image.width; - canvas.height = image.height; - - // Prevents anti-aliasing and loosing data - context.imageSmoothingEnabled = false; - context.webkitImageSmoothingEnabled = false; - context.mozImageSmoothingEnabled = false; - - // Puts the data into the image. - context.drawImage(image, 0, 0); - // Grabs the raw, uncompressed data. - var imageData = context.getImageData(0, 0, image.width, image.height).data; - - // Constructs the JSON. - var jsonData = ''; - for (var i = 0; i < imageData.length; i += 4) { - // RGB - jsonData += String.fromCharCode(imageData[i], imageData[i + 1], imageData[i + 2]); - } - callback(JSON.parse(jsonData)); - }; - // Sends the image data to load. - image.src = 'data:image/png;base64,' + data.data; +var decompressPng = require('../util/decompressPng'); +var CBOR = require('cbor-js'); +var typedArrayTagger = require('../util/cborTypedArrayTags'); +var BSON = null; +if(typeof bson !== 'undefined'){ + BSON = bson().BSON; } /** * Events listeners for a WebSocket or TCP socket to a JavaScript * ROS Client. Sets up Messages for a given topic to trigger an * event on the ROS client. + * + * @namespace SocketAdapter + * @private */ function SocketAdapter(client) { function handleMessage(message) { @@ -1089,14 +3055,44 @@ function SocketAdapter(client) { client.emit(message.topic, message.msg); } else if (message.op === 'service_response') { client.emit(message.id, message); + } else if (message.op === 'call_service') { + client.emit(message.service, message); + } else if(message.op === 'status'){ + if(message.id){ + client.emit('status:'+message.id, message); + } else { + client.emit('status', message); + } } } + function handlePng(message, callback) { + if (message.op === 'png') { + decompressPng(message.data, callback); + } else { + callback(message); + } + } + + function decodeBSON(data, callback) { + if (!BSON) { + throw 'Cannot process BSON encoded message without BSON header.'; + } + var reader = new FileReader(); + reader.onload = function() { + var uint8Array = new Uint8Array(this.result); + var msg = BSON.deserialize(uint8Array); + callback(msg); + }; + reader.readAsArrayBuffer(data); + } + return { /** * Emits a 'connection' event on WebSocket connection. * * @param event - the argument to emit with the event. + * @memberof SocketAdapter */ onopen: function onOpen(event) { client.isConnected = true; @@ -1107,6 +3103,7 @@ function SocketAdapter(client) { * Emits a 'close' event on WebSocket disconnection. * * @param event - the argument to emit with the event. + * @memberof SocketAdapter */ onclose: function onClose(event) { client.isConnected = false; @@ -1117,6 +3114,7 @@ function SocketAdapter(client) { * Emits an 'error' event whenever there was an error. * * @param event - the argument to emit with the event. + * @memberof SocketAdapter */ onerror: function onError(event) { client.emit('error', event); @@ -1127,13 +3125,19 @@ function SocketAdapter(client) { * topic, service, or param. * * @param message - the raw JSON message from rosbridge. + * @memberof SocketAdapter */ - onmessage: function onMessage(message) { - var data = JSON.parse(typeof message === 'string' ? message : message.data); - if (data.op === 'png') { - decompressPng(data, handleMessage); + onmessage: function onMessage(data) { + if (typeof Blob !== 'undefined' && data.data instanceof Blob) { + decodeBSON(data.data, function (message) { + handlePng(message, handleMessage); + }); + } else if (data.data instanceof ArrayBuffer) { + var decoded = CBOR.decode(data.data, typedArrayTagger); + handleMessage(decoded); } else { - handleMessage(data); + var message = JSON.parse(typeof data === 'string' ? data : data.data); + handlePng(message, handleMessage); } } }; @@ -1141,13 +3145,13 @@ function SocketAdapter(client) { module.exports = SocketAdapter; -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./../util/shim/WebSocket.js":39,"./../util/shim/canvas.js":40}],16:[function(require,module,exports){ +},{"../util/cborTypedArrayTags":42,"../util/decompressPng":45,"cbor-js":1}],20:[function(require,module,exports){ /** + * @fileoverview * @author Brandon Alexander - baalexander@gmail.com */ -var EventEmitter2 = require('./../util/shim/EventEmitter2.js').EventEmitter2; +var EventEmitter2 = require('eventemitter2').EventEmitter2; var Message = require('./Message'); /** @@ -1162,11 +3166,12 @@ var Message = require('./Message'); * * ros - the ROSLIB.Ros connection handle * * name - the topic name, like /cmd_vel * * messageType - the message type, like 'std_msgs/String' - * * compression - the type of compression to use, like 'png' + * * compression - the type of compression to use, like 'png' or 'cbor' * * throttle_rate - the rate (in ms in between messages) at which to throttle the topics * * queue_size - the queue created at bridge side for re-publishing webtopics (defaults to 100) * * latch - latch the topic when publishing * * queue_length - the queue length at bridge side used when subscribing (defaults to 0, no queueing). + * * reconnect_on_close - the flag to enable resubscription and readvertisement on close event(defaults to true). */ function Topic(options) { options = options || {}; @@ -1179,12 +3184,14 @@ function Topic(options) { this.latch = options.latch || false; this.queue_size = options.queue_size || 100; this.queue_length = options.queue_length || 0; + this.reconnect_on_close = options.reconnect_on_close !== undefined ? options.reconnect_on_close : true; // Check for valid compression types if (this.compression && this.compression !== 'png' && - this.compression !== 'none') { + this.compression !== 'cbor' && this.compression !== 'none') { this.emit('warning', this.compression + ' compression is not supported. No compression will be used.'); + this.compression = 'none'; } // Check if throttle rate is negative @@ -1194,6 +3201,27 @@ function Topic(options) { } var that = this; + if (this.reconnect_on_close) { + this.callForSubscribeAndAdvertise = function(message) { + that.ros.callOnConnection(message); + + that.waitForReconnect = false; + that.reconnectFunc = function() { + if(!that.waitForReconnect) { + that.waitForReconnect = true; + that.ros.callOnConnection(message); + that.ros.once('connection', function() { + that.waitForReconnect = false; + }); + } + }; + that.ros.on('close', that.reconnectFunc); + }; + } + else { + this.callForSubscribeAndAdvertise = this.ros.callOnConnection; + } + this._messageCallback = function(data) { that.emit('message', new Message(data)); }; @@ -1215,7 +3243,8 @@ Topic.prototype.subscribe = function(callback) { if (this.subscribeId) { return; } this.ros.on(this.name, this._messageCallback); this.subscribeId = 'subscribe:' + this.name + ':' + (++this.ros.idCounter); - this.ros.callOnConnection({ + + this.callForSubscribeAndAdvertise({ op: 'subscribe', id: this.subscribeId, type: this.messageType, @@ -1244,6 +3273,9 @@ Topic.prototype.unsubscribe = function(callback) { if (!this.subscribeId) { return; } // Note: Don't call this.removeAllListeners, allow client to handle that themselves this.ros.off(this.name, this._messageCallback); + if(this.reconnect_on_close) { + this.ros.off('close', this.reconnectFunc); + } this.emit('unsubscribe'); this.ros.callOnConnection({ op: 'unsubscribe', @@ -1253,6 +3285,7 @@ Topic.prototype.unsubscribe = function(callback) { this.subscribeId = null; }; + /** * Registers as a publisher for the topic. */ @@ -1261,7 +3294,7 @@ Topic.prototype.advertise = function() { return; } this.advertiseId = 'advertise:' + this.name + ':' + (++this.ros.idCounter); - this.ros.callOnConnection({ + this.callForSubscribeAndAdvertise({ op: 'advertise', id: this.advertiseId, type: this.messageType, @@ -1270,6 +3303,13 @@ Topic.prototype.advertise = function() { queue_size: this.queue_size }); this.isAdvertised = true; + + if(!this.reconnect_on_close) { + var that = this; + this.ros.on('close', function() { + that.isAdvertised = false; + }); + } }; /** @@ -1279,6 +3319,9 @@ Topic.prototype.unadvertise = function() { if (!this.isAdvertised) { return; } + if(this.reconnect_on_close) { + this.ros.off('close', this.reconnectFunc); + } this.emit('unadvertise'); this.ros.callOnConnection({ op: 'unadvertise', @@ -1311,7 +3354,7 @@ Topic.prototype.publish = function(message) { module.exports = Topic; -},{"./../util/shim/EventEmitter2.js":38,"./Message":9}],17:[function(require,module,exports){ +},{"./Message":13,"eventemitter2":2}],21:[function(require,module,exports){ var mixin = require('../mixin'); var core = module.exports = { @@ -1326,8 +3369,9 @@ var core = module.exports = { mixin(core.Ros, ['Param', 'Service', 'Topic'], core); -},{"../mixin":23,"./Message":9,"./Param":10,"./Ros":11,"./Service":12,"./ServiceRequest":13,"./ServiceResponse":14,"./Topic":16}],18:[function(require,module,exports){ +},{"../mixin":27,"./Message":13,"./Param":14,"./Ros":15,"./Service":16,"./ServiceRequest":17,"./ServiceResponse":18,"./Topic":20}],22:[function(require,module,exports){ /** + * @fileoverview * @author David Gossow - dgossow@willowgarage.com */ @@ -1371,9 +3415,36 @@ Pose.prototype.clone = function() { return new Pose(this); }; -module.exports = Pose; -},{"./Quaternion":19,"./Vector3":21}],19:[function(require,module,exports){ /** + * Multiplies this pose with another pose without altering this pose. + * + * @returns Result of multiplication. + */ +Pose.prototype.multiply = function(pose) { + var p = pose.clone(); + p.applyTransform({ rotation: this.orientation, translation: this.position }); + return p; +}; + +/** + * Computes the inverse of this pose. + * + * @returns Inverse of pose. + */ +Pose.prototype.getInverse = function() { + var inverse = this.clone(); + inverse.orientation.invert(); + inverse.position.multiplyQuaternion(inverse.orientation); + inverse.position.x *= -1; + inverse.position.y *= -1; + inverse.position.z *= -1; + return inverse; +}; + +module.exports = Pose; +},{"./Quaternion":23,"./Vector3":25}],23:[function(require,module,exports){ +/** + * @fileoverview * @author David Gossow - dgossow@willowgarage.com */ @@ -1465,8 +3536,9 @@ Quaternion.prototype.clone = function() { module.exports = Quaternion; -},{}],20:[function(require,module,exports){ +},{}],24:[function(require,module,exports){ /** + * @fileoverview * @author David Gossow - dgossow@willowgarage.com */ @@ -1498,8 +3570,9 @@ Transform.prototype.clone = function() { }; module.exports = Transform; -},{"./Quaternion":19,"./Vector3":21}],21:[function(require,module,exports){ +},{"./Quaternion":23,"./Vector3":25}],25:[function(require,module,exports){ /** + * @fileoverview * @author David Gossow - dgossow@willowgarage.com */ @@ -1566,7 +3639,7 @@ Vector3.prototype.clone = function() { }; module.exports = Vector3; -},{}],22:[function(require,module,exports){ +},{}],26:[function(require,module,exports){ module.exports = { Pose: require('./Pose'), Quaternion: require('./Quaternion'), @@ -1574,7 +3647,7 @@ module.exports = { Vector3: require('./Vector3') }; -},{"./Pose":18,"./Quaternion":19,"./Transform":20,"./Vector3":21}],23:[function(require,module,exports){ +},{"./Pose":22,"./Quaternion":23,"./Transform":24,"./Vector3":25}],27:[function(require,module,exports){ /** * Mixin a feature to the core/Ros prototype. * For example, mixin(Ros, ['Topic'], {Topic: }) @@ -1593,8 +3666,9 @@ module.exports = function(Ros, classes, features) { }); }; -},{}],24:[function(require,module,exports){ +},{}],28:[function(require,module,exports){ /** + * @fileoverview * @author David Gossow - dgossow@willowgarage.com */ @@ -1619,6 +3693,9 @@ var Transform = require('../math/Transform'); * * updateDelay - the time (in ms) to wait after a new subscription * to update the TF republisher's list of TFs * * topicTimeout - the timeout parameter for the TF republisher + * * serverName (optional) - the name of the tf2_web_republisher server + * * repubServiceName (optional) - the name of the republish_tfs service (non groovy compatibility mode only) + * default: '/republish_tfs' */ function TFClient(options) { options = options || {}; @@ -1635,6 +3712,8 @@ function TFClient(options) { secs: secs, nsecs: nsecs }; + this.serverName = options.serverName || '/tf2_web_republisher'; + this.repubServiceName = options.repubServiceName || '/republish_tfs'; this.currentGoal = false; this.currentTopic = false; @@ -1643,13 +3722,15 @@ function TFClient(options) { // Create an Action client this.actionClient = this.ros.ActionClient({ - serverName : '/tf2_web_republisher', - actionName : 'tf2_web_republisher/TFSubscriptionAction' + serverName : this.serverName, + actionName : 'tf2_web_republisher/TFSubscriptionAction', + omitStatus : true, + omitResult : true }); // Create a Service client this.serviceClient = this.ros.Service({ - name: '/republish_tfs', + name: this.repubServiceName, serviceType: 'tf2_web_republisher/RepublishTFs' }); } @@ -1794,9 +3875,19 @@ TFClient.prototype.unsubscribe = function(frameID, callback) { } }; +/** + * Unsubscribe and unadvertise all topics associated with this TFClient. + */ +TFClient.prototype.dispose = function() { + this.actionClient.dispose(); + if (this.currentTopic) { + this.currentTopic.unsubscribe(); + } +}; + module.exports = TFClient; -},{"../actionlib/ActionClient":5,"../actionlib/Goal":6,"../core/Service.js":12,"../core/ServiceRequest.js":13,"../math/Transform":20}],25:[function(require,module,exports){ +},{"../actionlib/ActionClient":8,"../actionlib/Goal":10,"../core/Service.js":16,"../core/ServiceRequest.js":17,"../math/Transform":24}],29:[function(require,module,exports){ var Ros = require('../core/Ros'); var mixin = require('../mixin'); @@ -1805,8 +3896,9 @@ var tf = module.exports = { }; mixin(Ros, ['TFClient'], tf); -},{"../core/Ros":11,"../mixin":23,"./TFClient":24}],26:[function(require,module,exports){ +},{"../core/Ros":15,"../mixin":27,"./TFClient":28}],30:[function(require,module,exports){ /** + * @fileOverview * @author Benjamin Pitzer - ben.pitzer@gmail.com * @author Russell Toris - rctoris@wpi.edu */ @@ -1835,8 +3927,9 @@ function UrdfBox(options) { } module.exports = UrdfBox; -},{"../math/Vector3":21,"./UrdfTypes":35}],27:[function(require,module,exports){ +},{"../math/Vector3":25,"./UrdfTypes":39}],31:[function(require,module,exports){ /** + * @fileOverview * @author Benjamin Pitzer - ben.pitzer@gmail.com * @author Russell Toris - rctoris@wpi.edu */ @@ -1858,8 +3951,9 @@ function UrdfColor(options) { } module.exports = UrdfColor; -},{}],28:[function(require,module,exports){ +},{}],32:[function(require,module,exports){ /** + * @fileOverview * @author Benjamin Pitzer - ben.pitzer@gmail.com * @author Russell Toris - rctoris@wpi.edu */ @@ -1880,11 +3974,16 @@ function UrdfCylinder(options) { } module.exports = UrdfCylinder; -},{"./UrdfTypes":35}],29:[function(require,module,exports){ +},{"./UrdfTypes":39}],33:[function(require,module,exports){ /** + * @fileOverview * @author David V. Lu!! davidvlu@gmail.com */ +var Pose = require('../math/Pose'); +var Vector3 = require('../math/Vector3'); +var Quaternion = require('../math/Quaternion'); + /** * A Joint element in a URDF. * @@ -1895,18 +3994,82 @@ module.exports = UrdfCylinder; function UrdfJoint(options) { this.name = options.xml.getAttribute('name'); this.type = options.xml.getAttribute('type'); - + + var parents = options.xml.getElementsByTagName('parent'); + if(parents.length > 0) { + this.parent = parents[0].getAttribute('link'); + } + + var children = options.xml.getElementsByTagName('child'); + if(children.length > 0) { + this.child = children[0].getAttribute('link'); + } + var limits = options.xml.getElementsByTagName('limit'); if (limits.length > 0) { this.minval = parseFloat( limits[0].getAttribute('lower') ); this.maxval = parseFloat( limits[0].getAttribute('upper') ); } + + // Origin + var origins = options.xml.getElementsByTagName('origin'); + if (origins.length === 0) { + // use the identity as the default + this.origin = new Pose(); + } else { + // Check the XYZ + var xyz = origins[0].getAttribute('xyz'); + var position = new Vector3(); + if (xyz) { + xyz = xyz.split(' '); + position = new Vector3({ + x : parseFloat(xyz[0]), + y : parseFloat(xyz[1]), + z : parseFloat(xyz[2]) + }); + } + + // Check the RPY + var rpy = origins[0].getAttribute('rpy'); + var orientation = new Quaternion(); + if (rpy) { + rpy = rpy.split(' '); + // Convert from RPY + var roll = parseFloat(rpy[0]); + var pitch = parseFloat(rpy[1]); + var yaw = parseFloat(rpy[2]); + var phi = roll / 2.0; + var the = pitch / 2.0; + var psi = yaw / 2.0; + var x = Math.sin(phi) * Math.cos(the) * Math.cos(psi) - Math.cos(phi) * Math.sin(the) + * Math.sin(psi); + var y = Math.cos(phi) * Math.sin(the) * Math.cos(psi) + Math.sin(phi) * Math.cos(the) + * Math.sin(psi); + var z = Math.cos(phi) * Math.cos(the) * Math.sin(psi) - Math.sin(phi) * Math.sin(the) + * Math.cos(psi); + var w = Math.cos(phi) * Math.cos(the) * Math.cos(psi) + Math.sin(phi) * Math.sin(the) + * Math.sin(psi); + + orientation = new Quaternion({ + x : x, + y : y, + z : z, + w : w + }); + orientation.normalize(); + } + this.origin = new Pose({ + position : position, + orientation : orientation + }); + } } module.exports = UrdfJoint; -},{}],30:[function(require,module,exports){ +},{"../math/Pose":22,"../math/Quaternion":23,"../math/Vector3":25}],34:[function(require,module,exports){ /** + * @fileOverview * @author Benjamin Pitzer - ben.pitzer@gmail.com * @author Russell Toris - rctoris@wpi.edu */ @@ -1933,8 +4096,9 @@ function UrdfLink(options) { } module.exports = UrdfLink; -},{"./UrdfVisual":36}],31:[function(require,module,exports){ +},{"./UrdfVisual":40}],35:[function(require,module,exports){ /** + * @fileOverview * @author Benjamin Pitzer - ben.pitzer@gmail.com * @author Russell Toris - rctoris@wpi.edu */ @@ -1982,8 +4146,9 @@ UrdfMaterial.prototype.assign = function(obj) { module.exports = UrdfMaterial; -},{"./UrdfColor":27,"object-assign":1}],32:[function(require,module,exports){ +},{"./UrdfColor":31,"object-assign":3}],36:[function(require,module,exports){ /** + * @fileOverview * @author Benjamin Pitzer - ben.pitzer@gmail.com * @author Russell Toris - rctoris@wpi.edu */ @@ -2018,8 +4183,9 @@ function UrdfMesh(options) { } module.exports = UrdfMesh; -},{"../math/Vector3":21,"./UrdfTypes":35}],33:[function(require,module,exports){ +},{"../math/Vector3":25,"./UrdfTypes":39}],37:[function(require,module,exports){ /** + * @fileOverview * @author Benjamin Pitzer - ben.pitzer@gmail.com * @author Russell Toris - rctoris@wpi.edu */ @@ -2027,7 +4193,7 @@ module.exports = UrdfMesh; var UrdfMaterial = require('./UrdfMaterial'); var UrdfLink = require('./UrdfLink'); var UrdfJoint = require('./UrdfJoint'); -var DOMParser = require('xmlshim').DOMParser; +var DOMParser = require('xmldom').DOMParser; // See https://developer.mozilla.org/docs/XPathResult#Constants var XPATH_FIRST_ORDERED_NODE_TYPE = 9; @@ -2057,7 +4223,7 @@ function UrdfModel(options) { // Initialize the model with the given XML node. // Get the robot tag - var robotXml = xmlDoc.evaluate('//robot', xmlDoc, null, XPATH_FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; + var robotXml = xmlDoc.documentElement; // Get the robot name this.name = robotXml.getAttribute('name'); @@ -2114,8 +4280,9 @@ function UrdfModel(options) { module.exports = UrdfModel; -},{"./UrdfJoint":29,"./UrdfLink":30,"./UrdfMaterial":31,"xmlshim":2}],34:[function(require,module,exports){ +},{"./UrdfJoint":33,"./UrdfLink":34,"./UrdfMaterial":35,"xmldom":46}],38:[function(require,module,exports){ /** + * @fileOverview * @author Benjamin Pitzer - ben.pitzer@gmail.com * @author Russell Toris - rctoris@wpi.edu */ @@ -2135,7 +4302,7 @@ function UrdfSphere(options) { } module.exports = UrdfSphere; -},{"./UrdfTypes":35}],35:[function(require,module,exports){ +},{"./UrdfTypes":39}],39:[function(require,module,exports){ module.exports = { URDF_SPHERE : 0, URDF_BOX : 1, @@ -2143,8 +4310,9 @@ module.exports = { URDF_MESH : 3 }; -},{}],36:[function(require,module,exports){ +},{}],40:[function(require,module,exports){ /** + * @fileOverview * @author Benjamin Pitzer - ben.pitzer@gmail.com * @author Russell Toris - rctoris@wpi.edu */ @@ -2271,7 +4439,7 @@ function UrdfVisual(options) { } module.exports = UrdfVisual; -},{"../math/Pose":18,"../math/Quaternion":19,"../math/Vector3":21,"./UrdfBox":26,"./UrdfCylinder":28,"./UrdfMaterial":31,"./UrdfMesh":32,"./UrdfSphere":34}],37:[function(require,module,exports){ +},{"../math/Pose":22,"../math/Quaternion":23,"../math/Vector3":25,"./UrdfBox":30,"./UrdfCylinder":32,"./UrdfMaterial":35,"./UrdfMesh":36,"./UrdfSphere":38}],41:[function(require,module,exports){ module.exports = require('object-assign')({ UrdfBox: require('./UrdfBox'), UrdfColor: require('./UrdfColor'), @@ -2284,19 +4452,291 @@ module.exports = require('object-assign')({ UrdfVisual: require('./UrdfVisual') }, require('./UrdfTypes')); -},{"./UrdfBox":26,"./UrdfColor":27,"./UrdfCylinder":28,"./UrdfLink":30,"./UrdfMaterial":31,"./UrdfMesh":32,"./UrdfModel":33,"./UrdfSphere":34,"./UrdfTypes":35,"./UrdfVisual":36,"object-assign":1}],38:[function(require,module,exports){ -(function (global){ -module.exports = { - EventEmitter2: global.EventEmitter2 +},{"./UrdfBox":30,"./UrdfColor":31,"./UrdfCylinder":32,"./UrdfLink":34,"./UrdfMaterial":35,"./UrdfMesh":36,"./UrdfModel":37,"./UrdfSphere":38,"./UrdfTypes":39,"./UrdfVisual":40,"object-assign":3}],42:[function(require,module,exports){ +'use strict'; + +var UPPER32 = Math.pow(2, 32); + +var warnedPrecision = false; +function warnPrecision() { + if (!warnedPrecision) { + warnedPrecision = true; + console.warn('CBOR 64-bit integer array values may lose precision. No further warnings.'); + } +} + +/** + * Unpacks 64-bit unsigned integer from byte array. + * @param {Uint8Array} bytes +*/ +function decodeUint64LE(bytes) { + warnPrecision(); + + var byteLen = bytes.byteLength; + var offset = bytes.byteOffset; + var arrLen = byteLen / 8; + + var buffer = bytes.buffer.slice(offset, offset + byteLen); + var uint32View = new Uint32Array(buffer); + + var arr = new Array(arrLen); + for (var i = 0; i < arrLen; i++) { + var si = i * 2; + var lo = uint32View[si]; + var hi = uint32View[si+1]; + arr[i] = lo + UPPER32 * hi; + } + + return arr; +} + +/** + * Unpacks 64-bit signed integer from byte array. + * @param {Uint8Array} bytes +*/ +function decodeInt64LE(bytes) { + warnPrecision(); + + var byteLen = bytes.byteLength; + var offset = bytes.byteOffset; + var arrLen = byteLen / 8; + + var buffer = bytes.buffer.slice(offset, offset + byteLen); + var uint32View = new Uint32Array(buffer); + var int32View = new Int32Array(buffer); + + var arr = new Array(arrLen); + for (var i = 0; i < arrLen; i++) { + var si = i * 2; + var lo = uint32View[si]; + var hi = int32View[si+1]; + arr[i] = lo + UPPER32 * hi; + } + + return arr; +} + +/** + * Unpacks typed array from byte array. + * @param {Uint8Array} bytes + * @param {type} ArrayType - desired output array type +*/ +function decodeNativeArray(bytes, ArrayType) { + var byteLen = bytes.byteLength; + var offset = bytes.byteOffset; + var buffer = bytes.buffer.slice(offset, offset + byteLen); + return new ArrayType(buffer); +} + +/** + * Support a subset of draft CBOR typed array tags: + * + * Only support little-endian tags for now. + */ +var nativeArrayTypes = { + 64: Uint8Array, + 69: Uint16Array, + 70: Uint32Array, + 72: Int8Array, + 77: Int16Array, + 78: Int32Array, + 85: Float32Array, + 86: Float64Array }; -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],39:[function(require,module,exports){ -(function (global){ -module.exports = global.WebSocket; -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],40:[function(require,module,exports){ + +/** + * We can also decode 64-bit integer arrays, since ROS has these types. + */ +var conversionArrayTypes = { + 71: decodeUint64LE, + 79: decodeInt64LE +}; + +/** + * Handles CBOR typed array tags during decoding. + * @param {Uint8Array} data + * @param {Number} tag + */ +function cborTypedArrayTagger(data, tag) { + if (tag in nativeArrayTypes) { + var arrayType = nativeArrayTypes[tag]; + return decodeNativeArray(data, arrayType); + } + if (tag in conversionArrayTypes) { + return conversionArrayTypes[tag](data); + } + return data; +} + +if (typeof module !== 'undefined' && module.exports) { + module.exports = cborTypedArrayTagger; +} + +},{}],43:[function(require,module,exports){ +module.exports = typeof window !== 'undefined' ? window.WebSocket : WebSocket; + +},{}],44:[function(require,module,exports){ /* global document */ module.exports = function Canvas() { return document.createElement('canvas'); }; -},{}]},{},[4]); +},{}],45:[function(require,module,exports){ +/** + * @fileOverview + * @author Graeme Yeates - github.com/megawac + */ + +'use strict'; + +var Canvas = require('canvas'); +var Image = Canvas.Image || window.Image; + +/** + * If a message was compressed as a PNG image (a compression hack since + * gzipping over WebSockets * is not supported yet), this function places the + * "image" in a canvas element then decodes the * "image" as a Base64 string. + * + * @private + * @param data - object containing the PNG data. + * @param callback - function with params: + * * data - the uncompressed data + */ +function decompressPng(data, callback) { + // Uncompresses the data before sending it through (use image/canvas to do so). + var image = new Image(); + // When the image loads, extracts the raw data (JSON message). + image.onload = function() { + // Creates a local canvas to draw on. + var canvas = new Canvas(); + var context = canvas.getContext('2d'); + + // Sets width and height. + canvas.width = image.width; + canvas.height = image.height; + + // Prevents anti-aliasing and loosing data + context.imageSmoothingEnabled = false; + context.webkitImageSmoothingEnabled = false; + context.mozImageSmoothingEnabled = false; + + // Puts the data into the image. + context.drawImage(image, 0, 0); + // Grabs the raw, uncompressed data. + var imageData = context.getImageData(0, 0, image.width, image.height).data; + + // Constructs the JSON. + var jsonData = ''; + for (var i = 0; i < imageData.length; i += 4) { + // RGB + jsonData += String.fromCharCode(imageData[i], imageData[i + 1], imageData[i + 2]); + } + callback(JSON.parse(jsonData)); + }; + // Sends the image data to load. + image.src = 'data:image/png;base64,' + data; +} + +module.exports = decompressPng; + +},{"canvas":44}],46:[function(require,module,exports){ +exports.DOMImplementation = window.DOMImplementation; +exports.XMLSerializer = window.XMLSerializer; +exports.DOMParser = window.DOMParser; + +},{}],47:[function(require,module,exports){ +var work = require('webworkify'); +var workerSocketImpl = require('./workerSocketImpl'); + +function WorkerSocket(uri) { + this.socket_ = work(workerSocketImpl); + + this.socket_.addEventListener('message', this.handleWorkerMessage_.bind(this)); + + this.socket_.postMessage({ + uri: uri, + }); +} + +WorkerSocket.prototype.handleWorkerMessage_ = function(ev) { + var data = ev.data; + if (data instanceof ArrayBuffer || typeof data === 'string') { + // binary or JSON message from rosbridge + this.onmessage(ev); + } else { + // control message from the wrapped WebSocket + var type = data.type; + if (type === 'close') { + this.onclose(null); + } else if (type === 'open') { + this.onopen(null); + } else if (type === 'error') { + this.onerror(null); + } else { + throw 'Unknown message from workersocket'; + } + } +}; + +WorkerSocket.prototype.send = function(data) { + this.socket_.postMessage(data); +}; + +WorkerSocket.prototype.close = function() { + this.socket_.postMessage({ + close: true + }); +}; + +module.exports = WorkerSocket; + +},{"./workerSocketImpl":48,"webworkify":5}],48:[function(require,module,exports){ +var WebSocket = WebSocket || require('ws'); + +module.exports = function(self) { + var socket = null; + + function handleSocketMessage(ev) { + var data = ev.data; + + if (data instanceof ArrayBuffer) { + // binary message, transfer for speed + self.postMessage(data, [data]); + } else { + // JSON message, copy string + self.postMessage(data); + } + } + + function handleSocketControl(ev) { + self.postMessage({type: ev.type}); + } + + self.addEventListener('message', function(ev) { + var data = ev.data; + + if (typeof data === 'string') { + // JSON message from ROSLIB + socket.send(data); + } else { + // control message + if (data.hasOwnProperty('close')) { + socket.close(); + socket = null; + } else if (data.hasOwnProperty('uri')) { + var uri = data.uri; + + socket = new WebSocket(uri); + socket.binaryType = 'arraybuffer'; + + socket.onmessage = handleSocketMessage; + socket.onclose = handleSocketControl; + socket.onopen = handleSocketControl; + socket.onerror = handleSocketControl; + } else { + throw 'Unknown message to WorkerSocket'; + } + } + }); +}; + +},{"ws":43}]},{},[7]); diff --git a/include/js/roslib.min.js b/include/js/roslib.min.js index 211b51d..96c6e26 100644 --- a/include/js/roslib.min.js +++ b/include/js/roslib.min.js @@ -1 +1,2 @@ -!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gb.secs?!1:a.secs0&&(this.minval=parseFloat(b[0].getAttribute("lower")),this.maxval=parseFloat(b[0].getAttribute("upper")))}b.exports=d},{}],30:[function(a,b,c){function d(a){this.name=a.xml.getAttribute("name"),this.visuals=[];for(var b=a.xml.getElementsByTagName("visual"),c=0;c0&&(this.textureFilename=b[0].getAttribute("filename"));var c=a.xml.getElementsByTagName("color");c.length>0&&(this.color=new e({xml:c[0]}))}var e=a("./UrdfColor");d.prototype.isLink=function(){return null===this.color&&null===this.textureFilename};var f=a("object-assign");d.prototype.assign=function(a){return f(this,a)},b.exports=d},{"./UrdfColor":27,"object-assign":1}],32:[function(a,b,c){function d(a){this.scale=null,this.type=f.URDF_MESH,this.filename=a.xml.getAttribute("filename");var b=a.xml.getAttribute("scale");if(b){var c=b.split(" ");this.scale=new e({x:parseFloat(c[0]),y:parseFloat(c[1]),z:parseFloat(c[2])})}}var e=a("../math/Vector3"),f=a("./UrdfTypes");b.exports=d},{"../math/Vector3":21,"./UrdfTypes":35}],33:[function(a,b,c){function d(a){a=a||{};var b=a.xml,c=a.string;if(this.materials={},this.links={},this.joints={},c){var d=new h;b=d.parseFromString(c,"text/xml")}var j=b.evaluate("//robot",b,null,i,null).singleNodeValue;this.name=j.getAttribute("name");for(var k=j.childNodes,l=0;l0){for(var A=z[0],B=null,C=0;C0&&(this.material=new j({xml:F[0]}))}var e=a("../math/Pose"),f=a("../math/Vector3"),g=a("../math/Quaternion"),h=a("./UrdfCylinder"),i=a("./UrdfBox"),j=a("./UrdfMaterial"),k=a("./UrdfMesh"),l=a("./UrdfSphere");b.exports=d},{"../math/Pose":18,"../math/Quaternion":19,"../math/Vector3":21,"./UrdfBox":26,"./UrdfCylinder":28,"./UrdfMaterial":31,"./UrdfMesh":32,"./UrdfSphere":34}],37:[function(a,b,c){b.exports=a("object-assign")({UrdfBox:a("./UrdfBox"),UrdfColor:a("./UrdfColor"),UrdfCylinder:a("./UrdfCylinder"),UrdfLink:a("./UrdfLink"),UrdfMaterial:a("./UrdfMaterial"),UrdfMesh:a("./UrdfMesh"),UrdfModel:a("./UrdfModel"),UrdfSphere:a("./UrdfSphere"),UrdfVisual:a("./UrdfVisual")},a("./UrdfTypes"))},{"./UrdfBox":26,"./UrdfColor":27,"./UrdfCylinder":28,"./UrdfLink":30,"./UrdfMaterial":31,"./UrdfMesh":32,"./UrdfModel":33,"./UrdfSphere":34,"./UrdfTypes":35,"./UrdfVisual":36,"object-assign":1}],38:[function(a,b,c){(function(a){b.exports={EventEmitter2:a.EventEmitter2}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],39:[function(a,b,c){(function(a){b.exports=a.WebSocket}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],40:[function(a,b,c){b.exports=function(){return document.createElement("canvas")}},{}]},{},[4]); \ No newline at end of file +!function(){function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c||a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g>2,f=0;f>6),d.push(128|63&g)):g<55296?(d.push(224|g>>12),d.push(128|g>>6&63),d.push(128|63&g)):(g=(1023&g)<<10,g|=1023&a.charCodeAt(++b),g+=65536,d.push(240|g>>18),d.push(128|g>>12&63),d.push(128|g>>6&63),d.push(128|63&g))}return m(3,d.length),i(d);default:var j;if(Array.isArray(a))for(j=a.length,m(4,j),b=0;b>5!==a)throw"Invalid indefinite length element";return c}function s(a,b){for(var c=0;c>10),a.push(56320|1023&d))}}function t(){var a,e,f=l(),g=f>>5,m=31&f;if(7===g)switch(m){case 25:return i();case 26:return j();case 27:return k()}if(e=q(m),e<0&&(g<2||6=0;)o+=e,n.push(h(e));var u=new Uint8Array(o),v=0;for(a=0;a=0;)s(w,e);else s(w,e);return String.fromCharCode.apply(null,w);case 4:var x;if(e<0)for(x=[];!p();)x.push(t());else for(x=new Array(e),a=0;a0&&f._listeners.length>this._maxListeners&&(f._listeners.warned=!0,g.call(this,f._listeners.length,h))):f._listeners=b,!0;h=a.shift()}return!0}var k=Array.isArray?Array.isArray:function(a){return"[object Array]"===Object.prototype.toString.call(a)},l=10;h.EventEmitter2=h,h.prototype.delimiter=".",h.prototype.setMaxListeners=function(a){a!==d&&(this._maxListeners=a,this._conf||(this._conf={}),this._conf.maxListeners=a)},h.prototype.event="",h.prototype.once=function(a,b){return this._once(a,b,!1)},h.prototype.prependOnceListener=function(a,b){return this._once(a,b,!0)},h.prototype._once=function(a,b,c){return this._many(a,1,b,c),this},h.prototype.many=function(a,b,c){return this._many(a,b,c,!1)},h.prototype.prependMany=function(a,b,c){return this._many(a,b,c,!0)},h.prototype._many=function(a,b,c,d){function e(){return 0===--b&&f.off(a,e),c.apply(this,arguments)}var f=this;if("function"!=typeof c)throw new Error("many only accepts instances of Function");return e._origin=c,this._on(a,e,d),f},h.prototype.emit=function(){this._events||e.call(this);var a=arguments[0];if("newListener"===a&&!this.newListener&&!this._events.newListener)return!1;var b,c,d,f,g,h=arguments.length;if(this._all&&this._all.length){if(g=this._all.slice(),h>3)for(b=new Array(h),f=0;f3)for(b=new Array(h-1),f=1;f3)for(b=new Array(j),f=1;f3)for(b=new Array(j-1),f=1;f0&&this._events[a].length>this._maxListeners&&(this._events[a].warned=!0,g.call(this,this._events[a].length,a))):this._events[a]=b,this)},h.prototype.off=function(a,b){function c(a){if(a!==d){var b=Object.keys(a);for(var e in b){var f=b[e],g=a[f];g instanceof Function||"object"!=typeof g||null===g||(Object.keys(g).length>0&&c(a[f]),0===Object.keys(g).length&&delete a[f])}}}if("function"!=typeof b)throw new Error("removeListener only takes instances of Function");var e,f=[];if(this.wildcard){var g="string"==typeof a?a.split(this.delimiter):a.slice();f=i.call(this,null,g,this.listenerTree,0)}else{if(!this._events[a])return this;e=this._events[a],f.push({_listeners:e})}for(var h=0;h0){for(b=this._all,c=0,d=b.length;c1)for(var c=1;cb.secs)&&(a.secs0&&(this.parent=b[0].getAttribute("link"));var c=a.xml.getElementsByTagName("child");c.length>0&&(this.child=c[0].getAttribute("link"));var d=a.xml.getElementsByTagName("limit");d.length>0&&(this.minval=parseFloat(d[0].getAttribute("lower")),this.maxval=parseFloat(d[0].getAttribute("upper")));var h=a.xml.getElementsByTagName("origin");if(0===h.length)this.origin=new e;else{var i=h[0].getAttribute("xyz"),j=new f;i&&(i=i.split(" "),j=new f({x:parseFloat(i[0]),y:parseFloat(i[1]),z:parseFloat(i[2])}));var k=h[0].getAttribute("rpy"),l=new g;if(k){k=k.split(" ");var m=parseFloat(k[0]),n=parseFloat(k[1]),o=parseFloat(k[2]),p=m/2,q=n/2,r=o/2,s=Math.sin(p)*Math.cos(q)*Math.cos(r)-Math.cos(p)*Math.sin(q)*Math.sin(r),t=Math.cos(p)*Math.sin(q)*Math.cos(r)+Math.sin(p)*Math.cos(q)*Math.sin(r),u=Math.cos(p)*Math.cos(q)*Math.sin(r)-Math.sin(p)*Math.sin(q)*Math.cos(r),v=Math.cos(p)*Math.cos(q)*Math.cos(r)+Math.sin(p)*Math.sin(q)*Math.sin(r);l=new g({x:s,y:t,z:u,w:v}),l.normalize()}this.origin=new e({position:j,orientation:l})}}var e=a("../math/Pose"),f=a("../math/Vector3"),g=a("../math/Quaternion");b.exports=d},{"../math/Pose":22,"../math/Quaternion":23,"../math/Vector3":25}],34:[function(a,b,c){function d(a){this.name=a.xml.getAttribute("name"),this.visuals=[];for(var b=a.xml.getElementsByTagName("visual"),c=0;c0&&(this.textureFilename=b[0].getAttribute("filename"));var c=a.xml.getElementsByTagName("color");c.length>0&&(this.color=new e({xml:c[0]}))}var e=a("./UrdfColor");d.prototype.isLink=function(){return null===this.color&&null===this.textureFilename};var f=a("object-assign");d.prototype.assign=function(a){return f(this,a)},b.exports=d},{"./UrdfColor":31,"object-assign":3}],36:[function(a,b,c){function d(a){this.scale=null,this.type=f.URDF_MESH,this.filename=a.xml.getAttribute("filename");var b=a.xml.getAttribute("scale");if(b){var c=b.split(" ");this.scale=new e({x:parseFloat(c[0]),y:parseFloat(c[1]),z:parseFloat(c[2])})}}var e=a("../math/Vector3"),f=a("./UrdfTypes");b.exports=d},{"../math/Vector3":25,"./UrdfTypes":39}],37:[function(a,b,c){function d(a){a=a||{};var b=a.xml,c=a.string;if(this.materials={},this.links={},this.joints={},c){var d=new h;b=d.parseFromString(c,"text/xml")}var i=b.documentElement;this.name=i.getAttribute("name");for(var j=i.childNodes,k=0;k0){for(var A=z[0],B=null,C=0;C0&&(this.material=new j({xml:F[0]}))}var e=a("../math/Pose"),f=a("../math/Vector3"),g=a("../math/Quaternion"),h=a("./UrdfCylinder"),i=a("./UrdfBox"),j=a("./UrdfMaterial"),k=a("./UrdfMesh"),l=a("./UrdfSphere");b.exports=d},{"../math/Pose":22,"../math/Quaternion":23,"../math/Vector3":25,"./UrdfBox":30,"./UrdfCylinder":32,"./UrdfMaterial":35,"./UrdfMesh":36,"./UrdfSphere":38}],41:[function(a,b,c){b.exports=a("object-assign")({UrdfBox:a("./UrdfBox"),UrdfColor:a("./UrdfColor"),UrdfCylinder:a("./UrdfCylinder"),UrdfLink:a("./UrdfLink"),UrdfMaterial:a("./UrdfMaterial"),UrdfMesh:a("./UrdfMesh"),UrdfModel:a("./UrdfModel"),UrdfSphere:a("./UrdfSphere"),UrdfVisual:a("./UrdfVisual")},a("./UrdfTypes"))},{"./UrdfBox":30,"./UrdfColor":31,"./UrdfCylinder":32,"./UrdfLink":34,"./UrdfMaterial":35,"./UrdfMesh":36,"./UrdfModel":37,"./UrdfSphere":38,"./UrdfTypes":39,"./UrdfVisual":40,"object-assign":3}],42:[function(a,b,c){"use strict";function d(){j||(j=!0,console.warn("CBOR 64-bit integer array values may lose precision. No further warnings."))}function e(a){d();for(var b=a.byteLength,c=a.byteOffset,e=b/8,f=a.buffer.slice(c,c+b),g=new Uint32Array(f),h=new Array(e),j=0;j - - - Predefined ROSBridge Connections - - - - - titan.aescape.co - - - - + @@ -149,15 +119,49 @@ Tutorials can be found here: http://www.w3schools.com/bootstrap/default.asp - - + + + Predefined ROSBridge Connections + + + + + titan.aescape.co + + + + + + ROS Topics.