mirror of
https://github.com/CopterExpress/clover.git
synced 2026-05-26 11:43:25 +00:00
* Clover Blockly: add first blocks set * Adjust Blockly settings * Fix get_position output type * Add screenshot * Rename readme.md to README.md * Resize screenshot * Add package.xml * Little change * Fixes * Add python_compressed to blockly * Implement some of the Clover blocks in Python * Make Python indentation 4 spaces * Fixes to Python blocks implementation * Implement set_velocity block in Python * Implement wait_arrival block in Python * Fix indentation in Python implementation of blocks * Fix * Fix land_wait template * Set reserved words in Python * Change default frame_id to aruco_map in get_position block * Fix * Move blocks definitions to blocks.js * Get rid of missing favicon error * Simplify navigate * Rearrange layout, add tabs * Generate Python code * Small style change * -console.log * Code style * Use modules * Move modules to the header * Correct order for ROS definitions + generating "backend" code * Fix rangefinder_distance block * simple_offboard: commands to change only yaw and yaw rate * Implement set_yaw block * Start working on Blockly documentation * Implement print block with a topic * Unneeded code * Little fixes * Fix indentation * Fixes * Fix wait_arival, get_distance * Implement running Blockly programs, implement prompt block, fixes * Add land button * Little change * Fix reserved words + little fixes * +x for main.py * Simplify run button * Auto-save and load workspace * Make land button work * Handle exceptions * Minor change * Add help URL for blocks * Fix * Implement arrived block * Mark blockly and highlight.js as linguist-vendored * Add forgotten CMakeLists.txt * Add wait checkbox to set_yaw block * Disable run button when disconnected * Add message and service files * Add some comments * Add tooltip to some blocks * Implement GPIO blocks * Don’t latch print message to prevent duplication * Prevent duplication prompts * Add ROS init code to backend code anyways * Make GPIO blocks color a constant * Minor fix * More correctly update blocks on input value changes * Minor fixes * Remove unneeded readonly attribute * Add marker ID shadow blocks to toolbox * Add lacking reserved words * Fix frame id generation for complex marker id expressions * Consider frame_id in set_yaw block * Shorten ros module import * Implement stop service * Disable and enable run button correctly * Don’t print KeyboardInterrupt exceptions * Put notifications to notifications element * Add 'running' mark * Disable signal in backend python code * Sleep a little bit to let rospy initialize publishers * Remove accidental code * Make ROS namespace and private namespace constants * editorconfig-lint: don’t check Blockly code * Use private namespace constant in Python generator * Implement ~running topic to display current program status more robustly * Make navigate tolerance and sleep time constants * Make set_leds and and set_effect services proxies persistent * Replace a number with constant * Limit ~block topic publishing rate Otherwise messages get queued making the frontend to freeze * Improve internal documentation * Append 'map' to frames list * Return degrees in get_attitude block * Move getting yaw in a separate block * Improve block tooltips * Add some more files to editorconfig-lint excludes * Add get_yaw block to toolbox * Implement get_time block * Implement ~store and ~load services for storing user programs * Set auto_arm only in take_off block * Minor CSS fixes * Make 'Python' tab textarea-like * Implement saving and loading programs * Adjust styles * Retrieve only .xml files in load service * Forgotten code * Documentation on store and load services * Add some examples * Add blocks programming arg to launch file * Update docs * Add package’s dependencies * Add dependency * Add title to select * Fix syntax * Minor fix in docs * Add forgotten roslib.js * Run user program in the same process * Use print function for print block in Python 2 * Add variables example * Fix url * Add functions example * Fix set_servo block * Fix gpio_read block * Update blocks screenshot * Update docs * Update docs * Fix set_effect block * Minor fix in example * Add setpoint block, remove set_velocity from toolbox * Remove unused modules * Unused variable * Add English article skeleton * Clarify backend node link error * Remove unused variable * Update documentation * Fix link to documentation * Add Blockly logo * Update English article * Add Blocks programming link to the main page * Minor change * Add catkin_install_python to CMakeLists.txt * Make navigate tolerance and sleep time configurable * Add minor todo * Add blockly examples directory to editorconfig-lint excludes * Rename main node to clover_blocks * Add a warning to the old blocks programming article * Fix editorconfig-lint exclude
4561 lines
124 KiB
JavaScript
Vendored
4561 lines
124 KiB
JavaScript
Vendored
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
|
/*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2014 Patrick Gansterer <paroga@paroga.com>
|
|
*
|
|
* 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);
|
|
|
|
},{}],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<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.eventNames = function(){
|
|
return Object.keys(this._events);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}();
|
|
|
|
}).call(this,require('_process'))
|
|
},{"_process":4}],3:[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;
|
|
};
|
|
|
|
},{}],4:[function(require,module,exports){
|
|
// shim for using process in browser
|
|
var process = module.exports = {};
|
|
|
|
// cached from whatever global is present so that test runners that stub it
|
|
// don't break things. But we need to wrap it in a try catch in case it is
|
|
// wrapped in strict mode code which doesn't define any globals. It's inside a
|
|
// function because try/catches deoptimize in certain engines.
|
|
|
|
var cachedSetTimeout;
|
|
var cachedClearTimeout;
|
|
|
|
function defaultSetTimout() {
|
|
throw new Error('setTimeout has not been defined');
|
|
}
|
|
function defaultClearTimeout () {
|
|
throw new Error('clearTimeout has not been defined');
|
|
}
|
|
(function () {
|
|
try {
|
|
if (typeof setTimeout === 'function') {
|
|
cachedSetTimeout = setTimeout;
|
|
} else {
|
|
cachedSetTimeout = defaultSetTimout;
|
|
}
|
|
} catch (e) {
|
|
cachedSetTimeout = defaultSetTimout;
|
|
}
|
|
try {
|
|
if (typeof clearTimeout === 'function') {
|
|
cachedClearTimeout = clearTimeout;
|
|
} else {
|
|
cachedClearTimeout = defaultClearTimeout;
|
|
}
|
|
} catch (e) {
|
|
cachedClearTimeout = defaultClearTimeout;
|
|
}
|
|
} ())
|
|
function runTimeout(fun) {
|
|
if (cachedSetTimeout === setTimeout) {
|
|
//normal enviroments in sane situations
|
|
return setTimeout(fun, 0);
|
|
}
|
|
// if setTimeout wasn't available but was latter defined
|
|
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
|
cachedSetTimeout = setTimeout;
|
|
return setTimeout(fun, 0);
|
|
}
|
|
try {
|
|
// when when somebody has screwed with setTimeout but no I.E. maddness
|
|
return cachedSetTimeout(fun, 0);
|
|
} catch(e){
|
|
try {
|
|
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
|
return cachedSetTimeout.call(null, fun, 0);
|
|
} catch(e){
|
|
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
|
|
return cachedSetTimeout.call(this, fun, 0);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
function runClearTimeout(marker) {
|
|
if (cachedClearTimeout === clearTimeout) {
|
|
//normal enviroments in sane situations
|
|
return clearTimeout(marker);
|
|
}
|
|
// if clearTimeout wasn't available but was latter defined
|
|
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
|
cachedClearTimeout = clearTimeout;
|
|
return clearTimeout(marker);
|
|
}
|
|
try {
|
|
// when when somebody has screwed with setTimeout but no I.E. maddness
|
|
return cachedClearTimeout(marker);
|
|
} catch (e){
|
|
try {
|
|
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
|
return cachedClearTimeout.call(null, marker);
|
|
} catch (e){
|
|
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
|
|
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
|
|
return cachedClearTimeout.call(this, marker);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
var queue = [];
|
|
var draining = false;
|
|
var currentQueue;
|
|
var queueIndex = -1;
|
|
|
|
function cleanUpNextTick() {
|
|
if (!draining || !currentQueue) {
|
|
return;
|
|
}
|
|
draining = false;
|
|
if (currentQueue.length) {
|
|
queue = currentQueue.concat(queue);
|
|
} else {
|
|
queueIndex = -1;
|
|
}
|
|
if (queue.length) {
|
|
drainQueue();
|
|
}
|
|
}
|
|
|
|
function drainQueue() {
|
|
if (draining) {
|
|
return;
|
|
}
|
|
var timeout = runTimeout(cleanUpNextTick);
|
|
draining = true;
|
|
|
|
var len = queue.length;
|
|
while(len) {
|
|
currentQueue = queue;
|
|
queue = [];
|
|
while (++queueIndex < len) {
|
|
if (currentQueue) {
|
|
currentQueue[queueIndex].run();
|
|
}
|
|
}
|
|
queueIndex = -1;
|
|
len = queue.length;
|
|
}
|
|
currentQueue = null;
|
|
draining = false;
|
|
runClearTimeout(timeout);
|
|
}
|
|
|
|
process.nextTick = function (fun) {
|
|
var args = new Array(arguments.length - 1);
|
|
if (arguments.length > 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){
|
|
/**
|
|
* @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 : '1.0.1'
|
|
};
|
|
|
|
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":11,"./core":20,"./math":25,"./tf":28,"./urdf":40,"object-assign":3}],6:[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":5}],7:[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":12,"../core/Topic":19,"eventemitter2":2}],8:[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":12,"../core/Topic":19,"eventemitter2":2}],9:[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":12,"eventemitter2":2}],10:[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":12,"../core/Topic":19,"eventemitter2":2}],11:[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":14,"../mixin":26,"./ActionClient":7,"./ActionListener":8,"./Goal":9,"./SimpleActionServer":10}],12:[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":3}],13:[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":15,"./ServiceRequest":16}],14:[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 {
|
|
if (!this.socket || this.socket.readyState === WebSocket.CLOSED) {
|
|
var sock = new WebSocket(url);
|
|
sock.binaryType = 'arraybuffer';
|
|
this.socket = assign(sock, 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":15,"./ServiceRequest":16,"./SocketAdapter.js":18,"eventemitter2":2,"object-assign":3,"ws":42}],15:[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. Does nothing if this service is currently advertised.
|
|
*
|
|
* @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,
|
|
type: this.serviceType,
|
|
args : request
|
|
};
|
|
this.ros.callOnConnection(call);
|
|
};
|
|
|
|
/**
|
|
* 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":16,"./ServiceResponse":17,"eventemitter2":2}],16:[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":3}],17:[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":3}],18:[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 CBOR = require('cbor-js');
|
|
var typedArrayTagger = require('../util/cborTypedArrayTags');
|
|
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 if (data.data instanceof ArrayBuffer) {
|
|
var decoded = CBOR.decode(data.data, typedArrayTagger);
|
|
handleMessage(decoded);
|
|
} else {
|
|
var message = JSON.parse(typeof data === 'string' ? data : data.data);
|
|
handlePng(message, handleMessage);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
module.exports = SocketAdapter;
|
|
|
|
},{"../util/cborTypedArrayTags":41,"../util/decompressPng":44,"cbor-js":1,"ws":42}],19:[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' 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 || {};
|
|
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 !== '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
|
|
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":12,"eventemitter2":2}],20:[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":26,"./Message":12,"./Param":13,"./Ros":14,"./Service":15,"./ServiceRequest":16,"./ServiceResponse":17,"./Topic":19}],21:[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);
|
|
};
|
|
|
|
/**
|
|
* 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":22,"./Vector3":24}],22:[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;
|
|
|
|
},{}],23:[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":22,"./Vector3":24}],24:[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;
|
|
},{}],25:[function(require,module,exports){
|
|
module.exports = {
|
|
Pose: require('./Pose'),
|
|
Quaternion: require('./Quaternion'),
|
|
Transform: require('./Transform'),
|
|
Vector3: require('./Vector3')
|
|
};
|
|
|
|
},{"./Pose":21,"./Quaternion":22,"./Transform":23,"./Vector3":24}],26:[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);
|
|
};
|
|
});
|
|
};
|
|
|
|
},{}],27:[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":7,"../actionlib/Goal":9,"../core/Service.js":15,"../core/ServiceRequest.js":16,"../math/Transform":23}],28:[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":14,"../mixin":26,"./TFClient":27}],29:[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":24,"./UrdfTypes":38}],30:[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;
|
|
},{}],31:[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":38}],32:[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.
|
|
*
|
|
* @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') );
|
|
}
|
|
|
|
// 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;
|
|
|
|
},{"../math/Pose":21,"../math/Quaternion":22,"../math/Vector3":24}],33:[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":39}],34:[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":30,"object-assign":3}],35:[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":24,"./UrdfTypes":38}],36:[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":32,"./UrdfLink":33,"./UrdfMaterial":34,"xmldom":45}],37:[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":38}],38:[function(require,module,exports){
|
|
module.exports = {
|
|
URDF_SPHERE : 0,
|
|
URDF_BOX : 1,
|
|
URDF_CYLINDER : 2,
|
|
URDF_MESH : 3
|
|
};
|
|
|
|
},{}],39:[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":21,"../math/Quaternion":22,"../math/Vector3":24,"./UrdfBox":29,"./UrdfCylinder":31,"./UrdfMaterial":34,"./UrdfMesh":35,"./UrdfSphere":37}],40:[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":29,"./UrdfColor":30,"./UrdfCylinder":31,"./UrdfLink":33,"./UrdfMaterial":34,"./UrdfMesh":35,"./UrdfModel":36,"./UrdfSphere":37,"./UrdfTypes":38,"./UrdfVisual":39,"object-assign":3}],41:[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:
|
|
* <https://tools.ietf.org/html/draft-ietf-cbor-array-tags-00>
|
|
* 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
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
},{}],42:[function(require,module,exports){
|
|
module.exports = window.WebSocket;
|
|
|
|
},{}],43:[function(require,module,exports){
|
|
/* global document */
|
|
module.exports = function Canvas() {
|
|
return document.createElement('canvas');
|
|
};
|
|
},{}],44:[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":43}],45:[function(require,module,exports){
|
|
exports.DOMImplementation = window.DOMImplementation;
|
|
exports.XMLSerializer = window.XMLSerializer;
|
|
exports.DOMParser = window.DOMParser;
|
|
|
|
},{}]},{},[6]);
|