rclnodejs 1.1.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 CHANGED
@@ -1,4 +1,4 @@
1
- # rclnodejs ![GitHub Workflow Status](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-build-and-test.yml/badge.svg?branch=jazzy)
1
+ # rclnodejs [![Linux](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-x64-build-and-test.yml/badge.svg?branch=develop)](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-x64-build-and-test.yml?query=branch%3Adevelop)[![Linux](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-arm64-build-and-test.yml/badge.svg?branch=develop)](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/jazzy/Installation.html) for details.
26
- - **DON'T FORGET TO [SOURCE THE ROS 2 STARTUP FILES](https://docs.ros.org/en/jazzy/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html#source-the-setup-files)**
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
 
@@ -45,7 +45,7 @@ npm i rclnodejs@x.y.z
45
45
 
46
46
  | RCLNODEJS Version | Compatible ROS 2 LTS |
47
47
  | :----------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
48
- | latest version (currently [v1.1.0](https://github.com/RobotWebTools/rclnodejs/tree/1.1.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) |
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/binding.gyp CHANGED
@@ -120,6 +120,9 @@
120
120
  'include_dirs': [
121
121
  './src/third_party/dlfcn-win32/',
122
122
  ],
123
+ 'libraries': [
124
+ '-lrmw_fastrtps_cpp',
125
+ ],
123
126
  'msvs_settings': {
124
127
  'VCCLCompilerTool': {
125
128
  'ExceptionHandling': '2', # /EHsc
@@ -170,6 +173,7 @@
170
173
  # After Humble, e.g., Jazzy, Kilted.
171
174
  'ros_version > 2205', {
172
175
  'sources': [
176
+ './src/rcl_event_handle_bindings.cpp',
173
177
  './src/rcl_type_description_service_bindings.cpp',
174
178
  ]
175
179
  }
@@ -0,0 +1,474 @@
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 rclnodejs = require('bindings')('rclnodejs');
18
+ const DistroUtils = require('./distro.js');
19
+ const Entity = require('./entity.js');
20
+
21
+ /**
22
+ * Enumeration for PublisherEventCallbacks event types.
23
+ * @enum {number}
24
+ */
25
+ const PublisherEventType = {
26
+ /** @member {number} */
27
+ PUBLISHER_OFFERED_DEADLINE_MISSED: 0,
28
+ /** @member {number} */
29
+ PUBLISHER_LIVELINESS_LOST: 1,
30
+ /** @member {number} */
31
+ PUBLISHER_OFFERED_INCOMPATIBLE_QOS: 2,
32
+ /** @member {number} */
33
+ PUBLISHER_INCOMPATIBLE_TYPE: 3,
34
+ /** @member {number} */
35
+ PUBLISHER_MATCHED: 4,
36
+ };
37
+
38
+ /**
39
+ * Enumeration for SubscriptionEventCallbacks event types.
40
+ * @enum {number}
41
+ */
42
+ const SubscriptionEventType = {
43
+ /** @member {number} */
44
+ SUBSCRIPTION_REQUESTED_DEADLINE_MISSED: 0,
45
+ /** @member {number} */
46
+ SUBSCRIPTION_LIVELINESS_CHANGED: 1,
47
+ /** @member {number} */
48
+ SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS: 2,
49
+ /** @member {number} */
50
+ SUBSCRIPTION_MESSAGE_LOST: 3,
51
+ /** @member {number} */
52
+ SUBSCRIPTION_INCOMPATIBLE_TYPE: 4,
53
+ /** @member {number} */
54
+ SUBSCRIPTION_MATCHED: 5,
55
+ };
56
+
57
+ class EventHandler extends Entity {
58
+ constructor(handle, callback, eventType, eventTypeName) {
59
+ super(handle, null, null);
60
+ this._callback = callback;
61
+ this._eventType = eventType;
62
+ this._eventTypeName = eventTypeName;
63
+ }
64
+
65
+ takeData() {
66
+ const data = rclnodejs.takeEvent(this._handle, {
67
+ [this._eventTypeName]: this._eventType,
68
+ });
69
+ if (this._callback) {
70
+ this._callback(data);
71
+ }
72
+ }
73
+ }
74
+
75
+ /**
76
+ * @class - Class representing a ROS 2 PublisherEventCallbacks
77
+ * @hideconstructor
78
+ */
79
+ class PublisherEventCallbacks {
80
+ constructor() {
81
+ if (DistroUtils.getDistroId() < DistroUtils.getDistroId('jazzy')) {
82
+ throw new Error(
83
+ 'PublisherEventCallbacks is only available in ROS 2 Jazzy and later.'
84
+ );
85
+ }
86
+ this._deadline = null;
87
+ this._incompatible_qos = null;
88
+ this._liveliness = null;
89
+ this._incompatible_type = null;
90
+ this._matched = null;
91
+ this._eventHandlers = [];
92
+ }
93
+
94
+ /**
95
+ * Set deadline missed callback.
96
+ * @param {function} callback - The callback function to be called.
97
+ */
98
+ set deadline(callback) {
99
+ this._deadline = callback;
100
+ }
101
+
102
+ /**
103
+ * Get deadline missed callback.
104
+ * @return {function} - The callback function.
105
+ */
106
+ get deadline() {
107
+ return this._deadline;
108
+ }
109
+
110
+ /**
111
+ * Set incompatible QoS callback.
112
+ * @param {function} callback - The callback function to be called.
113
+ */
114
+ set incompatibleQos(callback) {
115
+ this._incompatible_qos = callback;
116
+ }
117
+
118
+ /**
119
+ * Get incompatible QoS callback.
120
+ * @return {function} - The callback function.
121
+ */
122
+ get incompatibleQos() {
123
+ return this._incompatible_qos;
124
+ }
125
+
126
+ /**
127
+ * Set liveliness lost callback.
128
+ * @param {function} callback - The callback function to be called.
129
+ */
130
+ set liveliness(callback) {
131
+ this._liveliness = callback;
132
+ }
133
+
134
+ /**
135
+ * Get liveliness lost callback.
136
+ * @return {function} - The callback function.
137
+ */
138
+ get liveliness() {
139
+ return this._liveliness;
140
+ }
141
+
142
+ /**
143
+ * Set incompatible type callback.
144
+ * @param {function} callback - The callback function to be called.
145
+ */
146
+ set incompatibleType(callback) {
147
+ this._incompatible_type = callback;
148
+ }
149
+
150
+ /**
151
+ * Get incompatible type callback.
152
+ * @return {function} - The callback function.
153
+ */
154
+ get incompatibleType() {
155
+ return this._incompatible_type;
156
+ }
157
+
158
+ /**
159
+ * Set matched callback.
160
+ * @param {function} callback - The callback function to be called.
161
+ */
162
+ set matched(callback) {
163
+ this._matched = callback;
164
+ }
165
+
166
+ /**
167
+ * Get matched callback.
168
+ * @return {function} - The callback function.
169
+ */
170
+ get matched() {
171
+ return this._matched;
172
+ }
173
+
174
+ createEventHandlers(publisherHandle) {
175
+ if (this._deadline) {
176
+ const deadlineHandle = rclnodejs.createPublisherEventHandle(
177
+ publisherHandle,
178
+ PublisherEventType.PUBLISHER_OFFERED_DEADLINE_MISSED
179
+ );
180
+ this._eventHandlers.push(
181
+ new EventHandler(
182
+ deadlineHandle,
183
+ this._deadline,
184
+ PublisherEventType.PUBLISHER_OFFERED_DEADLINE_MISSED,
185
+ 'publisher_event_type'
186
+ )
187
+ );
188
+ }
189
+
190
+ if (this._incompatible_qos) {
191
+ const incompatibleQosHandle = rclnodejs.createPublisherEventHandle(
192
+ publisherHandle,
193
+ PublisherEventType.PUBLISHER_OFFERED_INCOMPATIBLE_QOS
194
+ );
195
+ this._eventHandlers.push(
196
+ new EventHandler(
197
+ incompatibleQosHandle,
198
+ this._incompatible_qos,
199
+ PublisherEventType.PUBLISHER_OFFERED_INCOMPATIBLE_QOS,
200
+ 'publisher_event_type'
201
+ )
202
+ );
203
+ }
204
+
205
+ if (this._liveliness) {
206
+ const livelinessHandle = rclnodejs.createPublisherEventHandle(
207
+ publisherHandle,
208
+ PublisherEventType.PUBLISHER_LIVELINESS_LOST
209
+ );
210
+ this._eventHandlers.push(
211
+ new EventHandler(
212
+ livelinessHandle,
213
+ this._liveliness,
214
+ PublisherEventType.PUBLISHER_LIVELINESS_LOST,
215
+ 'publisher_event_type'
216
+ )
217
+ );
218
+ }
219
+
220
+ if (this._incompatible_type) {
221
+ const incompatibleTypeHandle = rclnodejs.createPublisherEventHandle(
222
+ publisherHandle,
223
+ PublisherEventType.PUBLISHER_INCOMPATIBLE_TYPE
224
+ );
225
+ this._eventHandlers.push(
226
+ new EventHandler(
227
+ incompatibleTypeHandle,
228
+ this._incompatible_type,
229
+ PublisherEventType.PUBLISHER_INCOMPATIBLE_TYPE,
230
+ 'publisher_event_type'
231
+ )
232
+ );
233
+ }
234
+
235
+ if (this._matched) {
236
+ const matchedHandle = rclnodejs.createPublisherEventHandle(
237
+ publisherHandle,
238
+ PublisherEventType.PUBLISHER_MATCHED
239
+ );
240
+ this._eventHandlers.push(
241
+ new EventHandler(
242
+ matchedHandle,
243
+ this._matched,
244
+ PublisherEventType.PUBLISHER_MATCHED,
245
+ 'publisher_event_type'
246
+ )
247
+ );
248
+ }
249
+
250
+ return this._eventHandlers;
251
+ }
252
+
253
+ get eventHandlers() {
254
+ return this._eventHandlers;
255
+ }
256
+ }
257
+
258
+ /**
259
+ * @class - Class representing a ROS 2 SubscriptionEventCallbacks
260
+ * @hideconstructor
261
+ */
262
+ class SubscriptionEventCallbacks {
263
+ constructor() {
264
+ if (DistroUtils.getDistroId() < DistroUtils.getDistroId('jazzy')) {
265
+ throw new Error(
266
+ 'SubscriptionEventCallbacks is only available in ROS 2 Jazzy and later.'
267
+ );
268
+ }
269
+ this._deadline = null;
270
+ this._incompatible_qos = null;
271
+ this._liveliness = null;
272
+ this._message_lost = null;
273
+ this._incompatible_type = null;
274
+ this._matched = null;
275
+ this._eventHandlers = [];
276
+ }
277
+
278
+ /**
279
+ * Set the callback for deadline missed event.
280
+ * @param {function} callback - The callback function to be called.
281
+ */
282
+ set deadline(callback) {
283
+ this._deadline = callback;
284
+ }
285
+
286
+ /**
287
+ * Get the callback for deadline missed event.
288
+ * @return {function} - The callback function.
289
+ */
290
+ get deadline() {
291
+ return this._deadline;
292
+ }
293
+
294
+ /**
295
+ * Set the callback for incompatible QoS event.
296
+ * @param {function} callback - The callback function to be called.
297
+ */
298
+ set incompatibleQos(callback) {
299
+ this._incompatible_qos = callback;
300
+ }
301
+
302
+ /**
303
+ * Get the callback for incompatible QoS event.
304
+ * @return {function} - The callback function.
305
+ */
306
+ get incompatibleQos() {
307
+ return this._incompatible_qos;
308
+ }
309
+
310
+ /**
311
+ * Set the callback for liveliness changed event.
312
+ * @param {function} callback - The callback function to be called.
313
+ */
314
+ set liveliness(callback) {
315
+ this._liveliness = callback;
316
+ }
317
+
318
+ /**
319
+ * Get the callback for liveliness changed event.
320
+ * @return {function} - The callback function.
321
+ */
322
+ get liveliness() {
323
+ return this._liveliness;
324
+ }
325
+
326
+ /**
327
+ * Set the callback for message lost event.
328
+ * @param {function} callback - The callback function to be called.
329
+ */
330
+ set messageLost(callback) {
331
+ this._message_lost = callback;
332
+ }
333
+
334
+ /**
335
+ * Get the callback for message lost event.
336
+ * @return {function} - The callback function.
337
+ */
338
+ get messageLost() {
339
+ return this._message_lost;
340
+ }
341
+
342
+ /**
343
+ * Set the callback for incompatible type event.
344
+ * @param {function} callback - The callback function to be called.
345
+ */
346
+ set incompatibleType(callback) {
347
+ this._incompatible_type = callback;
348
+ }
349
+
350
+ /**
351
+ * Get the callback for incompatible type event.
352
+ * @return {function} - The callback function.
353
+ */
354
+ get incompatibleType() {
355
+ return this._incompatible_type;
356
+ }
357
+
358
+ /**
359
+ * Set the callback for matched event.
360
+ * @param {function} callback - The callback function to be called.
361
+ */
362
+ set matched(callback) {
363
+ this._matched = callback;
364
+ }
365
+
366
+ /**
367
+ * Get the callback for matched event.
368
+ * @return {function} - The callback function.
369
+ */
370
+ get matched() {
371
+ return this._matched;
372
+ }
373
+
374
+ createEventHandlers(subscriptionHandle) {
375
+ if (this._deadline) {
376
+ const deadlineHandle = rclnodejs.createSubscriptionEventHandle(
377
+ subscriptionHandle,
378
+ SubscriptionEventType.SUBSCRIPTION_REQUESTED_DEADLINE_MISSED
379
+ );
380
+ this._eventHandlers.push(
381
+ new EventHandler(
382
+ deadlineHandle,
383
+ this._deadline,
384
+ SubscriptionEventType.SUBSCRIPTION_REQUESTED_DEADLINE_MISSED,
385
+ 'subscription_event_type'
386
+ )
387
+ );
388
+ }
389
+
390
+ if (this._incompatible_qos) {
391
+ const incompatibleQosHandle = rclnodejs.createSubscriptionEventHandle(
392
+ subscriptionHandle,
393
+ SubscriptionEventType.SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS
394
+ );
395
+ this._eventHandlers.push(
396
+ new EventHandler(
397
+ incompatibleQosHandle,
398
+ this._incompatible_qos,
399
+ SubscriptionEventType.SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS,
400
+ 'subscription_event_type'
401
+ )
402
+ );
403
+ }
404
+
405
+ if (this._liveliness) {
406
+ const livelinessHandle = rclnodejs.createSubscriptionEventHandle(
407
+ subscriptionHandle,
408
+ SubscriptionEventType.SUBSCRIPTION_LIVELINESS_CHANGED
409
+ );
410
+ this._eventHandlers.push(
411
+ new EventHandler(
412
+ livelinessHandle,
413
+ this._liveliness,
414
+ SubscriptionEventType.SUBSCRIPTION_LIVELINESS_CHANGED,
415
+ 'subscription_event_type'
416
+ )
417
+ );
418
+ }
419
+
420
+ if (this._message_lost) {
421
+ const messageLostHandle = rclnodejs.createSubscriptionEventHandle(
422
+ subscriptionHandle,
423
+ SubscriptionEventType.SUBSCRIPTION_MESSAGE_LOST
424
+ );
425
+ this._eventHandlers.push(
426
+ new EventHandler(
427
+ messageLostHandle,
428
+ this._message_lost,
429
+ SubscriptionEventType.SUBSCRIPTION_MESSAGE_LOST,
430
+ 'subscription_event_type'
431
+ )
432
+ );
433
+ }
434
+
435
+ if (this._incompatible_type) {
436
+ const incompatibleTypeHandle = rclnodejs.createSubscriptionEventHandle(
437
+ subscriptionHandle,
438
+ SubscriptionEventType.SUBSCRIPTION_INCOMPATIBLE_TYPE
439
+ );
440
+ this._eventHandlers.push(
441
+ new EventHandler(
442
+ incompatibleTypeHandle,
443
+ this._incompatible_type,
444
+ SubscriptionEventType.SUBSCRIPTION_INCOMPATIBLE_TYPE,
445
+ 'subscription_event_type'
446
+ )
447
+ );
448
+ }
449
+
450
+ if (this._matched) {
451
+ const matchedHandle = rclnodejs.createSubscriptionEventHandle(
452
+ subscriptionHandle,
453
+ SubscriptionEventType.SUBSCRIPTION_MATCHED
454
+ );
455
+ this._eventHandlers.push(
456
+ new EventHandler(
457
+ matchedHandle,
458
+ this._matched,
459
+ SubscriptionEventType.SUBSCRIPTION_MATCHED,
460
+ 'subscription_event_type'
461
+ )
462
+ );
463
+ }
464
+
465
+ return this._eventHandlers;
466
+ }
467
+ }
468
+
469
+ module.exports = {
470
+ PublisherEventCallbacks,
471
+ PublisherEventType,
472
+ SubscriptionEventCallbacks,
473
+ SubscriptionEventType,
474
+ };
@@ -93,10 +93,10 @@ class LifecyclePublisher extends Publisher {
93
93
  this.deactivate();
94
94
  }
95
95
 
96
- static createPublisher(nodeHandle, typeClass, topic, options) {
96
+ static createPublisher(node, typeClass, topic, options) {
97
97
  let type = typeClass.type();
98
98
  let handle = rclnodejs.createPublisher(
99
- nodeHandle,
99
+ node.handle,
100
100
  type.pkgName,
101
101
  type.subFolder,
102
102
  type.interfaceName,
package/lib/node.js CHANGED
@@ -41,6 +41,8 @@ const TimeSource = require('./time_source.js');
41
41
  const Timer = require('./timer.js');
42
42
  const TypeDescriptionService = require('./type_description_service.js');
43
43
  const Entity = require('./entity.js');
44
+ const { SubscriptionEventCallbacks } = require('../lib/event_handler.js');
45
+ const { PublisherEventCallbacks } = require('../lib/event_handler.js');
44
46
 
45
47
  // Parameter event publisher constants
46
48
  const PARAMETER_EVENT_MSG_TYPE = 'rcl_interfaces/msg/ParameterEvent';
@@ -96,6 +98,7 @@ class Node extends rclnodejs.ShadowNode {
96
98
  this._services = [];
97
99
  this._timers = [];
98
100
  this._guards = [];
101
+ this._events = [];
99
102
  this._actionClients = [];
100
103
  this._actionServers = [];
101
104
  this._rateTimerServer = null;
@@ -181,6 +184,9 @@ class Node extends rclnodejs.ShadowNode {
181
184
  let actionServersReady = this._actionServers.filter((actionServer) =>
182
185
  handles.includes(actionServer.handle)
183
186
  );
187
+ let eventsReady = this._events.filter((event) =>
188
+ handles.includes(event.handle)
189
+ );
184
190
 
185
191
  timersReady.forEach((timer) => {
186
192
  if (timer.isReady()) {
@@ -189,6 +195,10 @@ class Node extends rclnodejs.ShadowNode {
189
195
  }
190
196
  });
191
197
 
198
+ eventsReady.forEach((event) => {
199
+ event.takeData();
200
+ });
201
+
192
202
  for (const subscription of subscriptionsReady) {
193
203
  if (subscription.isDestroyed()) continue;
194
204
  if (subscription.isRaw) {
@@ -588,27 +598,39 @@ class Node extends rclnodejs.ShadowNode {
588
598
  * @param {object} options - The options argument used to parameterize the publisher.
589
599
  * @param {boolean} options.enableTypedArray - The topic will use TypedArray if necessary, default: true.
590
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.
591
602
  * @return {Publisher} - An instance of Publisher.
592
603
  */
593
- createPublisher(typeClass, topic, options) {
594
- return this._createPublisher(typeClass, topic, options, Publisher);
604
+ createPublisher(typeClass, topic, options, eventCallbacks) {
605
+ return this._createPublisher(
606
+ typeClass,
607
+ topic,
608
+ options,
609
+ Publisher,
610
+ eventCallbacks
611
+ );
595
612
  }
596
613
 
597
- _createPublisher(typeClass, topic, options, publisherClass) {
614
+ _createPublisher(typeClass, topic, options, publisherClass, eventCallbacks) {
598
615
  if (typeof typeClass === 'string' || typeof typeClass === 'object') {
599
616
  typeClass = loader.loadInterface(typeClass);
600
617
  }
601
618
  options = this._validateOptions(options);
602
619
 
603
- if (typeof typeClass !== 'function' || typeof topic !== 'string') {
620
+ if (
621
+ typeof typeClass !== 'function' ||
622
+ typeof topic !== 'string' ||
623
+ (eventCallbacks && !(eventCallbacks instanceof PublisherEventCallbacks))
624
+ ) {
604
625
  throw new TypeError('Invalid argument');
605
626
  }
606
627
 
607
628
  let publisher = publisherClass.createPublisher(
608
- this.handle,
629
+ this,
609
630
  typeClass,
610
631
  topic,
611
- options
632
+ options,
633
+ eventCallbacks
612
634
  );
613
635
  debug('Finish creating publisher, topic = %s.', topic);
614
636
  this._publishers.push(publisher);
@@ -643,12 +665,13 @@ class Node extends rclnodejs.ShadowNode {
643
665
  * the ‘parameters’ (i.e., "%n" tokens) in the filter_expression. The number of supplied parameters must
644
666
  * fit with the requested values in the filter_expression (i.e., the number of %n tokens). default: undefined.
645
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.
646
669
  * @return {Subscription} - An instance of Subscription.
647
670
  * @throws {ERROR} - May throw an RMW error if content-filter is malformed.
648
671
  * @see {@link SubscriptionCallback}
649
672
  * @see {@link https://www.omg.org/spec/DDS/1.4/PDF|Content-filter details at DDS 1.4 specification, Annex B}
650
673
  */
651
- createSubscription(typeClass, topic, options, callback) {
674
+ createSubscription(typeClass, topic, options, callback, eventCallbacks) {
652
675
  if (typeof typeClass === 'string' || typeof typeClass === 'object') {
653
676
  typeClass = loader.loadInterface(typeClass);
654
677
  }
@@ -662,17 +685,20 @@ class Node extends rclnodejs.ShadowNode {
662
685
  if (
663
686
  typeof typeClass !== 'function' ||
664
687
  typeof topic !== 'string' ||
665
- typeof callback !== 'function'
688
+ typeof callback !== 'function' ||
689
+ (eventCallbacks &&
690
+ !(eventCallbacks instanceof SubscriptionEventCallbacks))
666
691
  ) {
667
692
  throw new TypeError('Invalid argument');
668
693
  }
669
694
 
670
695
  let subscription = Subscription.createSubscription(
671
- this.handle,
696
+ this,
672
697
  typeClass,
673
698
  topic,
674
699
  options,
675
- callback
700
+ callback,
701
+ eventCallbacks
676
702
  );
677
703
  debug('Finish creating subscription, topic = %s.', topic);
678
704
  this._subscriptions.push(subscription);
@@ -836,6 +862,12 @@ class Node extends rclnodejs.ShadowNode {
836
862
  if (!(publisher instanceof Publisher)) {
837
863
  throw new TypeError('Invalid argument');
838
864
  }
865
+ if (publisher.events) {
866
+ publisher.events.forEach((event) => {
867
+ this._destroyEntity(event, this._events);
868
+ });
869
+ publisher.events = [];
870
+ }
839
871
  this._destroyEntity(publisher, this._publishers, false);
840
872
  }
841
873
 
@@ -848,6 +880,13 @@ class Node extends rclnodejs.ShadowNode {
848
880
  if (!(subscription instanceof Subscription)) {
849
881
  throw new TypeError('Invalid argument');
850
882
  }
883
+ if (subscription.events) {
884
+ subscription.events.forEach((event) => {
885
+ this._destroyEntity(event, this._events);
886
+ });
887
+ subscription.events = [];
888
+ }
889
+
851
890
  this._destroyEntity(subscription, this._subscriptions);
852
891
  }
853
892
 
@@ -1637,6 +1676,14 @@ class Node extends rclnodejs.ShadowNode {
1637
1676
  return rclnodejs.getFullyQualifiedName(this.handle);
1638
1677
  }
1639
1678
 
1679
+ /**
1680
+ * Get the RMW implementation identifier
1681
+ * @returns {string} - The RMW implementation identifier.
1682
+ */
1683
+ getRMWImplementationIdentifier() {
1684
+ return rclnodejs.getRMWImplementationIdentifier();
1685
+ }
1686
+
1640
1687
  // returns on 1st error or result {successful, reason}
1641
1688
  _validateParameters(parameters = [], declareParameterMode = false) {
1642
1689
  for (const parameter of parameters) {