Skip to content

Commit b029dbc

Browse files
wayneparrottMinggang Wang
authored andcommitted
Improve UX of msg generation process (#759)
This patch implements: - define a npm binary script in package.json named generate-messages - add #! details to generate_messages.js script - add section to FAQ.md describing how to generate messages - added test case that runs the generate-messages script from root folder of a new node pkg, <my_pkg>/node_modules/.bin/generate-messages Fix #750
1 parent bcd7cc1 commit b029dbc

File tree

5 files changed

+179
-17
lines changed

5 files changed

+179
-17
lines changed

README.md

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
| develop | [![Build Status](https://travis-ci.org/RobotWebTools/rclnodejs.svg?branch=develop)](https://travis-ci.org/RobotWebTools/rclnodejs) | [![macOS Build Status](https://circleci.com/gh/RobotWebTools/rclnodejs/tree/develop.svg?style=shield)](https://circleci.com/gh/RobotWebTools/rclnodejs) | [![Build status](https://ci.appveyor.com/api/projects/status/upbc7tavdag1aa5e/branch/develop?svg=true)](https://ci.appveyor.com/project/minggangw/rclnodejs/branch/develop) |
66
| master | [![Build Status](https://travis-ci.org/RobotWebTools/rclnodejs.svg?branch=master)](https://travis-ci.org/RobotWebTools/rclnodejs) | [![macOS Build Status](https://circleci.com/gh/RobotWebTools/rclnodejs/tree/master.svg?style=shield)](https://circleci.com/gh/RobotWebTools/rclnodejs) | [![Build status](https://ci.appveyor.com/api/projects/status/upbc7tavdag1aa5e/branch/master?svg=true)](https://ci.appveyor.com/project/minggangw/rclnodejs/branch/master) |
77

8-
`rclnodejs` is a Node.js client library for the Robot Operating System (ROS 2). It provides a JavaScript API and TypeScript declarations for ROS 2 programming.
8+
**rclnodejs** is a Node.js client library for the Robot Operating System
9+
([ROS 2](https://index.ros.org/doc/ros2/)). It provides a JavaScript API
10+
and tooling for ROS 2 programming. TypeScript declarations, i.e., (*.d.ts),
11+
are included to support use in TypeScript projects.
912

1013
Here's an example for how to create a ROS 2 node that publishes a string message in a few lines of JavaScript.
1114

@@ -67,12 +70,11 @@ npm i [email protected]
6770

6871
API documentation is generated by `jsdoc` and can be viewed in the `docs/` folder or [on-line](http://robotwebtools.org/rclnodejs/docs/index.html). To create a local copy of the documentation run `npm run docs`.
6972

70-
## Using TypeScript
73+
## Using rclnodejs with TypeScript
7174

7275
`rclnodejs` API can be used in TypeScript projects. You can find the TypeScript declaration files (\*.d.ts) in the `types/` folder.
7376

74-
Your project `tsconfig.json` file should include the following compiler options:
75-
77+
Your `tsconfig.json` file should include the following compiler options:
7678
```jsonc
7779
{
7880
"compilerOptions": {
@@ -96,17 +98,57 @@ rclnodejs.init().then(() => {
9698
});
9799
```
98100

99-
The benefits of using TypeScript become evident when working with more complex use-cases. The ROS 2 messages in your environment are defined in the `types/interfaces.d.ts` module. This module is updated as part of the `generate-ros-messages` process. Here's a trivial example of working with a String msg.
101+
The benefits of using TypeScript become evident when working with more complex use-cases. ROS messages are defined in the `types/interfaces.d.ts` module. This module is updated as part of the `generate-messages` process described in the next section.
100102

101-
```typescript
102-
const msg: rclnodejs.std_msgs.msg.String = {
103-
data: 'hello ROS2 from rclnodejs',
104-
};
103+
## ROS2 Interface Message Generation (important)
104+
ROS components communicate by sending and receiving messages described
105+
by the interface definition language (IDL). ROS client libraries such as
106+
rclnodejs are responsible for converting these IDL message descriptions
107+
into source code of their target language. For this, rclnodejs provides
108+
the `generate-messages` npm script that reads in the IDL
109+
messages files of a ROS environment and generates corresponding JavaScript
110+
message interface files. Additionally, the tool generates the TypeScript
111+
`interface.d.ts` file containing declarations for every IDL message file
112+
processed.
113+
114+
Learn more about ROS interfaces and IDL [here](https://index.ros.org/doc/ros2/Concepts/About-ROS-Interfaces/).
115+
116+
In the following example rclnodejs loads a generated JavaScript message file corresponding to the ROS `std_msgs/msg/String' definition.
117+
118+
```
119+
import * as rclnodejs from 'rclnodejs';
120+
let stringMsgObject = rclnodejs.createMessageObject('std_msgs/msg/String');
121+
stringMsgObject.data = 'hello world';
122+
```
123+
124+
### Maintaining Generated JavaScript Message Files
125+
Message files are generated as a post-install step of the rclnodejs
126+
installation process. Thereafter, you will need to manually run the
127+
message generation script when new ROS message packages are installed
128+
for which your ROS2-nodejs project has a dependency.
129+
130+
### Running `generate-messages` Utility
131+
To use `generate-messages` from your Nodejs package, create an npm
132+
script entry in your package.json file as shown:
133+
134+
```
135+
"scripts": {
136+
"generate-messages": "generate-messages"
137+
// your other scripts here
138+
}
139+
````
140+
141+
To run the script use `npm` as follows:
105142
```
143+
npm run generate-messages
144+
```
145+
The newly generated JavaScript files can be found at
146+
`<yourproject>/node_modules/rclnodejs/generated/`.
147+
106148
107149
## Contributing
108150
109-
Please make sure to read the [Contributing Guide]() before making a pull request.
151+
Please read the [Contributing Guide]() before making a pull request.
110152
111153
Thank you to all the [people](CONTRIBUTORS.md) who already contributed to rclnodejs!
112154

docs/FAQ.md

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,9 @@ ros2 pkg list
1515
```
1616
If the package containing your target message is not listed then install it.
1717

18-
Next, inspect the generated JavaScript message files by viewing the `./node_modules/rclnodejs/generated/` folder of your project for your target message. If you are unable to locate the message file then regenerate messages by either 1) reinstalling rclnodejs which will regenerate all JavaScript and TypeScript files as a post-install step:
19-
20-
```
21-
npm i rclnodejs
22-
```
23-
or use the `rclnodejs cli` to generate JavaScript message files
18+
Next, inspect the generated JavaScript message files by viewing the `./node_modules/rclnodejs/generated/` folder of your project for your target message. If you are unable to locate the message file then use the `generate-messages` script:
2419
```
25-
rclnodejs generate-ros-messages
20+
<your_project>/node_modules/.bin/generate-messages
2621
```
2722

2823

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
"postinstall": "node scripts/generate_messages.js",
2222
"format": "clang-format -i -style=file ./src/*.cpp ./src/*.hpp && prettier --write \"{lib,rosidl_gen,rostsd_gen,rosidl_parser,types,example,test,scripts,benchmark}/**/*.{js,md,ts}\" ./*.{js,md,ts}"
2323
},
24+
"bin": {
25+
"generate-messages": "./scripts/generate_messages.js"
26+
},
2427
"authors": [
2528
"Minggang Wang <[email protected]>",
2629
"Kenny Yuan <[email protected]>",

scripts/generate_messages.js

100644100755
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#!/usr/bin/env node
2+
13
/* eslint-disable camelcase */
24
// Copyright (c) 2018 Intel Corporation. All rights reserved.
35

test/test-generate-messages-bin.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright (c) 2021 Wayne Parrott. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const assert = require('assert');
18+
const childProcess = require('child_process');
19+
const fs = require('fs');
20+
const os = require('os');
21+
const path = require('path');
22+
const rclnodejs = require('../index.js');
23+
24+
const GEN_FOLDER = 'generated';
25+
const SCRIPT_NAME = 'generate-messages';
26+
27+
describe('rclnodejs generate-messages binary-script tests', function () {
28+
let cwd;
29+
let tmpPkg;
30+
31+
this.timeout(90 * 1000); // 90 seconds to run this test suite
32+
33+
// Create a test pkg wtih rclnodejs dependency
34+
// create test package folder
35+
// run 'npm init' to create package.xml with rclnodejs as dependency
36+
// run 'npm pack' on rclnodejs saving into test package
37+
// in test package install from rclnodejs.x.y.z.tgz
38+
// delete generated/ folder as this will be generated as part of a test
39+
before(function () {
40+
this.cwd = process.cwd(); // back up rclnodejs path
41+
let tmpDir = os.tmpdir();
42+
this.tmpPkg = path.join(tmpDir, 'rclnodejs_test');
43+
while (fs.existsSync(this.tmpPkg)) {
44+
this.tmpPkg += Math.trunc(Math.random() * 100);
45+
}
46+
47+
fs.mkdirSync(this.tmpPkg);
48+
49+
childProcess.spawnSync('npm', ['init', '-y'], {
50+
stdio: 'inherit',
51+
shell: true,
52+
cwd: this.tmpPkg,
53+
});
54+
childProcess.spawnSync('npm', ['pack', this.cwd], {
55+
stdio: 'inherit',
56+
shell: true,
57+
cwd: this.tmpPkg,
58+
});
59+
60+
let tgz;
61+
const regex = /^rclnodejs-\d+.\d+.\d+.tgz/;
62+
for (let file of fs.readdirSync(this.tmpPkg)) {
63+
if (file.match(regex)) {
64+
tgz = file;
65+
break;
66+
}
67+
}
68+
if (!tgz) {
69+
console.error('ERROR: unable to successfully run npm pack');
70+
return;
71+
}
72+
let tgzPath = path.join(this.tmpPkg, tgz);
73+
childProcess.spawnSync('npm', ['install', tgzPath], {
74+
stdio: 'inherit',
75+
shell: true,
76+
cwd: this.tmpPkg,
77+
});
78+
79+
let generatedFolderPath = createGeneratedFolderPath(this.tmpPkg);
80+
if (fs.existsSync(generatedFolderPath)) {
81+
fs.rmdirSync(generatedFolderPath, { recursive: true });
82+
}
83+
});
84+
85+
after(function () {
86+
// recursively remove test package folder
87+
fs.rmdirSync(this.tmpPkg, { recursive: true });
88+
});
89+
90+
it('test generate-messages script installation', function (done) {
91+
// confirm script is installed at <pgk>/node_modules/.bin/<script>
92+
let script = createScriptFolderPath(this.tmpPkg);
93+
assert.ok(fs.existsSync(script));
94+
done();
95+
});
96+
97+
it('test generate-messages script operation', function (done) {
98+
let script = createScriptFolderPath(this.tmpPkg);
99+
childProcess.spawnSync(script, [], { stdio: 'inherit', shell: true });
100+
101+
let generatedFolderPath = createGeneratedFolderPath(this.tmpPkg);
102+
assert.ok(
103+
fs.existsSync(generatedFolderPath),
104+
'No generated message folder found'
105+
);
106+
assert.ok(
107+
fs.existsSync(path.join(generatedFolderPath, 'std_msgs')),
108+
'std_msgs folder found'
109+
);
110+
done();
111+
});
112+
});
113+
114+
function createGeneratedFolderPath(pkgFolder) {
115+
return path.join(pkgFolder, 'node_modules', 'rclnodejs', GEN_FOLDER);
116+
}
117+
118+
function createScriptFolderPath(pkgFolder) {
119+
return path.join(pkgFolder, 'node_modules', '.bin', SCRIPT_NAME);
120+
}

0 commit comments

Comments
 (0)