Skip to content

Commit ee5b7b1

Browse files
authored
Merge pull request #32 from chfritz/unify_interface_further
Unify interface further, expose action client
2 parents 5b17210 + 1a22dd6 commit ee5b7b1

File tree

7 files changed

+375
-165
lines changed

7 files changed

+375
-165
lines changed

example_turtle.js

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/**
22
An example of using rosnodejs with turtlesim, incl. services,
33
pub/sub, and actionlib. This example uses the on-demand generated
44
messages.
@@ -7,18 +7,7 @@
77
'use strict';
88

99
let rosnodejs = require('./index.js');
10-
const ActionClient = require('./lib/ActionClient.js');
11-
12-
rosnodejs.initNode('/my_node', {
13-
messages: [
14-
'turtlesim/Pose',
15-
'turtle_actionlib/ShapeActionGoal',
16-
'turtle_actionlib/ShapeActionFeedback',
17-
'turtle_actionlib/ShapeActionResult',
18-
'geometry_msgs/Twist',
19-
],
20-
services: ["turtlesim/TeleportRelative"]
21-
}).then((rosNode) => {
10+
rosnodejs.initNode('/my_node', {onTheFly: true}).then((rosNode) => {
2211

2312
// get list of existing publishers, subscribers, and services
2413
rosNode._node._masterApi.getSystemState("/my_node").then((data) => {
@@ -30,11 +19,11 @@ rosnodejs.initNode('/my_node', {
3019

3120
const TeleportRelative = rosnodejs.require('turtlesim').srv.TeleportRelative;
3221
const teleport_request = new TeleportRelative.Request({
33-
linear: -1,
22+
linear: -1,
3423
angular: 0.0
3524
});
3625

37-
let serviceClient = rosNode.serviceClient("/turtle1/teleport_relative",
26+
let serviceClient = rosNode.serviceClient("/turtle1/teleport_relative",
3827
"turtlesim/TeleportRelative");
3928

4029
rosNode.waitForService(serviceClient.getService(), 2000)
@@ -52,17 +41,17 @@ rosnodejs.initNode('/my_node', {
5241
// ---------------------------------------------------------
5342
// Subscribe
5443
rosNode.subscribe(
55-
'/turtle1/pose',
44+
'/turtle1/pose',
5645
'turtlesim/Pose',
5746
(data) => {
5847
console.log('pose', data);
5948
},
6049
{queueSize: 1,
61-
throttleMs: 1000});
50+
throttleMs: 1000});
6251

6352
// ---------------------------------------------------------
6453
// Publish
65-
// equivalent to:
54+
// equivalent to:
6655
// rostopic pub /turtle1/cmd_vel geometry_msgs/Twist '[1, 0, 0]' '[0, 0, 0]'
6756
let cmd_vel = rosNode.advertise('/turtle1/cmd_vel','geometry_msgs/Twist', {
6857
queueSize: 1,
@@ -85,17 +74,57 @@ rosnodejs.initNode('/my_node', {
8574

8675
// wait two seconds for previous example to complete
8776
setTimeout(function() {
88-
let shapeActionGoal = rosnodejs.require('turtle_actionlib').msg.ShapeActionGoal;
89-
let ac = new ActionClient({
90-
type: "turtle_actionlib/ShapeAction",
91-
actionServer: "/turtle_shape"
77+
let shapeActionGoal = rosnodejs.require('turtle_actionlib').msg.ShapeActionGoal;
78+
let ac = rosnodejs.getActionClient({
79+
type: "turtle_actionlib/ShapeAction",
80+
actionServer: "/turtle_shape"
81+
});
82+
ac.sendGoal(new shapeActionGoal({
83+
goal: {
84+
edges: 3,
85+
radius: 1
86+
}
87+
}));
88+
setTimeout(function() {
89+
ac.cancel();
90+
}, 1000);
91+
}, 2000);
92+
93+
// ---------------------------------------------------------
94+
// test int64 + uint64
95+
96+
rosNode.subscribe(
97+
'/int64',
98+
'std_msgs/Int64',
99+
(data) => {
100+
console.log('int64', data);
101+
},
102+
{queueSize: 1,
103+
throttleMs: 1000});
104+
105+
let int64pub = rosNode.advertise('/int64','std_msgs/Int64', {
106+
queueSize: 1,
107+
latching: true,
108+
throttleMs: 9
92109
});
93-
ac.sendGoal(new shapeActionGoal({
94-
goal: {
95-
edges: 3,
96-
radius: 1
97-
}
98-
}));
99-
}, 2000);
110+
const Int64 = rosnodejs.require('std_msgs').msg.Int64;
111+
int64pub.publish(new Int64({ data: "429496729456789012" }));
112+
113+
rosNode.subscribe(
114+
'/uint64',
115+
'std_msgs/UInt64',
116+
(data) => {
117+
console.log('uint64', data);
118+
},
119+
{queueSize: 1,
120+
throttleMs: 1000});
121+
122+
let uint64pub = rosNode.advertise('/uint64','std_msgs/UInt64', {
123+
queueSize: 1,
124+
latching: true,
125+
throttleMs: 9
126+
});
127+
const UInt64 = rosnodejs.require('std_msgs').msg.UInt64;
128+
uint64pub.publish(new UInt64({ data: "9223372036854775807" }));
100129

101130
});

index.js

Lines changed: 36 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const LogFormatter = require('./utils/log/LogFormatter.js');
2929
const RosNode = require('./lib/RosNode.js');
3030
const NodeHandle = require('./lib/NodeHandle.js');
3131
const Logging = require('./lib/Logging.js');
32+
const ActionClient = require('./lib/ActionClient.js');
3233

3334
msgUtils.findMessageFiles();
3435

@@ -133,15 +134,19 @@ let Rosnodejs = {
133134
rosNode = new RosNode(nodeName, rosMasterUri);
134135

135136
return new Promise((resolve, reject) => {
136-
this.use(options.messages, options.services).then(() => {
137-
138-
const connectedToMasterCallback = () => {
139-
Logging.initializeOptions(this, options.logging);
140-
resolve(this.getNodeHandle());
141-
};
142-
137+
const connectedToMasterCallback = () => {
138+
Logging.initializeOptions(this, options.logging);
139+
resolve(this.getNodeHandle());
140+
};
141+
142+
if (options.onTheFly) {
143+
// generate definitions for all messages and services
144+
messages.getAll(function() {
145+
_checkMasterHelper(connectedToMasterCallback, 0);
146+
});
147+
} else {
143148
_checkMasterHelper(connectedToMasterCallback, 0);
144-
});
149+
}
145150
})
146151
.catch((err) => {
147152
log.error('Error: ' + err);
@@ -188,68 +193,6 @@ let Rosnodejs = {
188193
return rtv;
189194
},
190195

191-
/** create message classes and services classes for all the given
192-
* types before calling callback */
193-
use(messages, services) {
194-
const self = this;
195-
return new Promise((resolve, reject) => {
196-
self._useMessages(messages)
197-
.then(() => { return self._useServices(services); })
198-
.then(() => { resolve(); });
199-
});
200-
},
201-
202-
/** create message classes for all the given types */
203-
_useMessages(types) {
204-
const self = this;
205-
206-
// make sure required types are available
207-
[
208-
// for action lib:
209-
'actionlib_msgs/GoalStatusArray',
210-
'actionlib_msgs/GoalID',
211-
// for logging:
212-
'rosgraph_msgs/Log',
213-
].forEach(function(type) {
214-
if (!self.checkMessage(type)) {
215-
// required message definition not available yet, load it
216-
// on-demand
217-
types.unshift(type);
218-
}
219-
});
220-
221-
if (!types || types.length == 0) {
222-
return Promise.resolve();
223-
}
224-
var count = types.length;
225-
return new Promise((resolve, reject) => {
226-
types.forEach(function(type) {
227-
messages.getMessage(type, function(error, Message) {
228-
if (--count == 0) {
229-
resolve();
230-
}
231-
});
232-
});
233-
});
234-
},
235-
236-
/** create service classes for all the given types */
237-
_useServices(types) {
238-
if (!types || types.length == 0) {
239-
return Promise.resolve();
240-
}
241-
var count = types.length;
242-
return new Promise((resolve, reject) => {
243-
types.forEach(function(type) {
244-
messages.getService(type, function() {
245-
if (--count == 0) {
246-
resolve();
247-
}
248-
});
249-
});
250-
});
251-
},
252-
253196
/**
254197
* @return {NodeHandle} for initialized node
255198
*/
@@ -274,6 +217,29 @@ let Rosnodejs = {
274217
console: ConsoleLogStream,
275218
ros: RosLogStream
276219
}
220+
},
221+
222+
223+
//------------------------------------------------------------------
224+
// ActionLib
225+
//------------------------------------------------------------------
226+
227+
/**
228+
Get an action client for a given type and action server.
229+
230+
Example:
231+
let ac = rosNode.getActionClient({
232+
type: "turtle_actionlib/ShapeAction",
233+
actionServer: "/turtle_shape"
234+
});
235+
let shapeActionGoal =
236+
rosnodejs.require('turtle_actionlib').msg.ShapeActionGoal;
237+
ac.sendGoal(new shapeActionGoal({
238+
goal: { edges: 3, radius: 1 } }));
239+
*/
240+
getActionClient(options) {
241+
options.rosnodejs = Rosnodejs;
242+
return new ActionClient(options);
277243
}
278244
}
279245

lib/ActionClient.js

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
'use strict';
1919

20-
const rosnodejs = require('../index.js');
2120
const timeUtils = require('../utils/time_utils.js');
2221
let EventEmitter = require('events');
2322

@@ -29,28 +28,29 @@ class ActionClient extends EventEmitter {
2928

3029
this._actionServer = options.actionServer;
3130

32-
const nh = rosnodejs.nh;
31+
this._rosnodejs = options.rosnodejs;
32+
const nh = this._rosnodejs.getNodeHandle();
3333

3434
// FIXME: support user options for these parameters
35-
this._goalPub = nh.advertise(this._actionServer + '/goal',
35+
this._goalPub = nh.advertise(this._actionServer + '/goal',
3636
this._actionType + 'Goal',
3737
{ queueSize: 1, latching: true });
3838

39-
this._cancelPub = nh.advertise(this._actionServer + '/cancel',
39+
this._cancelPub = nh.advertise(this._actionServer + '/cancel',
4040
'actionlib_msgs/GoalID',
4141
{ queueSize: 1, latching: true });
4242

43-
this._statusSub = nh.subscribe(this._actionServer + '/status',
43+
this._statusSub = nh.subscribe(this._actionServer + '/status',
4444
'actionlib_msgs/GoalStatusArray',
4545
(msg) => { this._handleStatus(msg); },
4646
{ queueSize: 1 } );
4747

48-
this._feedbackSub = nh.subscribe(this._actionServer + '/feedback',
48+
this._feedbackSub = nh.subscribe(this._actionServer + '/feedback',
4949
this._actionType + 'Feedback',
5050
(msg) => { this._handleFeedback(msg); },
5151
{ queueSize: 1 } );
5252

53-
this._statusSub = nh.subscribe(this._actionServer + '/result',
53+
this._statusSub = nh.subscribe(this._actionServer + '/result',
5454
this._actionType + 'Result',
5555
(msg) => { this._handleResult(msg); },
5656
{ queueSize: 1 } );
@@ -97,6 +97,19 @@ class ActionClient extends EventEmitter {
9797
this._goals[goalId] = goal;
9898

9999
this._goalPub.publish(goal);
100+
return goal;
101+
}
102+
103+
/** Cancel the given goal. If none is given, send an empty goal message,
104+
i.e. cancel all goals. See
105+
http://wiki.ros.org/actionlib/DetailedDescription#The_Messages
106+
*/
107+
cancel(goal) {
108+
if (!goal) {
109+
let GoalID = this._rosnodejs.require('actionlib_msgs').msg.GoalID;
110+
goal = new GoalID();
111+
}
112+
this._cancelPub.publish(goal);
100113
}
101114

102115
_generateGoalId() {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"xmlrpc": "chfritz/node-xmlrpc",
2828
"walker" : "1.0.7",
2929
"md5" : "2.1.0",
30-
"async" : "0.1.22",
30+
"async" : "2.0.1",
3131
"bunyan": "1.8.1"
3232
}
3333
}

0 commit comments

Comments
 (0)