rclnodejs 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/binding.gyp +12 -0
- package/lib/action/client.js +40 -0
- package/lib/action/server.js +21 -0
- package/lib/client.js +1 -1
- package/lib/distro.js +2 -0
- package/lib/event_handler.js +474 -0
- package/lib/lifecycle.js +9 -0
- package/lib/lifecycle_publisher.js +2 -2
- package/lib/node.js +85 -11
- package/lib/node_options.js +21 -1
- package/lib/publisher.js +50 -4
- package/lib/service.js +1 -1
- package/lib/subscription.js +48 -4
- package/lib/type_description_service.js +82 -0
- package/package.json +1 -1
- package/scripts/npmjs-readme.md +6 -6
- package/src/addon.cpp +8 -0
- package/src/executor.cpp +3 -4
- package/src/handle_manager.cpp +13 -2
- package/src/handle_manager.h +4 -1
- package/src/rcl_action_client_bindings.cpp +99 -24
- package/src/rcl_action_server_bindings.cpp +69 -19
- package/src/rcl_event_handle_bindings.cpp +294 -0
- package/src/rcl_event_handle_bindings.h +26 -0
- package/src/rcl_lifecycle_bindings.cpp +13 -0
- package/src/rcl_node_bindings.cpp +43 -8
- package/src/rcl_publisher_bindings.cpp +19 -0
- package/src/rcl_type_description_service_bindings.cpp +79 -0
- package/src/rcl_type_description_service_bindings.h +27 -0
- package/types/action_client.d.ts +18 -0
- package/types/action_server.d.ts +12 -0
- package/types/lifecycle.d.ts +7 -0
- package/types/node.d.ts +44 -2
- package/types/publisher.d.ts +17 -0
package/lib/node.js
CHANGED
|
@@ -39,7 +39,10 @@ const Service = require('./service.js');
|
|
|
39
39
|
const Subscription = require('./subscription.js');
|
|
40
40
|
const TimeSource = require('./time_source.js');
|
|
41
41
|
const Timer = require('./timer.js');
|
|
42
|
+
const TypeDescriptionService = require('./type_description_service.js');
|
|
42
43
|
const Entity = require('./entity.js');
|
|
44
|
+
const { SubscriptionEventCallbacks } = require('../lib/event_handler.js');
|
|
45
|
+
const { PublisherEventCallbacks } = require('../lib/event_handler.js');
|
|
43
46
|
|
|
44
47
|
// Parameter event publisher constants
|
|
45
48
|
const PARAMETER_EVENT_MSG_TYPE = 'rcl_interfaces/msg/ParameterEvent';
|
|
@@ -95,12 +98,14 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
95
98
|
this._services = [];
|
|
96
99
|
this._timers = [];
|
|
97
100
|
this._guards = [];
|
|
101
|
+
this._events = [];
|
|
98
102
|
this._actionClients = [];
|
|
99
103
|
this._actionServers = [];
|
|
100
104
|
this._rateTimerServer = null;
|
|
101
105
|
this._parameterDescriptors = new Map();
|
|
102
106
|
this._parameters = new Map();
|
|
103
107
|
this._parameterService = null;
|
|
108
|
+
this._typeDescriptionService = null;
|
|
104
109
|
this._parameterEventPublisher = null;
|
|
105
110
|
this._setParametersCallbacks = [];
|
|
106
111
|
this._logger = new Logging(rclnodejs.getNodeLoggerName(this.handle));
|
|
@@ -147,6 +152,14 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
147
152
|
this._parameterService = new ParameterService(this);
|
|
148
153
|
this._parameterService.start();
|
|
149
154
|
}
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
DistroUtils.getDistroId() >= DistroUtils.getDistroId('jazzy') &&
|
|
158
|
+
options.startTypeDescriptionService
|
|
159
|
+
) {
|
|
160
|
+
this._typeDescriptionService = new TypeDescriptionService(this);
|
|
161
|
+
this._typeDescriptionService.start();
|
|
162
|
+
}
|
|
150
163
|
}
|
|
151
164
|
|
|
152
165
|
execute(handles) {
|
|
@@ -171,6 +184,9 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
171
184
|
let actionServersReady = this._actionServers.filter((actionServer) =>
|
|
172
185
|
handles.includes(actionServer.handle)
|
|
173
186
|
);
|
|
187
|
+
let eventsReady = this._events.filter((event) =>
|
|
188
|
+
handles.includes(event.handle)
|
|
189
|
+
);
|
|
174
190
|
|
|
175
191
|
timersReady.forEach((timer) => {
|
|
176
192
|
if (timer.isReady()) {
|
|
@@ -179,6 +195,10 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
179
195
|
}
|
|
180
196
|
});
|
|
181
197
|
|
|
198
|
+
eventsReady.forEach((event) => {
|
|
199
|
+
event.takeData();
|
|
200
|
+
});
|
|
201
|
+
|
|
182
202
|
for (const subscription of subscriptionsReady) {
|
|
183
203
|
if (subscription.isDestroyed()) continue;
|
|
184
204
|
if (subscription.isRaw) {
|
|
@@ -578,27 +598,39 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
578
598
|
* @param {object} options - The options argument used to parameterize the publisher.
|
|
579
599
|
* @param {boolean} options.enableTypedArray - The topic will use TypedArray if necessary, default: true.
|
|
580
600
|
* @param {QoS} options.qos - ROS Middleware "quality of service" settings for the publisher, default: QoS.profileDefault.
|
|
601
|
+
* @param {PublisherEventCallbacks} eventCallbacks - The event callbacks for the publisher.
|
|
581
602
|
* @return {Publisher} - An instance of Publisher.
|
|
582
603
|
*/
|
|
583
|
-
createPublisher(typeClass, topic, options) {
|
|
584
|
-
return this._createPublisher(
|
|
604
|
+
createPublisher(typeClass, topic, options, eventCallbacks) {
|
|
605
|
+
return this._createPublisher(
|
|
606
|
+
typeClass,
|
|
607
|
+
topic,
|
|
608
|
+
options,
|
|
609
|
+
Publisher,
|
|
610
|
+
eventCallbacks
|
|
611
|
+
);
|
|
585
612
|
}
|
|
586
613
|
|
|
587
|
-
_createPublisher(typeClass, topic, options, publisherClass) {
|
|
614
|
+
_createPublisher(typeClass, topic, options, publisherClass, eventCallbacks) {
|
|
588
615
|
if (typeof typeClass === 'string' || typeof typeClass === 'object') {
|
|
589
616
|
typeClass = loader.loadInterface(typeClass);
|
|
590
617
|
}
|
|
591
618
|
options = this._validateOptions(options);
|
|
592
619
|
|
|
593
|
-
if (
|
|
620
|
+
if (
|
|
621
|
+
typeof typeClass !== 'function' ||
|
|
622
|
+
typeof topic !== 'string' ||
|
|
623
|
+
(eventCallbacks && !(eventCallbacks instanceof PublisherEventCallbacks))
|
|
624
|
+
) {
|
|
594
625
|
throw new TypeError('Invalid argument');
|
|
595
626
|
}
|
|
596
627
|
|
|
597
628
|
let publisher = publisherClass.createPublisher(
|
|
598
|
-
this
|
|
629
|
+
this,
|
|
599
630
|
typeClass,
|
|
600
631
|
topic,
|
|
601
|
-
options
|
|
632
|
+
options,
|
|
633
|
+
eventCallbacks
|
|
602
634
|
);
|
|
603
635
|
debug('Finish creating publisher, topic = %s.', topic);
|
|
604
636
|
this._publishers.push(publisher);
|
|
@@ -633,12 +665,13 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
633
665
|
* the ‘parameters’ (i.e., "%n" tokens) in the filter_expression. The number of supplied parameters must
|
|
634
666
|
* fit with the requested values in the filter_expression (i.e., the number of %n tokens). default: undefined.
|
|
635
667
|
* @param {SubscriptionCallback} callback - The callback to be call when receiving the topic subscribed. The topic will be an instance of null-terminated Buffer when options.isRaw is true.
|
|
668
|
+
* @param {SubscriptionEventCallbacks} eventCallbacks - The event callbacks for the subscription.
|
|
636
669
|
* @return {Subscription} - An instance of Subscription.
|
|
637
670
|
* @throws {ERROR} - May throw an RMW error if content-filter is malformed.
|
|
638
671
|
* @see {@link SubscriptionCallback}
|
|
639
672
|
* @see {@link https://www.omg.org/spec/DDS/1.4/PDF|Content-filter details at DDS 1.4 specification, Annex B}
|
|
640
673
|
*/
|
|
641
|
-
createSubscription(typeClass, topic, options, callback) {
|
|
674
|
+
createSubscription(typeClass, topic, options, callback, eventCallbacks) {
|
|
642
675
|
if (typeof typeClass === 'string' || typeof typeClass === 'object') {
|
|
643
676
|
typeClass = loader.loadInterface(typeClass);
|
|
644
677
|
}
|
|
@@ -652,17 +685,20 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
652
685
|
if (
|
|
653
686
|
typeof typeClass !== 'function' ||
|
|
654
687
|
typeof topic !== 'string' ||
|
|
655
|
-
typeof callback !== 'function'
|
|
688
|
+
typeof callback !== 'function' ||
|
|
689
|
+
(eventCallbacks &&
|
|
690
|
+
!(eventCallbacks instanceof SubscriptionEventCallbacks))
|
|
656
691
|
) {
|
|
657
692
|
throw new TypeError('Invalid argument');
|
|
658
693
|
}
|
|
659
694
|
|
|
660
695
|
let subscription = Subscription.createSubscription(
|
|
661
|
-
this
|
|
696
|
+
this,
|
|
662
697
|
typeClass,
|
|
663
698
|
topic,
|
|
664
699
|
options,
|
|
665
|
-
callback
|
|
700
|
+
callback,
|
|
701
|
+
eventCallbacks
|
|
666
702
|
);
|
|
667
703
|
debug('Finish creating subscription, topic = %s.', topic);
|
|
668
704
|
this._subscriptions.push(subscription);
|
|
@@ -826,6 +862,12 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
826
862
|
if (!(publisher instanceof Publisher)) {
|
|
827
863
|
throw new TypeError('Invalid argument');
|
|
828
864
|
}
|
|
865
|
+
if (publisher.events) {
|
|
866
|
+
publisher.events.forEach((event) => {
|
|
867
|
+
this._destroyEntity(event, this._events);
|
|
868
|
+
});
|
|
869
|
+
publisher.events = [];
|
|
870
|
+
}
|
|
829
871
|
this._destroyEntity(publisher, this._publishers, false);
|
|
830
872
|
}
|
|
831
873
|
|
|
@@ -838,6 +880,13 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
838
880
|
if (!(subscription instanceof Subscription)) {
|
|
839
881
|
throw new TypeError('Invalid argument');
|
|
840
882
|
}
|
|
883
|
+
if (subscription.events) {
|
|
884
|
+
subscription.events.forEach((event) => {
|
|
885
|
+
this._destroyEntity(event, this._events);
|
|
886
|
+
});
|
|
887
|
+
subscription.events = [];
|
|
888
|
+
}
|
|
889
|
+
|
|
841
890
|
this._destroyEntity(subscription, this._subscriptions);
|
|
842
891
|
}
|
|
843
892
|
|
|
@@ -1053,7 +1102,15 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
1053
1102
|
* @return {Array<{name: string, namespace: string}>} An array of the names and namespaces.
|
|
1054
1103
|
*/
|
|
1055
1104
|
getNodeNamesAndNamespaces() {
|
|
1056
|
-
return rclnodejs.getNodeNames(this.handle);
|
|
1105
|
+
return rclnodejs.getNodeNames(this.handle, /*getEnclaves=*/ false);
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* Get the list of nodes and their namespaces with enclaves discovered by the provided node.
|
|
1110
|
+
* @return {Array<{name: string, namespace: string, enclave: string}>} An array of the names, namespaces and enclaves.
|
|
1111
|
+
*/
|
|
1112
|
+
getNodeNamesAndNamespacesWithEnclaves() {
|
|
1113
|
+
return rclnodejs.getNodeNames(this.handle, /*getEnclaves=*/ true);
|
|
1057
1114
|
}
|
|
1058
1115
|
|
|
1059
1116
|
/**
|
|
@@ -1610,6 +1667,23 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
1610
1667
|
}
|
|
1611
1668
|
}
|
|
1612
1669
|
|
|
1670
|
+
/**
|
|
1671
|
+
* Get the fully qualified name of the node.
|
|
1672
|
+
*
|
|
1673
|
+
* @returns {string} - String containing the fully qualified name of the node.
|
|
1674
|
+
*/
|
|
1675
|
+
getFullyQualifiedName() {
|
|
1676
|
+
return rclnodejs.getFullyQualifiedName(this.handle);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
/**
|
|
1680
|
+
* Get the RMW implementation identifier
|
|
1681
|
+
* @returns {string} - The RMW implementation identifier.
|
|
1682
|
+
*/
|
|
1683
|
+
getRMWImplementationIdentifier() {
|
|
1684
|
+
return rclnodejs.getRMWImplementationIdentifier();
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1613
1687
|
// returns on 1st error or result {successful, reason}
|
|
1614
1688
|
_validateParameters(parameters = [], declareParameterMode = false) {
|
|
1615
1689
|
for (const parameter of parameters) {
|
package/lib/node_options.js
CHANGED
|
@@ -26,16 +26,19 @@ class NodeOptions {
|
|
|
26
26
|
* @param {boolean} [startParameterServices=true]
|
|
27
27
|
* @param {array} [parameterOverrides=[]]
|
|
28
28
|
* @param {boolean} [automaticallyDeclareParametersFromOverrides=false]
|
|
29
|
+
* @param {boolean} [startTypeDescriptionService=true]
|
|
29
30
|
*/
|
|
30
31
|
constructor(
|
|
31
32
|
startParameterServices = true,
|
|
32
33
|
parameterOverrides = [],
|
|
33
|
-
automaticallyDeclareParametersFromOverrides = false
|
|
34
|
+
automaticallyDeclareParametersFromOverrides = false,
|
|
35
|
+
startTypeDescriptionService = true
|
|
34
36
|
) {
|
|
35
37
|
this._startParameterServices = startParameterServices;
|
|
36
38
|
this._parameterOverrides = parameterOverrides;
|
|
37
39
|
this._automaticallyDeclareParametersFromOverrides =
|
|
38
40
|
automaticallyDeclareParametersFromOverrides;
|
|
41
|
+
this._startTypeDescriptionService = startTypeDescriptionService;
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
/**
|
|
@@ -105,6 +108,23 @@ class NodeOptions {
|
|
|
105
108
|
this._automaticallyDeclareParametersFromOverrides = declareParamsFlag;
|
|
106
109
|
}
|
|
107
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Get the startTypeDescriptionService option, only available for ROS2 > Humble.
|
|
113
|
+
* Default value = true;
|
|
114
|
+
* @returns {boolean} - true if the type description service is enabled.
|
|
115
|
+
*/
|
|
116
|
+
get startTypeDescriptionService() {
|
|
117
|
+
return this._startTypeDescriptionService;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Set startTypeDescriptionService, only available for ROS2 > Humble
|
|
122
|
+
* @param {boolean} willStartTypeDescriptionService
|
|
123
|
+
*/
|
|
124
|
+
set startTypeDescriptionService(willStartTypeDescriptionService) {
|
|
125
|
+
this._startTypeDescriptionService = willStartTypeDescriptionService;
|
|
126
|
+
}
|
|
127
|
+
|
|
108
128
|
/**
|
|
109
129
|
* Return an instance configured with default options.
|
|
110
130
|
* @returns {NodeOptions} - An instance with default values.
|
package/lib/publisher.js
CHANGED
|
@@ -24,8 +24,12 @@ const Entity = require('./entity.js');
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
class Publisher extends Entity {
|
|
27
|
-
constructor(handle, typeClass, topic, options) {
|
|
27
|
+
constructor(handle, typeClass, topic, options, node, eventCallbacks) {
|
|
28
28
|
super(handle, typeClass, options);
|
|
29
|
+
if (node && eventCallbacks) {
|
|
30
|
+
this._events = eventCallbacks.createEventHandlers(this.handle);
|
|
31
|
+
node._events.push(...this._events);
|
|
32
|
+
}
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
/**
|
|
@@ -60,17 +64,24 @@ class Publisher extends Entity {
|
|
|
60
64
|
debug(`Message of topic ${this.topic} has been published.`);
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
static createPublisher(
|
|
67
|
+
static createPublisher(node, typeClass, topic, options, eventCallbacks) {
|
|
64
68
|
let type = typeClass.type();
|
|
65
69
|
let handle = rclnodejs.createPublisher(
|
|
66
|
-
|
|
70
|
+
node.handle,
|
|
67
71
|
type.pkgName,
|
|
68
72
|
type.subFolder,
|
|
69
73
|
type.interfaceName,
|
|
70
74
|
topic,
|
|
71
75
|
options.qos
|
|
72
76
|
);
|
|
73
|
-
return new Publisher(
|
|
77
|
+
return new Publisher(
|
|
78
|
+
handle,
|
|
79
|
+
typeClass,
|
|
80
|
+
topic,
|
|
81
|
+
options,
|
|
82
|
+
node,
|
|
83
|
+
eventCallbacks
|
|
84
|
+
);
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
/**
|
|
@@ -80,6 +91,41 @@ class Publisher extends Entity {
|
|
|
80
91
|
get subscriptionCount() {
|
|
81
92
|
return rclnodejs.getSubscriptionCount(this._handle);
|
|
82
93
|
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Wait until all published message data is acknowledged or until the specified timeout elapses
|
|
97
|
+
*
|
|
98
|
+
* If the timeout is negative then this function will block indefinitely until all published
|
|
99
|
+
* message data is acknowledged.
|
|
100
|
+
* If the timeout is 0 then it will check if all published message has been acknowledged without
|
|
101
|
+
* waiting.
|
|
102
|
+
* If the timeout is greater than 0 then it will return after that period of time has elapsed or
|
|
103
|
+
* all published message data is acknowledged.
|
|
104
|
+
*
|
|
105
|
+
* Raises an error if failed, such as the middleware not supporting this feature.
|
|
106
|
+
*
|
|
107
|
+
* @param {timeout} timeout - The duration to wait for all published message data to be acknowledged in nanoseconds.
|
|
108
|
+
* @return {boolean} `true` if all published message data is acknowledged before the timeout, otherwise `false`.
|
|
109
|
+
*/
|
|
110
|
+
waitForAllAcked(timeout) {
|
|
111
|
+
return rclnodejs.waitForAllAcked(this._handle, timeout);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get the event handlers for this publisher.
|
|
116
|
+
* @returns {Array} The array of event handlers for this publisher.
|
|
117
|
+
*/
|
|
118
|
+
get events() {
|
|
119
|
+
return this._events;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Set the event handlers for this publisher.
|
|
124
|
+
* @param {Array} events - The array of event handlers to be set for this publisher.
|
|
125
|
+
*/
|
|
126
|
+
set events(events) {
|
|
127
|
+
this._events = events;
|
|
128
|
+
}
|
|
83
129
|
}
|
|
84
130
|
|
|
85
131
|
module.exports = Publisher;
|
package/lib/service.js
CHANGED
|
@@ -125,7 +125,7 @@ class Service extends Entity {
|
|
|
125
125
|
configureIntrospection(clock, qos, introspectionState) {
|
|
126
126
|
if (DistroUtils.getDistroId() <= DistroUtils.getDistroId('humble')) {
|
|
127
127
|
console.warn(
|
|
128
|
-
'Service introspection is not supported by this
|
|
128
|
+
'Service introspection is not supported by this version of ROS 2'
|
|
129
129
|
);
|
|
130
130
|
return;
|
|
131
131
|
}
|
package/lib/subscription.js
CHANGED
|
@@ -29,11 +29,24 @@ const debug = require('debug')('rclnodejs:subscription');
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
class Subscription extends Entity {
|
|
32
|
-
constructor(
|
|
32
|
+
constructor(
|
|
33
|
+
handle,
|
|
34
|
+
typeClass,
|
|
35
|
+
topic,
|
|
36
|
+
options,
|
|
37
|
+
callback,
|
|
38
|
+
node,
|
|
39
|
+
eventCallbacks
|
|
40
|
+
) {
|
|
33
41
|
super(handle, typeClass, options);
|
|
34
42
|
this._topic = topic;
|
|
35
43
|
this._callback = callback;
|
|
36
44
|
this._isRaw = options.isRaw || false;
|
|
45
|
+
|
|
46
|
+
if (node && eventCallbacks) {
|
|
47
|
+
this._events = eventCallbacks.createEventHandlers(this.handle);
|
|
48
|
+
node._events.push(...this._events);
|
|
49
|
+
}
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
processResponse(msg) {
|
|
@@ -45,7 +58,14 @@ class Subscription extends Entity {
|
|
|
45
58
|
}
|
|
46
59
|
}
|
|
47
60
|
|
|
48
|
-
static createSubscription(
|
|
61
|
+
static createSubscription(
|
|
62
|
+
node,
|
|
63
|
+
typeClass,
|
|
64
|
+
topic,
|
|
65
|
+
options,
|
|
66
|
+
callback,
|
|
67
|
+
eventCallbacks
|
|
68
|
+
) {
|
|
49
69
|
let type = typeClass.type();
|
|
50
70
|
|
|
51
71
|
// convert contentFilter.parameters to a string[]
|
|
@@ -56,14 +76,22 @@ class Subscription extends Entity {
|
|
|
56
76
|
}
|
|
57
77
|
|
|
58
78
|
let handle = rclnodejs.createSubscription(
|
|
59
|
-
|
|
79
|
+
node.handle,
|
|
60
80
|
type.pkgName,
|
|
61
81
|
type.subFolder,
|
|
62
82
|
type.interfaceName,
|
|
63
83
|
topic,
|
|
64
84
|
options
|
|
65
85
|
);
|
|
66
|
-
return new Subscription(
|
|
86
|
+
return new Subscription(
|
|
87
|
+
handle,
|
|
88
|
+
typeClass,
|
|
89
|
+
topic,
|
|
90
|
+
options,
|
|
91
|
+
callback,
|
|
92
|
+
node,
|
|
93
|
+
eventCallbacks
|
|
94
|
+
);
|
|
67
95
|
}
|
|
68
96
|
|
|
69
97
|
/**
|
|
@@ -124,6 +152,22 @@ class Subscription extends Entity {
|
|
|
124
152
|
get publisherCount() {
|
|
125
153
|
return rclnodejs.getPublisherCount(this._handle);
|
|
126
154
|
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get the event handlers for this subscription.
|
|
158
|
+
* @returns {Array} The array of event handlers for this subscription.
|
|
159
|
+
*/
|
|
160
|
+
get events() {
|
|
161
|
+
return this._events;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Set the event handlers for this subscription.
|
|
166
|
+
* @param {Array} events - The array of event handlers for this subscription.
|
|
167
|
+
*/
|
|
168
|
+
set events(events) {
|
|
169
|
+
this._events = events;
|
|
170
|
+
}
|
|
127
171
|
}
|
|
128
172
|
|
|
129
173
|
module.exports = Subscription;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Copyright (c) 2025, The Robot Web Tools Contributors
|
|
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 loader = require('./interface_loader.js');
|
|
18
|
+
const rclnodejs = require('bindings')('rclnodejs');
|
|
19
|
+
const Service = require('./service.js');
|
|
20
|
+
|
|
21
|
+
// This class is used to create a TypeDescriptionService which can be used to
|
|
22
|
+
// retrieve information about types used by the node’s publishers, subscribers,
|
|
23
|
+
// services or actions.
|
|
24
|
+
class TypeDescriptionService {
|
|
25
|
+
constructor(node) {
|
|
26
|
+
this._node = node;
|
|
27
|
+
this._serviceName = this._node.name() + '/get_type_description';
|
|
28
|
+
this._typeDescriptionServiceHandle = rclnodejs.initTypeDescriptionService(
|
|
29
|
+
this._node.handle
|
|
30
|
+
);
|
|
31
|
+
this._typeClass = loader.loadInterface(
|
|
32
|
+
'type_description_interfaces/srv/GetTypeDescription'
|
|
33
|
+
);
|
|
34
|
+
this._typeDescriptionService = null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
start() {
|
|
38
|
+
if (this._typeDescriptionService) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
this._typeDescriptionService = new Service(
|
|
43
|
+
this._node.handle,
|
|
44
|
+
this._typeDescriptionServiceHandle,
|
|
45
|
+
this._serviceName,
|
|
46
|
+
this._typeClass,
|
|
47
|
+
this._node._validateOptions(undefined),
|
|
48
|
+
(request, response) => {
|
|
49
|
+
const responseToBeSent = new this._typeClass.Response();
|
|
50
|
+
const requestReceived = new this._typeClass.Request(request);
|
|
51
|
+
rclnodejs.handleRequest(
|
|
52
|
+
this._node.handle,
|
|
53
|
+
requestReceived.serialize(),
|
|
54
|
+
responseToBeSent.serialize()
|
|
55
|
+
);
|
|
56
|
+
responseToBeSent.deserialize(responseToBeSent.refObject);
|
|
57
|
+
rclnodejs.sendResponse(
|
|
58
|
+
this._typeDescriptionServiceHandle,
|
|
59
|
+
responseToBeSent.serialize(),
|
|
60
|
+
response._header
|
|
61
|
+
);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
this._node._services.push(this._typeDescriptionService);
|
|
66
|
+
this._node.syncHandles();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get the node this
|
|
71
|
+
* @return {Node} - The supported node.
|
|
72
|
+
*/
|
|
73
|
+
get node() {
|
|
74
|
+
return this._node;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static toTypeHash(topicTypeHash) {
|
|
78
|
+
return `RIHS0${topicTypeHash.version}_${topicTypeHash.value.toString('hex')}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = TypeDescriptionService;
|
package/package.json
CHANGED
package/scripts/npmjs-readme.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# rclnodejs ](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-x64-build-and-test.yml?query=branch%3Adevelop)[](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-arm64-build-and-test.yml?query=branch%3Adevelop)
|
|
2
2
|
|
|
3
3
|
`rclnodejs` is a Node.js client for the Robot Operating System (ROS 2). It provides a simple and easy JavaScript API for ROS 2 programming. TypeScript declarations are included to support use of rclnodejs in TypeScript projects.
|
|
4
4
|
|
|
@@ -22,8 +22,8 @@ rclnodejs.init().then(() => {
|
|
|
22
22
|
|
|
23
23
|
**ROS 2 SDK**
|
|
24
24
|
|
|
25
|
-
- See the ROS 2 SDK [Installation Guide](https://docs.ros.org/en/
|
|
26
|
-
- **DON'T FORGET TO [SOURCE THE ROS 2 STARTUP FILES](https://docs.ros.org/en/
|
|
25
|
+
- See the ROS 2 SDK [Installation Guide](https://docs.ros.org/en/kilted/Installation.html) for details.
|
|
26
|
+
- **DON'T FORGET TO [SOURCE THE ROS 2 STARTUP FILES](https://docs.ros.org/en/kilted/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.htmls)**
|
|
27
27
|
|
|
28
28
|
## Install rclnodejs
|
|
29
29
|
|
|
@@ -43,9 +43,9 @@ npm i rclnodejs@x.y.z
|
|
|
43
43
|
|
|
44
44
|
#### RCLNODEJS - ROS 2 Version Compatibility
|
|
45
45
|
|
|
46
|
-
| RCLNODEJS Version |
|
|
47
|
-
| :----------------------------------------------------------------------------------------: |
|
|
48
|
-
| latest version (currently [v1.
|
|
46
|
+
| RCLNODEJS Version | Compatible ROS 2 LTS |
|
|
47
|
+
| :----------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
48
|
+
| latest version (currently [v1.2.0](https://github.com/RobotWebTools/rclnodejs/tree/1.2.0)) | [Kilted](https://github.com/RobotWebTools/rclnodejs/tree/kilted)<br>[Jazzy](https://github.com/RobotWebTools/rclnodejs/tree/jazzy)<br>[Humble](https://github.com/RobotWebTools/rclnodejs/tree/humble-hawksbill) |
|
|
49
49
|
|
|
50
50
|
## Documentation
|
|
51
51
|
|
package/src/addon.cpp
CHANGED
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
#include "rcl_subscription_bindings.h"
|
|
35
35
|
#include "rcl_time_point_bindings.h"
|
|
36
36
|
#include "rcl_timer_bindings.h"
|
|
37
|
+
#if ROS_VERSION > 2205 // ROS2 > Humble
|
|
38
|
+
#include "rcl_event_handle_bindings.h"
|
|
39
|
+
#include "rcl_type_description_service_bindings.h"
|
|
40
|
+
#endif
|
|
37
41
|
#include "rcl_utilities.h"
|
|
38
42
|
#include "shadow_node.h"
|
|
39
43
|
|
|
@@ -79,6 +83,10 @@ Napi::Object InitModule(Napi::Env env, Napi::Object exports) {
|
|
|
79
83
|
rclnodejs::InitSubscriptionBindings(env, exports);
|
|
80
84
|
rclnodejs::InitTimePointBindings(env, exports);
|
|
81
85
|
rclnodejs::InitTimerBindings(env, exports);
|
|
86
|
+
#if ROS_VERSION > 2205 // ROS2 > Humble
|
|
87
|
+
rclnodejs::InitTypeDescriptionServiceBindings(env, exports);
|
|
88
|
+
rclnodejs::InitEventHandleBindings(env, exports);
|
|
89
|
+
#endif
|
|
82
90
|
rclnodejs::InitLifecycleBindings(env, exports);
|
|
83
91
|
rclnodejs::ShadowNode::Init(env, exports);
|
|
84
92
|
rclnodejs::RclHandle::Init(env, exports);
|
package/src/executor.cpp
CHANGED
|
@@ -190,10 +190,11 @@ RclResult Executor::WaitForReadyCallbacks(rcl_wait_set_t* wait_set,
|
|
|
190
190
|
size_t num_timers = 0u;
|
|
191
191
|
size_t num_clients = 0u;
|
|
192
192
|
size_t num_services = 0u;
|
|
193
|
+
size_t num_events = 0u;
|
|
193
194
|
|
|
194
195
|
rcl_ret_t get_entity_ret = handle_manager_->GetEntityCounts(
|
|
195
196
|
&num_subscriptions, &num_guard_conditions, &num_timers, &num_clients,
|
|
196
|
-
&num_services);
|
|
197
|
+
&num_services, &num_events);
|
|
197
198
|
if (get_entity_ret != RCL_RET_OK) {
|
|
198
199
|
std::string error_message = std::string("Failed to get entity counts: ") +
|
|
199
200
|
std::string(rcl_get_error_string().str);
|
|
@@ -202,9 +203,7 @@ RclResult Executor::WaitForReadyCallbacks(rcl_wait_set_t* wait_set,
|
|
|
202
203
|
|
|
203
204
|
rcl_ret_t resize_ret =
|
|
204
205
|
rcl_wait_set_resize(wait_set, num_subscriptions, num_guard_conditions,
|
|
205
|
-
num_timers, num_clients, num_services,
|
|
206
|
-
// TODO(minggang): support events.
|
|
207
|
-
0u);
|
|
206
|
+
num_timers, num_clients, num_services, num_events);
|
|
208
207
|
if (resize_ret != RCL_RET_OK) {
|
|
209
208
|
std::string error_message = std::string("Failed to resize: ") +
|
|
210
209
|
std::string(rcl_get_error_string().str);
|
package/src/handle_manager.cpp
CHANGED
|
@@ -40,7 +40,6 @@ HandleManager::~HandleManager() {
|
|
|
40
40
|
|
|
41
41
|
void HandleManager::SynchronizeHandles(const Napi::Object& node) {
|
|
42
42
|
Napi::HandleScope scope(node.Env());
|
|
43
|
-
|
|
44
43
|
Napi::Value timers = node.Get("_timers");
|
|
45
44
|
Napi::Value subscriptions = node.Get("_subscriptions");
|
|
46
45
|
Napi::Value clients = node.Get("_clients");
|
|
@@ -48,6 +47,7 @@ void HandleManager::SynchronizeHandles(const Napi::Object& node) {
|
|
|
48
47
|
Napi::Value guard_conditions = node.Get("_guards");
|
|
49
48
|
Napi::Value action_clients = node.Get("_actionClients");
|
|
50
49
|
Napi::Value action_servers = node.Get("_actionServers");
|
|
50
|
+
Napi::Value events = node.Get("_events");
|
|
51
51
|
|
|
52
52
|
uint32_t sum = 0;
|
|
53
53
|
is_synchronizing_.store(true);
|
|
@@ -66,6 +66,7 @@ void HandleManager::SynchronizeHandles(const Napi::Object& node) {
|
|
|
66
66
|
&action_clients_);
|
|
67
67
|
sum += SynchronizeHandlesByType(action_servers.As<Napi::Object>(),
|
|
68
68
|
&action_servers_);
|
|
69
|
+
sum += SynchronizeHandlesByType(events.As<Napi::Object>(), &events_);
|
|
69
70
|
}
|
|
70
71
|
is_synchronizing_.store(false);
|
|
71
72
|
|
|
@@ -98,6 +99,7 @@ void HandleManager::ClearHandles() {
|
|
|
98
99
|
guard_conditions_.clear();
|
|
99
100
|
action_clients_.clear();
|
|
100
101
|
action_servers_.clear();
|
|
102
|
+
events_.clear();
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
rcl_ret_t HandleManager::AddHandlesToWaitSet(rcl_wait_set_t* wait_set) {
|
|
@@ -152,6 +154,11 @@ rcl_ret_t HandleManager::AddHandlesToWaitSet(rcl_wait_set_t* wait_set) {
|
|
|
152
154
|
if (ret != RCL_RET_OK) return ret;
|
|
153
155
|
}
|
|
154
156
|
|
|
157
|
+
for (auto& event : events_) {
|
|
158
|
+
rcl_event_t* rcl_event = reinterpret_cast<rcl_event_t*>(event->ptr());
|
|
159
|
+
rcl_ret_t ret = rcl_wait_set_add_event(wait_set, rcl_event, nullptr);
|
|
160
|
+
if (ret != RCL_RET_OK) return ret;
|
|
161
|
+
}
|
|
155
162
|
return RCL_RET_OK;
|
|
156
163
|
}
|
|
157
164
|
|
|
@@ -169,6 +176,8 @@ rcl_ret_t HandleManager::CollectReadyHandles(rcl_wait_set_t* wait_set) {
|
|
|
169
176
|
CollectReadyHandlesByType(wait_set->guard_conditions,
|
|
170
177
|
wait_set->size_of_guard_conditions,
|
|
171
178
|
guard_conditions_, &ready_handles);
|
|
179
|
+
CollectReadyHandlesByType(wait_set->events, wait_set->size_of_events, events_,
|
|
180
|
+
&ready_handles);
|
|
172
181
|
|
|
173
182
|
rcl_ret_t ret = CollectReadyActionHandles(wait_set, &ready_handles);
|
|
174
183
|
if (!ready_handles.empty()) {
|
|
@@ -184,7 +193,8 @@ rcl_ret_t HandleManager::GetEntityCounts(size_t* subscriptions_size,
|
|
|
184
193
|
size_t* guard_conditions_size,
|
|
185
194
|
size_t* timers_size,
|
|
186
195
|
size_t* clients_size,
|
|
187
|
-
size_t* services_size
|
|
196
|
+
size_t* services_size,
|
|
197
|
+
size_t* events_size) {
|
|
188
198
|
size_t num_subscriptions = 0u;
|
|
189
199
|
size_t num_guard_conditions = 0u;
|
|
190
200
|
size_t num_timers = 0u;
|
|
@@ -230,6 +240,7 @@ rcl_ret_t HandleManager::GetEntityCounts(size_t* subscriptions_size,
|
|
|
230
240
|
*timers_size += timer_count();
|
|
231
241
|
*clients_size += client_count();
|
|
232
242
|
*services_size += service_count();
|
|
243
|
+
*events_size += event_count();
|
|
233
244
|
|
|
234
245
|
return RCL_RET_OK;
|
|
235
246
|
}
|