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

  1. // Copyright (c) 2015-2016 The Regents of the University of California.
  2. // All rights reserved.
  3. //
  4. // Permission is hereby granted, without written agreement and without
  5. // license or royalty fees, to use, copy, modify, and distribute this
  6. // software and its documentation for any purpose, provided that the above
  7. // copyright notice and the following two paragraphs appear in all copies
  8. // of this software.
  9. //
  10. // IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
  11. // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
  12. // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
  13. // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
  14. // SUCH DAMAGE.
  15. //
  16. // THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  17. // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  18. // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
  19. // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
  20. // CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
  21. // ENHANCEMENTS, OR MODIFICATIONS.
  22. //
  23. /** Publish location X,Y,Z coordinates as a PointCloud to a ROS topic.
  24. *
  25. * Incoming packets should have at least these keys:
  26. * {
  27. * X: <x coordinate>,
  28. * Y: <y coordinate>,
  29. * id: <unique id for this location>
  30. * }
  31. *
  32. * All locations are published as a PointCloud so that the ROS visualization
  33. * only needs to subscribe to one topic.
  34. *
  35. * @accessor robotics/LocationRosPublisher
  36. * @parameter {string} topic The ROS topic to publish to.
  37. * @parameter {string} frame_id The frame_id of the header (only needed if a header is required).
  38. * @author Brad Campbell
  39. * @version $$Id$$
  40. */
  41. // Stop extra messages from jslint and jshint. Note that there should
  42. // be no space between the / and the * and global. See
  43. // https://chess.eecs.berkeley.edu/ptexternal/wiki/Main/JSHint */
  44. /*globals exports, extend, get, getParameter, parameter */
  45. /*jshint globalstrict: true*/
  46. 'use strict';
  47. var ROS_TYPE = 'sensor_msgs/PointCloud';
  48. // location_id -> {position: {X, Y, Z}, color: <some color as a float>}
  49. var locations = {};
  50. /** Sets up by accessor by inheriting inputs from setup() in
  51. * WebSocketClient. Adds additional parameters regarding the ROS topic
  52. * to publish to.
  53. */
  54. exports.setup = function () {
  55. this.extend('net/WebSocketClient');
  56. this.parameter('topic', {
  57. type: "string"
  58. });
  59. this.parameter('frame_id', {
  60. type: "string",
  61. value: ""
  62. });
  63. };
  64. /** Inherits initialize from WebSocketClient.
  65. * Advertise the topic we are publishing to.
  66. */
  67. exports.initialize = function () {
  68. this.exports.ssuper.initialize.call(this);
  69. };
  70. /** Override onOpen from WebSocketClient */
  71. exports.onOpen = function () {
  72. this.exports.ssuper.onOpen.call(this);
  73. // Advertise what we have when the websocket opens.
  74. var advertise = {
  75. "op": "advertise",
  76. "topic": this.getParameter('topic'),
  77. "type": ROS_TYPE
  78. };
  79. this.exports.sendToWebSocket.call(this, advertise);
  80. };
  81. function random_color() {
  82. var letters = '0123456789ABCDEF'.split('');
  83. var color = '0x';
  84. for (var i = 0; i < 6; i += 1) {
  85. color += letters[Math.floor(Math.random() * 16)];
  86. }
  87. return parseInt(color, 16);
  88. }
  89. /** Override inputHandler on 'toSend' from WebSocketClient. */
  90. exports.toSendInputHandler = function () {
  91. var msg = this.get('toSend');
  92. // Update the current location map with this incoming packet
  93. var id = msg.id;
  94. var x = msg.X || 0;
  95. var y = msg.Y || 0;
  96. var z = msg.Z || 0;
  97. // Check if this ID already has a color
  98. var color = 0.0;
  99. if (id in locations) {
  100. color = locations[id].color;
  101. } else {
  102. color = random_color();
  103. }
  104. // Actually update the record
  105. locations[id] = {
  106. position: {
  107. x: x,
  108. y: y,
  109. z: z
  110. },
  111. color: color
  112. };
  113. // Create arrays we can publish
  114. var location_points = [];
  115. var colors = [];
  116. Object.keys(locations).forEach(function (key) {
  117. location_points.push(locations[key].position);
  118. colors.push(locations[key].color);
  119. });
  120. var out = {
  121. header: {
  122. frame_id: this.getParameter('frame_id')
  123. },
  124. points: location_points,
  125. channels: [{
  126. name: 'rgb',
  127. values: colors
  128. }]
  129. };
  130. var data = {
  131. "op": "publish",
  132. "topic": this.getParameter('topic'),
  133. "msg": out
  134. };
  135. exports.sendToWebSocket(data);
  136. };
  137. /** Unadvertise the topic and inherit wrapup from WebSocketClient. */
  138. exports.wrapup = function () {
  139. if (this.exports.isOpen()) {
  140. var unadvertise = {
  141. "op": "unadvertise",
  142. "topic": this.getParameter('topic')
  143. };
  144. this.exports.sendToWebSocket.call(this, unadvertise);
  145. }
  146. this.exports.ssuper.wrapup.call(this);
  147. };