Skip to content

Commit e5668f7

Browse files
authored
Merge pull request #29 from chfritz/using_actionclient
Getting the action client to work, and required updates for on-demand messages
2 parents 2166e71 + 9525ae7 commit e5668f7

5 files changed

Lines changed: 111 additions & 101 deletions

File tree

example_turtle.js

Lines changed: 29 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,41 @@
1+
/**
2+
An example of using rosnodejs with turtlesim, incl. services,
3+
pub/sub, and actionlib. This example uses the on-demand generated
4+
messages.
5+
*/
6+
17
'use strict';
28

39
let rosnodejs = require('./index.js');
410
const ActionClient = require('./lib/ActionClient.js');
511

612
rosnodejs.initNode('/my_node', {
713
messages: [
8-
'rosgraph_msgs/Log', // required for new logging approach
914
'turtlesim/Pose',
1015
'turtle_actionlib/ShapeActionGoal',
1116
'turtle_actionlib/ShapeActionFeedback',
1217
'turtle_actionlib/ShapeActionResult',
1318
'geometry_msgs/Twist',
14-
'actionlib_msgs/GoalStatusArray',
15-
'actionlib_msgs/GoalID'
1619
],
17-
services: ['std_srvs/SetBool', "turtlesim/TeleportRelative"]
20+
services: ["turtlesim/TeleportRelative"]
1821
}).then((rosNode) => {
1922

20-
// console.log(new (rosnodejs.require('rosgraph_msgs').msg.Log)());
21-
22-
2323
// ---------------------------------------------------------
2424
// Service Call
2525

2626
const TeleportRelative = rosnodejs.require('turtlesim').srv.TeleportRelative;
2727
const teleport_request = new TeleportRelative.Request({
28-
linear: 0.1,
28+
linear: -1,
2929
angular: 0.0
3030
});
3131

32-
let serviceClient2 = rosNode.serviceClient("/turtle1/teleport_relative",
32+
let serviceClient = rosNode.serviceClient("/turtle1/teleport_relative",
3333
"turtlesim/TeleportRelative");
34-
rosNode.waitForService(serviceClient2.getService(), 2000)
34+
35+
rosNode.waitForService(serviceClient.getService(), 2000)
3536
.then((available) => {
3637
if (available) {
37-
serviceClient2.call(teleport_request, (resp) => {
38+
serviceClient.call(teleport_request, (resp) => {
3839
console.log('Service response ' + JSON.stringify(resp));
3940
});
4041
} else {
@@ -58,84 +59,38 @@ rosnodejs.initNode('/my_node', {
5859
// Publish
5960
// equivalent to:
6061
// rostopic pub /turtle1/cmd_vel geometry_msgs/Twist '[1, 0, 0]' '[0, 0, 0]'
61-
// sudo tcpdump -ASs 0 -i lo | tee tmp/rostopic.dump
6262
let cmd_vel = rosNode.advertise('/turtle1/cmd_vel','geometry_msgs/Twist', {
6363
queueSize: 1,
6464
latching: true,
6565
throttleMs: 9
6666
});
6767

6868
const Twist = rosnodejs.require('geometry_msgs').msg.Twist;
69-
const msgTwist = new Twist();
70-
msgTwist.linear = new (rosnodejs.require('geometry_msgs').msg.Vector3)();
71-
msgTwist.linear.x = 1;
72-
msgTwist.linear.y = 0;
73-
msgTwist.linear.z = 0;
74-
msgTwist.angular = new (rosnodejs.require('geometry_msgs').msg.Vector3)();
75-
msgTwist.angular.x = 0;
76-
msgTwist.angular.y = 0;
77-
msgTwist.angular.z = 0;
78-
// console.log("Twist", msgTwist);
69+
const msgTwist = new Twist({
70+
linear: { x: 1, y: 0, z: 0 },
71+
angular: { x: 0, y: 0, z: 1 }
72+
});
7973
cmd_vel.publish(msgTwist);
8074

81-
// cmd_vel.on('connection', function(s) {
82-
// console.log("connected", s);
83-
// });
84-
8575

8676
// ---------------------------------------------------------
8777
// test actionlib
8878
// rosrun turtlesim turtlesim_node
8979
// rosrun turtle_actionlib shape_server
9080

91-
let pub_action =
92-
rosNode.advertise('/turtle_shape/goal', 'turtle_actionlib/ShapeActionGoal', {
93-
queueSize: 1,
94-
latching: true,
95-
throttleMs: 9
81+
// wait two seconds for previous example to complete
82+
setTimeout(function() {
83+
let shapeActionGoal = rosnodejs.require('turtle_actionlib').msg.ShapeActionGoal;
84+
let ac = new ActionClient({
85+
type: "turtle_actionlib/ShapeAction",
86+
actionServer: "/turtle_shape"
9687
});
97-
98-
let shapeActionGoal = rosnodejs.require('turtle_actionlib').msg.ShapeActionGoal;
99-
// console.log("shapeMsgGoal", shapeActionGoal);
100-
var now = Date.now();
101-
var secs = parseInt(now/1000);
102-
var nsecs = (now % 1000) * 1000;
103-
let shapeMsg = new shapeActionGoal({
104-
header: {
105-
seq: 0,
106-
stamp: new Date(),
107-
frame_id: ''
108-
},
109-
goal_id: {
110-
stamp: new Date(),
111-
id: "/my_node-1-"+secs+"."+nsecs+"000"
112-
},
113-
goal: {
114-
edges: 5,
115-
radius: 1
116-
}
117-
});
118-
119-
// console.log("shapeMsg", shapeMsg);
120-
pub_action.publish(shapeMsg);
121-
122-
123-
// ---- Same with ActionClient:
124-
// console.log("start");
125-
// let ac = new ActionClient({
126-
// type: "turtle_actionlib/ShapeAction",
127-
// actionServer: "turtle_shape"
128-
// });
129-
// // console.log(ac);
130-
// // ac.sendGoal(new shapeActionGoal({
131-
// // goal: {
132-
// // edges: 5,
133-
// // radius: 1
134-
// // }
135-
// // }));
136-
// ac.sendGoal(shapeMsg);
137-
138-
console.log("\n** done\n");
139-
88+
ac.sendGoal(new shapeActionGoal({
89+
goal: {
90+
edges: 3,
91+
radius: 1
92+
}
93+
}));
94+
}, 2000);
14095

14196
});

index.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,28 @@ let Rosnodejs = {
166166
return pack;
167167
},
168168

169+
/** check that a message definition is loaded for a ros message
170+
type, e.g., geometry_msgs/Twist */
171+
checkMessage(type) {
172+
const parts = type.split('/');
173+
let rtv;
174+
try {
175+
rtv = this.require(parts[0]).msg[parts[1]];
176+
} catch(e) {}
177+
return rtv;
178+
},
179+
180+
/** check that a service definition is loaded for a ros service
181+
type, e.g., turtlesim/TeleportRelative */
182+
checkService(type) {
183+
const parts = type.split('/');
184+
let rtv;
185+
try {
186+
rtv = this.require(parts[0]).srv[parts[1]];
187+
} catch(e) {}
188+
return rtv;
189+
},
190+
169191
/** create message classes and services classes for all the given
170192
* types before calling callback */
171193
use(messages, services) {
@@ -179,6 +201,23 @@ let Rosnodejs = {
179201

180202
/** create message classes for all the given types */
181203
_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+
182221
if (!types || types.length == 0) {
183222
return Promise.resolve();
184223
}

lib/ActionClient.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ class ActionClient extends EventEmitter {
3030
this._actionServer = options.actionServer;
3131

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

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

4343
this._statusSub = nh.subscribe(this._actionServer + '/status',
4444
'actionlib_msgs/GoalStatusArray',
@@ -80,13 +80,13 @@ class ActionClient extends EventEmitter {
8080
}
8181

8282
sendGoal(goal) {
83-
if (!goal.hasOwnProperty('goal_id')) {
83+
if (!goal.goal_id) {
8484
goal.goal_id = {
8585
stamp: timeUtils.now(),
8686
id: this._generateGoalId()
8787
};
8888
}
89-
if (!goal.hasOwnProperty(header)) {
89+
if (!goal.header) {
9090
goal.header = {
9191
seq: this._goalSeqNum++,
9292
stamp: goal.goal_id.stamp,

utils/fields.js

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
1+
'use strict';
2+
13
var fields = exports;
24

3-
fields.primitiveTypes = [
4-
'char'
5-
, 'byte'
6-
, 'bool'
7-
, 'int8'
8-
, 'uint8'
9-
, 'int16'
10-
, 'uint16'
11-
, 'int32'
12-
, 'uint32'
13-
, 'int64'
14-
, 'uint64'
15-
, 'float32'
16-
, 'float64'
17-
, 'string'
18-
, 'time'
19-
, 'duration'
20-
];
5+
/* map of all primitive types and their default values */
6+
var map = {
7+
'char': '',
8+
'byte': 0,
9+
'bool': false,
10+
'int8': 0,
11+
'uint8': 0,
12+
'int16': 0,
13+
'uint16': 0,
14+
'int32': 0,
15+
'uint32': 0,
16+
'int64': 0,
17+
'uint64': 0,
18+
'float32': 0,
19+
'float64': 0,
20+
'string': '',
21+
'time': 0,
22+
'duration': 0
23+
};
24+
25+
fields.primitiveTypes = Object.keys(map);
26+
27+
fields.getDefaultValue = function(type) {
28+
let match = type.match(/(.*)\[(\d*)\]/);
29+
if (match) {
30+
// it's an array
31+
const basetype = match[1];
32+
const length = (match[2].length > 0 ? parseInt(match[2]) : 0);
33+
return new Array(length).fill(fields.getDefaultValue(basetype));
34+
} else {
35+
return map[type];
36+
}
37+
};
2138

2239
fields.isPrimitive = function(fieldType) {
2340
return (fields.primitiveTypes.indexOf(fieldType) >= 0);

utils/messages.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,6 @@ function buildValidator (details) {
433433
* use with ROS, incl. serialization, deserialization, and md5sum. */
434434
function buildMessageClass(details) {
435435
function Message(values) {
436-
// console.log("buildMessageClass, new", details, values);
437436
if (!(this instanceof Message)) {
438437
return new Message(values);
439438
}
@@ -448,14 +447,14 @@ function buildMessageClass(details) {
448447

449448
if (details.fields) {
450449
details.fields.forEach(function(field) {
451-
// console.log("buildMessageClass", details, field, values);
452450
if (field.messageType) {
453451
// sub-message class
454452
that[field.name] =
455453
new (field.messageType)(values ? values[field.name] : undefined);
456454
} else {
457455
// simple value
458-
that[field.name] = values ? values[field.name] : (field.value || null);
456+
that[field.name] = values ? values[field.name] :
457+
(field.value || fieldsUtil.getDefaultValue(field.type));
459458
}
460459
});
461460
}
@@ -598,7 +597,7 @@ function deserializeInnerMessage(message, buffer, bufferOffset) {
598597
bufferOffset += fieldsUtil.getPrimitiveSize(arrayType, value);
599598
array.push(value);
600599
}
601-
else if (fieldsUtil.isMessageType(arrayType)) {
600+
else if (fieldsUtil.isMessage(arrayType)) {
602601
var arrayMessage = new field.messageType();
603602
arrayMessage = deserializeInnerMessage(
604603
arrayMessage, buffer, bufferOffset);

0 commit comments

Comments
 (0)