rclnodejs 1.8.3 → 1.9.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.
Files changed (90) hide show
  1. package/README.md +46 -37
  2. package/index.js +39 -0
  3. package/lib/action/client.js +61 -3
  4. package/lib/action/server_goal_handle.js +26 -1
  5. package/lib/message_info.js +94 -0
  6. package/lib/node.js +260 -13
  7. package/lib/parameter_event_handler.js +566 -0
  8. package/lib/parameter_watcher.js +12 -12
  9. package/lib/qos_overriding_options.js +358 -0
  10. package/lib/subscription.js +38 -5
  11. package/lib/timer.js +3 -2
  12. package/lib/wait_for_message.js +111 -0
  13. package/package.json +7 -4
  14. package/prebuilds/linux-arm64/humble-jammy-arm64-rclnodejs.node +0 -0
  15. package/prebuilds/linux-arm64/jazzy-noble-arm64-rclnodejs.node +0 -0
  16. package/prebuilds/linux-arm64/kilted-noble-arm64-rclnodejs.node +0 -0
  17. package/prebuilds/linux-x64/humble-jammy-x64-rclnodejs.node +0 -0
  18. package/prebuilds/linux-x64/jazzy-noble-x64-rclnodejs.node +0 -0
  19. package/prebuilds/linux-x64/kilted-noble-x64-rclnodejs.node +0 -0
  20. package/scripts/run_asan_test.sh +118 -0
  21. package/src/executor.cpp +36 -2
  22. package/src/executor.h +11 -0
  23. package/src/rcl_action_client_bindings.cpp +70 -1
  24. package/src/rcl_context_bindings.cpp +3 -3
  25. package/src/rcl_graph_bindings.cpp +2 -2
  26. package/src/rcl_subscription_bindings.cpp +70 -2
  27. package/src/rcl_timer_bindings.cpp +21 -2
  28. package/src/rcl_utilities.cpp +2 -2
  29. package/tools/jsdoc/Makefile +5 -0
  30. package/tools/jsdoc/README.md +96 -0
  31. package/tools/jsdoc/build-index.js +610 -0
  32. package/tools/jsdoc/publish.js +854 -0
  33. package/tools/jsdoc/regenerate-published-docs.js +605 -0
  34. package/tools/jsdoc/static/fonts/OpenSans-Bold-webfont.eot +0 -0
  35. package/tools/jsdoc/static/fonts/OpenSans-Bold-webfont.svg +1830 -0
  36. package/tools/jsdoc/static/fonts/OpenSans-Bold-webfont.woff +0 -0
  37. package/tools/jsdoc/static/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  38. package/tools/jsdoc/static/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
  39. package/tools/jsdoc/static/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  40. package/tools/jsdoc/static/fonts/OpenSans-Italic-webfont.eot +0 -0
  41. package/tools/jsdoc/static/fonts/OpenSans-Italic-webfont.svg +1830 -0
  42. package/tools/jsdoc/static/fonts/OpenSans-Italic-webfont.woff +0 -0
  43. package/tools/jsdoc/static/fonts/OpenSans-Light-webfont.eot +0 -0
  44. package/tools/jsdoc/static/fonts/OpenSans-Light-webfont.svg +1831 -0
  45. package/tools/jsdoc/static/fonts/OpenSans-Light-webfont.woff +0 -0
  46. package/tools/jsdoc/static/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  47. package/tools/jsdoc/static/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
  48. package/tools/jsdoc/static/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  49. package/tools/jsdoc/static/fonts/OpenSans-Regular-webfont.eot +0 -0
  50. package/tools/jsdoc/static/fonts/OpenSans-Regular-webfont.svg +1831 -0
  51. package/tools/jsdoc/static/fonts/OpenSans-Regular-webfont.woff +0 -0
  52. package/tools/jsdoc/static/scripts/linenumber.js +25 -0
  53. package/tools/jsdoc/static/scripts/prettify/Apache-License-2.0.txt +202 -0
  54. package/tools/jsdoc/static/scripts/prettify/lang-css.js +36 -0
  55. package/tools/jsdoc/static/scripts/prettify/prettify.js +738 -0
  56. package/tools/jsdoc/static/styles/jsdoc-default.css +1012 -0
  57. package/tools/jsdoc/static/styles/prettify-jsdoc.css +111 -0
  58. package/tools/jsdoc/static/styles/prettify-tomorrow.css +132 -0
  59. package/tools/jsdoc/tmpl/augments.tmpl +10 -0
  60. package/tools/jsdoc/tmpl/container.tmpl +193 -0
  61. package/tools/jsdoc/tmpl/details.tmpl +143 -0
  62. package/tools/jsdoc/tmpl/example.tmpl +2 -0
  63. package/tools/jsdoc/tmpl/examples.tmpl +13 -0
  64. package/tools/jsdoc/tmpl/exceptions.tmpl +17 -0
  65. package/tools/jsdoc/tmpl/layout.tmpl +83 -0
  66. package/tools/jsdoc/tmpl/mainpage.tmpl +163 -0
  67. package/tools/jsdoc/tmpl/members.tmpl +43 -0
  68. package/tools/jsdoc/tmpl/method.tmpl +124 -0
  69. package/tools/jsdoc/tmpl/params.tmpl +133 -0
  70. package/tools/jsdoc/tmpl/properties.tmpl +110 -0
  71. package/tools/jsdoc/tmpl/returns.tmpl +12 -0
  72. package/tools/jsdoc/tmpl/source.tmpl +8 -0
  73. package/tools/jsdoc/tmpl/tutorial.tmpl +19 -0
  74. package/tools/jsdoc/tmpl/type.tmpl +7 -0
  75. package/types/action_client.d.ts +8 -0
  76. package/types/index.d.ts +34 -0
  77. package/types/message_info.d.ts +72 -0
  78. package/types/node.d.ts +90 -5
  79. package/types/parameter_event_handler.d.ts +150 -0
  80. package/types/qos.d.ts +55 -0
  81. package/types/subscription.d.ts +14 -2
  82. package/types/timer.d.ts +3 -2
  83. package/test_data_integrity.js +0 -108
  84. package/test_repro_exact.js +0 -57
  85. package/test_repro_hz.js +0 -86
  86. package/test_repro_pub.js +0 -36
  87. package/test_repro_stress.js +0 -83
  88. package/test_repro_sub.js +0 -64
  89. package/test_xproc_data.js +0 -64
  90. package/types/interfaces.d.ts +0 -8895
@@ -0,0 +1,19 @@
1
+ <section class="doc-section prose-section tutorial-section">
2
+
3
+ <header class="doc-page-header">
4
+ <?js if (children.length > 0) { ?>
5
+ <ul class="tutorial-children"><?js
6
+ var self = this;
7
+ children.forEach(function(t) { ?>
8
+ <li><?js= self.tutoriallink(t.name) ?></li>
9
+ <?js }); ?></ul>
10
+ <?js } ?>
11
+
12
+ <h2 class="doc-heading"><?js= header ?></h2>
13
+ </header>
14
+
15
+ <article class="prose-card">
16
+ <?js= content ?>
17
+ </article>
18
+
19
+ </section>
@@ -0,0 +1,7 @@
1
+ <?js
2
+ var data = obj;
3
+ var self = this;
4
+ data.forEach(function(name, i) { ?>
5
+ <span class="param-type"><?js= self.linkto(name, self.htmlsafe(name)) ?></span>
6
+ <?js if (i < data.length-1) { ?>|<?js } ?>
7
+ <?js }); ?>
@@ -142,6 +142,14 @@ declare module 'rclnodejs' {
142
142
  options?: Options<ActionQoS> & {
143
143
  validateGoals?: boolean;
144
144
  validationOptions?: MessageValidationOptions;
145
+ /**
146
+ * Enable feedback subscription content filter to optimize the handling
147
+ * of feedback messages. When enabled, the content filter is used to
148
+ * configure the goal ID for the subscription, avoiding reception of
149
+ * irrelevant feedback messages. An action client can handle up to 6
150
+ * goals simultaneously with this optimization. Default: false.
151
+ */
152
+ enableFeedbackMsgOptimization?: boolean;
145
153
  }
146
154
  );
147
155
 
package/types/index.d.ts CHANGED
@@ -2,6 +2,8 @@
2
2
  /// <reference path="./clock_event.d.ts" />
3
3
  /// <reference path="./clock_change.d.ts" />
4
4
  /// <reference path="./message_validation.d.ts" />
5
+ /// <reference path="./parameter_event_handler.d.ts" />
6
+ /// <reference path="./message_info.d.ts" />
5
7
 
6
8
  import { ChildProcess } from 'child_process';
7
9
 
@@ -85,6 +87,38 @@ declare module 'rclnodejs' {
85
87
  * @deprecated since 0.18.0, Use Node.spinOnce(timeout)*/
86
88
  function spinOnce(node: Node, timeout?: number): void;
87
89
 
90
+ /**
91
+ * Options for waitForMessage.
92
+ */
93
+ interface WaitForMessageOptions {
94
+ /** Timeout in milliseconds. If omitted, waits indefinitely. */
95
+ timeout?: number;
96
+ /** QoS profile for the temporary subscription. */
97
+ qos?: QoS;
98
+ }
99
+
100
+ /**
101
+ * Wait for a single message on a topic.
102
+ *
103
+ * Creates a temporary subscription, waits for the first message to arrive,
104
+ * and returns it. The node must be spinning before calling this function.
105
+ *
106
+ * This is the rclnodejs equivalent of rclpy's `wait_for_message`.
107
+ *
108
+ * @param typeClass - The ROS message type class.
109
+ * @param node - The node to create the temporary subscription on.
110
+ * @param topic - The topic name to listen on.
111
+ * @param options - Options including timeout and QoS.
112
+ * @returns Resolves with the received message.
113
+ * @throws Error if timeout expires before a message arrives.
114
+ */
115
+ function waitForMessage<T extends TypeClass<MessageTypeClassName>>(
116
+ typeClass: T,
117
+ node: Node,
118
+ topic: string,
119
+ options?: WaitForMessageOptions
120
+ ): Promise<MessageType<T>>;
121
+
88
122
  /**
89
123
  * Stop all activity, destroy all nodes and node components.
90
124
  *
@@ -0,0 +1,72 @@
1
+ // Copyright (c) 2026, 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
+ declare module 'rclnodejs' {
16
+ /**
17
+ * Contains metadata about a received message, including timestamps,
18
+ * sequence numbers, and the publisher's globally unique identifier (GID).
19
+ *
20
+ * Passed as the second argument to subscription callbacks when the
21
+ * callback accepts two parameters.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * node.createSubscription(
26
+ * 'std_msgs/msg/String',
27
+ * 'topic',
28
+ * (msg: rclnodejs.std_msgs.msg.String, info: MessageInfo) => {
29
+ * console.log('Source timestamp:', info.sourceTimestamp);
30
+ * }
31
+ * );
32
+ * ```
33
+ */
34
+ class MessageInfo {
35
+ /**
36
+ * The timestamp when the message was published (nanoseconds since epoch).
37
+ */
38
+ readonly sourceTimestamp: bigint;
39
+
40
+ /**
41
+ * The timestamp when the message was received by the subscription (nanoseconds since epoch).
42
+ */
43
+ readonly receivedTimestamp: bigint;
44
+
45
+ /**
46
+ * The publication sequence number assigned by the publisher.
47
+ */
48
+ readonly publicationSequenceNumber: bigint;
49
+
50
+ /**
51
+ * The reception sequence number assigned by the subscriber.
52
+ */
53
+ readonly receptionSequenceNumber: bigint;
54
+
55
+ /**
56
+ * The globally unique identifier (GID) of the publisher.
57
+ * A Buffer containing the raw GID bytes.
58
+ */
59
+ readonly publisherGid: Buffer;
60
+
61
+ /**
62
+ * Convert to a plain object representation.
63
+ */
64
+ toPlainObject(): {
65
+ sourceTimestamp: bigint;
66
+ receivedTimestamp: bigint;
67
+ publicationSequenceNumber: bigint;
68
+ receptionSequenceNumber: bigint;
69
+ publisherGid: Buffer;
70
+ };
71
+ }
72
+ }
package/types/node.d.ts CHANGED
@@ -80,6 +80,13 @@ declare module 'rclnodejs' {
80
80
  * inspect and limit messages that it accepts.
81
81
  */
82
82
  contentFilter?: SubscriptionContentFilter;
83
+
84
+ /**
85
+ * If provided, declares read-only ROS parameters for the specified QoS policies.
86
+ * These can be overridden at startup via `--ros-args -p` or `--params-file`.
87
+ * If qos is a profile string, it will be resolved to a mutable QoS object.
88
+ */
89
+ qosOverridingOptions?: QoSOverridingOptions;
83
90
  }
84
91
 
85
92
  /**
@@ -97,14 +104,24 @@ declare module 'rclnodejs' {
97
104
  */
98
105
  const DEFAULT_OPTIONS: Options;
99
106
 
107
+ interface TimerInfo {
108
+ expectedCallTime: bigint;
109
+ actualCallTime: bigint;
110
+ }
111
+
112
+ interface TimerOptions {
113
+ autostart?: boolean;
114
+ }
115
+
100
116
  /**
101
117
  * Callback for receiving periodic interrupts from a Timer.
118
+ * Receives timer metadata when the underlying ROS distro exposes it.
102
119
  *
103
120
  * @remarks
104
121
  * See {@link Node.createTimer | Node.createTimer}
105
122
  * See {@link Timer}
106
123
  */
107
- type TimerRequestCallback = () => void;
124
+ type TimerRequestCallback = (timerInfo?: TimerInfo) => void;
108
125
 
109
126
  /**
110
127
  * Callback indicating parameters are about to be declared or set.
@@ -123,6 +140,19 @@ declare module 'rclnodejs' {
123
140
  parameters: Parameter[]
124
141
  ) => rcl_interfaces.msg.SetParametersResult;
125
142
 
143
+ /**
144
+ * Callback invoked before parameter validation and setting.
145
+ * Receives the parameter list, must return a (possibly modified) parameter list.
146
+ * Returning an empty list rejects the set.
147
+ */
148
+ type PreSetParametersCallback = (parameters: Parameter[]) => Parameter[];
149
+
150
+ /**
151
+ * Callback invoked after parameters have been successfully set.
152
+ * For side effects only (return value is ignored).
153
+ */
154
+ type PostSetParametersCallback = (parameters: Parameter[]) => void;
155
+
126
156
  /**
127
157
  * Standard result of Node.getXXXNamesAndTypes() queries
128
158
  *
@@ -293,15 +323,18 @@ declare module 'rclnodejs' {
293
323
  /**
294
324
  * Create a Timer.
295
325
  *
296
- * @param period - Elapsed time between interrupt events (milliseconds).
297
- * @param callback - Called on timeout interrupt.
298
- * @param clock - Optional clock to use for the timer.
326
+ * @param period - Elapsed time between interrupt events in nanoseconds.
327
+ * @param callback - Called when the timer fires. Receives a `TimerInfo` argument when available.
328
+ * @param optionsOrClock - Optional timer options or clock to use for the timer.
329
+ * Supports `{ autostart?: boolean }` when an options object is provided.
330
+ * @param clock - Optional clock to use for the timer when options are provided.
299
331
  * @returns New instance of Timer.
300
332
  */
301
333
  createTimer(
302
334
  period: bigint,
303
335
  callback: TimerRequestCallback,
304
- clock?: Clock
336
+ optionsOrClock?: TimerOptions | Clock | null,
337
+ clock?: Clock | null
305
338
  ): Timer;
306
339
 
307
340
  /**
@@ -511,6 +544,27 @@ declare module 'rclnodejs' {
511
544
  */
512
545
  destroyParameterWatcher(watcher: ParameterWatcher): void;
513
546
 
547
+ /**
548
+ * Create a ParameterEventHandler that monitors parameter changes on any node.
549
+ *
550
+ * Unlike ParameterWatcher which watches specific parameters on a single
551
+ * remote node, ParameterEventHandler can register callbacks for parameters
552
+ * on any node in the ROS 2 graph by subscribing to /parameter_events.
553
+ *
554
+ * @param options - Options for the handler.
555
+ * @returns An instance of ParameterEventHandler.
556
+ */
557
+ createParameterEventHandler(
558
+ options?: ParameterEventHandlerOptions
559
+ ): ParameterEventHandler;
560
+
561
+ /**
562
+ * Destroy a ParameterEventHandler.
563
+ *
564
+ * @param handler - ParameterEventHandler to be destroyed.
565
+ */
566
+ destroyParameterEventHandler(handler: ParameterEventHandler): void;
567
+
514
568
  /**
515
569
  * Destroy a Timer.
516
570
  *
@@ -728,6 +782,37 @@ declare module 'rclnodejs' {
728
782
  */
729
783
  removeOnSetParametersCallback(call: SetParametersCallback): void;
730
784
 
785
+ /**
786
+ * Add a callback invoked before parameter validation.
787
+ * The callback receives the parameter list and must return a (possibly modified)
788
+ * parameter list. Returning an empty list rejects the set.
789
+ *
790
+ * @param callback - The callback to add.
791
+ */
792
+ addPreSetParametersCallback(callback: PreSetParametersCallback): void;
793
+
794
+ /**
795
+ * Remove a pre-set parameters callback.
796
+ *
797
+ * @param callback - The callback to remove.
798
+ */
799
+ removePreSetParametersCallback(callback: PreSetParametersCallback): void;
800
+
801
+ /**
802
+ * Add a callback invoked after parameters are successfully set.
803
+ * Useful for triggering side effects (e.g., reconfiguring a component).
804
+ *
805
+ * @param callback - The callback to add.
806
+ */
807
+ addPostSetParametersCallback(callback: PostSetParametersCallback): void;
808
+
809
+ /**
810
+ * Remove a post-set parameters callback.
811
+ *
812
+ * @param callback - The callback to remove.
813
+ */
814
+ removePostSetParametersCallback(callback: PostSetParametersCallback): void;
815
+
731
816
  /**
732
817
  * Get a remote node's published topics.
733
818
  *
@@ -0,0 +1,150 @@
1
+ // Copyright (c) 2026, 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
+ declare module 'rclnodejs' {
16
+ /**
17
+ * Options for ParameterEventHandler constructor.
18
+ */
19
+ export interface ParameterEventHandlerOptions {
20
+ /**
21
+ * QoS profile for the parameter_events subscription.
22
+ */
23
+ qos?: QoS;
24
+ }
25
+
26
+ /**
27
+ * Opaque handle returned when adding a parameter callback.
28
+ * Used to remove the callback later via removeParameterCallback().
29
+ */
30
+ class ParameterCallbackHandle {
31
+ readonly parameterName: string;
32
+ readonly nodeName: string;
33
+ readonly callback: (parameter: any) => void;
34
+ }
35
+
36
+ /**
37
+ * Opaque handle returned when adding a parameter event callback.
38
+ * Used to remove the callback later via removeParameterEventCallback().
39
+ */
40
+ class ParameterEventCallbackHandle {
41
+ readonly callback: (event: any) => void;
42
+ }
43
+
44
+ /**
45
+ * ParameterEventHandler - Monitors and responds to parameter changes
46
+ * on any node in the ROS 2 graph.
47
+ *
48
+ * Subscribes to `/parameter_events` and dispatches callbacks when
49
+ * parameters are added, changed, or deleted on any node.
50
+ *
51
+ * Two types of callbacks:
52
+ * - **Parameter callbacks**: for a specific parameter on a specific node
53
+ * - **Event callbacks**: for every ParameterEvent message received
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const handler = node.createParameterEventHandler();
58
+ *
59
+ * const handle = handler.addParameterCallback(
60
+ * 'my_param', '/my_node',
61
+ * (param) => console.log(`Changed: ${param.name}`)
62
+ * );
63
+ *
64
+ * handler.removeParameterCallback(handle);
65
+ * handler.destroy();
66
+ * ```
67
+ */
68
+ class ParameterEventHandler {
69
+ /**
70
+ * Add a callback for a specific parameter on a specific node.
71
+ *
72
+ * @param parameterName - Name of the parameter to monitor.
73
+ * @param nodeName - Fully qualified name of the node (e.g., '/my_node').
74
+ * @param callback - Called with the parameter message when it changes.
75
+ * @returns A handle for removing this callback later.
76
+ */
77
+ addParameterCallback(
78
+ parameterName: string,
79
+ nodeName: string,
80
+ callback: (parameter: any) => void
81
+ ): ParameterCallbackHandle;
82
+
83
+ /**
84
+ * Remove a previously added parameter callback.
85
+ *
86
+ * @param handle - The handle returned by addParameterCallback.
87
+ */
88
+ removeParameterCallback(handle: ParameterCallbackHandle): void;
89
+
90
+ /**
91
+ * Add a callback that is invoked for every parameter event.
92
+ *
93
+ * @param callback - Called with the full ParameterEvent message.
94
+ * @returns A handle for removing this callback later.
95
+ */
96
+ addParameterEventCallback(
97
+ callback: (event: any) => void
98
+ ): ParameterEventCallbackHandle;
99
+
100
+ /**
101
+ * Configure which node parameter events will be received.
102
+ *
103
+ * If nodeNames is omitted or empty, the node filter is cleared.
104
+ * Relative names are resolved against the handler node namespace.
105
+ *
106
+ * @param nodeNames - Node names to filter parameter events from.
107
+ * @returns True if the filter is active or was successfully cleared.
108
+ */
109
+ configureNodesFilter(nodeNames?: string[]): boolean;
110
+
111
+ /**
112
+ * Remove a previously added parameter event callback.
113
+ *
114
+ * @param handle - The handle returned by addParameterEventCallback.
115
+ */
116
+ removeParameterEventCallback(handle: ParameterEventCallbackHandle): void;
117
+
118
+ /**
119
+ * Check if the handler has been destroyed.
120
+ */
121
+ isDestroyed(): boolean;
122
+
123
+ /**
124
+ * Destroy the handler and clean up resources.
125
+ */
126
+ destroy(): void;
127
+
128
+ /**
129
+ * Get a specific parameter from a ParameterEvent message.
130
+ *
131
+ * @param event - A ParameterEvent message.
132
+ * @param parameterName - The parameter name to look for.
133
+ * @param nodeName - The node name to match.
134
+ * @returns The matching parameter message, or null.
135
+ */
136
+ static getParameterFromEvent(
137
+ event: any,
138
+ parameterName: string,
139
+ nodeName: string
140
+ ): any | null;
141
+
142
+ /**
143
+ * Get all parameters from a ParameterEvent message (new + changed).
144
+ *
145
+ * @param event - A ParameterEvent message.
146
+ * @returns Array of parameter messages.
147
+ */
148
+ static getParametersFromEvent(event: any): any[];
149
+ }
150
+ }
package/types/qos.d.ts CHANGED
@@ -134,4 +134,59 @@ declare module 'rclnodejs' {
134
134
  RMW_QOS_POLICY_LIVELINESS_BEST_AVAILABLE = 5,
135
135
  }
136
136
  }
137
+
138
+ /**
139
+ * Enum of overridable QoS policy kinds.
140
+ */
141
+ enum QoSPolicyKind {
142
+ HISTORY = 1,
143
+ DEPTH = 2,
144
+ RELIABILITY = 3,
145
+ DURABILITY = 4,
146
+ LIVELINESS = 5,
147
+ AVOID_ROS_NAMESPACE_CONVENTIONS = 6,
148
+ }
149
+
150
+ /**
151
+ * Options for overriding QoS policies via ROS parameters.
152
+ *
153
+ * When passed to `createPublisher()` or `createSubscription()`, the node
154
+ * declares read-only parameters for each specified policy kind. These
155
+ * parameters can be overridden at startup via `--ros-args -p` or `--params-file`.
156
+ *
157
+ * Parameter naming convention:
158
+ * `qos_overrides.<topic>.<publisher|subscription>[_<entityId>].<policy>`
159
+ */
160
+ class QoSOverridingOptions {
161
+ /**
162
+ * @param policyKinds - Which QoS policies to expose as parameters.
163
+ * @param opts - Optional callback and entityId.
164
+ */
165
+ constructor(
166
+ policyKinds: QoSPolicyKind[],
167
+ opts?: {
168
+ callback?: (qos: QoS) => { successful: boolean; reason?: string };
169
+ entityId?: string;
170
+ }
171
+ );
172
+
173
+ /** Which QoS policies are exposed as parameters. */
174
+ readonly policyKinds: QoSPolicyKind[];
175
+
176
+ /** Optional validation callback. */
177
+ readonly callback:
178
+ | ((qos: QoS) => { successful: boolean; reason?: string })
179
+ | null;
180
+
181
+ /** Optional entity disambiguation suffix. */
182
+ readonly entityId: string | null;
183
+
184
+ /**
185
+ * Create options that override history, depth, and reliability.
186
+ */
187
+ static withDefaultPolicies(opts?: {
188
+ callback?: (qos: QoS) => { successful: boolean; reason?: string };
189
+ entityId?: string;
190
+ }): QoSOverridingOptions;
191
+ }
137
192
  }
@@ -1,8 +1,11 @@
1
1
  declare module 'rclnodejs' {
2
2
  /**
3
3
  * A callback for receiving published messages.
4
+ * If the callback accepts two parameters, the second will be a MessageInfo
5
+ * containing metadata about the received message.
4
6
  *
5
7
  * @param message - The published message.
8
+ * @param messageInfo - Optional metadata about the message (timestamps, publisher GID, etc).
6
9
  *
7
10
  * @remarks
8
11
  * See {@link Node#createSubscription | Node.createSubscription}
@@ -13,12 +16,15 @@ declare module 'rclnodejs' {
13
16
  */
14
17
  type SubscriptionCallback<T extends TypeClass<MessageTypeClassName>> =
15
18
  // * @param message - The published message
16
- (message: MessageType<T>) => void;
19
+ (message: MessageType<T>, messageInfo?: MessageInfo) => void;
17
20
 
18
21
  /**
19
22
  * A callback for receiving published raw messages.
23
+ * If the callback accepts a second parameter, it will receive a MessageInfo
24
+ * containing metadata about the received message.
20
25
  *
21
26
  * @param message - The published message.
27
+ * @param messageInfo - Optional metadata about the message.
22
28
  *
23
29
  * @remarks
24
30
  * See {@link Node#createSubscription | Node.createSubscription}
@@ -29,7 +35,7 @@ declare module 'rclnodejs' {
29
35
  */
30
36
  type SubscriptionWithRawMessageCallback =
31
37
  // * @param message - The published raw message
32
- (message: Buffer) => void;
38
+ (message: Buffer, messageInfo?: MessageInfo) => void;
33
39
 
34
40
  /**
35
41
  * A ROS Subscription for published messages on a topic.
@@ -45,6 +51,12 @@ declare module 'rclnodejs' {
45
51
  */
46
52
  readonly isRaw: boolean;
47
53
 
54
+ /**
55
+ * Check if content filtering is supported for this subscription.
56
+ * @returns True if the subscription instance supports content filtering; otherwise false.
57
+ */
58
+ isContentFilterSupported(): boolean;
59
+
48
60
  /**
49
61
  * Test if the RMW supports content-filtered topics and that this subscription
50
62
  * is configured with a well formed content-filter.
package/types/timer.d.ts CHANGED
@@ -78,8 +78,9 @@ declare module 'rclnodejs' {
78
78
 
79
79
  /**
80
80
  * Call a timer and starts counting again, retrieves actual and expected call time.
81
- * @return - The timer information.
81
+ *
82
+ * @return The timer information with expected and actual call timestamps.
82
83
  */
83
- callTimerWithInfo(): object;
84
+ callTimerWithInfo(): TimerInfo;
84
85
  }
85
86
  }
@@ -1,108 +0,0 @@
1
- // Data integrity + throughput test for subscription
2
- 'use strict';
3
-
4
- const rclnodejs = require('./index.js');
5
-
6
- const PUBLISH_HZ = 50;
7
- const TEST_DURATION_SEC = 10;
8
-
9
- async function main() {
10
- await rclnodejs.init();
11
-
12
- const pubNode = new rclnodejs.Node('data_pub_node');
13
- const subNode = new rclnodejs.Node('data_sub_node');
14
-
15
- const publisher = pubNode.createPublisher(
16
- 'std_msgs/msg/Float64MultiArray',
17
- '/data_integrity_topic'
18
- );
19
-
20
- let msgCount = 0;
21
- let errCount = 0;
22
- let lastTs = null;
23
- const hzSamples = [];
24
- const errors = [];
25
-
26
- subNode.createSubscription(
27
- 'std_msgs/msg/Float64MultiArray',
28
- '/data_integrity_topic',
29
- (msg) => {
30
- const now = Date.now();
31
- msgCount++;
32
-
33
- // --- Data validation ---
34
- // Each published message has data = [seqNo, seqNo*1.5, seqNo*2.5]
35
- // Verify structure and values
36
- if (!msg || !msg.data) {
37
- errCount++;
38
- errors.push(`msg#${msgCount}: missing data field, got: ${JSON.stringify(msg)}`);
39
- } else if (!Array.isArray(msg.data) && !(msg.data instanceof Float64Array)) {
40
- errCount++;
41
- errors.push(`msg#${msgCount}: data is not array-like, type=${typeof msg.data}`);
42
- } else if (msg.data.length !== 3) {
43
- errCount++;
44
- errors.push(`msg#${msgCount}: expected 3 elements, got ${msg.data.length}`);
45
- } else {
46
- const seqNo = msg.data[0];
47
- const expectedB = seqNo * 1.5;
48
- const expectedC = seqNo * 2.5;
49
-
50
- if (Math.abs(msg.data[1] - expectedB) > 1e-9) {
51
- errCount++;
52
- errors.push(
53
- `msg#${msgCount}: data[1] expected ${expectedB}, got ${msg.data[1]}`
54
- );
55
- }
56
- if (Math.abs(msg.data[2] - expectedC) > 1e-9) {
57
- errCount++;
58
- errors.push(
59
- `msg#${msgCount}: data[2] expected ${expectedC}, got ${msg.data[2]}`
60
- );
61
- }
62
- }
63
-
64
- if (lastTs) {
65
- hzSamples.push(1000 / (now - lastTs));
66
- }
67
- lastTs = now;
68
- }
69
- );
70
-
71
- pubNode.spin();
72
- subNode.spin();
73
-
74
- let pubSeq = 0;
75
- const pubInterval = setInterval(() => {
76
- pubSeq++;
77
- publisher.publish({ data: [pubSeq, pubSeq * 1.5, pubSeq * 2.5] });
78
- }, 1000 / PUBLISH_HZ);
79
-
80
- setTimeout(() => {
81
- clearInterval(pubInterval);
82
-
83
- console.log(`\n=== Data Integrity Test Results ===`);
84
- console.log(`Published: ${pubSeq} messages at ${PUBLISH_HZ} Hz`);
85
- console.log(`Received: ${msgCount} messages`);
86
- console.log(`Data errors: ${errCount}`);
87
-
88
- if (errors.length > 0) {
89
- console.log(`\nFirst 10 errors:`);
90
- errors.slice(0, 10).forEach((e) => console.log(` ${e}`));
91
- }
92
-
93
- if (hzSamples.length > 0) {
94
- const avgHz = hzSamples.reduce((a, b) => a + b, 0) / hzSamples.length;
95
- console.log(`\nAvg Hz: ${avgHz.toFixed(2)}`);
96
- }
97
-
98
- const pass = errCount === 0 && msgCount > 0;
99
- console.log(`\nResult: ${pass ? 'PASS - all data correct' : 'FAIL'}`);
100
-
101
- pubNode.stop();
102
- subNode.stop();
103
- rclnodejs.shutdown();
104
- process.exit(pass ? 0 : 1);
105
- }, TEST_DURATION_SEC * 1000);
106
- }
107
-
108
- main().catch(console.error);