Files
clover/apps/ios/cleverrc/roslib.js
2018-02-19 06:38:24 +03:00

3694 lines
101 KiB
JavaScript
Vendored

(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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/*!
* 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._events.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._events.maxListeners = defaultMaxListeners;
}
}
function logPossibleMemoryLeak(count, eventName) {
var errorMsg = '(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.';
if(this.verboseMemoryLeak){
errorMsg += ' Event name: %s.';
console.error(errorMsg, count, eventName);
} else {
console.error(errorMsg, count);
}
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._events.maxListeners > 0 &&
tree._listeners.length > this._events.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._events || init.call(this);
this._events.maxListeners = n;
if (!this._conf) this._conf = {};
this._conf.maxListeners = n;
}
};
EventEmitter.prototype.event = '';
EventEmitter.prototype.once = function(event, fn) {
this.many(event, 1, fn);
return this;
};
EventEmitter.prototype.many = function(event, ttl, fn) {
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);
}
fn.apply(this, arguments);
}
listener._origin = fn;
this.on(event, listener);
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) {
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) {
if (typeof type === 'function') {
this.onAny(type);
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 append.
this._events[type].push(listener);
// Check for listener leak
if (
!this._events[type].warned &&
this._events.maxListeners > 0 &&
this._events[type].length > this._events.maxListeners
) {
this._events[type].warned = true;
logPossibleMemoryLeak.call(this, this._events[type].length, type);
}
}
return this;
};
EventEmitter.prototype.onAny = function(fn) {
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.
this._all.push(fn);
return this;
};
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
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<leafs.length; iLeaf++) {
var leaf = leafs[iLeaf];
handlers = leaf._listeners;
if (isArray(handlers)) {
var position = -1;
for (var i = 0, length = handlers.length; i < length; i++) {
if (handlers[i] === listener ||
(handlers[i].listener && handlers[i].listener === listener) ||
(handlers[i]._origin && handlers[i]._origin === listener)) {
position = i;
break;
}
}
if (position < 0) {
continue;
}
if(this.wildcard) {
leaf._listeners.splice(position, 1);
}
else {
this._events[type].splice(position, 1);
}
if (handlers.length === 0) {
if(this.wildcard) {
delete leaf._listeners;
}
else {
delete this._events[type];
}
}
this.emit("removeListener", type, listener);
return this;
}
else if (handlers === listener ||
(handlers.listener && handlers.listener === listener) ||
(handlers._origin && handlers._origin === listener)) {
if(this.wildcard) {
delete leaf._listeners;
}
else {
delete this._events[type];
}
this.emit("removeListener", type, listener);
}
}
function recursivelyGarbageCollect(root) {
if (root === undefined) {
return;
}
var keys = Object.keys(root);
for (var i in keys) {
var key = keys[i];
var obj = root[key];
if ((obj instanceof Function) || (typeof obj !== "object") || (obj === null))
continue;
if (Object.keys(obj).length > 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<leafs.length; iLeaf++) {
var leaf = leafs[iLeaf];
leaf._listeners = null;
}
}
else if (this._events) {
this._events[type] = null;
}
return this;
};
EventEmitter.prototype.listeners = function(type) {
if (this.wildcard) {
var handlers = [];
var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
searchListenerTree.call(this, handlers, ns, this.listenerTree, 0);
return handlers;
}
this._events || init.call(this);
if (!this._events[type]) this._events[type] = [];
if (!isArray(this._events[type])) {
this._events[type] = [this._events[type]];
}
return this._events[type];
};
EventEmitter.prototype.listenerCount = function(type) {
return this.listeners(type).length;
};
EventEmitter.prototype.listenersAny = function() {
if(this._all) {
return this._all;
}
else {
return [];
}
};
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(function() {
return EventEmitter;
});
} else if (typeof exports === 'object') {
// CommonJS
module.exports = EventEmitter;
}
else {
// Browser global.
window.EventEmitter2 = EventEmitter;
}
}();
},{}],2:[function(require,module,exports){
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
'use strict';
/* eslint-disable no-unused-vars */
var getOwnPropertySymbols = Object.getOwnPropertySymbols;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var propIsEnumerable = Object.prototype.propertyIsEnumerable;
function toObject(val) {
if (val === null || val === undefined) {
throw new TypeError('Object.assign cannot be called with null or undefined');
}
return Object(val);
}
function shouldUseNative() {
try {
if (!Object.assign) {
return false;
}
// Detect buggy property enumeration order in older V8 versions.
// https://bugs.chromium.org/p/v8/issues/detail?id=4118
var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
test1[5] = 'de';
if (Object.getOwnPropertyNames(test1)[0] === '5') {
return false;
}
// https://bugs.chromium.org/p/v8/issues/detail?id=3056
var test2 = {};
for (var i = 0; i < 10; i++) {
test2['_' + String.fromCharCode(i)] = i;
}
var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
return test2[n];
});
if (order2.join('') !== '0123456789') {
return false;
}
// https://bugs.chromium.org/p/v8/issues/detail?id=3056
var test3 = {};
'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
test3[letter] = letter;
});
if (Object.keys(Object.assign({}, test3)).join('') !==
'abcdefghijklmnopqrst') {
return false;
}
return true;
} catch (err) {
// We don't expect any of the above to throw, but better to be safe.
return false;
}
}
module.exports = shouldUseNative() ? Object.assign : function (target, source) {
var from;
var to = toObject(target);
var symbols;
for (var s = 1; s < arguments.length; s++) {
from = Object(arguments[s]);
for (var key in from) {
if (hasOwnProperty.call(from, key)) {
to[key] = from[key];
}
}
if (getOwnPropertySymbols) {
symbols = getOwnPropertySymbols(from);
for (var i = 0; i < symbols.length; i++) {
if (propIsEnumerable.call(from, symbols[i])) {
to[symbols[i]] = from[symbols[i]];
}
}
}
}
return to;
};
},{}],3:[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.20.0'
};
var assign = require('object-assign');
// Add core components
assign(ROSLIB, require('./core'));
assign(ROSLIB, require('./actionlib'));
assign(ROSLIB, require('./math'));
assign(ROSLIB, require('./tf'));
assign(ROSLIB, require('./urdf'));
module.exports = ROSLIB;
},{"./actionlib":9,"./core":18,"./math":23,"./tf":26,"./urdf":38,"object-assign":2}],4:[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){
/**
* @fileOverview
* @author Russell Toris - rctoris@wpi.edu
*/
var Topic = require('../core/Topic');
var Message = require('../core/Message');
var EventEmitter2 = require('eventemitter2').EventEmitter2;
/**
* An actionlib action client.
*
* Emits the following events:
* * 'timeout' - if a timeout occurred while sending a goal
* * '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'
* * timeout - the timeout length when connecting to the action server
*/
function ActionClient(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;
this.goals = {};
// flag to check if a status has been received
var receivedStatus = false;
// create the topics associated with actionlib
this.feedbackListener = new Topic({
ros : this.ros,
name : this.serverName + '/feedback',
messageType : this.actionName + 'Feedback'
});
this.statusListener = new Topic({
ros : this.ros,
name : this.serverName + '/status',
messageType : 'actionlib_msgs/GoalStatusArray'
});
this.resultListener = new Topic({
ros : this.ros,
name : this.serverName + '/result',
messageType : this.actionName + 'Result'
});
this.goalTopic = new Topic({
ros : this.ros,
name : this.serverName + '/goal',
messageType : this.actionName + 'Goal'
});
this.cancelTopic = new Topic({
ros : this.ros,
name : this.serverName + '/cancel',
messageType : 'actionlib_msgs/GoalID'
});
// advertise the goal and cancel topics
this.goalTopic.advertise();
this.cancelTopic.advertise();
// subscribe to the status topic
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
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
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 timeout specified, emit a 'timeout' event if the action server does not respond
if (this.timeout) {
setTimeout(function() {
if (!receivedStatus) {
that.emit('timeout');
}
}, this.timeout);
}
}
ActionClient.prototype.__proto__ = EventEmitter2.prototype;
/**
* Cancel all goals associated with this ActionClient.
*/
ActionClient.prototype.cancel = function() {
var cancelMessage = new Message();
this.cancelTopic.publish(cancelMessage);
};
/**
* 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":10,"../core/Topic":17,"eventemitter2":1}],6:[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":10,"../core/Topic":17,"eventemitter2":1}],7:[function(require,module,exports){
/**
* @fileOverview
* @author Russell Toris - rctoris@wpi.edu
*/
var Message = require('../core/Message');
var EventEmitter2 = require('eventemitter2').EventEmitter2;
/**
* An actionlib goal goal is associated with an action server.
*
* Emits the following events:
* * 'timeout' - if a timeout occurred while sending a goal
*
* @constructor
* @param object with following keys:
* * actionClient - the ROSLIB.ActionClient to use with this goal
* * goalMessage - The JSON object containing the goal for the action server
*/
function Goal(options) {
var that = this;
this.actionClient = options.actionClient;
this.goalMessage = options.goalMessage;
this.isFinished = false;
// Used to create random IDs
var date = new Date();
// Create a random ID
this.goalID = 'goal_' + Math.random() + '_' + date.getTime();
// Fill in the goal message
this.goalMessage = new Message({
goal_id : {
stamp : {
secs : 0,
nsecs : 0
},
id : this.goalID
},
goal : this.goalMessage
});
this.on('status', function(status) {
that.status = status;
});
this.on('result', function(result) {
that.isFinished = true;
that.result = result;
});
this.on('feedback', function(feedback) {
that.feedback = feedback;
});
// Add the goal
this.actionClient.goals[this.goalID] = this;
}
Goal.prototype.__proto__ = EventEmitter2.prototype;
/**
* Send the goal to the action server.
*
* @param timeout (optional) - a timeout length for the goal's result
*/
Goal.prototype.send = function(timeout) {
var that = this;
that.actionClient.goalTopic.publish(that.goalMessage);
if (timeout) {
setTimeout(function() {
if (!that.isFinished) {
that.emit('timeout');
}
}, timeout);
}
};
/**
* Cancel the current goal.
*/
Goal.prototype.cancel = function() {
var cancelMessage = new Message({
id : this.goalID
});
this.actionClient.cancelTopic.publish(cancelMessage);
};
module.exports = Goal;
},{"../core/Message":10,"eventemitter2":1}],8:[function(require,module,exports){
/**
* @fileOverview
* @author Laura Lindzey - lindzey@gmail.com
*/
var Topic = require('../core/Topic');
var Message = require('../core/Message');
var EventEmitter2 = require('eventemitter2').EventEmitter2;
/**
* An actionlib action server client.
*
* Emits the following events:
* * 'goal' - goal sent by action client
* * 'cancel' - action client has canceled the request
*
* @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 SimpleActionServer(options) {
var that = this;
options = options || {};
this.ros = options.ros;
this.serverName = options.serverName;
this.actionName = options.actionName;
// create and advertise publishers
this.feedbackPublisher = new Topic({
ros : this.ros,
name : this.serverName + '/feedback',
messageType : this.actionName + 'Feedback'
});
this.feedbackPublisher.advertise();
var statusPublisher = new Topic({
ros : this.ros,
name : this.serverName + '/status',
messageType : 'actionlib_msgs/GoalStatusArray'
});
statusPublisher.advertise();
this.resultPublisher = new Topic({
ros : this.ros,
name : this.serverName + '/result',
messageType : this.actionName + 'Result'
});
this.resultPublisher.advertise();
// create and subscribe to listeners
var goalListener = new Topic({
ros : this.ros,
name : this.serverName + '/goal',
messageType : this.actionName + 'Goal'
});
var cancelListener = new Topic({
ros : this.ros,
name : this.serverName + '/cancel',
messageType : 'actionlib_msgs/GoalID'
});
// Track the goals and their status in order to publish status...
this.statusMessage = new Message({
header : {
stamp : {secs : 0, nsecs : 100},
frame_id : ''
},
status_list : []
});
// needed for handling preemption prompted by a new goal being received
this.currentGoal = null; // currently tracked goal
this.nextGoal = null; // the one that'll be preempting
goalListener.subscribe(function(goalMessage) {
if(that.currentGoal) {
that.nextGoal = goalMessage;
// needs to happen AFTER rest is set up
that.emit('cancel');
} else {
that.statusMessage.status_list = [{goal_id : goalMessage.goal_id, status : 1}];
that.currentGoal = goalMessage;
that.emit('goal', goalMessage.goal);
}
});
// helper function for determing ordering of timestamps
// returns t1 < t2
var isEarlier = function(t1, t2) {
if(t1.secs > t2.secs) {
return false;
} else if(t1.secs < t2.secs) {
return true;
} else if(t1.nsecs < t2.nsecs) {
return true;
} else {
return false;
}
};
// TODO: this may be more complicated than necessary, since I'm
// not sure if the callbacks can ever wind up with a scenario
// where we've been preempted by a next goal, it hasn't finished
// processing, and then we get a cancel message
cancelListener.subscribe(function(cancelMessage) {
// cancel ALL goals if both empty
if(cancelMessage.stamp.secs === 0 && cancelMessage.stamp.secs === 0 && cancelMessage.id === '') {
that.nextGoal = null;
if(that.currentGoal) {
that.emit('cancel');
}
} else { // treat id and stamp independently
if(that.currentGoal && cancelMessage.id === that.currentGoal.goal_id.id) {
that.emit('cancel');
} else if(that.nextGoal && cancelMessage.id === that.nextGoal.goal_id.id) {
that.nextGoal = null;
}
if(that.nextGoal && isEarlier(that.nextGoal.goal_id.stamp,
cancelMessage.stamp)) {
that.nextGoal = null;
}
if(that.currentGoal && isEarlier(that.currentGoal.goal_id.stamp,
cancelMessage.stamp)) {
that.emit('cancel');
}
}
});
// publish status at pseudo-fixed rate; required for clients to know they've connected
var statusInterval = setInterval( function() {
var currentTime = new Date();
var secs = Math.floor(currentTime.getTime()/1000);
var nsecs = Math.round(1000000000*(currentTime.getTime()/1000-secs));
that.statusMessage.header.stamp.secs = secs;
that.statusMessage.header.stamp.nsecs = nsecs;
statusPublisher.publish(that.statusMessage);
}, 500); // publish every 500ms
}
SimpleActionServer.prototype.__proto__ = EventEmitter2.prototype;
/**
* Set action state to succeeded and return to client
*/
SimpleActionServer.prototype.setSucceeded = function(result2) {
var resultMessage = new Message({
status : {goal_id : this.currentGoal.goal_id, status : 3},
result : result2
});
this.resultPublisher.publish(resultMessage);
this.statusMessage.status_list = [];
if(this.nextGoal) {
this.currentGoal = this.nextGoal;
this.nextGoal = null;
this.emit('goal', this.currentGoal.goal);
} else {
this.currentGoal = null;
}
};
/**
* Function to send feedback
*/
SimpleActionServer.prototype.sendFeedback = function(feedback2) {
var feedbackMessage = new Message({
status : {goal_id : this.currentGoal.goal_id, status : 1},
feedback : feedback2
});
this.feedbackPublisher.publish(feedbackMessage);
};
/**
* Handle case where client requests preemption
*/
SimpleActionServer.prototype.setPreempted = function() {
this.statusMessage.status_list = [];
var resultMessage = new Message({
status : {goal_id : this.currentGoal.goal_id, status : 2},
});
this.resultPublisher.publish(resultMessage);
if(this.nextGoal) {
this.currentGoal = this.nextGoal;
this.nextGoal = null;
this.emit('goal', this.currentGoal.goal);
} else {
this.currentGoal = null;
}
};
module.exports = SimpleActionServer;
},{"../core/Message":10,"../core/Topic":17,"eventemitter2":1}],9:[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":12,"../mixin":24,"./ActionClient":5,"./ActionListener":6,"./Goal":7,"./SimpleActionServer":8}],10:[function(require,module,exports){
/**
* @fileoverview
* @author Brandon Alexander - baalexander@gmail.com
*/
var assign = require('object-assign');
/**
* Message objects are used for publishing and subscribing to and from topics.
*
* @constructor
* @param values - object matching the fields defined in the .msg definition file
*/
function Message(values) {
assign(this, values);
}
module.exports = Message;
},{"object-assign":2}],11:[function(require,module,exports){
/**
* @fileoverview
* @author Brandon Alexander - baalexander@gmail.com
*/
var Service = require('./Service');
var ServiceRequest = require('./ServiceRequest');
/**
* A ROS parameter.
*
* @constructor
* @param options - possible keys include:
* * ros - the ROSLIB.Ros connection handle
* * name - the param name, like max_vel_x
*/
function Param(options) {
options = options || {};
this.ros = options.ros;
this.name = options.name;
}
/**
* Fetches the value of the param.
*
* @param callback - function with the following params:
* * value - the value of the param from ROS.
*/
Param.prototype.get = function(callback) {
var paramClient = new Service({
ros : this.ros,
name : '/rosapi/get_param',
serviceType : 'rosapi/GetParam'
});
var request = new ServiceRequest({
name : this.name
});
paramClient.callService(request, function(result) {
var value = JSON.parse(result.value);
callback(value);
});
};
/**
* Sets the value of the param in ROS.
*
* @param value - value to set param to.
*/
Param.prototype.set = function(value, callback) {
var paramClient = new Service({
ros : this.ros,
name : '/rosapi/set_param',
serviceType : 'rosapi/SetParam'
});
var request = new ServiceRequest({
name : this.name,
value : JSON.stringify(value)
});
paramClient.callService(request, callback);
};
/**
* Delete this parameter on the ROS server.
*/
Param.prototype.delete = function(callback) {
var paramClient = new Service({
ros : this.ros,
name : '/rosapi/delete_param',
serviceType : 'rosapi/DeleteParam'
});
var request = new ServiceRequest({
name : this.name
});
paramClient.callService(request, callback);
};
module.exports = Param;
},{"./Service":13,"./ServiceRequest":14}],12:[function(require,module,exports){
/**
* @fileoverview
* @author Brandon Alexander - baalexander@gmail.com
*/
var WebSocket = require('ws');
var socketAdapter = require('./SocketAdapter.js');
var Service = require('./Service');
var ServiceRequest = require('./ServiceRequest');
var assign = require('object-assign');
var EventEmitter2 = require('eventemitter2').EventEmitter2;
/**
* Manages connection to the server and all interactions with ROS.
*
* Emits the following events:
* * 'error' - there was an error with ROS
* * 'connection' - connected to the WebSocket server
* * 'close' - disconnected to the WebSocket server
* * <topicName> - a message came from rosbridge with the given topic name
* * <serviceID> - a service response came from rosbridge with the given ID
*
* @constructor
* @param options - possible keys include: <br>
* * 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) <br>
* * 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' (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;
}
else {
this.groovyCompatibility = options.groovyCompatibility;
}
// Sets unlimited event listeners.
this.setMaxListeners(0);
// begin by checking if a URL was given
if (options.url) {
this.connect(options.url);
}
}
Ros.prototype.__proto__ = EventEmitter2.prototype;
/**
* Connect to the specified WebSocket.
*
* @param url - WebSocket URL or RTCDataChannel label for Rosbridge
*/
Ros.prototype.connect = function(url) {
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 {
this.socket = assign(new WebSocket(url), socketAdapter(this));
}
};
/**
* Disconnect from the WebSocket server.
*/
Ros.prototype.close = function() {
if (this.socket) {
this.socket.close();
}
};
/**
* Sends an authorization request to the server.
*
* @param mac - MAC (hash) string given by the trusted source.
* @param client - IP of the client.
* @param dest - IP of the destination.
* @param rand - Random string given by the trusted source.
* @param t - Time of the authorization request.
* @param level - User level as a string given by the client.
* @param end - End time of the client's session.
*/
Ros.prototype.authenticate = function(mac, client, dest, rand, t, level, end) {
// create the request
var auth = {
op : 'auth',
mac : mac,
client : client,
dest : dest,
rand : rand,
t : t,
level : level,
end : end
};
// send the request
this.callOnConnection(auth);
};
/**
* Sends the message over the WebSocket, but queues the message up if not yet
* connected.
*/
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() {
emitter(messageJson);
});
} else {
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);
});
}
};
/**
* Retrieves list of topics in ROS as an array.
*
* @param callback function with params:
* * topics - Array of topic names
*/
Ros.prototype.getTopics = function(callback, failedCallback) {
var topicsClient = new Service({
ros : this,
name : '/rosapi/topics',
serviceType : 'rosapi/Topics'
});
var request = new ServiceRequest();
if (typeof failedCallback === 'function'){
topicsClient.callService(request,
function(result) {
callback(result);
},
function(message){
failedCallback(message);
}
);
}else{
topicsClient.callService(request, function(result) {
callback(result);
});
}
};
/**
* Retrieves Topics in ROS as an array as specific type
*
* @param topicType topic type to find:
* @param callback function with params:
* * topics - Array of topic names
*/
Ros.prototype.getTopicsForType = function(topicType, callback, failedCallback) {
var topicsForTypeClient = new Service({
ros : this,
name : '/rosapi/topics_for_type',
serviceType : 'rosapi/TopicsForType'
});
var request = new ServiceRequest({
type: topicType
});
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);
});
}
};
/**
* Retrieves list of active service names in ROS.
*
* @param callback - function with the following params:
* * services - array of service names
*/
Ros.prototype.getServices = function(callback, failedCallback) {
var servicesClient = new Service({
ros : this,
name : '/rosapi/services',
serviceType : 'rosapi/Services'
});
var request = new ServiceRequest();
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);
});
}
};
/**
* Retrieves list of services in ROS as an array as specific type
*
* @param serviceType service type to find:
* @param callback function with params:
* * topics - Array of service names
*/
Ros.prototype.getServicesForType = function(serviceType, callback, failedCallback) {
var servicesForTypeClient = new Service({
ros : this,
name : '/rosapi/services_for_type',
serviceType : 'rosapi/ServicesForType'
});
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);
});
}
};
/**
* 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);
});
}
};
/**
* Retrieves list of active node names in ROS.
*
* @param callback - function with the following params:
* * nodes - array of node names
*/
Ros.prototype.getNodes = function(callback, failedCallback) {
var nodesClient = new Service({
ros : this,
name : '/rosapi/nodes',
serviceType : 'rosapi/Nodes'
});
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);
});
}
};
/**
* 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);
});
}
};
/**
* Retrieves list of param names from the ROS Parameter Server.
*
* @param callback function with params:
* * params - array of param names.
*/
Ros.prototype.getParams = function(callback, failedCallback) {
var paramsClient = new Service({
ros : this,
name : '/rosapi/get_param_names',
serviceType : 'rosapi/GetParamNames'
});
var request = new ServiceRequest();
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, failedCallback) {
var topicTypeClient = new Service({
ros : this,
name : '/rosapi/topic_type',
serviceType : 'rosapi/TopicType'
});
var request = new ServiceRequest({
topic: topic
});
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);
});
}
};
/**
* Retrieves a detail of ROS message.
*
* @param callback - function with params:
* * details - Array of the message detail
* @param message - String of a topic type
*/
Ros.prototype.getMessageDetails = function(message, callback, failedCallback) {
var messageDetailClient = new Service({
ros : this,
name : '/rosapi/message_details',
serviceType : 'rosapi/MessageDetails'
});
var request = new ServiceRequest({
type: message
});
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);
});
}
};
/**
* Decode a typedefs into a dictionary like `rosmsg show foo/bar`
*
* @param defs - array of type_def dictionary
*/
Ros.prototype.decodeTypeDefs = function(defs) {
var that = this;
// calls itself recursively to resolve type definition using hints.
var decodeTypeDefsRec = function(theType, hints) {
var typeDefDict = {};
for (var i = 0; i < theType.fieldnames.length; i++) {
var arrayLen = theType.fieldarraylen[i];
var fieldName = theType.fieldnames[i];
var fieldType = theType.fieldtypes[i];
if (fieldType.indexOf('/') === -1) { // check the fieldType includes '/' or not
if (arrayLen === -1) {
typeDefDict[fieldName] = fieldType;
}
else {
typeDefDict[fieldName] = [fieldType];
}
}
else {
// lookup the name
var sub = false;
for (var j = 0; j < hints.length; j++) {
if (hints[j].type.toString() === fieldType.toString()) {
sub = hints[j];
break;
}
}
if (sub) {
var subResult = decodeTypeDefsRec(sub, hints);
if (arrayLen === -1) {
}
else {
typeDefDict[fieldName] = [subResult];
}
}
else {
that.emit('error', 'Cannot find ' + fieldType + ' in decodeTypeDefs');
}
}
}
return typeDefDict;
};
return decodeTypeDefsRec(defs[0], defs);
};
module.exports = Ros;
},{"./Service":13,"./ServiceRequest":14,"./SocketAdapter.js":16,"eventemitter2":1,"object-assign":2,"ws":39}],13:[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.
*
* @constructor
* @params options - possible keys include:
* * ros - the ROSLIB.Ros connection handle
* * name - the service name, like /add_two_ints
* * serviceType - the service type, like 'rospy_tutorials/AddTwoInts'
*/
function Service(options) {
options = 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.
*
* @param request - the ROSLIB.ServiceRequest to send
* @param callback - function with params:
* * response - the response from the service request
* @param failedCallback - the callback function when the service call failed (optional). Params:
* * 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) {
this.ros.once(serviceCallId, function(message) {
if (message.result !== undefined && message.result === false) {
if (typeof failedCallback === 'function') {
failedCallback(message.values);
}
} else if (typeof callback === 'function') {
callback(new ServiceResponse(message.values));
}
});
}
var call = {
op : 'call_service',
id : serviceCallId,
service : this.name,
args : request
};
this.ros.callOnConnection(call);
};
/**
* Every time a message is published for the given topic, the callback
* will be called with the message object.
*
* @param callback - function with the following params:
* * message - the published message
*/
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":14,"./ServiceResponse":15,"eventemitter2":1}],14:[function(require,module,exports){
/**
* @fileoverview
* @author Brandon Alexander - balexander@willowgarage.com
*/
var assign = require('object-assign');
/**
* A ServiceRequest is passed into the service call.
*
* @constructor
* @param values - object matching the fields defined in the .srv definition file
*/
function ServiceRequest(values) {
assign(this, values);
}
module.exports = ServiceRequest;
},{"object-assign":2}],15:[function(require,module,exports){
/**
* @fileoverview
* @author Brandon Alexander - balexander@willowgarage.com
*/
var assign = require('object-assign');
/**
* A ServiceResponse is returned from the service call.
*
* @constructor
* @param values - object matching the fields defined in the .srv definition file
*/
function ServiceResponse(values) {
assign(this, values);
}
module.exports = ServiceResponse;
},{"object-assign":2}],16:[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 decompressPng = require('../util/decompressPng');
var WebSocket = require('ws');
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) {
if (message.op === 'publish') {
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;
client.emit('connection', event);
},
/**
* 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;
client.emit('close', event);
},
/**
* 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);
},
/**
* Parses message responses from rosbridge and sends to the appropriate
* topic, service, or param.
*
* @param message - the raw JSON message from rosbridge.
* @memberof SocketAdapter
*/
onmessage: function onMessage(data) {
if (typeof Blob !== 'undefined' && data.data instanceof Blob) {
decodeBSON(data.data, function (message) {
handlePng(message, handleMessage);
});
} else {
var message = JSON.parse(typeof data === 'string' ? data : data.data);
handlePng(message, handleMessage);
}
}
};
}
module.exports = SocketAdapter;
},{"../util/decompressPng":41,"ws":39}],17:[function(require,module,exports){
/**
* @fileoverview
* @author Brandon Alexander - baalexander@gmail.com
*/
var EventEmitter2 = require('eventemitter2').EventEmitter2;
var Message = require('./Message');
/**
* Publish and/or subscribe to a topic in ROS.
*
* Emits the following events:
* * 'warning' - if there are any warning during the Topic creation
* * 'message' - the message data from rosbridge
*
* @constructor
* @param options - object with following keys:
* * 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'
* * 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 || {};
this.ros = options.ros;
this.name = options.name;
this.messageType = options.messageType;
this.isAdvertised = false;
this.compression = options.compression || 'none';
this.throttle_rate = options.throttle_rate || 0;
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 || true;
// Check for valid compression types
if (this.compression && this.compression !== 'png' &&
this.compression !== 'none') {
this.emit('warning', this.compression +
' compression is not supported. No compression will be used.');
}
// Check if throttle rate is negative
if (this.throttle_rate < 0) {
this.emit('warning', this.throttle_rate + ' is not allowed. Set to 0');
this.throttle_rate = 0;
}
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));
};
}
Topic.prototype.__proto__ = EventEmitter2.prototype;
/**
* Every time a message is published for the given topic, the callback
* will be called with the message object.
*
* @param callback - function with the following params:
* * message - the published message
*/
Topic.prototype.subscribe = function(callback) {
if (typeof callback === 'function') {
this.on('message', callback);
}
if (this.subscribeId) { return; }
this.ros.on(this.name, this._messageCallback);
this.subscribeId = 'subscribe:' + this.name + ':' + (++this.ros.idCounter);
this.callForSubscribeAndAdvertise({
op: 'subscribe',
id: this.subscribeId,
type: this.messageType,
topic: this.name,
compression: this.compression,
throttle_rate: this.throttle_rate,
queue_length: this.queue_length
});
};
/**
* Unregisters as a subscriber for the topic. Unsubscribing stop remove
* all subscribe callbacks. To remove a call back, you must explicitly
* pass the callback function in.
*
* @param callback - the optional callback to unregister, if
* * provided and other listeners are registered the topic won't
* * unsubscribe, just stop emitting to the passed listener
*/
Topic.prototype.unsubscribe = function(callback) {
if (callback) {
this.off('message', callback);
// If there is any other callbacks still subscribed don't unsubscribe
if (this.listeners('message').length) { return; }
}
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',
id: this.subscribeId,
topic: this.name
});
this.subscribeId = null;
};
/**
* Registers as a publisher for the topic.
*/
Topic.prototype.advertise = function() {
if (this.isAdvertised) {
return;
}
this.advertiseId = 'advertise:' + this.name + ':' + (++this.ros.idCounter);
this.callForSubscribeAndAdvertise({
op: 'advertise',
id: this.advertiseId,
type: this.messageType,
topic: this.name,
latch: this.latch,
queue_size: this.queue_size
});
this.isAdvertised = true;
if(!this.reconnect_on_close) {
var that = this;
this.ros.on('close', function() {
that.isAdvertised = false;
});
}
};
/**
* Unregisters as a publisher for the topic.
*/
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',
id: this.advertiseId,
topic: this.name
});
this.isAdvertised = false;
};
/**
* Publish the message.
*
* @param message - A ROSLIB.Message object.
*/
Topic.prototype.publish = function(message) {
if (!this.isAdvertised) {
this.advertise();
}
this.ros.idCounter++;
var call = {
op: 'publish',
id: 'publish:' + this.name + ':' + this.ros.idCounter,
topic: this.name,
msg: message,
latch: this.latch
};
this.ros.callOnConnection(call);
};
module.exports = Topic;
},{"./Message":10,"eventemitter2":1}],18:[function(require,module,exports){
var mixin = require('../mixin');
var core = module.exports = {
Ros: require('./Ros'),
Topic: require('./Topic'),
Message: require('./Message'),
Param: require('./Param'),
Service: require('./Service'),
ServiceRequest: require('./ServiceRequest'),
ServiceResponse: require('./ServiceResponse')
};
mixin(core.Ros, ['Param', 'Service', 'Topic'], core);
},{"../mixin":24,"./Message":10,"./Param":11,"./Ros":12,"./Service":13,"./ServiceRequest":14,"./ServiceResponse":15,"./Topic":17}],19:[function(require,module,exports){
/**
* @fileoverview
* @author David Gossow - dgossow@willowgarage.com
*/
var Vector3 = require('./Vector3');
var Quaternion = require('./Quaternion');
/**
* A Pose in 3D space. Values are copied into this object.
*
* @constructor
* @param options - object with following keys:
* * position - the Vector3 describing the position
* * orientation - the ROSLIB.Quaternion describing the orientation
*/
function Pose(options) {
options = options || {};
// copy the values into this object if they exist
this.position = new Vector3(options.position);
this.orientation = new Quaternion(options.orientation);
}
/**
* Apply a transform against this pose.
*
* @param tf the transform
*/
Pose.prototype.applyTransform = function(tf) {
this.position.multiplyQuaternion(tf.rotation);
this.position.add(tf.translation);
var tmp = tf.rotation.clone();
tmp.multiply(this.orientation);
this.orientation = tmp;
};
/**
* Clone a copy of this pose.
*
* @returns the cloned pose
*/
Pose.prototype.clone = function() {
return new Pose(this);
};
module.exports = Pose;
},{"./Quaternion":20,"./Vector3":22}],20:[function(require,module,exports){
/**
* @fileoverview
* @author David Gossow - dgossow@willowgarage.com
*/
/**
* A Quaternion.
*
* @constructor
* @param options - object with following keys:
* * x - the x value
* * y - the y value
* * z - the z value
* * w - the w value
*/
function Quaternion(options) {
options = options || {};
this.x = options.x || 0;
this.y = options.y || 0;
this.z = options.z || 0;
this.w = (typeof options.w === 'number') ? options.w : 1;
}
/**
* Perform a conjugation on this quaternion.
*/
Quaternion.prototype.conjugate = function() {
this.x *= -1;
this.y *= -1;
this.z *= -1;
};
/**
* Return the norm of this quaternion.
*/
Quaternion.prototype.norm = function() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
};
/**
* Perform a normalization on this quaternion.
*/
Quaternion.prototype.normalize = function() {
var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
if (l === 0) {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 1;
} else {
l = 1 / l;
this.x = this.x * l;
this.y = this.y * l;
this.z = this.z * l;
this.w = this.w * l;
}
};
/**
* Convert this quaternion into its inverse.
*/
Quaternion.prototype.invert = function() {
this.conjugate();
this.normalize();
};
/**
* Set the values of this quaternion to the product of itself and the given quaternion.
*
* @param q the quaternion to multiply with
*/
Quaternion.prototype.multiply = function(q) {
var newX = this.x * q.w + this.y * q.z - this.z * q.y + this.w * q.x;
var newY = -this.x * q.z + this.y * q.w + this.z * q.x + this.w * q.y;
var newZ = this.x * q.y - this.y * q.x + this.z * q.w + this.w * q.z;
var newW = -this.x * q.x - this.y * q.y - this.z * q.z + this.w * q.w;
this.x = newX;
this.y = newY;
this.z = newZ;
this.w = newW;
};
/**
* Clone a copy of this quaternion.
*
* @returns the cloned quaternion
*/
Quaternion.prototype.clone = function() {
return new Quaternion(this);
};
module.exports = Quaternion;
},{}],21:[function(require,module,exports){
/**
* @fileoverview
* @author David Gossow - dgossow@willowgarage.com
*/
var Vector3 = require('./Vector3');
var Quaternion = require('./Quaternion');
/**
* A Transform in 3-space. Values are copied into this object.
*
* @constructor
* @param options - object with following keys:
* * translation - the Vector3 describing the translation
* * rotation - the ROSLIB.Quaternion describing the rotation
*/
function Transform(options) {
options = options || {};
// Copy the values into this object if they exist
this.translation = new Vector3(options.translation);
this.rotation = new Quaternion(options.rotation);
}
/**
* Clone a copy of this transform.
*
* @returns the cloned transform
*/
Transform.prototype.clone = function() {
return new Transform(this);
};
module.exports = Transform;
},{"./Quaternion":20,"./Vector3":22}],22:[function(require,module,exports){
/**
* @fileoverview
* @author David Gossow - dgossow@willowgarage.com
*/
/**
* A 3D vector.
*
* @constructor
* @param options - object with following keys:
* * x - the x value
* * y - the y value
* * z - the z value
*/
function Vector3(options) {
options = options || {};
this.x = options.x || 0;
this.y = options.y || 0;
this.z = options.z || 0;
}
/**
* Set the values of this vector to the sum of itself and the given vector.
*
* @param v the vector to add with
*/
Vector3.prototype.add = function(v) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
};
/**
* Set the values of this vector to the difference of itself and the given vector.
*
* @param v the vector to subtract with
*/
Vector3.prototype.subtract = function(v) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
};
/**
* Multiply the given Quaternion with this vector.
*
* @param q - the quaternion to multiply with
*/
Vector3.prototype.multiplyQuaternion = function(q) {
var ix = q.w * this.x + q.y * this.z - q.z * this.y;
var iy = q.w * this.y + q.z * this.x - q.x * this.z;
var iz = q.w * this.z + q.x * this.y - q.y * this.x;
var iw = -q.x * this.x - q.y * this.y - q.z * this.z;
this.x = ix * q.w + iw * -q.x + iy * -q.z - iz * -q.y;
this.y = iy * q.w + iw * -q.y + iz * -q.x - ix * -q.z;
this.z = iz * q.w + iw * -q.z + ix * -q.y - iy * -q.x;
};
/**
* Clone a copy of this vector.
*
* @returns the cloned vector
*/
Vector3.prototype.clone = function() {
return new Vector3(this);
};
module.exports = Vector3;
},{}],23:[function(require,module,exports){
module.exports = {
Pose: require('./Pose'),
Quaternion: require('./Quaternion'),
Transform: require('./Transform'),
Vector3: require('./Vector3')
};
},{"./Pose":19,"./Quaternion":20,"./Transform":21,"./Vector3":22}],24:[function(require,module,exports){
/**
* Mixin a feature to the core/Ros prototype.
* For example, mixin(Ros, ['Topic'], {Topic: <Topic>})
* will add a topic bound to any Ros instances so a user
* can call `var topic = ros.Topic({name: '/foo'});`
*
* @author Graeme Yeates - github.com/megawac
*/
module.exports = function(Ros, classes, features) {
classes.forEach(function(className) {
var Class = features[className];
Ros.prototype[className] = function(options) {
options.ros = this;
return new Class(options);
};
});
};
},{}],25:[function(require,module,exports){
/**
* @fileoverview
* @author David Gossow - dgossow@willowgarage.com
*/
var ActionClient = require('../actionlib/ActionClient');
var Goal = require('../actionlib/Goal');
var Service = require('../core/Service.js');
var ServiceRequest = require('../core/ServiceRequest.js');
var Transform = require('../math/Transform');
/**
* A TF Client that listens to TFs from tf2_web_republisher.
*
* @constructor
* @param options - object with following keys:
* * ros - the ROSLIB.Ros connection handle
* * fixedFrame - the fixed frame, like /base_link
* * angularThres - the angular threshold for the TF republisher
* * transThres - the translation threshold for the TF republisher
* * rate - the rate for the TF republisher
* * 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 || {};
this.ros = options.ros;
this.fixedFrame = options.fixedFrame || '/base_link';
this.angularThres = options.angularThres || 2.0;
this.transThres = options.transThres || 0.01;
this.rate = options.rate || 10.0;
this.updateDelay = options.updateDelay || 50;
var seconds = options.topicTimeout || 2.0;
var secs = Math.floor(seconds);
var nsecs = Math.floor((seconds - secs) * 1000000000);
this.topicTimeout = {
secs: secs,
nsecs: nsecs
};
this.serverName = options.serverName || '/tf2_web_republisher';
this.repubServiceName = options.repubServiceName || '/republish_tfs';
this.currentGoal = false;
this.currentTopic = false;
this.frameInfos = {};
this.republisherUpdateRequested = false;
// Create an Action client
this.actionClient = this.ros.ActionClient({
serverName : this.serverName,
actionName : 'tf2_web_republisher/TFSubscriptionAction',
omitStatus : true,
omitResult : true
});
// Create a Service client
this.serviceClient = this.ros.Service({
name: this.repubServiceName,
serviceType: 'tf2_web_republisher/RepublishTFs'
});
}
/**
* Process the incoming TF message and send them out using the callback
* functions.
*
* @param tf - the TF message from the server
*/
TFClient.prototype.processTFArray = function(tf) {
var that = this;
tf.transforms.forEach(function(transform) {
var frameID = transform.child_frame_id;
if (frameID[0] === '/')
{
frameID = frameID.substring(1);
}
var info = this.frameInfos[frameID];
if (info) {
info.transform = new Transform({
translation : transform.transform.translation,
rotation : transform.transform.rotation
});
info.cbs.forEach(function(cb) {
cb(info.transform);
});
}
}, this);
};
/**
* Create and send a new goal (or service request) to the tf2_web_republisher
* based on the current list of TFs.
*/
TFClient.prototype.updateGoal = function() {
var goalMessage = {
source_frames : Object.keys(this.frameInfos),
target_frame : this.fixedFrame,
angular_thres : this.angularThres,
trans_thres : this.transThres,
rate : this.rate
};
// if we're running in groovy compatibility mode (the default)
// then use the action interface to tf2_web_republisher
if(this.ros.groovyCompatibility) {
if (this.currentGoal) {
this.currentGoal.cancel();
}
this.currentGoal = new Goal({
actionClient : this.actionClient,
goalMessage : goalMessage
});
this.currentGoal.on('feedback', this.processTFArray.bind(this));
this.currentGoal.send();
}
else {
// otherwise, use the service interface
// The service interface has the same parameters as the action,
// plus the timeout
goalMessage.timeout = this.topicTimeout;
var request = new ServiceRequest(goalMessage);
this.serviceClient.callService(request, this.processResponse.bind(this));
}
this.republisherUpdateRequested = false;
};
/**
* Process the service response and subscribe to the tf republisher
* topic
*
* @param response the service response containing the topic name
*/
TFClient.prototype.processResponse = function(response) {
// if we subscribed to a topic before, unsubscribe so
// the republisher stops publishing it
if (this.currentTopic) {
this.currentTopic.unsubscribe();
}
this.currentTopic = this.ros.Topic({
name: response.topic_name,
messageType: 'tf2_web_republisher/TFArray'
});
this.currentTopic.subscribe(this.processTFArray.bind(this));
};
/**
* Subscribe to the given TF frame.
*
* @param frameID - the TF frame to subscribe to
* @param callback - function with params:
* * transform - the transform data
*/
TFClient.prototype.subscribe = function(frameID, callback) {
// remove leading slash, if it's there
if (frameID[0] === '/')
{
frameID = frameID.substring(1);
}
// if there is no callback registered for the given frame, create emtpy callback list
if (!this.frameInfos[frameID]) {
this.frameInfos[frameID] = {
cbs: []
};
if (!this.republisherUpdateRequested) {
setTimeout(this.updateGoal.bind(this), this.updateDelay);
this.republisherUpdateRequested = true;
}
}
// if we already have a transform, call back immediately
else if (this.frameInfos[frameID].transform) {
callback(this.frameInfos[frameID].transform);
}
this.frameInfos[frameID].cbs.push(callback);
};
/**
* Unsubscribe from the given TF frame.
*
* @param frameID - the TF frame to unsubscribe from
* @param callback - the callback function to remove
*/
TFClient.prototype.unsubscribe = function(frameID, callback) {
// remove leading slash, if it's there
if (frameID[0] === '/')
{
frameID = frameID.substring(1);
}
var info = this.frameInfos[frameID];
for (var cbs = info && info.cbs || [], idx = cbs.length; idx--;) {
if (cbs[idx] === callback) {
cbs.splice(idx, 1);
}
}
if (!callback || cbs.length === 0) {
delete this.frameInfos[frameID];
}
};
/**
* 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":7,"../core/Service.js":13,"../core/ServiceRequest.js":14,"../math/Transform":21}],26:[function(require,module,exports){
var Ros = require('../core/Ros');
var mixin = require('../mixin');
var tf = module.exports = {
TFClient: require('./TFClient')
};
mixin(Ros, ['TFClient'], tf);
},{"../core/Ros":12,"../mixin":24,"./TFClient":25}],27:[function(require,module,exports){
/**
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
var Vector3 = require('../math/Vector3');
var UrdfTypes = require('./UrdfTypes');
/**
* A Box element in a URDF.
*
* @constructor
* @param options - object with following keys:
* * xml - the XML element to parse
*/
function UrdfBox(options) {
this.dimension = null;
this.type = UrdfTypes.URDF_BOX;
// Parse the xml string
var xyz = options.xml.getAttribute('size').split(' ');
this.dimension = new Vector3({
x : parseFloat(xyz[0]),
y : parseFloat(xyz[1]),
z : parseFloat(xyz[2])
});
}
module.exports = UrdfBox;
},{"../math/Vector3":22,"./UrdfTypes":36}],28:[function(require,module,exports){
/**
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
/**
* A Color element in a URDF.
*
* @constructor
* @param options - object with following keys:
* * xml - the XML element to parse
*/
function UrdfColor(options) {
// Parse the xml string
var rgba = options.xml.getAttribute('rgba').split(' ');
this.r = parseFloat(rgba[0]);
this.g = parseFloat(rgba[1]);
this.b = parseFloat(rgba[2]);
this.a = parseFloat(rgba[3]);
}
module.exports = UrdfColor;
},{}],29:[function(require,module,exports){
/**
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
var UrdfTypes = require('./UrdfTypes');
/**
* A Cylinder element in a URDF.
*
* @constructor
* @param options - object with following keys:
* * xml - the XML element to parse
*/
function UrdfCylinder(options) {
this.type = UrdfTypes.URDF_CYLINDER;
this.length = parseFloat(options.xml.getAttribute('length'));
this.radius = parseFloat(options.xml.getAttribute('radius'));
}
module.exports = UrdfCylinder;
},{"./UrdfTypes":36}],30:[function(require,module,exports){
/**
* @fileOverview
* @author David V. Lu!! davidvlu@gmail.com
*/
/**
* A Joint element in a URDF.
*
* @constructor
* @param options - object with following keys:
* * xml - the XML element to parse
*/
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') );
}
}
module.exports = UrdfJoint;
},{}],31:[function(require,module,exports){
/**
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
var UrdfVisual = require('./UrdfVisual');
/**
* A Link element in a URDF.
*
* @constructor
* @param options - object with following keys:
* * xml - the XML element to parse
*/
function UrdfLink(options) {
this.name = options.xml.getAttribute('name');
this.visuals = [];
var visuals = options.xml.getElementsByTagName('visual');
for( var i=0; i<visuals.length; i++ ) {
this.visuals.push( new UrdfVisual({
xml : visuals[i]
}) );
}
}
module.exports = UrdfLink;
},{"./UrdfVisual":37}],32:[function(require,module,exports){
/**
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
var UrdfColor = require('./UrdfColor');
/**
* A Material element in a URDF.
*
* @constructor
* @param options - object with following keys:
* * xml - the XML element to parse
*/
function UrdfMaterial(options) {
this.textureFilename = null;
this.color = null;
this.name = options.xml.getAttribute('name');
// Texture
var textures = options.xml.getElementsByTagName('texture');
if (textures.length > 0) {
this.textureFilename = textures[0].getAttribute('filename');
}
// Color
var colors = options.xml.getElementsByTagName('color');
if (colors.length > 0) {
// Parse the RBGA string
this.color = new UrdfColor({
xml : colors[0]
});
}
}
UrdfMaterial.prototype.isLink = function() {
return this.color === null && this.textureFilename === null;
};
var assign = require('object-assign');
UrdfMaterial.prototype.assign = function(obj) {
return assign(this, obj);
};
module.exports = UrdfMaterial;
},{"./UrdfColor":28,"object-assign":2}],33:[function(require,module,exports){
/**
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
var Vector3 = require('../math/Vector3');
var UrdfTypes = require('./UrdfTypes');
/**
* A Mesh element in a URDF.
*
* @constructor
* @param options - object with following keys:
* * xml - the XML element to parse
*/
function UrdfMesh(options) {
this.scale = null;
this.type = UrdfTypes.URDF_MESH;
this.filename = options.xml.getAttribute('filename');
// Check for a scale
var scale = options.xml.getAttribute('scale');
if (scale) {
// Get the XYZ
var xyz = scale.split(' ');
this.scale = new Vector3({
x : parseFloat(xyz[0]),
y : parseFloat(xyz[1]),
z : parseFloat(xyz[2])
});
}
}
module.exports = UrdfMesh;
},{"../math/Vector3":22,"./UrdfTypes":36}],34:[function(require,module,exports){
/**
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
var UrdfMaterial = require('./UrdfMaterial');
var UrdfLink = require('./UrdfLink');
var UrdfJoint = require('./UrdfJoint');
var DOMParser = require('xmldom').DOMParser;
// See https://developer.mozilla.org/docs/XPathResult#Constants
var XPATH_FIRST_ORDERED_NODE_TYPE = 9;
/**
* A URDF Model can be used to parse a given URDF into the appropriate elements.
*
* @constructor
* @param options - object with following keys:
* * xml - the XML element to parse
* * string - the XML element to parse as a string
*/
function UrdfModel(options) {
options = options || {};
var xmlDoc = options.xml;
var string = options.string;
this.materials = {};
this.links = {};
this.joints = {};
// Check if we are using a string or an XML element
if (string) {
// Parse the string
var parser = new DOMParser();
xmlDoc = parser.parseFromString(string, 'text/xml');
}
// Initialize the model with the given XML node.
// Get the robot tag
var robotXml = xmlDoc.documentElement;
// Get the robot name
this.name = robotXml.getAttribute('name');
// Parse all the visual elements we need
for (var nodes = robotXml.childNodes, i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node.tagName === 'material') {
var material = new UrdfMaterial({
xml : node
});
// Make sure this is unique
if (this.materials[material.name] !== void 0) {
if( this.materials[material.name].isLink() ) {
this.materials[material.name].assign( material );
} else {
console.warn('Material ' + material.name + 'is not unique.');
}
} else {
this.materials[material.name] = material;
}
} else if (node.tagName === 'link') {
var link = new UrdfLink({
xml : node
});
// Make sure this is unique
if (this.links[link.name] !== void 0) {
console.warn('Link ' + link.name + ' is not unique.');
} else {
// Check for a material
for( var j=0; j<link.visuals.length; j++ )
{
var mat = link.visuals[j].material;
if ( mat !== null ) {
if (this.materials[mat.name] !== void 0) {
link.visuals[j].material = this.materials[mat.name];
} else {
this.materials[mat.name] = mat;
}
}
}
// Add the link
this.links[link.name] = link;
}
} else if (node.tagName === 'joint') {
var joint = new UrdfJoint({
xml : node
});
this.joints[joint.name] = joint;
}
}
}
module.exports = UrdfModel;
},{"./UrdfJoint":30,"./UrdfLink":31,"./UrdfMaterial":32,"xmldom":42}],35:[function(require,module,exports){
/**
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
var UrdfTypes = require('./UrdfTypes');
/**
* A Sphere element in a URDF.
*
* @constructor
* @param options - object with following keys:
* * xml - the XML element to parse
*/
function UrdfSphere(options) {
this.type = UrdfTypes.URDF_SPHERE;
this.radius = parseFloat(options.xml.getAttribute('radius'));
}
module.exports = UrdfSphere;
},{"./UrdfTypes":36}],36:[function(require,module,exports){
module.exports = {
URDF_SPHERE : 0,
URDF_BOX : 1,
URDF_CYLINDER : 2,
URDF_MESH : 3
};
},{}],37:[function(require,module,exports){
/**
* @fileOverview
* @author Benjamin Pitzer - ben.pitzer@gmail.com
* @author Russell Toris - rctoris@wpi.edu
*/
var Pose = require('../math/Pose');
var Vector3 = require('../math/Vector3');
var Quaternion = require('../math/Quaternion');
var UrdfCylinder = require('./UrdfCylinder');
var UrdfBox = require('./UrdfBox');
var UrdfMaterial = require('./UrdfMaterial');
var UrdfMesh = require('./UrdfMesh');
var UrdfSphere = require('./UrdfSphere');
/**
* A Visual element in a URDF.
*
* @constructor
* @param options - object with following keys:
* * xml - the XML element to parse
*/
function UrdfVisual(options) {
var xml = options.xml;
this.origin = null;
this.geometry = null;
this.material = null;
// Origin
var origins = 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
});
}
// Geometry
var geoms = xml.getElementsByTagName('geometry');
if (geoms.length > 0) {
var geom = geoms[0];
var shape = null;
// Check for the shape
for (var i = 0; i < geom.childNodes.length; i++) {
var node = geom.childNodes[i];
if (node.nodeType === 1) {
shape = node;
break;
}
}
// Check the type
var type = shape.nodeName;
if (type === 'sphere') {
this.geometry = new UrdfSphere({
xml : shape
});
} else if (type === 'box') {
this.geometry = new UrdfBox({
xml : shape
});
} else if (type === 'cylinder') {
this.geometry = new UrdfCylinder({
xml : shape
});
} else if (type === 'mesh') {
this.geometry = new UrdfMesh({
xml : shape
});
} else {
console.warn('Unknown geometry type ' + type);
}
}
// Material
var materials = xml.getElementsByTagName('material');
if (materials.length > 0) {
this.material = new UrdfMaterial({
xml : materials[0]
});
}
}
module.exports = UrdfVisual;
},{"../math/Pose":19,"../math/Quaternion":20,"../math/Vector3":22,"./UrdfBox":27,"./UrdfCylinder":29,"./UrdfMaterial":32,"./UrdfMesh":33,"./UrdfSphere":35}],38:[function(require,module,exports){
module.exports = require('object-assign')({
UrdfBox: require('./UrdfBox'),
UrdfColor: require('./UrdfColor'),
UrdfCylinder: require('./UrdfCylinder'),
UrdfLink: require('./UrdfLink'),
UrdfMaterial: require('./UrdfMaterial'),
UrdfMesh: require('./UrdfMesh'),
UrdfModel: require('./UrdfModel'),
UrdfSphere: require('./UrdfSphere'),
UrdfVisual: require('./UrdfVisual')
}, require('./UrdfTypes'));
},{"./UrdfBox":27,"./UrdfColor":28,"./UrdfCylinder":29,"./UrdfLink":31,"./UrdfMaterial":32,"./UrdfMesh":33,"./UrdfModel":34,"./UrdfSphere":35,"./UrdfTypes":36,"./UrdfVisual":37,"object-assign":2}],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){
/* global document */
module.exports = function Canvas() {
return document.createElement('canvas');
};
},{}],41:[function(require,module,exports){
(function (global){
/**
* @fileOverview
* @author Graeme Yeates - github.com/megawac
*/
'use strict';
var Canvas = require('canvas');
var Image = Canvas.Image || global.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;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"canvas":40}],42:[function(require,module,exports){
(function (global){
exports.DOMImplementation = global.DOMImplementation;
exports.XMLSerializer = global.XMLSerializer;
exports.DOMParser = global.DOMParser;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}]},{},[4]);