rclnodejs 1.7.0 → 1.8.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/binding.gyp +2 -0
- package/index.js +93 -0
- package/lib/action/client.js +54 -1
- package/lib/client.js +66 -1
- package/lib/clock.js +178 -0
- package/lib/clock_change.js +49 -0
- package/lib/clock_event.js +88 -0
- package/lib/errors.js +50 -0
- package/lib/logging.js +78 -0
- package/lib/message_introspector.js +123 -0
- package/lib/message_validation.js +512 -0
- package/lib/node.js +133 -1
- package/lib/node_options.js +40 -1
- package/lib/observable_subscription.js +105 -0
- package/lib/publisher.js +56 -1
- package/lib/qos.js +57 -0
- package/lib/subscription.js +8 -0
- package/lib/timer.js +42 -0
- package/lib/validator.js +63 -7
- package/package.json +4 -2
- package/prebuilds/linux-arm64/humble-jammy-arm64-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/jazzy-noble-arm64-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/kilted-noble-arm64-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/humble-jammy-x64-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/jazzy-noble-x64-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/kilted-noble-x64-rclnodejs.node +0 -0
- package/rosidl_gen/message_translator.js +0 -61
- package/scripts/config.js +1 -0
- package/src/addon.cpp +2 -0
- package/src/clock_event.cpp +268 -0
- package/src/clock_event.hpp +62 -0
- package/src/macros.h +2 -4
- package/src/rcl_action_server_bindings.cpp +21 -3
- package/src/rcl_bindings.cpp +59 -0
- package/src/rcl_context_bindings.cpp +5 -0
- package/src/rcl_graph_bindings.cpp +73 -0
- package/src/rcl_logging_bindings.cpp +158 -0
- package/src/rcl_node_bindings.cpp +14 -2
- package/src/rcl_publisher_bindings.cpp +12 -0
- package/src/rcl_service_bindings.cpp +7 -6
- package/src/rcl_subscription_bindings.cpp +51 -14
- package/src/rcl_time_point_bindings.cpp +135 -0
- package/src/rcl_timer_bindings.cpp +140 -0
- package/src/rcl_utilities.cpp +103 -2
- package/src/rcl_utilities.h +7 -1
- package/types/action_client.d.ts +27 -2
- package/types/base.d.ts +3 -0
- package/types/client.d.ts +29 -1
- package/types/clock.d.ts +86 -0
- package/types/clock_change.d.ts +27 -0
- package/types/clock_event.d.ts +51 -0
- package/types/errors.d.ts +49 -0
- package/types/index.d.ts +10 -0
- package/types/interfaces.d.ts +1 -1910
- package/types/logging.d.ts +32 -0
- package/types/message_introspector.d.ts +75 -0
- package/types/message_validation.d.ts +183 -0
- package/types/node.d.ts +67 -0
- package/types/node_options.d.ts +13 -0
- package/types/observable_subscription.d.ts +39 -0
- package/types/publisher.d.ts +28 -1
- package/types/qos.d.ts +18 -0
- package/types/subscription.d.ts +6 -0
- package/types/timer.d.ts +18 -0
- package/types/validator.d.ts +86 -0
package/lib/node_options.js
CHANGED
|
@@ -27,18 +27,24 @@ class NodeOptions {
|
|
|
27
27
|
* @param {array} [parameterOverrides=[]]
|
|
28
28
|
* @param {boolean} [automaticallyDeclareParametersFromOverrides=false]
|
|
29
29
|
* @param {boolean} [startTypeDescriptionService=true]
|
|
30
|
+
* @param {boolean} [enableRosout=true]
|
|
31
|
+
* @param {QoS} [rosoutQos=QoS.profileDefault]
|
|
30
32
|
*/
|
|
31
33
|
constructor(
|
|
32
34
|
startParameterServices = true,
|
|
33
35
|
parameterOverrides = [],
|
|
34
36
|
automaticallyDeclareParametersFromOverrides = false,
|
|
35
|
-
startTypeDescriptionService = true
|
|
37
|
+
startTypeDescriptionService = true,
|
|
38
|
+
enableRosout = true,
|
|
39
|
+
rosoutQos = null
|
|
36
40
|
) {
|
|
37
41
|
this._startParameterServices = startParameterServices;
|
|
38
42
|
this._parameterOverrides = parameterOverrides;
|
|
39
43
|
this._automaticallyDeclareParametersFromOverrides =
|
|
40
44
|
automaticallyDeclareParametersFromOverrides;
|
|
41
45
|
this._startTypeDescriptionService = startTypeDescriptionService;
|
|
46
|
+
this._enableRosout = enableRosout;
|
|
47
|
+
this._rosoutQos = rosoutQos;
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
/**
|
|
@@ -125,6 +131,39 @@ class NodeOptions {
|
|
|
125
131
|
this._startTypeDescriptionService = willStartTypeDescriptionService;
|
|
126
132
|
}
|
|
127
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Get the enableRosout option.
|
|
136
|
+
* Default value = true;
|
|
137
|
+
* @returns {boolean} - true if the rosout logging is enabled.
|
|
138
|
+
*/
|
|
139
|
+
get enableRosout() {
|
|
140
|
+
return this._enableRosout;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Set enableRosout.
|
|
145
|
+
* @param {boolean} enableRosout
|
|
146
|
+
*/
|
|
147
|
+
set enableRosout(enableRosout) {
|
|
148
|
+
this._enableRosout = enableRosout;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get the rosoutQos option.
|
|
153
|
+
* @returns {QoS} - The QoS profile for rosout.
|
|
154
|
+
*/
|
|
155
|
+
get rosoutQos() {
|
|
156
|
+
return this._rosoutQos;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Set rosoutQos.
|
|
161
|
+
* @param {QoS} rosoutQos
|
|
162
|
+
*/
|
|
163
|
+
set rosoutQos(rosoutQos) {
|
|
164
|
+
this._rosoutQos = rosoutQos;
|
|
165
|
+
}
|
|
166
|
+
|
|
128
167
|
/**
|
|
129
168
|
* Return an instance configured with default options.
|
|
130
169
|
* @returns {NodeOptions} - An instance with default values.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Copyright (c) 2025 Mahmoud Alghalayini. All rights reserved.
|
|
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 { Subject } = require('rxjs');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A wrapper that provides RxJS Observable support for ROS 2 subscriptions.
|
|
21
|
+
* This class wraps a standard Subscription and emits messages through an Observable.
|
|
22
|
+
*
|
|
23
|
+
* @class ObservableSubscription
|
|
24
|
+
* @hideconstructor
|
|
25
|
+
*/
|
|
26
|
+
class ObservableSubscription {
|
|
27
|
+
#subscription;
|
|
28
|
+
#subject;
|
|
29
|
+
#destroyed;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create an ObservableSubscription wrapper.
|
|
33
|
+
* @param {Subscription} subscription - The underlying ROS 2 subscription
|
|
34
|
+
*/
|
|
35
|
+
constructor(subscription) {
|
|
36
|
+
this.#subscription = subscription;
|
|
37
|
+
this.#subject = new Subject();
|
|
38
|
+
this.#destroyed = false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get the RxJS Observable for this subscription.
|
|
43
|
+
* Use this to pipe operators and subscribe to messages.
|
|
44
|
+
* @type {Observable}
|
|
45
|
+
*/
|
|
46
|
+
get observable() {
|
|
47
|
+
return this.#subject.asObservable();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get the underlying ROS 2 subscription.
|
|
52
|
+
* @type {Subscription}
|
|
53
|
+
*/
|
|
54
|
+
get subscription() {
|
|
55
|
+
return this.#subscription;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the topic name.
|
|
60
|
+
* @type {string}
|
|
61
|
+
*/
|
|
62
|
+
get topic() {
|
|
63
|
+
return this.#subscription.topic;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if this observable subscription has been destroyed.
|
|
68
|
+
* @type {boolean}
|
|
69
|
+
*/
|
|
70
|
+
get isDestroyed() {
|
|
71
|
+
return this.#destroyed;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Internal method to emit a message to subscribers.
|
|
76
|
+
* Called by the subscription's processResponse.
|
|
77
|
+
* @private
|
|
78
|
+
* @param {any} message - The message to emit
|
|
79
|
+
*/
|
|
80
|
+
_emit(message) {
|
|
81
|
+
if (!this.#destroyed) {
|
|
82
|
+
this.#subject.next(message);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Complete the observable and clean up resources.
|
|
88
|
+
* After calling this, no more messages will be emitted.
|
|
89
|
+
*/
|
|
90
|
+
complete() {
|
|
91
|
+
if (!this.#destroyed) {
|
|
92
|
+
this.#destroyed = true;
|
|
93
|
+
this.#subject.complete();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Alias for complete() for consistency with RxJS naming.
|
|
99
|
+
*/
|
|
100
|
+
destroy() {
|
|
101
|
+
this.complete();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = ObservableSubscription;
|
package/lib/publisher.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
const rclnodejs = require('./native_loader.js');
|
|
18
18
|
const debug = require('debug')('rclnodejs:publisher');
|
|
19
19
|
const Entity = require('./entity.js');
|
|
20
|
+
const { assertValidMessage } = require('./message_validation.js');
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* @class - Class representing a Publisher in ROS
|
|
@@ -27,6 +28,11 @@ class Publisher extends Entity {
|
|
|
27
28
|
constructor(handle, typeClass, topic, options, node, eventCallbacks) {
|
|
28
29
|
super(handle, typeClass, options);
|
|
29
30
|
this._node = node;
|
|
31
|
+
this._validateMessages = options.validateMessages || false;
|
|
32
|
+
this._validationOptions = options.validationOptions || {
|
|
33
|
+
strict: true,
|
|
34
|
+
checkTypes: true,
|
|
35
|
+
};
|
|
30
36
|
if (node && eventCallbacks) {
|
|
31
37
|
this._events = eventCallbacks.createEventHandlers(this.handle);
|
|
32
38
|
node._events.push(...this._events);
|
|
@@ -44,12 +50,24 @@ class Publisher extends Entity {
|
|
|
44
50
|
* Publish a message
|
|
45
51
|
* @param {object|Buffer} message - The message to be sent, could be kind of JavaScript message generated from .msg
|
|
46
52
|
* or be a Buffer for a raw message.
|
|
53
|
+
* @param {object} [options] - Publish options
|
|
54
|
+
* @param {boolean} [options.validate] - Override validateMessages setting for this publish call
|
|
47
55
|
* @return {undefined}
|
|
56
|
+
* @throws {MessageValidationError} If validation is enabled and message is invalid
|
|
48
57
|
*/
|
|
49
|
-
publish(message) {
|
|
58
|
+
publish(message, options = {}) {
|
|
50
59
|
if (message instanceof Buffer) {
|
|
51
60
|
rclnodejs.publishRawMessage(this._handle, message);
|
|
52
61
|
} else {
|
|
62
|
+
const shouldValidate =
|
|
63
|
+
options.validate !== undefined
|
|
64
|
+
? options.validate
|
|
65
|
+
: this._validateMessages;
|
|
66
|
+
|
|
67
|
+
if (shouldValidate && !(message instanceof this._typeClass)) {
|
|
68
|
+
assertValidMessage(message, this._typeClass, this._validationOptions);
|
|
69
|
+
}
|
|
70
|
+
|
|
53
71
|
// Enables call by plain object/number/string argument
|
|
54
72
|
// e.g. publisher.publish(3.14);
|
|
55
73
|
// publisher.publish('The quick brown fox...');
|
|
@@ -65,6 +83,35 @@ class Publisher extends Entity {
|
|
|
65
83
|
debug(`Message of topic ${this.topic} has been published.`);
|
|
66
84
|
}
|
|
67
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Whether messages will be validated before publishing.
|
|
88
|
+
* @type {boolean}
|
|
89
|
+
*/
|
|
90
|
+
get willValidateMessage() {
|
|
91
|
+
return this._validateMessages;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Enable or disable message validation for this publisher.
|
|
96
|
+
* @param {boolean} value - Whether to validate messages before publishing
|
|
97
|
+
*/
|
|
98
|
+
set willValidateMessage(value) {
|
|
99
|
+
this._validateMessages = value;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Set validation options for this publisher.
|
|
104
|
+
* @param {object} options - Validation options
|
|
105
|
+
* @param {boolean} [options.strict=true] - Throw on unknown fields
|
|
106
|
+
* @param {boolean} [options.checkTypes=true] - Validate field types
|
|
107
|
+
* @param {boolean} [options.checkRequired=false] - Check for missing fields
|
|
108
|
+
*/
|
|
109
|
+
setValidation(options) {
|
|
110
|
+
if (options && Object.keys(options).length > 0) {
|
|
111
|
+
this._validationOptions = { ...this._validationOptions, ...options };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
68
115
|
static createPublisher(node, typeClass, topic, options, eventCallbacks) {
|
|
69
116
|
let type = typeClass.type();
|
|
70
117
|
let handle = rclnodejs.createPublisher(
|
|
@@ -112,6 +159,14 @@ class Publisher extends Entity {
|
|
|
112
159
|
return rclnodejs.waitForAllAcked(this._handle, timeout);
|
|
113
160
|
}
|
|
114
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Manually assert that this Publisher is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC).
|
|
164
|
+
* @return {undefined}
|
|
165
|
+
*/
|
|
166
|
+
assertLiveliness() {
|
|
167
|
+
rclnodejs.assertLiveliness(this._handle);
|
|
168
|
+
}
|
|
169
|
+
|
|
115
170
|
/**
|
|
116
171
|
* Get the event handlers for this publisher.
|
|
117
172
|
* @returns {Array} The array of event handlers for this publisher.
|
package/lib/qos.js
CHANGED
|
@@ -58,6 +58,24 @@ let DurabilityPolicy = {
|
|
|
58
58
|
RMW_QOS_POLICY_DURABILITY_VOLATILE: 2,
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Enum for LivelinessPolicy
|
|
63
|
+
* @readonly
|
|
64
|
+
* @enum {number}
|
|
65
|
+
*/
|
|
66
|
+
let LivelinessPolicy = {
|
|
67
|
+
/** @member {number} */
|
|
68
|
+
RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT: 0,
|
|
69
|
+
/** @member {number} */
|
|
70
|
+
RMW_QOS_POLICY_LIVELINESS_AUTOMATIC: 1,
|
|
71
|
+
/** @member {number} */
|
|
72
|
+
RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC: 3,
|
|
73
|
+
/** @member {number} */
|
|
74
|
+
RMW_QOS_POLICY_LIVELINESS_UNKNOWN: 4,
|
|
75
|
+
/** @member {number} */
|
|
76
|
+
RMW_QOS_POLICY_LIVELINESS_BEST_AVAILABLE: 5,
|
|
77
|
+
};
|
|
78
|
+
|
|
61
79
|
/** Class representing middleware quality of service */
|
|
62
80
|
class QoS {
|
|
63
81
|
/**
|
|
@@ -73,12 +91,14 @@ class QoS {
|
|
|
73
91
|
depth = 0,
|
|
74
92
|
reliability = ReliabilityPolicy.RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT,
|
|
75
93
|
durability = DurabilityPolicy.RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT,
|
|
94
|
+
liveliness = LivelinessPolicy.RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT,
|
|
76
95
|
avoidRosNameSpaceConventions = false
|
|
77
96
|
) {
|
|
78
97
|
this._history = history;
|
|
79
98
|
this._depth = depth;
|
|
80
99
|
this._reliability = reliability;
|
|
81
100
|
this._durability = durability;
|
|
101
|
+
this._liveliness = liveliness;
|
|
82
102
|
this._avoidRosNameSpaceConventions = avoidRosNameSpaceConventions;
|
|
83
103
|
}
|
|
84
104
|
|
|
@@ -112,6 +132,16 @@ class QoS {
|
|
|
112
132
|
return DurabilityPolicy;
|
|
113
133
|
}
|
|
114
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Get LivelinessPolicy enum.
|
|
137
|
+
* @name QoS#static get:LivelinessPolicy
|
|
138
|
+
* @function
|
|
139
|
+
* @return {LivelinessPolicy}
|
|
140
|
+
*/
|
|
141
|
+
static get LivelinessPolicy() {
|
|
142
|
+
return LivelinessPolicy;
|
|
143
|
+
}
|
|
144
|
+
|
|
115
145
|
/**
|
|
116
146
|
* Get the history value.
|
|
117
147
|
* @name QoS#get:history
|
|
@@ -220,6 +250,33 @@ class QoS {
|
|
|
220
250
|
this._durability = durability;
|
|
221
251
|
}
|
|
222
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Get the liveliness value.
|
|
255
|
+
* @name QoS#get:liveliness
|
|
256
|
+
* @function
|
|
257
|
+
* @return {number}
|
|
258
|
+
*/
|
|
259
|
+
get liveliness() {
|
|
260
|
+
return this._liveliness;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Set the liveliness value.
|
|
265
|
+
* @param {number} liveliness - value to be set.
|
|
266
|
+
* @name QoS#set:liveliness
|
|
267
|
+
* @function
|
|
268
|
+
* @return {undefined}
|
|
269
|
+
*/
|
|
270
|
+
set liveliness(liveliness) {
|
|
271
|
+
if (typeof liveliness !== 'number') {
|
|
272
|
+
throw new TypeValidationError('liveliness', liveliness, 'number', {
|
|
273
|
+
entityType: 'qos',
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
this._liveliness = liveliness;
|
|
278
|
+
}
|
|
279
|
+
|
|
223
280
|
/**
|
|
224
281
|
* Get the avoidRosNameSpaceConventions value.
|
|
225
282
|
* @name QoS#get:avoidRosNameSpaceConventions
|
package/lib/subscription.js
CHANGED
|
@@ -151,6 +151,14 @@ class Subscription extends Entity {
|
|
|
151
151
|
: this.clearContentFilter();
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Get the current content-filter.
|
|
156
|
+
* @returns {object} - The content-filter description {expression: string, parameters: string[]} or undefined if not set/supported.
|
|
157
|
+
*/
|
|
158
|
+
getContentFilter() {
|
|
159
|
+
return rclnodejs.getContentFilter(this.handle);
|
|
160
|
+
}
|
|
161
|
+
|
|
154
162
|
/**
|
|
155
163
|
* Clear the current content-filter. No filtering is to be applied.
|
|
156
164
|
* @returns {boolean} - True if successful; false otherwise
|
package/lib/timer.js
CHANGED
|
@@ -88,6 +88,19 @@ class Timer {
|
|
|
88
88
|
return rclnodejs.timerGetTimeUntilNextCall(this._handle);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Get the absolute time in nanoseconds when the next callback is due.
|
|
93
|
+
* Note: Only available on ROS2 distributions after Humble.
|
|
94
|
+
* @return {bigint | null} - The next call time in nanoseconds, or null if the timer is canceled.
|
|
95
|
+
* Returns undefined if not supported on current ROS2 distribution.
|
|
96
|
+
*/
|
|
97
|
+
getNextCallTime() {
|
|
98
|
+
if (typeof rclnodejs.getTimerNextCallTime !== 'function') {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return rclnodejs.getTimerNextCallTime(this._handle);
|
|
102
|
+
}
|
|
103
|
+
|
|
91
104
|
/**
|
|
92
105
|
* Change the timer period.
|
|
93
106
|
* @param {bigint} period - The new period in nanoseconds.
|
|
@@ -105,6 +118,35 @@ class Timer {
|
|
|
105
118
|
return rclnodejs.getTimerPeriod(this._handle);
|
|
106
119
|
}
|
|
107
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Set the on reset callback.
|
|
123
|
+
* @param {function} callback - The callback to be called when the timer is reset.
|
|
124
|
+
* @return {undefined}
|
|
125
|
+
*/
|
|
126
|
+
setOnResetCallback(callback) {
|
|
127
|
+
if (DistroUtils.getDistroId() <= DistroUtils.getDistroId('humble')) {
|
|
128
|
+
console.warn(
|
|
129
|
+
'setOnResetCallback is not supported by this version of ROS 2'
|
|
130
|
+
);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
rclnodejs.setTimerOnResetCallback(this._handle, callback);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Clear the on reset callback.
|
|
138
|
+
* @return {undefined}
|
|
139
|
+
*/
|
|
140
|
+
clearOnResetCallback() {
|
|
141
|
+
if (DistroUtils.getDistroId() <= DistroUtils.getDistroId('humble')) {
|
|
142
|
+
console.warn(
|
|
143
|
+
'clearOnResetCallback is not supported by this version of ROS 2'
|
|
144
|
+
);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
rclnodejs.clearTimerOnResetCallback(this._handle);
|
|
148
|
+
}
|
|
149
|
+
|
|
108
150
|
/**
|
|
109
151
|
* Call a timer and starts counting again, retrieves actual and expected call time.
|
|
110
152
|
* @return {object} - The timer information.
|
package/lib/validator.js
CHANGED
|
@@ -28,8 +28,10 @@ let validator = {
|
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Validate a given topic or service name, and throw an error if invalid.
|
|
31
|
-
* @param {string} topic - The name of topic/service.
|
|
32
|
-
* @
|
|
31
|
+
* @param {string} topic - The name of topic/service. Must be fully-qualified and already expanded.
|
|
32
|
+
* @returns {true} Always returns true if valid.
|
|
33
|
+
* @throws {TypeValidationError} If topic is not a string.
|
|
34
|
+
* @throws {NameValidationError} If the topic name is invalid.
|
|
33
35
|
*/
|
|
34
36
|
validateFullTopicName(topic) {
|
|
35
37
|
if (typeof topic !== 'string') {
|
|
@@ -43,10 +45,24 @@ let validator = {
|
|
|
43
45
|
throw this._createErrorFromValidation(result, topic, 'topic');
|
|
44
46
|
},
|
|
45
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Check if a fully-qualified topic name is valid without throwing.
|
|
50
|
+
* @param {string} topic - The name of topic/service. Must be fully-qualified and already expanded.
|
|
51
|
+
* @returns {boolean} True if valid, false otherwise.
|
|
52
|
+
*/
|
|
53
|
+
isValidFullTopicName(topic) {
|
|
54
|
+
if (typeof topic !== 'string') {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return rclnodejs.validateFullTopicName(topic) === null;
|
|
58
|
+
},
|
|
59
|
+
|
|
46
60
|
/**
|
|
47
61
|
* Validate a given node name, and throw an error if invalid.
|
|
48
62
|
* @param {string} name - The name of node.
|
|
49
|
-
* @
|
|
63
|
+
* @returns {true} Always returns true if valid.
|
|
64
|
+
* @throws {TypeValidationError} If name is not a string.
|
|
65
|
+
* @throws {NameValidationError} If the node name is invalid.
|
|
50
66
|
*/
|
|
51
67
|
validateNodeName(name) {
|
|
52
68
|
if (typeof name !== 'string') {
|
|
@@ -60,10 +76,24 @@ let validator = {
|
|
|
60
76
|
throw this._createErrorFromValidation(result, name, 'node');
|
|
61
77
|
},
|
|
62
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Check if a node name is valid without throwing.
|
|
81
|
+
* @param {string} name - The name of node.
|
|
82
|
+
* @returns {boolean} True if valid, false otherwise.
|
|
83
|
+
*/
|
|
84
|
+
isValidNodeName(name) {
|
|
85
|
+
if (typeof name !== 'string') {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return rclnodejs.validateNodeName(name) === null;
|
|
89
|
+
},
|
|
90
|
+
|
|
63
91
|
/**
|
|
64
92
|
* Validate a given topic or service name, and throw an error if invalid.
|
|
65
|
-
* @param {string} topic - The name of topic/service
|
|
66
|
-
* @
|
|
93
|
+
* @param {string} topic - The name of topic/service. Does not have to be fully-qualified.
|
|
94
|
+
* @returns {true} Always returns true if valid.
|
|
95
|
+
* @throws {TypeValidationError} If topic is not a string.
|
|
96
|
+
* @throws {NameValidationError} If the topic name is invalid.
|
|
67
97
|
*/
|
|
68
98
|
validateTopicName(topic) {
|
|
69
99
|
if (typeof topic !== 'string') {
|
|
@@ -77,10 +107,24 @@ let validator = {
|
|
|
77
107
|
throw this._createErrorFromValidation(result, topic, 'topic');
|
|
78
108
|
},
|
|
79
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Check if a topic name is valid without throwing.
|
|
112
|
+
* @param {string} topic - The name of topic/service. Does not have to be fully-qualified.
|
|
113
|
+
* @returns {boolean} True if valid, false otherwise.
|
|
114
|
+
*/
|
|
115
|
+
isValidTopicName(topic) {
|
|
116
|
+
if (typeof topic !== 'string') {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return rclnodejs.validateTopicName(topic) === null;
|
|
120
|
+
},
|
|
121
|
+
|
|
80
122
|
/**
|
|
81
123
|
* Validate a given namespace, and throw an error if invalid.
|
|
82
|
-
* @param {string} namespace - The namespace to be validated
|
|
83
|
-
* @
|
|
124
|
+
* @param {string} namespace - The namespace to be validated.
|
|
125
|
+
* @returns {true} Always returns true if valid.
|
|
126
|
+
* @throws {TypeValidationError} If namespace is not a string.
|
|
127
|
+
* @throws {NameValidationError} If the namespace is invalid.
|
|
84
128
|
*/
|
|
85
129
|
validateNamespace(namespace) {
|
|
86
130
|
if (typeof namespace !== 'string') {
|
|
@@ -93,6 +137,18 @@ let validator = {
|
|
|
93
137
|
}
|
|
94
138
|
throw this._createErrorFromValidation(result, namespace, 'namespace');
|
|
95
139
|
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Check if a namespace is valid without throwing.
|
|
143
|
+
* @param {string} namespace - The namespace to be validated.
|
|
144
|
+
* @returns {boolean} True if valid, false otherwise.
|
|
145
|
+
*/
|
|
146
|
+
isValidNamespace(namespace) {
|
|
147
|
+
if (typeof namespace !== 'string') {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
return rclnodejs.validateNamespace(namespace) === null;
|
|
151
|
+
},
|
|
96
152
|
};
|
|
97
153
|
|
|
98
154
|
module.exports = validator;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rclnodejs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "ROS2.0 JavaScript client with Node.js",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@eslint/js": "^9.36.0",
|
|
52
|
-
"@types/node": "^
|
|
52
|
+
"@types/node": "^25.0.2",
|
|
53
53
|
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
|
54
54
|
"@typescript-eslint/parser": "^8.18.0",
|
|
55
55
|
"clang-format": "^1.8.0",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"jsdoc": "^4.0.4",
|
|
65
65
|
"lint-staged": "^16.2.0",
|
|
66
66
|
"mocha": "^11.0.2",
|
|
67
|
+
"node-gyp": "^12.1.0",
|
|
67
68
|
"nyc": "^17.1.0",
|
|
68
69
|
"prebuildify": "^6.0.1",
|
|
69
70
|
"rimraf": "^6.0.1",
|
|
@@ -79,6 +80,7 @@
|
|
|
79
80
|
"debug": "^4.4.0",
|
|
80
81
|
"json-bigint": "^1.0.0",
|
|
81
82
|
"node-addon-api": "^8.3.1",
|
|
83
|
+
"rxjs": "^7.8.1",
|
|
82
84
|
"walk": "^2.3.15"
|
|
83
85
|
},
|
|
84
86
|
"husky": {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -58,66 +58,6 @@ function copyMsgObject(msg, obj) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
function verifyMessage(message, obj) {
|
|
62
|
-
if (message.constructor.isROSArray) {
|
|
63
|
-
// It's a ROS message array
|
|
64
|
-
// Note: there won't be any JavaScript array in message.
|
|
65
|
-
if (!Array.isArray(obj)) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
// TODO(Kenny): deal with TypedArray in the future
|
|
69
|
-
// TODO(Kenny): if the elements are objects, check the objects
|
|
70
|
-
} else {
|
|
71
|
-
// It's a ROS message
|
|
72
|
-
const def = message.constructor.ROSMessageDef;
|
|
73
|
-
let obj = {};
|
|
74
|
-
for (let i in def.fields) {
|
|
75
|
-
const name = def.fields[i].name;
|
|
76
|
-
if (def.fields[i].type.isPrimitiveType) {
|
|
77
|
-
// check type/existence
|
|
78
|
-
switch (def.fields[i].type) {
|
|
79
|
-
case 'char':
|
|
80
|
-
case 'int16':
|
|
81
|
-
case 'int32':
|
|
82
|
-
case 'byte':
|
|
83
|
-
case 'uint16':
|
|
84
|
-
case 'uint32':
|
|
85
|
-
case 'float32':
|
|
86
|
-
case 'float64':
|
|
87
|
-
if (typeof obj[name] != 'number') {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
break;
|
|
91
|
-
case 'int64':
|
|
92
|
-
case 'uint64':
|
|
93
|
-
if (typeof obj[name] != 'bigint') {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
case 'bool':
|
|
97
|
-
if (typeof obj[name] != 'boolean') {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
break;
|
|
101
|
-
case 'string':
|
|
102
|
-
if (typeof obj[name] != 'string') {
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
} else if (!verifyMessage(message[name], obj[name])) {
|
|
108
|
-
// Proceed further on this member
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function verifyMessageStruct(MessageType, obj) {
|
|
117
|
-
const msg = new MessageType();
|
|
118
|
-
return verifyMessage(msg, obj);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
61
|
function toPlainObject(message, enableTypedArray = true) {
|
|
122
62
|
if (!message) return undefined;
|
|
123
63
|
|
|
@@ -186,7 +126,6 @@ function constructFromPlanObject(msg, obj) {
|
|
|
186
126
|
}
|
|
187
127
|
|
|
188
128
|
module.exports = {
|
|
189
|
-
verifyMessageStruct: verifyMessageStruct,
|
|
190
129
|
toROSMessage: toROSMessage,
|
|
191
130
|
toPlainObject: toPlainObject,
|
|
192
131
|
constructFromPlanObject: constructFromPlanObject,
|
package/scripts/config.js
CHANGED
package/src/addon.cpp
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
#include <node_api.h>
|
|
16
16
|
#include <rcutils/logging.h>
|
|
17
17
|
|
|
18
|
+
#include "clock_event.hpp"
|
|
18
19
|
#include "macros.h"
|
|
19
20
|
#include "rcl_action_client_bindings.h"
|
|
20
21
|
#include "rcl_action_goal_bindings.h"
|
|
@@ -74,6 +75,7 @@ Napi::Object InitModule(Napi::Env env, Napi::Object exports) {
|
|
|
74
75
|
rclnodejs::InitActionGoalBindings(env, exports);
|
|
75
76
|
rclnodejs::InitActionServerBindings(env, exports);
|
|
76
77
|
rclnodejs::InitClientBindings(env, exports);
|
|
78
|
+
rclnodejs::InitClockEventBindings(env, exports);
|
|
77
79
|
rclnodejs::InitContextBindings(env, exports);
|
|
78
80
|
rclnodejs::InitGraphBindings(env, exports);
|
|
79
81
|
rclnodejs::InitGuardConditionBindings(env, exports);
|