Source: org/terraswarm/accessor/accessors/web/robotics/RosPublisher.js

// Copyright (c) sock-2016 The Regents of the University of California.
// All rights reserved.
//
// Permission is hereby granted, without written agreement and without
// license or royalty fees, to use, copy, modify, and distribute this
// software and its documentation for any purpose, provided that the above
// copyright notice and the following two paragraphs appear in all copies
// of this software.
//
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
// ENHANCEMENTS, OR MODIFICATIONS.
//

/** This accessor advertises and publishes to a ROS topic. It extends
 * the WebSocketClient to communicate to a rosbridge websocket. <br>
 *
 * During intialize, it will advertise the topic it will publish to,
 * along with its type. The purpose of 'advertise' here is to
 * establish the ROS topic if isn't already established. This is
 * slightly different from the 'advertise' function in NodeHandle
 * which simply publicizes that the the node will be publishing
 * messages to the topic.
 * <ul>
 * <li>If the topic does not already exist, and the type is valid, a
 * topic will be established with this type.</li>
 * <li>If the topic already exists with a different type, no new topic
 * will be established</li>
 * <li>If the topic already exists with the same type, no new topic
 * will be established.</li>
 * <li>If the topic already exists but the type isn't resolved, no new
 * topic will be established.</li>
 * </ul>
 *
 *  On input from 'toSend', this accessor publishes that input to the
 *  aforementioned topic.
 *  The input from 'toSend' must be in JSON form, and must match the message
 *  datatype that the ROS topic is expecting. For example if ROS topic is
 *  expecting <code>std_msgs/String</code>, then the input in 'toSend' should be
 *  as follows:
 *  <pre> { "data": "hello world" } </pre>
 *  Some ROS types contain a header which consists of a sequence,
 *  timestamp, and a frame_id. If the ROS type has a header, you can
 *  either:
 *  <ul>
 *  <li>send the message on toSend without the header, and check the
 *  parameter 'addHeader'. This sends the message with a header with
 *  only the frame_id specified, and rosbridge will add the sequence
 *  and timestamp for you.</li>
 *  <li>send a fully formed message with all fields in the header
 *  included and don't check the parameter 'addHeader'.</li>
 *  </ul>
 *  This accessor doesn't do any error checking. All error messages
 *  orginate from rosbridge and will appear on the console running
 *  rosbridge. More information ccan be viewed on the <a href='https://github.com/RobotWebTools/rosbridge_suite/blob/groovy-devel/ROSBRIDGE_PROTOCOL.md'>rosbridge
 *  protocol specification</a> site.
 *
 *  @accessor robotics/RosPublisher
 *  @parameter {string} topic The ROS topic to publish to.
 *  @parameter {string} ROStype The ROS datatype of the topic.
 *  @parameter {boolean} addHeader If (true), this accessor will send a header with a blank seq and timestamp field, and rosbridge will add the header for you.
 *  @parameter {string} frame_id The frame_id of the header (only needed if a header is required).
 *  @author Marcus Pan
 *  @version $$Id$$
 */

// Stop extra messages from jslint and jshint.  Note that there should
// be no space between the / and the * and global. See
// https://chess.eecs.berkeley.edu/ptexternal/wiki/Main/JSHint */
/*globals exports, extend, get, getParameter, parameter */
/*jshint globalstrict: true*/
'use strict';

/** Sets up by accessor by inheriting inputs from setup() in
 * WebSocketClient. Adds additional parameters regarding the ROS topic
 * to which to publish to.
 */
exports.setup = function () {
    this.extend('net/WebSocketClient');
    this.parameter('topic', {
        type: "string"
    });
    this.parameter('ROStype', {
        type: "string"
    });
    this.parameter('addHeader', {
        type: "boolean",
        value: false
    });
    this.parameter('frame_id', {
        type: "string",
        value: ""
    });
};

/** Override inputHandler on 'toSend' from WebSocketClient. */
exports.toSendInputHandler = function () {
    var msg = this.get('toSend');
    // Add a header with a blank time and sequence info. This will be added by rosbridge.
    if (this.getParameter('addHeader')) {
        msg.header = {
            "frame_id": this.getParameter('frame_id')
        };
    }

    var data = {
        "op": "publish",
        "topic": this.getParameter('topic'),
        "msg": msg
    };

    this.exports.sendToWebSocket.call(this, data);
};

/**  Inherits initialize from WebSocketClient.
 *   Advertise the topic we are publishing to.
 */
exports.initialize = function () {
    this.exports.ssuper.initialize.call(this);

    var advertise = {
        "op": "advertise",
        "topic": this.getParameter('topic'),
        "type": this.getParameter('ROStype')
    };
    this.exports.sendToWebSocket.call(this, advertise);

};

/** Unadvertise the topic and inherit wrapup from WebSocketClient. */
exports.wrapup = function () {
    var unadvertise = {
        "op": "unadvertise",
        "topic": this.getParameter('topic')
    };
    this.exports.sendToWebSocket.call(this, unadvertise);
    this.ssuper.wrapup();
};