(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 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":9,"../core/Topic":16,"./../util/shim/EventEmitter2.js":38}],8:[function(require,module,exports){ var Ros = require('../core/Ros'); var mixin = require('../mixin'); var action = module.exports = { ActionClient: require('./ActionClient'), Goal: require('./Goal'), SimpleActionServer: require('./SimpleActionServer') }; mixin(Ros, ['ActionClient', 'SimpleActionServer'], action); },{"../core/Ros":11,"../mixin":23,"./ActionClient":5,"./Goal":6,"./SimpleActionServer":7}],9:[function(require,module,exports){ /** * @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":1}],10:[function(require,module,exports){ /** * @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":12,"./ServiceRequest":13}],11:[function(require,module,exports){ /** * @author Brandon Alexander - baalexander@gmail.com */ var WebSocket = require('./../util/shim/WebSocket.js'); var socketAdapter = require('./SocketAdapter.js'); var Service = require('./Service'); var ServiceRequest = require('./ServiceRequest'); var assign = require('object-assign'); var EventEmitter2 = require('./../util/shim/EventEmitter2.js').EventEmitter2; /** * 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 * * - a message came from rosbridge with the given topic name * * - a service response came from rosbridge with the given ID * * @constructor * @param options - possible keys include: * * url (optional) - the WebSocket URL for rosbridge (can be specified later with `connect`) */ function Ros(options) { options = options || {}; this.socket = null; this.idCounter = 0; this.isConnected = false; 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 for Rosbridge */ Ros.prototype.connect = function(url) { this.socket = assign(new WebSocket(url), socketAdapter(this)); }; /** * Disconnect from the WebSocket server. */ Ros.prototype.close = function() { if (this.socket) { this.socket.close(); } }; /** * Sends an authorization request to the server. * * @param mac - MAC (hash) string given by the trusted source. * @param client - IP of the client. * @param dest - IP of the destination. * @param rand - Random string given by the trusted source. * @param t - Time of the authorization request. * @param level - User level as a string given by the client. * @param end - End time of the client's session. */ Ros.prototype.authenticate = function(mac, client, dest, rand, t, level, end) { // create the request var auth = { op : 'auth', mac : mac, client : client, dest : dest, rand : rand, t : t, level : level, end : end }; // send the request this.callOnConnection(auth); }; /** * Sends the message over the WebSocket, but queues the message up if not yet * connected. */ Ros.prototype.callOnConnection = function(message) { var that = this; var messageJson = JSON.stringify(message); if (!this.isConnected) { that.once('connection', function() { that.socket.send(messageJson); }); } else { that.socket.send(messageJson); } }; /** * Retrieves list of topics in ROS as an array. * * @param callback function with params: * * topics - Array of topic names */ Ros.prototype.getTopics = function(callback) { var topicsClient = new Service({ ros : this, name : '/rosapi/topics', serviceType : 'rosapi/Topics' }); var request = new ServiceRequest(); topicsClient.callService(request, function(result) { callback(result.topics); }); }; /** * 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) { var topicsForTypeClient = new Service({ ros : this, name : '/rosapi/topics_for_type', serviceType : 'rosapi/TopicsForType' }); var request = new ServiceRequest({ type: topicType }); 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) { var servicesClient = new Service({ ros : this, name : '/rosapi/services', serviceType : 'rosapi/Services' }); var request = new ServiceRequest(); 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) { var servicesForTypeClient = new Service({ ros : this, name : '/rosapi/services_for_type', serviceType : 'rosapi/ServicesForType' }); var request = new ServiceRequest({ type: serviceType }); servicesForTypeClient.callService(request, function(result) { callback(result.services); }); }; /** * 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) { var nodesClient = new Service({ ros : this, name : '/rosapi/nodes', serviceType : 'rosapi/Nodes' }); var request = new ServiceRequest(); nodesClient.callService(request, function(result) { callback(result.nodes); }); }; /** * 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) { var paramsClient = new Service({ ros : this, name : '/rosapi/get_param_names', serviceType : 'rosapi/GetParamNames' }); var request = new ServiceRequest(); paramsClient.callService(request, function(result) { callback(result.names); }); }; /** * Retrieves a type of ROS topic. * * @param callback - function with params: * * type - String of the topic type */ Ros.prototype.getTopicType = function(topic, callback) { var topicTypeClient = new Service({ ros : this, name : '/rosapi/topic_type', serviceType : 'rosapi/TopicType' }); var request = new ServiceRequest({ topic: topic }); topicTypeClient.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) { var messageDetailClient = new Service({ ros : this, name : '/rosapi/message_details', serviceType : 'rosapi/MessageDetails' }); var request = new ServiceRequest({ type: message }); 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) { typeDefDict[fieldName] = subResult; } else { typeDefDict[fieldName] = [subResult]; } } else { that.emit('error', 'Cannot find ' + fieldType + ' in decodeTypeDefs'); } } } return typeDefDict; }; return decodeTypeDefsRec(defs[0], defs); }; module.exports = Ros; },{"./../util/shim/EventEmitter2.js":38,"./../util/shim/WebSocket.js":39,"./Service":12,"./ServiceRequest":13,"./SocketAdapter.js":15,"object-assign":1}],12:[function(require,module,exports){ /** * @author Brandon Alexander - baalexander@gmail.com */ var ServiceResponse = require('./ServiceResponse'); /** * 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; } /** * Calls the service. Returns the service response in the callback. * * @param request - the ROSLIB.ServiceRequest to send * @param callback - function with params: * * response - the response from the service request * @param failedCallback - the callback function when the service call failed (optional). Params: * * error - the error message reported by ROS */ Service.prototype.callService = function(request, callback, failedCallback) { var serviceCallId = 'call_service:' + this.name + ':' + (++this.ros.idCounter); if (callback || failedCallback) { this.ros.once(serviceCallId, function(message) { if (message.result !== undefined && message.result === false) { if (typeof failedCallback === 'function') { failedCallback(message.values); } } else if (typeof callback === 'function') { callback(new ServiceResponse(message.values)); } }); } var call = { op : 'call_service', id : serviceCallId, service : this.name, args : request }; this.ros.callOnConnection(call); }; module.exports = Service; },{"./ServiceResponse":14}],13:[function(require,module,exports){ /** * @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":1}],14:[function(require,module,exports){ /** * @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":1}],15:[function(require,module,exports){ (function (global){ /** * 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 */ 'use strict'; var Canvas = require('./../util/shim/canvas.js'); var Image = Canvas.Image || global.Image; var WebSocket = require('./../util/shim/WebSocket.js'); /** * If a message was compressed as a PNG image (a compression hack since * gzipping over WebSockets * is not supported yet), this function places the * "image" in a canvas element then decodes the * "image" as a Base64 string. * * @param data - object containing the PNG data. * @param callback - function with params: * * data - the uncompressed data */ function decompressPng(data, callback) { // Uncompresses the data before sending it through (use image/canvas to do so). var image = new Image(); // When the image loads, extracts the raw data (JSON message). image.onload = function() { // Creates a local canvas to draw on. var canvas = new Canvas(); var context = canvas.getContext('2d'); // Sets width and height. canvas.width = image.width; canvas.height = image.height; // Prevents anti-aliasing and loosing data context.imageSmoothingEnabled = false; context.webkitImageSmoothingEnabled = false; context.mozImageSmoothingEnabled = false; // Puts the data into the image. context.drawImage(image, 0, 0); // Grabs the raw, uncompressed data. var imageData = context.getImageData(0, 0, image.width, image.height).data; // Constructs the JSON. var jsonData = ''; for (var i = 0; i < imageData.length; i += 4) { // RGB jsonData += String.fromCharCode(imageData[i], imageData[i + 1], imageData[i + 2]); } callback(JSON.parse(jsonData)); }; // Sends the image data to load. image.src = 'data:image/png;base64,' + data.data; } /** * 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. */ 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); } } return { /** * Emits a 'connection' event on WebSocket connection. * * @param event - the argument to emit with the event. */ 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. */ 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. */ 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. */ onmessage: function onMessage(message) { var data = JSON.parse(typeof message === 'string' ? message : message.data); if (data.op === 'png') { decompressPng(data, handleMessage); } else { handleMessage(data); } } }; } module.exports = SocketAdapter; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./../util/shim/WebSocket.js":39,"./../util/shim/canvas.js":40}],16:[function(require,module,exports){ /** * @author Brandon Alexander - baalexander@gmail.com */ var EventEmitter2 = require('./../util/shim/EventEmitter2.js').EventEmitter2; var Message = require('./Message'); /** * Publish and/or subscribe to a topic in ROS. * * Emits the following events: * * 'warning' - if there are any warning during the Topic creation * * 'message' - the message data from rosbridge * * @constructor * @param options - object with following keys: * * ros - the ROSLIB.Ros connection handle * * name - the topic name, like /cmd_vel * * messageType - the message type, like 'std_msgs/String' * * compression - the type of compression to use, like 'png' * * throttle_rate - the rate (in ms in between messages) at which to throttle the topics * * queue_size - the queue created at bridge side for re-publishing webtopics (defaults to 100) * * latch - latch the topic when publishing * * queue_length - the queue length at bridge side used when subscribing (defaults to 0, no queueing). */ 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; // Check for valid compression types if (this.compression && this.compression !== 'png' && this.compression !== 'none') { this.emit('warning', this.compression + ' compression is not supported. No compression will be used.'); } // Check if throttle rate is negative if (this.throttle_rate < 0) { this.emit('warning', this.throttle_rate + ' is not allowed. Set to 0'); this.throttle_rate = 0; } var that = this; 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.ros.callOnConnection({ 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); 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.ros.callOnConnection({ op: 'advertise', id: this.advertiseId, type: this.messageType, topic: this.name, latch: this.latch, queue_size: this.queue_size }); this.isAdvertised = true; }; /** * Unregisters as a publisher for the topic. */ Topic.prototype.unadvertise = function() { if (!this.isAdvertised) { return; } 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; },{"./../util/shim/EventEmitter2.js":38,"./Message":9}],17:[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":23,"./Message":9,"./Param":10,"./Ros":11,"./Service":12,"./ServiceRequest":13,"./ServiceResponse":14,"./Topic":16}],18:[function(require,module,exports){ /** * @author David Gossow - dgossow@willowgarage.com */ var Vector3 = require('./Vector3'); var Quaternion = require('./Quaternion'); /** * A Pose in 3D space. Values are copied into this object. * * @constructor * @param options - object with following keys: * * position - the Vector3 describing the position * * orientation - the ROSLIB.Quaternion describing the orientation */ function Pose(options) { options = options || {}; // copy the values into this object if they exist this.position = new Vector3(options.position); this.orientation = new Quaternion(options.orientation); } /** * Apply a transform against this pose. * * @param tf the transform */ Pose.prototype.applyTransform = function(tf) { this.position.multiplyQuaternion(tf.rotation); this.position.add(tf.translation); var tmp = tf.rotation.clone(); tmp.multiply(this.orientation); this.orientation = tmp; }; /** * Clone a copy of this pose. * * @returns the cloned pose */ Pose.prototype.clone = function() { return new Pose(this); }; module.exports = Pose; },{"./Quaternion":19,"./Vector3":21}],19:[function(require,module,exports){ /** * @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; },{}],20:[function(require,module,exports){ /** * @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":19,"./Vector3":21}],21:[function(require,module,exports){ /** * @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; },{}],22:[function(require,module,exports){ module.exports = { Pose: require('./Pose'), Quaternion: require('./Quaternion'), Transform: require('./Transform'), Vector3: require('./Vector3') }; },{"./Pose":18,"./Quaternion":19,"./Transform":20,"./Vector3":21}],23:[function(require,module,exports){ /** * Mixin a feature to the core/Ros prototype. * For example, mixin(Ros, ['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); }; }); }; },{}],24:[function(require,module,exports){ /** * @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 */ 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.currentGoal = false; this.currentTopic = false; this.frameInfos = {}; this.republisherUpdateRequested = false; // Create an Action client this.actionClient = this.ros.ActionClient({ serverName : '/tf2_web_republisher', actionName : 'tf2_web_republisher/TFSubscriptionAction' }); // Create a Service client this.serviceClient = this.ros.Service({ name: '/republish_tfs', 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]; } }; module.exports = TFClient; },{"../actionlib/ActionClient":5,"../actionlib/Goal":6,"../core/Service.js":12,"../core/ServiceRequest.js":13,"../math/Transform":20}],25:[function(require,module,exports){ var Ros = require('../core/Ros'); var mixin = require('../mixin'); var tf = module.exports = { TFClient: require('./TFClient') }; mixin(Ros, ['TFClient'], tf); },{"../core/Ros":11,"../mixin":23,"./TFClient":24}],26:[function(require,module,exports){ /** * @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":21,"./UrdfTypes":35}],27:[function(require,module,exports){ /** * @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; },{}],28:[function(require,module,exports){ /** * @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":35}],29:[function(require,module,exports){ /** * @author David V. Lu!! davidvlu@gmail.com */ /** * A Joint element in a URDF. * * @constructor * @param options - object with following keys: * * xml - the XML element to parse */ function UrdfJoint(options) { this.name = options.xml.getAttribute('name'); this.type = options.xml.getAttribute('type'); var limits = options.xml.getElementsByTagName('limit'); if (limits.length > 0) { this.minval = parseFloat( limits[0].getAttribute('lower') ); this.maxval = parseFloat( limits[0].getAttribute('upper') ); } } module.exports = UrdfJoint; },{}],30:[function(require,module,exports){ /** * @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 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":27,"object-assign":1}],32:[function(require,module,exports){ /** * @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":21,"./UrdfTypes":35}],33:[function(require,module,exports){ /** * @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('xmlshim').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.evaluate('//robot', xmlDoc, null, XPATH_FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; // 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 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":18,"../math/Quaternion":19,"../math/Vector3":21,"./UrdfBox":26,"./UrdfCylinder":28,"./UrdfMaterial":31,"./UrdfMesh":32,"./UrdfSphere":34}],37:[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":26,"./UrdfColor":27,"./UrdfCylinder":28,"./UrdfLink":30,"./UrdfMaterial":31,"./UrdfMesh":32,"./UrdfModel":33,"./UrdfSphere":34,"./UrdfTypes":35,"./UrdfVisual":36,"object-assign":1}],38:[function(require,module,exports){ (function (global){ module.exports = { EventEmitter2: global.EventEmitter2 }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],39:[function(require,module,exports){ (function (global){ module.exports = global.WebSocket; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],40:[function(require,module,exports){ /* global document */ module.exports = function Canvas() { return document.createElement('canvas'); }; },{}]},{},[4]);