Giter VIP home page Giter VIP logo

ros2-web-bridge's Introduction

ros2-web-bridge Build StatusBuild statusnpmlicense


⚠️ Warning ⚠️

This package is not actively maintained. Please use the rosbridge_suite package instead for your ROS 2 websocket communication needs.

ros2-web-bridge vs rosbridge_suite

  • rosbridge_suite (or rosbridge_server) is recommended for communicating with ROS 2 over websockets. It is written in Python and is actively maintained by the ROS web working group.
  • ros2-web-bridge (this project) is an earlier attempt at enabling ROS 2 communication over websockets. It is written in JavaScript, and requires Node.js to be installed on your robot. It cannot be installed via rosdep or apt like a regular ROS package, and must be cloned and built locally.

Server Implementations of the rosbridge v2 Protocol

ros2-web-bridge, which leverages the rclnodejs client, provides a JSON interface to ROS 2 by adopting the rosbridge v2 protocol. The bridge can process commands through JSON tuneled over WebSockets.

ROS 2 support

The ros2-web-bridge SUPPORTS the latest ROS 2 stable release by default (currently Dashing Patch 2), please visit the relase channel to check out the information.

Any one who wants to run on the nightly build of ROS 2, please change the dependencies section of package.json file to install other version of rclnodejs.

Supported Clients

A client is a program that communicates with ros2-web-bridge using its JSON API. Clients include:

  • roslibjs - A JavaScript API, which communicates with ros2-web-bridge over WebSockets.

Install

  1. Prepare for ROS 2 Please reference the documentation to install ROS 2.
  2. Install Node.js You can install Node.js:
    • Download from Node.js offical website, and install it.
    • Use the Node Version Manager (nvm) to install it.
  3. Clone and install dependencies Note that a ROS 2 installation has to be sourced before installing dependencies.
    $ git clone https://github.com/RobotWebTools/ros2-web-bridge.git
    $ cd ros2-web-bridge
    $ source /opt/ros/$DISTRO/setup.sh  # or a source installation
    $ npm install

Run Examples

  1. Make sure to source a ROS 2 installation, e.g.:
    $ source /opt/ros/$DISTRO/setup.sh  # or a source installation
  2. Start ros2-web-bridge module:
    $ node bin/rosbridge.js
    If you want to start in client mode (i.e. connecting the bridge to an existing websocket server), do this instead:
    $ node bin/rosbridge.js --address ws://<address>:<port>
  3. Start the express server:
    $ cd examples && node index.js
  4. Open your browser, and navigate to URL: http://localhost:3000/html/publisher.html

Not supported op

Some experimental operations defined by rosbridge v2.0 protocol specification are not supported by ros2-web-bridge now, please check out the list:

and the authentication

Compability with rosbridge v2.0 protocol

We are trying to obey the rosbridge v2 protocol, but there are still some operation commands which can not follow the spec. The table below lists the differences:

opreations rosbridge v2.0 protocol spec ros2-web-bridge implementation
publish If the msg is a subset of the type of the topic, then a warning status message is sent and the unspecified fields are filled in with defaults. If the subset of the msg is unspecified, then an error status message is sent and this message is dropped.
subscribe The type of the topic is optional. The type of the topic must be offered.

If you use roslibjs as the client running in the browser, please reference the code snippet below:

  • Subscribe to a topic.
    // Define a topic with its type.
    var example = new ROSLIB.Topic({
      ros : ros,
      name : '/example_topic',
      messageType : 'std_msgs/String'
    });
    
    // Subscribe to a topic.
    example.subscribe(function(message) {
      console.log(`Receive message: ${message}`);
    });

Contributing

If you want to contribute code to this project, first you need to fork the project. The next step is to send a pull request (PR) for review. The PR will be reviewed by the project team members. Once you have gained "Look Good To Me (LGTM)", the project maintainers will merge the PR.

License

This project abides by Apache License 2.0.

ros2-web-bridge's People

Contributors

amacneil avatar christophebedard avatar jihoonl avatar kenny-y avatar newcanopies avatar qiuzhong avatar smartin015 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ros2-web-bridge's Issues

Put the script section at the bottom of the body element for examples

Currently the example html files are all like this:

...
<script src="https://static.robotwebtools.org/roslibjs/current/roslib.js"></script>
<script>
// inline JS code calls roslibjs API
</script>

<body>
// div elements
</body>

We suggest to put the inline JavaScript code at the bottom of the body element. The inline JavaScript code contains DOM operations. If there is only async code, it might be fine. But if there is only sync code, it will raise errors. In brief, to avoid any errors, we suggest to put the inline code at the bottom.

Failed to launch rosbridge.js server on Windows

I can install ros2-web-bridge on Windows successfully. However, it failed to launch bin\rosbridge.js:

call /path/to/ros2-windows/local_setup.bat
git clone https://github.com/RobotWebTools/ros2-web-bridge.git
cd ros2-web-bridge
npm install   // succeed by far

node bin\ros2-bridge.js
C:\dev\ros2\ros2-web-bridge\node_modules\bindings\bindings.js:88
        throw e
        ^

Error: The specified module could not be found.
\\?\C:\dev\ros2\ros2-web-bridge\node_modules\rclnodejs\build\Release\rclnodejs.node
    at Object.Module._extensions..node (module.js:672:18)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)
    at Module.require (module.js:587:17)
    at require (internal/module.js:11:18)
    at bindings (C:\dev\ros2\ros2-web-bridge\node_modules\bindings\bindings.js:81:44)
    at Object.<anonymous> (C:\dev\ros2\ros2-web-bridge\node_modules\rclnodejs\lib\node.js:17:38)
    at Module._compile (module.js:643:30)
    at Object.Module._extensions..js (module.js:654:10)

Environments:

  • Windows 10 1607
  • Node.js: 8.9.4
  • VS2017 Community Edition

Bridge server cannot be shutdown by CTRL + C at some condition

Fork from #69 , at some condition, especially sending some requests to the bridge server. Then the bridge server cannot be shutdown by CTRL + C. You have to send SIGTERM signal to kill the bridge server.

screenshot from 2018-03-14 16-54-38

In he screenshot above, sending CTRL + C several times cannot shutdown the bridge server.

AppVeyor CI failed due to permission

Error log from AppVeyor CI:

pip install -U setuptools pip
Collecting setuptools
  Downloading setuptools-39.0.1-py2.py3-none-any.whl (569kB)
Collecting pip
  Downloading pip-9.0.2-py2.py3-none-any.whl (1.4MB)
Installing collected packages: setuptools, pip
  Found existing installation: setuptools 28.8.0
    Uninstalling setuptools-28.8.0:
      Successfully uninstalled setuptools-28.8.0
  Found existing installation: pip 9.0.1
    Uninstalling pip-9.0.1:
      Successfully uninstalled pip-9.0.1
Exception:
Traceback (most recent call last):
  File "c:\python36\lib\shutil.py", line 387, in _rmtree_unsafe
    os.unlink(fullname)
PermissionError: [WinError 5] Access is denied: 'C:\\Users\\appveyor\\AppData\\Local\\Temp\\pip-w_v7cqng-uninstall\\python36\\scripts\\pip.exe'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "c:\python36\lib\site-packages\pip\basecommand.py", line 215, in main
    status = self.run(options, args)
  File "c:\python36\lib\site-packages\pip\commands\install.py", line 342, in run
    prefix=options.prefix_path,
  File "c:\python36\lib\site-packages\pip\req\req_set.py", line 795, in install
    requirement.commit_uninstall()
  File "c:\python36\lib\site-packages\pip\req\req_install.py", line 767, in commit_uninstall
    self.uninstalled.commit()
  File "c:\python36\lib\site-packages\pip\req\req_uninstall.py", line 142, in commit
    rmtree(self.save_dir)
  File "c:\python36\lib\site-packages\pip\_vendor\retrying.py", line 49, in wrapped_f
    return Retrying(*dargs, **dkw).call(f, *args, **kw)
  File "c:\python36\lib\site-packages\pip\_vendor\retrying.py", line 212, in call
    raise attempt.get()
  File "c:\python36\lib\site-packages\pip\_vendor\retrying.py", line 247, in get
    six.reraise(self.value[0], self.value[1], self.value[2])
  File "c:\python36\lib\site-packages\pip\_vendor\six.py", line 686, in reraise
    raise value
  File "c:\python36\lib\site-packages\pip\_vendor\retrying.py", line 200, in call
    attempt = Attempt(fn(*args, **kwargs), attempt_number, False)
  File "c:\python36\lib\site-packages\pip\utils\__init__.py", line 102, in rmtree
    onerror=rmtree_errorhandler)
  File "c:\python36\lib\shutil.py", line 494, in rmtree
    return _rmtree_unsafe(path, onerror)
  File "c:\python36\lib\shutil.py", line 384, in _rmtree_unsafe
    _rmtree_unsafe(fullname, onerror)
  File "c:\python36\lib\shutil.py", line 384, in _rmtree_unsafe
    _rmtree_unsafe(fullname, onerror)
  File "c:\python36\lib\shutil.py", line 389, in _rmtree_unsafe
    onerror(os.unlink, fullname, sys.exc_info())
  File "c:\python36\lib\site-packages\pip\utils\__init__.py", line 114, in rmtree_errorhandler
    func(path)
PermissionError: [WinError 5] Access is denied: 'C:\\Users\\appveyor\\AppData\\Local\\Temp\\pip-w_v7cqng-uninstall\\python36\\scripts\\pip.exe'
Command exited with code 2

Rosbridge v2 protocol: publish with subset msg of type of topic

In rosbridge v2 protocol section 3.4.3 - publish

If the msg is a subset of the type of the topic, then a warning status message is sent and the unspecified fields are filled in with defaults

Let's say we advertise a topic with type std_msgs/Header (contains a builtin_interface/Time field and a std_msgs/String field) and send a Time type data. The response status is none which indicates the operation is successful.

Header.msg:
builtin_interfaces/Time stamp
string frame_id

Time.msg:
int32 sec
uint32 nanosec
 
Request: {"op": "adverstie", "topic": "example_topic", "type": "std_msgs/Header"}
Response: {"op": "set_level", "level": "none"}

Request: {"op": "publish", "topic": "example_topic", "msg": {stamp: {sec: 123456, nanosec: 789}}}
Response: {"op": "set_level", "level": "none"}

Actually, this is because rclnodejs doesn't complain if the message to be publish is a subset of the type of the topic. For rclnodejs the unspecified fields are filled with garbage data.

Multiple subscriptions support

In terms of the subscribe-related operations, like subscribe and unsubscribe, the id field is to identify the subscriptions when the topic is same. According to the rosbridge v2 protocol subscribe section:

id – if specified, then this specific subscription can be unsubscribed by referencing the ID.

and unsubscribe section:

If an id is provided, then only the corresponding subscription is unsubscribed. If no ID is provided, then all subscriptions are unsubscribed.

All the operations should support multiple subscriptions action. The id field can be useful to identify them.

This feature should be supported by the bridge.

Status message response should be sent back to clients for every message

This project implemented the rosbridge v2 protocol
Generally, sending an rosbridge message to the bridge should always return a response no matter the message is handled well.

However, this is not the cases, web socket client cannot receive response from the bridge web socket server:

const webSocket = require('ws');
const ws = new webSocket('ws://localhost:9090');

ws.on('open', function open() {
  ws.send('{"op": "advertise", ... }');
});

ws.on('message', function incoming() {
  // never enter
});

To make the rosbridge v2 protocol testing easier, please implement this feature.

call_service operation arguments incompatible with spec

According to the rosbridge protocol call_service section

{ "op": "call_service",
  (optional) "id": <string>,
  "service": <string>,
  (optional) "args": <list<json>>,
  (optional) "fragment_size": <int>,
  (optional) "compression": <string>
}

args – if the service has no args, then args does not have to be provided, though an empty list is equally acceptable. Args should be a list of json objects representing the arguments to the service

There are two problems of current implementation:

    this._registerOpMap('call_service', (command) => {
      let serviceName = command.service;
      let client =
        this._resourceProvider.createClient(this._exractServiceType(command.args.type), serviceName);

      if (client) {
        client.sendRequest(command.args.request, (response) => {
          let serviceResponse =
            {op: 'service_response', service: command.service, values: response, id: command.id, result: true};

          this._ws.send(JSON.stringify(serviceResponse));
        });
      }
    });
  • The spec asks for list of JSON object, however current implementation assumes args as an object and access request property directly.

  • The spec never guarantees that args has the type property while the implementation assume it has. The bridge client may send a call_service request provided extra service type message field, in that case, the calling still failed as args.type is undefined.

auth operation not integrated

It seems the auth operation is not intergrated with the bridge as rosauth.js is never required or referred by the bridge implementation. The authentication function is implemented, though.

rosbridge v2 protocol specification: advertise

According to the rosbridge v2 protocol specification 3.4.1 Advertise ( advertise )

If the topic already exists with a different type, an error status message is sent and this message is dropped.

// 1st message
Message: '{"op": "advertise", "id": "create_topic1", "topic": "example_topic", "type": "std_msgs/String"}'
Expected: success
Actual response: '{"op": "set_level", "level": "none"}'

// 2nd message
Message: '{"op": "advertise", "id": "create_topic2", "topic": "example_topic", "type": "std_msgs/Char"}'
Expected: error as same topic already exists but with different type
Actual response: '{"op": "set_level", "level": "none"}'

set_level as a request operation

According to the protocol spec, set_level is a response operation from the ros2-web-bridge to the roslibjs.

However, there is a API in roslibjs: Ros.setStatusLevel

Ros.prototype.setStatusLevel = function(level, id){
  var levelMsg = {
    op: 'set_level',
    level: level,
    id: id
  };

  this.callOnConnection(levelMsg);
};

This implies set_level should be a request from roslibjs. Currently, there is no handler for this kind of request operation.

Implementation of ros2-api

It would be interesting to have the rosapi in ros2.

But what is the best way to do it ?

  • a dedicated python package/node that respond to services like in ros1
  • a dedicated nodejs package/node that respond to services
  • a direct integration in ros2-web-bridge, so it does not go through services; it ask directly to rclnodejs

Last two proposition needs to add the appropriate functions in rclnodejs to get topic and node list for example.

Failed to call destroyService when receiving unadvertise_service op request

unadvertise_service operation request is mapped to destroyService of rclnodejs in current rosbridge implementation. Here it is:

    this._registerOpMap('unadvertise_service', (command) => {
      this._nodeManager.destroyService(command.service, bridgeId);
    });

Web socket test code:

'use strict';

/* eslint-disable camelcase */

const assert = require('assert');
const rclnodejs = require('rclnodejs');
const WebSocket = require('ws');

rclnodejs.init().then(() => {

  let node = rclnodejs.createNode('service');
  let wsService = node.createService('example_interfaces/srv/AddTwoInts', 'add_two_ints',
    (request, response) => {
      let result = response.template;
      result.sum = request.a + request.b;
      response.send(result);
      console.log('result sent');
    });
  rclnodejs.spin(node);

  const advertise_service = {
    op: 'advertise_service', type: 'example_interfaces/AddTwoInts', service: 'add_two_ints'};
  const unadvertise_service = {op: 'unadvertise_service', service: 'add_two_ints'};

  let count = 0;
  let ws = new WebSocket('ws://localhost:9090');

  ws.on('open', function() {
    ws.send(JSON.stringify(advertise_service));
    count++;
  });

  ws.on('message', function(data) {
    console.log(data.toString());
    if (count === 2) {
      ws.close();
      rclnodejs.shutdown();
    }
    if (count === 1) {
      ws.send(JSON.stringify(unadvertise_service));
      count++;      
    }
  });
}).catch((err) => {
  console.log(err);
});

When running the test code, we got this bridge log:

$ node bin/rosbridge.js 
  ros2-web-bridge:index The ros2-web-bridge has started. +0ms
The web socket server started on ws://localhost:9090
  ros2-web-bridge:Bridge Web bridge 1579c261-c5d0-430f-b1fc-e8c1391e5004 is created +0ms
  ros2-web-bridge:Bridge JSON command received: {"op":"advertise_service","type":"example_interfaces/AddTwoInts","service":"add_two_ints"} +2ms
  ros2-web-bridge:ResourceProvider Service has been created, and the service name is add_two_ints. +0ms
  ros2-web-bridge:Bridge Response: {"op":"set_level","level":"none"} +12ms
  ros2-web-bridge:Bridge JSON command received: {"op":"unadvertise_service","service":"add_two_ints"} +2ms
  ros2-web-bridge:Bridge Exception caught in Bridge.executeCommand(): TypeError: Cannot read property 'destroyService' of undefined +0ms
  ros2-web-bridge:Bridge Error: TypeError: Cannot read property 'destroyService' of undefined happened when executing command unadvertise_service +0ms
  ros2-web-bridge:Bridge Response: {"op":"set_level","level":"error"} +0ms
  ros2-web-bridge:RefCountingHandle Handle is destroyed. +0ms
  ros2-web-bridge:Bridge Web bridge 1579c261-c5d0-430f-b1fc-e8c1391e5004 is closed +7ms

As we see, the this._nodeManager is undefined and the calling of destroyService failed.

Web socket seems blocked after sending more than 3 call_service requests

For the call_service operation, sending a request more than 3 times can cause a bridge client blocked. Here are the steps:

  • Start the rosbridge server
$ node bin/rosbridge.js
The web socket server started on ws://localhost:9090
  • Running the client for 4 times:
'use strict';

/* eslint-disable camelcase */

const assert = require('assert');
const rclnodejs = require('rclnodejs');
const WebSocket = require('ws');

rclnodejs.init().then(() => {

  let node = rclnodejs.createNode('service');
  let wsService = node.createService('example_interfaces/srv/AddTwoInts', 'add_two_ints',
    (request, response) => {
      let result = response.template;
      result.sum = request.a + request.b;
      response.send(result);
      console.log('result sent');
    });
  rclnodejs.spin(node);

  const advertise_service = {op: 'advertise_service', type: 'example_interfaces/AddTwoInts', service: 'add_two_ints'};
  const call_service = {op: 'call_service', service: 'add_two_ints',
    args: {request: {a: 1, b: 2}, type: 'example_interfaces/AddTwoInts'}};

  let count = 0;
  let ws = new WebSocket('ws://localhost:9090');
  ws.on('open', function() {
    ws.send(JSON.stringify(call_service));
    count++;
  });
  ws.on('message', function(data) {
    let response = JSON.parse(data);
    console.log(`${count}`, response);
    count++;
    if (response.op == 'service_response') {
      ws.close();
      rclnodejs.shutdown();
    }
  });
}).catch((err) => {
  console.log(err);
});

We get this output for 1/2/3 time to run it.

1 { op: 'set_level', level: 'none' }
2 { op: 'service_response',
  service: 'add_two_ints',
  values: { sum: 3 },
  result: true }

However, for the 4th time, the client hangs and cannot receive the service response:

1 { op: 'set_level', level: 'none' }

Bridge server process signal handler

The bridge server sometimes cannot response CTRL + C well.

When the bridge server is running, it will initialize the rclnodejs, which can catch the SIGINT signal. So the rclnodejs loop might exit but the web socket server might not exit.

web socket security consideration

Currently, the bridge provides web socket interface by

ws://localhost:9090

In that case, all the security-related operations (auth) data is exposed in the socket transmission. What about using web socket security protocol (wss://)?

Server stability bug -- unhandled underlying layer exceptions

Case 1: Refresh browser as fast as you can, you'll hit this

events.js:160
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at exports._errnoException (util.js:949:11)
    at TCP.onread (net.js:563:26)

Case 2: connect to bridge with service.html, and then refresh.

   throw Error(`There is already service named ${serviceName}`);
    ^
Error: There is already service named /add_two_ints
    at Error (native)

Invalid topic type for unadvertise operation should return error not warning

For advertise operation, invalid topic will cause an error status response as an TypeError is caught during the executeCommand. However, invalid topic type for unadvertise operation will return a warning status response, like this:

    this._registerOpMap('unadvertise', (command) => {
      if (!this._topicsPublished.has(command.topic)) {
        debug(`The topic ${command.topic} does not exist.`);
        let error = new Error();
        error.level = 'warning';
        throw error;
      }
      debug(`unadvertise a topic: ${command.topic}`);
      this._topicsPublished.delete(command.topic);
      this._resourceProvider.destroyPublisher(command.topic);
    });

Since the topic is not in _topicPublished, it simply returns a warning status response. This is logic of another rule indicated in the spec while the topic type is valid.

Well, the specification doesn't indicate this explicitly but I think it should keep aligned with the advertise operation.

run example "subscription.html" error

I test example as READ.me, run the command as follow:
DEBUG=ros2-web-bridge:* node bin/rosbridge.js
node ./index.js
and open "http://localhost:3000/html/subscription.html" ,then I get following ouput:
ros2-web-bridge:index The ros2-web-bridge has started. +0ms The web socket server started on ws://localhost:9090 ros2-web-bridge:Bridge Web bridge 7b683e66-89d1-45ed-a537-f404790d3256 is created +0ms ros2-web-bridge:Bridge JSON command received: {"op":"subscribe","id":"subscribe:/example_topic:1","type":"std_msgs/String","topic":"/example_topic","compression":"none","throttle_rate":0,"queue_length":0} +1ms ros2-web-bridge:Bridge subscribe a topic named /example_topic +0ms ros2-web-bridge:Bridge Exception caught in Bridge.executeCommand(): TypeError: Invalid argument +16ms ros2-web-bridge:Bridge Error: TypeError: Invalid argument happened when executing command subscribe +0ms ros2-web-bridge:Bridge Response: {"op":"set_level","id":"subscribe:/example_topic:1","level":"error"} +1ms
I wonder what's the matter ?
@minggangw

npm i in docker says: cannot convert ‘rcutils_allocator_t’ to ‘rcl_context_t*’

Hi, i get the following error when i try to npm install in the official ros2 docker image:

make: Entering directory '/ros2-web-bridge/node_modules/rclnodejs/build'
  CXX(target) Release/obj.target/rclnodejs/src/addon.o
  CXX(target) Release/obj.target/rclnodejs/src/executor.o
../src/executor.cpp: In static member function ‘static void rclnodejs::Executor::Run(void*)’:
../src/executor.cpp:96:66: error: cannot convert ‘rcutils_allocator_t’ to ‘rcl_context_t*’ for argument ‘7’ to ‘rcl_ret_t rcl_wait_set_init(rcl_wait_set_t*, size_t, size_t, size_t, size_t, size_t, rcl_context_t*, rcl_allocator_t)’
                                       rcl_get_default_allocator());

logs with node 8 and node 11

[email protected] seems to install fine

ros2-web-bridge version: 0.2.5

How should i continue debugging this?

Error in npm install, please suggest the possible solution.

[email protected] install /home/username/ros2-web-bridge/node_modules/rclnodejs/node_modules/ref
node-gyp rebuild

make: Entering directory '/home/username/ros2-web-bridge/node_modules/rclnodejs/node_modules/ref/build'
CXX(target) Release/obj.target/binding/src/binding.o
../src/binding.cc: In function ‘void init(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE)’:
../src/binding.cc:643:8: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
Nan::ForceSet(target, Nan::Newv8::String("endianness").ToLocalChecked(), Nan::Newv8::String(CheckEndianness()).ToLocalChecked(), static_
^
In file included from ../node_modules/nan/nan.h:197:0,
from ../src/binding.cc:7:
../node_modules/nan/nan_maybe_43_inl.h:116:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
../src/binding.cc:643:8: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
Nan::ForceSet(target, Nan::Newv8::String("endianness").ToLocalChecked(), Nan::Newv8::String(CheckEndianness()).ToLocalChecked(), static_
^
In file included from ../node_modules/nan/nan.h:197:0,
from ../src/binding.cc:7:
../node_modules/nan/nan_maybe_43_inl.h:116:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
../src/binding.cc:643:187: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
endianness").ToLocalChecked(), Nan::Newv8::String(CheckEndianness()).ToLocalChecked(), static_cast(ReadOnly|DontDelete));
^
In file included from ../node_modules/nan/nan.h:197:0,
from ../src/binding.cc:7:
../node_modules/nan/nan_maybe_43_inl.h:116:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
../src/binding.cc:644:8: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
Nan::ForceSet(target, Nan::Newv8::String("NULL").ToLocalChecked(), WrapNullPointer(), static_cast(ReadOnly|DontDelete))
^
In file included from ../node_modules/nan/nan.h:197:0,
from ../src/binding.cc:7:
../node_modules/nan/nan_maybe_43_inl.h:116:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
../src/binding.cc:644:8: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
Nan::ForceSet(target, Nan::Newv8::String("NULL").ToLocalChecked(), WrapNullPointer(), static_cast(ReadOnly|DontDelete))
^
In file included from ../node_modules/nan/nan.h:197:0,
from ../src/binding.cc:7:
../node_modules/nan/nan_maybe_43_inl.h:116:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
../src/binding.cc:644:142: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
Nan::ForceSet(target, Nan::Newv8::String("NULL").ToLocalChecked(), WrapNullPointer(), static_cast(ReadOnly|DontDelete));
^
In file included from ../node_modules/nan/nan.h:197:0,
from ../src/binding.cc:7:
../node_modules/nan/nan_maybe_43_inl.h:116:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
SOLINK_MODULE(target) Release/obj.target/binding.node
COPY Release/binding.node
make: Leaving directory '/home/username/ros2-web-bridge/node_modules/rclnodejs/node_modules/ref/build'

[email protected] install /home/username/ros2-web-bridge/node_modules/rclnodejs
node-gyp rebuild

module.js:327
throw err;
^

Error: Cannot find module 'nan'
at Function.Module._resolveFilename (module.js:325:15)
at Function.Module._load (module.js:276:25)
at Module.require (module.js:353:17)
at require (internal/module.js:12:17)
at [eval]:1:1
at Object.exports.runInThisContext (vm.js:54:17)
at Object. ([eval]-wrapper:6:22)
at Module._compile (module.js:409:26)
at node.js:579:27
at nextTickCallbackWith0Args (node.js:420:9)
gyp: Call to 'node -e "require('nan')"' returned exit status 1 while in binding.gyp. while trying to load binding.gyp
gyp ERR! configure error
gyp ERR! stack Error: gyp failed with exit code: 1
gyp ERR! stack at ChildProcess.onCpExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:305:16)
gyp ERR! stack at emitTwo (events.js:87:13)
gyp ERR! stack at ChildProcess.emit (events.js:172:7)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:200:12)
gyp ERR! System Linux 4.13.0-26-generic
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /home/username/ros2-web-bridge/node_modules/rclnodejs
gyp ERR! node -v v4.4.2
gyp ERR! node-gyp -v v3.3.1
gyp ERR! not ok
npm ERR! Linux 4.13.0-26-generic
npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "install"
npm ERR! node v4.4.2
npm ERR! npm v2.15.0
npm ERR! code ELIFECYCLE

npm ERR! [email protected] install: node-gyp rebuild
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] install script 'node-gyp rebuild'.
npm ERR! This is most likely a problem with the rclnodejs package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node-gyp rebuild
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs rclnodejs
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!
npm ERR! npm owner ls rclnodejs
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR! /home/username/ros2-web-bridge/npm-debug.log

Subscriber example not working

System Information

Windows 10, ROS2 [Bouncy Bolson]

Problem

When i run the basic example listener

http://localhost:3000/html/subscription.html

and the basic example publisher

http://localhost:3000/html/publisher.html

the listener can't recive any message.

The debug log shows that there is an error when the subscriber attached :

subscribe a topic named /example_topic
Exception caught in Bridge.executeCommand(): TypeError: Invalid argument
Error: TypeError: Invalid argument happened when executing command subscribe
Response: {"op":"set_level","id":"subscribe:/example_topic:1","level":"error"}

The publisher works correct?:

The ros2-web-bridge has started.
The web socket server started on ws://localhost:9090
Web bridge 8bf4f36b-07d9-438d-bcce-cbe3c3308fe5 is created
JSON command received: {"op":"advertise","id":"advertise:/example_topic:1","type":"std_msgs/String","topic":"/example_topic","latch":false,"queue_size":100}
advertise a topic: /example_topic
Publisher has been created, and the topic name is /example_topic.
Response: {"op":"set_level","level":"none"}
JSON command received: {"op":"publish","id":"publish:/example_topic:2","topic":"/example_topic","msg":{"data":"Test: 0"},"latch":false}
Publish a topic named /example_topic with {"data":"Test: 0"}
Response: {"op":"set_level","level":"none"}
JSON command received: {"op":"publish","id":"publish:/example_topic:3","topic":"/example_topic","msg":{"data":"Test: 1"},"latch":false}
Publish a topic named /example_topic with {"data":"Test: 1"}
Response: {"op":"set_level","level":"none"}

My package.json

{
  "name": "ros2-web-bridge",
  "version": "0.2.5",
  "description": "Bridge the web clients to ROS2.0 by a JSON interface",
  "main": "index.js",
  "keywords": [
    "ros2",
    "webbridge",
    "rcl",
    "rclnodejs"
  ],
  "bin": {
    "rosbridge": "bin/rosbridge.js"
  },
  "scripts": {
    "test": "mocha test/nodejs/",
    "wsserver": "node bin/rosbridge.js",
    "browser": "node test/browser/test-html.js",
    "lint": "eslint --max-warnings=0 index.js lib examples test",
    "protocol": "mocha test/nodejs/protocol/entry.js",
    "ci": "npm run test && npm run protocol && npm run browser"
  },
  "license": "Apache-2.0",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/RobotWebTools/ros2-web-bridge.git"
  },
  "authors": [
    "Minggang Wang <[email protected]>",
    "Kenny Yuan <[email protected]>",
    "Wanming Lin <[email protected]>",
    "Zhong Qiu <[email protected]>"
  ],
  "devDependencies": {
    "async": "^2.6.0",
    "express": "^4.16.2",
    "eslint": "^4.19.1",
    "js-sha512": "^0.7.1",
    "mocha": "^5.0.5",
    "jsdom": "^11.5.1",
    "puppeteer": "^1.2.0"
  },
  "dependencies": {
    "commander": "^2.12.2",
    "debug": "^3.1.0",
    "rclnodejs": "0.3.5",
    "uuid": "^3.1.0",
    "ws": "^6.0.0"
  }
}


So what happend there?

best regards

ros2bridge kick-off

Hi @jihoonl, as rclnodejs is nearly done, we want to move forward to start the ros2bridge project. Generally, we want to follow the rosbridge v2 Protocol and leverage the rclnodejs, our aim is to influence the roslibjs as little as possible, thus a series of components, which are based on roslibjs, could be available to avoid huge effort on porting to ros2bridge. Do you have any suggestions or thing we can do to promote the rclnodejs client? Thanks!

Failed to receive complex type message via bridge

Let's say we send and receive complex type message like sensor_msgs/Image, here is the code based on
roslibjs:

 var ros = new ROSLIB.Ros();

 ros.connect('ws://localhost:9090');
 var example = new ROSLIB.Topic({
     ros: ros,
     name: '/big_data_topic',
     messageType : 'sensor_msgs/Image'
  });
let dataLength = 320 * 240 * 4 * 4;
let uint8Data = new Array(dataLength);
for (let i = 0; i < dataLength; i++) {
  uint8Data[i] = i % 255;
}   
example.subscribe(function(message) {        
  var length = message.data.length;
  console.log(`${length} bytes received`);
});
      
example.publish({
  header: {stamp: {sec: 11223, nanosec: 44556}, frame_id: 'f001', },
  height: 240, width: 320, encoding: 'rgba', is_bigendian: false, step: 320*16, is_dense: false,
  data: Uint8Array.from(uint8Data),
});

Since there is no way to construct the message object and assign member for it, we can only publish message data in JavaScript object notation way, like above.

However, the subscriber failed to receive the message. We found the subscriber callback was never called and and got this error in from the ros2-web-bridge log:

  ros2-web-bridge:Bridge Exception caught in Bridge.executeCommand(): TypeError: msg.hasMember is not a function +1m
  ros2-web-bridge:Bridge Error: TypeError: msg.hasMember is not a function happened when executing command publish +1ms
  ros2-web-bridge:Bridge Response: {"op":"set_level","id":"publish:/big_data_topic:3","level":"error"} +0ms

The same JavaScript object notation way of message can be published and received by rclnodejs node

Message data not inferred for subscribe operation

According to rosbridge v2 protocol section 3.4.4 subscription

If left off, type will be inferred.

Let's take a look at the subscribe operation:

{ "op": "subscribe",
  (optional) "id": <string>,
  "topic": <string>,
  (optional) "type": <string>,
  (optional) "throttle_rate": <int>,
  (optional) "queue_length": <int>,
  (optional) "fragment_size": <int>,
  (optional) "compression": <string>
}

type field is optional, however, sending a subscribe operation without type field to the bridge server will get an error response.

Request: {op: 'subscribe', id: 'subscribe_id1', topic: 'example_topic'}
Response: {"op":"set_level","id":"subscribe_id1","level":"error"}

This error happened in _exractMessageType(type) before creating the rclnodejs subscription.

“npm install” error

I git clone this rep, and I run "npm install", then get the error:
npm install

[email protected] install /home/hzh/xinliu/ros2-web-bridge/node_modules/ref
node-gyp rebuild

make: Entering directory '/home/hzh/xinliu/ros2-web-bridge/node_modules/ref/build'
CXX(target) Release/obj.target/binding/src/binding.o
../src/binding.cc: In function ‘void init(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE)’:
../src/binding.cc:643:8: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
Nan::ForceSet(target, Nan::Newv8::String("endianness").ToLocalChecked(), Nan::Newv8::String(CheckEndianness()).
^
In file included from ../../nan/nan.h:197:0,
from ../src/binding.cc:7:
../../nan/nan_maybe_43_inl.h:130:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
../src/binding.cc:643:8: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
Nan::ForceSet(target, Nan::Newv8::String("endianness").ToLocalChecked(), Nan::Newv8::String(CheckEndianness()).
^
In file included from ../../nan/nan.h:197:0,
from ../src/binding.cc:7:
../../nan/nan_maybe_43_inl.h:130:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
../src/binding.cc:643:187: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
ed(), Nan::Newv8::String(CheckEndianness()).ToLocalChecked(), static_cast(ReadOnly|DontDelete));
^
In file included from ../../nan/nan.h:197:0,
from ../src/binding.cc:7:
../../nan/nan_maybe_43_inl.h:130:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
../src/binding.cc:644:8: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
Nan::ForceSet(target, Nan::Newv8::String("NULL").ToLocalChecked(), WrapNullPointer(), static_cast<PropertyAttribu
^
In file included from ../../nan/nan.h:197:0,
from ../src/binding.cc:7:
../../nan/nan_maybe_43_inl.h:130:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
../src/binding.cc:644:8: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
Nan::ForceSet(target, Nan::Newv8::String("NULL").ToLocalChecked(), WrapNullPointer(), static_cast<PropertyAttribu
^
In file included from ../../nan/nan.h:197:0,
from ../src/binding.cc:7:
../../nan/nan_maybe_43_inl.h:130:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
../src/binding.cc:644:142: warning: ‘Nan::Maybe Nan::ForceSet(v8::Localv8::Object, v8::Localv8::Value, v8::Localv8::Value, v8::PropertyAttribute)’ is deprecated [-Wdeprecated-declarations]
n::Newv8::String("NULL").ToLocalChecked(), WrapNullPointer(), static_cast(ReadOnly|DontDelete));
^
In file included from ../../nan/nan.h:197:0,
from ../src/binding.cc:7:
../../nan/nan_maybe_43_inl.h:130:35: note: declared here
NAN_DEPRECATED inline Maybe ForceSet(
^
SOLINK_MODULE(target) Release/obj.target/binding.node
COPY Release/binding.node
make: Leaving directory '/home/hzh/xinliu/ros2-web-bridge/node_modules/ref/build'

[email protected] install /home/hzh/xinliu/ros2-web-bridge/node_modules/puppeteer
node install.js

Downloading Chromium r579032 - 102.3 Mb [====================] 100% 0.0s
Chromium downloaded to /home/hzh/xinliu/ros2-web-bridge/node_modules/puppeteer/.local-chromium/linux-579032

[email protected] install /home/hzh/xinliu/ros2-web-bridge/node_modules/rclnodejs
node-gyp rebuild

console.log(process.env.COLCON_PREFIX_PATH.replace(/:/, '/include/ ') + '/include/')
^

TypeError: Cannot read property 'replace' of undefined
at eval:1:44
at ContextifyScript.Script.runInThisContext (vm.js:50:33)
at Object.runInThisContext (vm.js:139:38)
at Object. (eval-wrapper:6:22)
at Module._compile (module.js:652:30)
at evalScript (bootstrap_node.js:466:27)
at startup (bootstrap_node.js:167:9)
at bootstrap_node.js:612:3
gyp: Call to 'node -e "console.log(process.env.COLCON_PREFIX_PATH.replace(/:/, '/include/ ') + '/include/')"' returned exit status 1 while in binding.gyp. while trying to load binding.gyp
gyp ERR! configure error
gyp ERR! stack Error: gyp failed with exit code: 1
gyp ERR! stack at ChildProcess.onCpExit (/usr/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:336:16)
gyp ERR! stack at emitTwo (events.js:126:13)
gyp ERR! stack at ChildProcess.emit (events.js:214:7)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:198:12)
gyp ERR! System Linux 4.15.0-30-generic
gyp ERR! command "/usr/bin/node" "/usr/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /home/hzh/xinliu/ros2-web-bridge/node_modules/rclnodejs
gyp ERR! node -v v8.11.2
gyp ERR! node-gyp -v v3.6.2
gyp ERR! not ok
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] install: node-gyp rebuild
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /home/hzh/.npm/_logs/2018-08-21T11_36_13_719Z-debug.log

and the node version is v8.11.2 , nvm version is : 0.33.11

Bridge server crashed when running a websocket client

I got bridge server crashes when a web socket client running for the second time. Here is the crash traceback information:

$ node bin/rosbridge.js 
The web socket server started on ws://localhost:9090
FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal.
 1: node::Abort() [node]
 2: 0x121a2cc [node]
 3: v8::Utils::ReportApiFailure(char const*, char const*) [node]
 4: node::TCPWrap::Instantiate(node::Environment*, node::AsyncWrap*) [node]
 5: node::ConnectionWrap<node::TCPWrap, uv_tcp_s>::OnConnection(uv_stream_s*, int) [node]
 6: 0x144a930 [node]
 7: 0x144ffa8 [node]
 8: uv_run [node]
 9: node::Start(uv_loop_s*, int, char const* const*, int, char const* const*) [node]
10: node::Start(int, char**) [node]
11: __libc_start_main [/lib/x86_64-linux-gnu/libc.so.6]
12: 0x8aee41 [node]
Aborted (core dumped)

A core dump file was generated but its size is too big to attach to here. I can provide it in other way.
Here is my websocket client code:

const WebSocket = require('ws');

const ws = new WebSocket('ws://localhost:9090');
var count = 0;
ws.on('open', function open() {
  const msg = {
    "op": "subscribe",
    "id": "subscribe_id1",
    "topic": "example_topic",
    "type": "std_msgs/String"
  };
  ws.send(JSON.stringify(msg));
  count++;
});

ws.on('message', function incoming(data) {

  console.log(`${count}:` + data.toString());
  if (count === 4) {
    console.log('about to close ws');
    ws.close();
    count++;
  }

  if (count === 3) { 
    count++;
  }

  if (count === 2) {
    const msg2 = {
      op: 'publish', id: 'publish_id1', topic: 'example_topic', msg: {data: 'hello world!'}
    };
    ws.send(JSON.stringify(msg2));
    count++;
  }

  if (count === 1) {
    const msg1 = {
      op: 'advertise', id: 'advertise_id1', topic: 'example_topic', type: 'std_msgs/String'      
    };
    ws.send(JSON.stringify(msg1));
    count++;
  }
});

Reproduce steps:

``
$ node client.js
1:{"op":"set_level","level":"none"}
2:{"op":"set_level","level":"none"}
3:{"op":"set_level","level":"none"}
4:{"op":"publish","topic":"example_topic","msg":{"data":"hello world!"}}
about to close ws

$ node client.js
events.js:183
throw er; // Unhandled 'error' event
^

Error: read ECONNRESET
at _errnoException (util.js:1022:11)
at TCP.onread (net.js:615:25)

``

Environments:

Platform differences:

I cannot reproduce it on Windows platform.

Both ros1 and ros2 message or interface types support

This proposal comes from the bridge client to interact with the rclnodejs.

In ROS1, like roslibjs the message or interface type is defined as std_msgs/String, example_interfaces/AddTwoInts.

In ROS2, like rclnodejs, the message or interface types is defined as std_msgs/msg/String, example_interfaces/srv/AddTwoInts.

Currently, the bridge server will assume all the message or interface types are in ROS1 format and insert msg or srv. When the service is in rclnodejs environment, there might be inconsistent code.

Suggest to support both ROS1 and ROS2 message or interface types.

Lack of handler for unrecognized operations

Currently, the bridge can process valid operations received from the web socket server after #26 fixed.
However, for unrecognized operations, there is no good handler to process them.

// lib/bridge.js: 119
  _receiveMessage(message) {
    const command = this._parser.process(message);
    if (!command) return;  // simply return
...

When the bridge receives message that contains unrecognized operations, it simply ignore it and thus the client will not receive any feedback. Any web socket clients will hang after send an invalid operation message. This is not a good handler.

Header members incorrespondence

In roslibjs, we found the builtin_interfaces/Time members are secs and nsecs. While in ROS2, the members are sec and nanosec. When we work the bridge with roslibjs, there will be errors in the log of bridge. Which side should be updated to keep correspondence?

Failed to run subscribe test cases serially

This issue is created for track the failure of CI checking of PR #66. The issue is described as:

  • They're subscribe-operation-related.
  • Running individual test case will pass
  • Failed to run multiple test cases or run it twice
  • The failure because of bridge server crash during running the test cases.

This issue may be duplicated to #58.

Subscribe op implementation incompatible with rosbridge protocol spec

Here is the publish operation interface defined in the spec

{ "op": "subscribe",
  (optional) "id": <string>,
  "topic": <string>,
  (optional) "type": <string>,
  (optional) "throttle_rate": <int>,
  (optional) "queue_length": <int>,
  (optional) "fragment_size": <int>,
  (optional) "compression": <string>
}

Though the throttle_rate, queue_length are optional fields, they will impact the subscriber's behavior. The spec requires:

If queue_length is specified, then messages are placed into the queue before being sent.
If a client has multiple subscriptions to the same topic, then messages are sent at the lowest throttle_rate, with the lowest fragmentation size, and highest queue_length.

In current implementation of rosbridge, this field was never considered for subscribe operation.

Complex message type altered after bridge server sending back to clients

For complex message type, like sensor_msgs/msg/JointState, the message object altered when the rosbridge server tried sending back it to the clients. Here it is:

  • publish sensor_msg/msg/JointState message type:
{ op: 'publish', id: 'publish_setup_jointstate', topic: 'subscribe_jointstate_topic',
  msg: {
    header: {
      stamp: {
        sec: 123456, nanosec: 789
      },
      frame_id: 'main frame'},
    name: ['Tom', 'Jerry'],
    position: [1, 2],
    velocity: [2, 3],
    effort: [4, 5, 6]}}
  • Returned response:
{ op: 'publish', topic: 'subscribe_jointstate_topic',
  msg: {
    header: {
        stamp: [Object],
        frame_id: 'main frame' },
     name: [ 'Tom', 'Jerry' ],
     position: { '0': 1, '1': 2 },
     velocity: { '0': 2, '1': 3 },
     effort: { '0': 4, '1': 5, '2': 6 } } }

The structure of the message altered after serialization. This happened when calling JSON.stringify() and JSON.parse() of the JointState message object.

service_response should be considered as a response operation

According to the spec: service_response should be a response operation, which means it not considered as a request operation. Because it contains some necessary fields that cannot get from the request side.

{ "op": "service_response",
  (optional) "id": <string>,
  "service": <string>,
  (optional) "values": <list<json>>,
  "result": <boolean>
}

As a web socket client cannot get the result. Meanwhile, from the call_service will send a service_response, which proves the service_response is not a request operation.

However, current bridge implementation consider the service_response as a request operation:

    this._registerOpMap('service_response', (command) => {
      let serviceName = command.service;
      let id = command.id;
      let response = this._servicesResponse.get(id);
      if (response) {
        response.send(command.values);
        this._servicesResponse.delete(id);
      }
    });

Add command line wrapper and argument processor

Add command line wrapper so that user can run a specific script instead of running node index.js command

Add argument processor that is compatible with 1.0 bridge, e.g. --port, --address, --retry_startup_delay, --fragment_timeout and etc.

Add Authentication feature

op auth - optional authentication information can be passed via the rosbridge protocol to authenticate a client connection. This information should come from some trusted third-party authenticator.

According to the rosbridge Protocol Specification document

  • Any server that enabled authentication should wait for this request to come in first before accepting any other op code from the client.
  • Once the request comes in, it would verify the information (in a ROS system, using rosauth; however, the verification method is not tied to ROS).
  • If the authentication is good, the connection would be kept and rosbridge would function as normal. If the authentication is bad, the connection would be severed.
  • In the case that authentication is not enabled on the server, this op code can be ignored.

Note: The rosbridge for ROS 1.0 project depends on this rosauth to do authentication

import rospy
from rosauth.srv import Authentication

    def on_message(self, message):
        cls = self.__class__
        # check if we need to authenticate
        if cls.authenticate and not self.authenticated:
            try:
                msg = json.loads(message)
                if msg['op'] == 'auth':
                    # check the authorization information
                    auth_srv = rospy.ServiceProxy('authenticate', Authentication)
                    resp = auth_srv(msg['mac'], msg['client'], msg['dest'],
                                                  msg['rand'], rospy.Time(msg['t']), msg['level'],
                                                  rospy.Time(msg['end']))
                    self.authenticated = resp.authenticated
                    if self.authenticated:
                        rospy.loginfo("Client %d has authenticated.", self.protocol.client_id)
                        return
                # if we are here, no valid authentication was given
                rospy.logwarn("Client %d did not authenticate. Closing connection.",
                              self.protocol.client_id)
                self.close()
            except:
                # proper error will be handled in the protocol class
                self.protocol.incoming(message)
        else:
            # no authentication required
            self.protocol.incoming(message)

The service definition:

# MAC string given by the client
string mac
# IP of the client
string client
# IP of the destination
string dest
# Random string given by the client
string rand
# Time of the authorization request given by the client
time t
# User level as a string given by the client
string level
# End time of the client's session given by the client
time end
---
# If the user has proper authentication
bool authenticated

actionlib feature integrated

Many roslibjs APIs depend on the actionlib feature, which is implementing in rclnodejs at present. Since rclnodejs is a dependency of ros2-web-bridge, when this feature is implemented, the bridge should integrate it seamlessly.

More strict check for topic of all unsubscribe-related operations

All the unsubscribe-related operations: unsubscribe and unsubscribe_service should be treated as more strict check, like type checking or whether the topic exists.

Here is the current implementation of unsubscribe operation.

    this._registerOpMap('unsubscribe', (command) => {
      debug(`unsubscribe a topic named ${command.topic}`);
      this._resourceProvider.destroySubscription(command.topic);
    });

This is simply hands the control to the resourceProvide during unsubscribe and it only handles the normal branch: when this topic exists.

  destroySubscription(topicName, bridgeId) {
    if (this._subscripions.has(topicName)) {
      let handle = this._subscripions.get(topicName);
      if (handle.hasCallbackForId(bridgeId)) {
        handle.removeCallback(bridgeId);
        handle.release();
        if (handle.count === 0) {
          this._subscripions.delete(topicName);
        }
      }
    }
  }

As a result, invalid type of topic brings a none level response, which means the unsubscribe succeed. This is wrong and confusing.

What we expected:

  • Invalid topic type: a error returned.
  • Valid topic type but not existed, a warning returned.

For the unsubscribe_service operation, same proposal had been issued in the comments of PR #82.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.