rclnodejs 1.9.0-alpha.0 → 2.0.0-beta.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/.prettierignore +4 -0
- package/README.md +2 -2
- package/binding.gyp +6 -5
- package/index.js +10 -0
- package/lib/action/client.js +5 -4
- package/lib/action/server_goal_handle.js +26 -1
- package/lib/action/uuid.js +1 -1
- package/lib/client.js +0 -45
- package/lib/distro.js +11 -4
- package/lib/interface_loader.js +1 -1
- package/lib/message_introspector.js +1 -29
- package/lib/message_serialization.js +2 -2
- package/lib/native_loader.js +21 -9
- package/lib/node.js +209 -12
- package/lib/parameter_event_handler.js +98 -0
- package/lib/prebuilds.js +47 -0
- package/lib/qos_overriding_options.js +358 -0
- package/lib/rmw.js +6 -1
- package/lib/subscription.js +2 -2
- package/lib/timer.js +1 -1
- package/package.json +11 -6
- package/prebuilds/linux-arm64/humble-jammy-arm64-electron-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/humble-jammy-arm64-node-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/jazzy-noble-arm64-electron-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/jazzy-noble-arm64-node-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/kilted-noble-arm64-electron-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/kilted-noble-arm64-node-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/lyrical-resolute-arm64-electron-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/lyrical-resolute-arm64-node-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/humble-jammy-x64-electron-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/humble-jammy-x64-node-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/jazzy-noble-x64-electron-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/jazzy-noble-x64-node-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/kilted-noble-x64-electron-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/kilted-noble-x64-node-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/lyrical-resolute-x64-electron-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/lyrical-resolute-x64-node-rclnodejs.node +0 -0
- package/rosidl_gen/packages.js +4 -4
- package/rosidl_gen/templates/message-template.js +20 -6
- package/rosocket/README.md +152 -0
- package/rosocket/cli.js +168 -0
- package/rosocket/index.js +245 -0
- package/scripts/install.js +14 -3
- package/scripts/tag_prebuilds.js +26 -9
- package/src/rcl_action_client_bindings.cpp +4 -4
- package/src/rcl_graph_bindings.cpp +8 -8
- package/src/rcl_lifecycle_bindings.cpp +1 -1
- package/src/rcl_subscription_bindings.cpp +2 -2
- package/src/rcl_timer_bindings.cpp +21 -2
- package/src/rcl_utilities.cpp +4 -4
- package/src/rcl_utilities.h +2 -2
- package/types/distro.d.ts +15 -1
- package/types/node.d.ts +69 -5
- package/types/parameter_event_handler.d.ts +11 -0
- package/types/qos.d.ts +55 -0
- package/types/timer.d.ts +3 -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/tools/jsdoc/Makefile +0 -5
- package/tools/jsdoc/README.md +0 -96
- package/tools/jsdoc/build-index.js +0 -610
- package/tools/jsdoc/publish.js +0 -854
- package/tools/jsdoc/regenerate-published-docs.js +0 -605
- package/tools/jsdoc/static/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-Bold-webfont.svg +0 -1830
- package/tools/jsdoc/static/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-BoldItalic-webfont.svg +0 -1830
- package/tools/jsdoc/static/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-Italic-webfont.svg +0 -1830
- package/tools/jsdoc/static/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-Light-webfont.eot +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-Light-webfont.svg +0 -1831
- package/tools/jsdoc/static/fonts/OpenSans-Light-webfont.woff +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-LightItalic-webfont.svg +0 -1835
- package/tools/jsdoc/static/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/tools/jsdoc/static/fonts/OpenSans-Regular-webfont.svg +0 -1831
- package/tools/jsdoc/static/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/tools/jsdoc/static/scripts/linenumber.js +0 -25
- package/tools/jsdoc/static/scripts/prettify/Apache-License-2.0.txt +0 -202
- package/tools/jsdoc/static/scripts/prettify/lang-css.js +0 -36
- package/tools/jsdoc/static/scripts/prettify/prettify.js +0 -738
- package/tools/jsdoc/static/styles/jsdoc-default.css +0 -1012
- package/tools/jsdoc/static/styles/prettify-jsdoc.css +0 -111
- package/tools/jsdoc/static/styles/prettify-tomorrow.css +0 -132
- package/tools/jsdoc/tmpl/augments.tmpl +0 -10
- package/tools/jsdoc/tmpl/container.tmpl +0 -193
- package/tools/jsdoc/tmpl/details.tmpl +0 -143
- package/tools/jsdoc/tmpl/example.tmpl +0 -2
- package/tools/jsdoc/tmpl/examples.tmpl +0 -13
- package/tools/jsdoc/tmpl/exceptions.tmpl +0 -17
- package/tools/jsdoc/tmpl/layout.tmpl +0 -83
- package/tools/jsdoc/tmpl/mainpage.tmpl +0 -163
- package/tools/jsdoc/tmpl/members.tmpl +0 -43
- package/tools/jsdoc/tmpl/method.tmpl +0 -124
- package/tools/jsdoc/tmpl/params.tmpl +0 -133
- package/tools/jsdoc/tmpl/properties.tmpl +0 -110
- package/tools/jsdoc/tmpl/returns.tmpl +0 -12
- package/tools/jsdoc/tmpl/source.tmpl +0 -8
- package/tools/jsdoc/tmpl/tutorial.tmpl +0 -19
- package/tools/jsdoc/tmpl/type.tmpl +0 -7
package/lib/node.js
CHANGED
|
@@ -48,6 +48,10 @@ const Service = require('./service.js');
|
|
|
48
48
|
const Subscription = require('./subscription.js');
|
|
49
49
|
const ObservableSubscription = require('./observable_subscription.js');
|
|
50
50
|
const MessageInfo = require('./message_info.js');
|
|
51
|
+
const {
|
|
52
|
+
declareQosParameters,
|
|
53
|
+
_resolveQoS,
|
|
54
|
+
} = require('./qos_overriding_options.js');
|
|
51
55
|
const TimeSource = require('./time_source.js');
|
|
52
56
|
const Timer = require('./timer.js');
|
|
53
57
|
const TypeDescriptionService = require('./type_description_service.js');
|
|
@@ -157,7 +161,9 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
157
161
|
this._parameterService = null;
|
|
158
162
|
this._typeDescriptionService = null;
|
|
159
163
|
this._parameterEventPublisher = null;
|
|
164
|
+
this._preSetParametersCallbacks = [];
|
|
160
165
|
this._setParametersCallbacks = [];
|
|
166
|
+
this._postSetParametersCallbacks = [];
|
|
161
167
|
this._logger = new Logging(rclnodejs.getNodeLoggerName(this.handle));
|
|
162
168
|
this._spinning = false;
|
|
163
169
|
this._enableRosout = options.enableRosout;
|
|
@@ -250,8 +256,14 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
250
256
|
|
|
251
257
|
timersReady.forEach((timer) => {
|
|
252
258
|
if (timer.isReady()) {
|
|
253
|
-
|
|
254
|
-
|
|
259
|
+
let timerInfo;
|
|
260
|
+
if (typeof rclnodejs.callTimerWithInfo === 'function') {
|
|
261
|
+
timerInfo = rclnodejs.callTimerWithInfo(timer.handle);
|
|
262
|
+
timer.callback(timerInfo);
|
|
263
|
+
} else {
|
|
264
|
+
rclnodejs.callTimer(timer.handle);
|
|
265
|
+
timer.callback();
|
|
266
|
+
}
|
|
255
267
|
}
|
|
256
268
|
});
|
|
257
269
|
|
|
@@ -622,15 +634,43 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
622
634
|
/**
|
|
623
635
|
* Create a Timer.
|
|
624
636
|
* @param {bigint} period - The number representing period in nanoseconds.
|
|
625
|
-
* @param {function} callback - The callback to be called when
|
|
626
|
-
*
|
|
637
|
+
* @param {function} callback - The callback to be called when the timer fires.
|
|
638
|
+
* On distros with native support, the callback receives a `TimerInfo` object
|
|
639
|
+
* describing the expected and actual call time.
|
|
640
|
+
* @param {object|Clock} [optionsOrClock] - Timer options or the clock which the timer gets time from.
|
|
641
|
+
* Supported options: `{ autostart?: boolean }`.
|
|
642
|
+
* @param {Clock} [clock] - The clock which the timer gets time from when options are provided.
|
|
627
643
|
* @return {Timer} - An instance of Timer.
|
|
628
644
|
*/
|
|
629
|
-
createTimer(period, callback, clock = null) {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
clock =
|
|
645
|
+
createTimer(period, callback, optionsOrClock = null, clock = null) {
|
|
646
|
+
let options = {};
|
|
647
|
+
|
|
648
|
+
if (optionsOrClock instanceof Clock.Clock) {
|
|
649
|
+
clock = optionsOrClock;
|
|
650
|
+
} else if (optionsOrClock === null || optionsOrClock === undefined) {
|
|
651
|
+
// Keep the 4th argument as the clock when the 3rd argument is omitted or explicitly null.
|
|
652
|
+
} else {
|
|
653
|
+
if (typeof optionsOrClock !== 'object' || Array.isArray(optionsOrClock)) {
|
|
654
|
+
throw new TypeValidationError(
|
|
655
|
+
'options',
|
|
656
|
+
optionsOrClock,
|
|
657
|
+
'object or Clock',
|
|
658
|
+
{
|
|
659
|
+
nodeName: this.name(),
|
|
660
|
+
}
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
options = optionsOrClock;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (
|
|
667
|
+
arguments.length === 4 &&
|
|
668
|
+
clock !== null &&
|
|
669
|
+
!(clock instanceof Clock.Clock)
|
|
670
|
+
) {
|
|
671
|
+
throw new TypeValidationError('clock', clock, 'Clock', {
|
|
672
|
+
nodeName: this.name(),
|
|
673
|
+
});
|
|
634
674
|
}
|
|
635
675
|
|
|
636
676
|
if (typeof period !== 'bigint') {
|
|
@@ -643,12 +683,27 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
643
683
|
nodeName: this.name(),
|
|
644
684
|
});
|
|
645
685
|
}
|
|
686
|
+
if (
|
|
687
|
+
options.autostart !== undefined &&
|
|
688
|
+
typeof options.autostart !== 'boolean'
|
|
689
|
+
) {
|
|
690
|
+
throw new TypeValidationError(
|
|
691
|
+
'options.autostart',
|
|
692
|
+
options.autostart,
|
|
693
|
+
'boolean',
|
|
694
|
+
{
|
|
695
|
+
nodeName: this.name(),
|
|
696
|
+
}
|
|
697
|
+
);
|
|
698
|
+
}
|
|
646
699
|
|
|
647
700
|
const timerClock = clock || this._clock;
|
|
701
|
+
const autostart = options.autostart ?? true;
|
|
648
702
|
let timerHandle = rclnodejs.createTimer(
|
|
649
703
|
timerClock.handle,
|
|
650
704
|
this.context.handle,
|
|
651
|
-
period
|
|
705
|
+
period,
|
|
706
|
+
autostart
|
|
652
707
|
);
|
|
653
708
|
let timer = new Timer(timerHandle, period, callback);
|
|
654
709
|
debug('Finish creating timer, period = %d.', period);
|
|
@@ -705,6 +760,10 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
705
760
|
* @param {object} options - The options argument used to parameterize the publisher.
|
|
706
761
|
* @param {boolean} options.enableTypedArray - The topic will use TypedArray if necessary, default: true.
|
|
707
762
|
* @param {QoS} options.qos - ROS Middleware "quality of service" settings for the publisher, default: QoS.profileDefault.
|
|
763
|
+
* @param {QoSOverridingOptions} [options.qosOverridingOptions] - If provided, declares read-only ROS parameters
|
|
764
|
+
* for the specified QoS policies (e.g. `qos_overrides./topic.publisher.depth`). These can be overridden at
|
|
765
|
+
* startup via `--ros-args -p` or `--params-file`. If qos is a profile string, it will be resolved to a
|
|
766
|
+
* mutable QoS object before overrides are applied.
|
|
708
767
|
* @param {PublisherEventCallbacks} eventCallbacks - The event callbacks for the publisher.
|
|
709
768
|
* @return {Publisher} - An instance of Publisher.
|
|
710
769
|
*/
|
|
@@ -752,6 +811,21 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
752
811
|
);
|
|
753
812
|
}
|
|
754
813
|
|
|
814
|
+
// Apply QoS overriding options if provided
|
|
815
|
+
if (options.qosOverridingOptions) {
|
|
816
|
+
const resolvedTopic = this.resolveTopicName(topic);
|
|
817
|
+
if (typeof options.qos === 'string' || !(options.qos instanceof QoS)) {
|
|
818
|
+
options.qos = _resolveQoS(options.qos);
|
|
819
|
+
}
|
|
820
|
+
declareQosParameters(
|
|
821
|
+
'publisher',
|
|
822
|
+
this,
|
|
823
|
+
resolvedTopic,
|
|
824
|
+
options.qos,
|
|
825
|
+
options.qosOverridingOptions
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
|
|
755
829
|
let publisher = publisherClass.createPublisher(
|
|
756
830
|
this,
|
|
757
831
|
typeClass,
|
|
@@ -795,6 +869,10 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
795
869
|
* @param {string[]} [options.contentFilter.parameters=undefined] - Array of strings that give values to
|
|
796
870
|
* the ‘parameters’ (i.e., "%n" tokens) in the filter_expression. The number of supplied parameters must
|
|
797
871
|
* fit with the requested values in the filter_expression (i.e., the number of %n tokens). default: undefined.
|
|
872
|
+
* @param {QoSOverridingOptions} [options.qosOverridingOptions] - If provided, declares read-only ROS parameters
|
|
873
|
+
* for the specified QoS policies (e.g. `qos_overrides./topic.subscription.depth`). These can be overridden at
|
|
874
|
+
* startup via `--ros-args -p` or `--params-file`. If qos is a profile string, it will be resolved to a
|
|
875
|
+
* mutable QoS object before overrides are applied.
|
|
798
876
|
* @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.
|
|
799
877
|
* @param {SubscriptionEventCallbacks} eventCallbacks - The event callbacks for the subscription.
|
|
800
878
|
* @return {Subscription} - An instance of Subscription.
|
|
@@ -848,6 +926,21 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
848
926
|
);
|
|
849
927
|
}
|
|
850
928
|
|
|
929
|
+
// Apply QoS overriding options if provided
|
|
930
|
+
if (options.qosOverridingOptions) {
|
|
931
|
+
const resolvedTopic = this.resolveTopicName(topic);
|
|
932
|
+
if (typeof options.qos === 'string' || !(options.qos instanceof QoS)) {
|
|
933
|
+
options.qos = _resolveQoS(options.qos);
|
|
934
|
+
}
|
|
935
|
+
declareQosParameters(
|
|
936
|
+
'subscription',
|
|
937
|
+
this,
|
|
938
|
+
resolvedTopic,
|
|
939
|
+
options.qos,
|
|
940
|
+
options.qosOverridingOptions
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
|
|
851
944
|
let subscription = Subscription.createSubscription(
|
|
852
945
|
this,
|
|
853
946
|
typeClass,
|
|
@@ -1494,7 +1587,7 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
1494
1587
|
* @returns {Array} - list of clients
|
|
1495
1588
|
*/
|
|
1496
1589
|
getClientsInfoByService(service, noDemangle = false) {
|
|
1497
|
-
if (DistroUtils.getDistroId() < DistroUtils.DistroId.
|
|
1590
|
+
if (DistroUtils.getDistroId() < DistroUtils.DistroId.LYRICAL) {
|
|
1498
1591
|
console.warn(
|
|
1499
1592
|
'getClientsInfoByService is not supported by this version of ROS 2'
|
|
1500
1593
|
);
|
|
@@ -1528,7 +1621,7 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
1528
1621
|
* @returns {Array} - list of servers
|
|
1529
1622
|
*/
|
|
1530
1623
|
getServersInfoByService(service, noDemangle = false) {
|
|
1531
|
-
if (DistroUtils.getDistroId() < DistroUtils.DistroId.
|
|
1624
|
+
if (DistroUtils.getDistroId() < DistroUtils.DistroId.LYRICAL) {
|
|
1532
1625
|
console.warn(
|
|
1533
1626
|
'getServersInfoByService is not supported by this version of ROS 2'
|
|
1534
1627
|
);
|
|
@@ -2015,6 +2108,29 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
2015
2108
|
* @return {SetParameterResult} - A single collective result.
|
|
2016
2109
|
*/
|
|
2017
2110
|
_setParametersAtomically(parameters = [], declareParameterMode = false) {
|
|
2111
|
+
// 1) PRE callbacks — pipeline: each callback receives the output of the previous
|
|
2112
|
+
if (this._preSetParametersCallbacks.length > 0) {
|
|
2113
|
+
for (const callback of this._preSetParametersCallbacks) {
|
|
2114
|
+
const result = callback(parameters);
|
|
2115
|
+
if (!Array.isArray(result)) {
|
|
2116
|
+
return {
|
|
2117
|
+
successful: false,
|
|
2118
|
+
reason:
|
|
2119
|
+
'pre-set parameters callback must return an array of Parameters',
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
parameters = result;
|
|
2123
|
+
if (parameters.length === 0) {
|
|
2124
|
+
return {
|
|
2125
|
+
successful: false,
|
|
2126
|
+
reason:
|
|
2127
|
+
'parameter list is empty after pre-set callback; set rejected',
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
// 2) Validate
|
|
2018
2134
|
let result = this._validateParameters(parameters, declareParameterMode);
|
|
2019
2135
|
if (!result.successful) {
|
|
2020
2136
|
return result;
|
|
@@ -2084,6 +2200,11 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
2084
2200
|
// Publish ParameterEvent.
|
|
2085
2201
|
this._parameterEventPublisher.publish(parameterEvent);
|
|
2086
2202
|
|
|
2203
|
+
// POST callbacks — for side effects after successful set
|
|
2204
|
+
for (const callback of this._postSetParametersCallbacks) {
|
|
2205
|
+
callback(parameters);
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2087
2208
|
return {
|
|
2088
2209
|
successful: true,
|
|
2089
2210
|
reason: '',
|
|
@@ -2128,6 +2249,82 @@ class Node extends rclnodejs.ShadowNode {
|
|
|
2128
2249
|
}
|
|
2129
2250
|
}
|
|
2130
2251
|
|
|
2252
|
+
/**
|
|
2253
|
+
* A callback invoked before parameter validation and setting.
|
|
2254
|
+
* It receives the parameter list and must return a (possibly modified) parameter list.
|
|
2255
|
+
*
|
|
2256
|
+
* @callback PreSetParametersCallback
|
|
2257
|
+
* @param {Parameter[]} parameters - The parameters about to be set.
|
|
2258
|
+
* @returns {Parameter[]} - The modified parameter list to proceed with.
|
|
2259
|
+
*
|
|
2260
|
+
* @see [Node.addPreSetParametersCallback]{@link Node#addPreSetParametersCallback}
|
|
2261
|
+
* @see [Node.removePreSetParametersCallback]{@link Node#removePreSetParametersCallback}
|
|
2262
|
+
*/
|
|
2263
|
+
|
|
2264
|
+
/**
|
|
2265
|
+
* Add a callback invoked before parameter validation.
|
|
2266
|
+
* The callback receives the parameter list and must return a (possibly modified)
|
|
2267
|
+
* parameter list. This can be used to coerce, add, or remove parameters before
|
|
2268
|
+
* they are validated and applied. If any pre-set callback returns an empty list,
|
|
2269
|
+
* the set is rejected.
|
|
2270
|
+
*
|
|
2271
|
+
* @param {PreSetParametersCallback} callback - The callback to add.
|
|
2272
|
+
* @returns {undefined}
|
|
2273
|
+
*/
|
|
2274
|
+
addPreSetParametersCallback(callback) {
|
|
2275
|
+
this._preSetParametersCallbacks.unshift(callback);
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
/**
|
|
2279
|
+
* Remove a pre-set parameters callback.
|
|
2280
|
+
*
|
|
2281
|
+
* @param {PreSetParametersCallback} callback - The callback to remove.
|
|
2282
|
+
* @returns {undefined}
|
|
2283
|
+
*/
|
|
2284
|
+
removePreSetParametersCallback(callback) {
|
|
2285
|
+
const idx = this._preSetParametersCallbacks.indexOf(callback);
|
|
2286
|
+
if (idx > -1) {
|
|
2287
|
+
this._preSetParametersCallbacks.splice(idx, 1);
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
/**
|
|
2292
|
+
* A callback invoked after parameters have been successfully set.
|
|
2293
|
+
* It receives the final parameter list. For side effects only (return value is ignored).
|
|
2294
|
+
*
|
|
2295
|
+
* @callback PostSetParametersCallback
|
|
2296
|
+
* @param {Parameter[]} parameters - The parameters that were set.
|
|
2297
|
+
* @returns {undefined}
|
|
2298
|
+
*
|
|
2299
|
+
* @see [Node.addPostSetParametersCallback]{@link Node#addPostSetParametersCallback}
|
|
2300
|
+
* @see [Node.removePostSetParametersCallback]{@link Node#removePostSetParametersCallback}
|
|
2301
|
+
*/
|
|
2302
|
+
|
|
2303
|
+
/**
|
|
2304
|
+
* Add a callback invoked after parameters are successfully set.
|
|
2305
|
+
* The callback receives the final parameter list. Useful for triggering
|
|
2306
|
+
* side effects (e.g., reconfiguring a component when a parameter changes).
|
|
2307
|
+
*
|
|
2308
|
+
* @param {PostSetParametersCallback} callback - The callback to add.
|
|
2309
|
+
* @returns {undefined}
|
|
2310
|
+
*/
|
|
2311
|
+
addPostSetParametersCallback(callback) {
|
|
2312
|
+
this._postSetParametersCallbacks.unshift(callback);
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
/**
|
|
2316
|
+
* Remove a post-set parameters callback.
|
|
2317
|
+
*
|
|
2318
|
+
* @param {PostSetParametersCallback} callback - The callback to remove.
|
|
2319
|
+
* @returns {undefined}
|
|
2320
|
+
*/
|
|
2321
|
+
removePostSetParametersCallback(callback) {
|
|
2322
|
+
const idx = this._postSetParametersCallbacks.indexOf(callback);
|
|
2323
|
+
if (idx > -1) {
|
|
2324
|
+
this._postSetParametersCallbacks.splice(idx, 1);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2131
2328
|
/**
|
|
2132
2329
|
* Get the fully qualified name of the node.
|
|
2133
2330
|
*
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
const { TypeValidationError, OperationError } = require('./errors');
|
|
18
18
|
const { normalizeNodeName } = require('./utils');
|
|
19
|
+
const validator = require('./validator');
|
|
19
20
|
const debug = require('debug')('rclnodejs:parameter_event_handler');
|
|
20
21
|
|
|
21
22
|
const PARAMETER_EVENT_MSG_TYPE = 'rcl_interfaces/msg/ParameterEvent';
|
|
@@ -210,6 +211,64 @@ class ParameterEventHandler {
|
|
|
210
211
|
return handle;
|
|
211
212
|
}
|
|
212
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Configure which node parameter events will be received.
|
|
216
|
+
*
|
|
217
|
+
* If nodeNames is omitted or empty, the current node filter is cleared.
|
|
218
|
+
* When a filter is active, parameter and event callbacks only receive
|
|
219
|
+
* events from the specified nodes.
|
|
220
|
+
*
|
|
221
|
+
* @param {string[]} [nodeNames] - Node names to filter parameter events from.
|
|
222
|
+
* Relative names are resolved against the handler node namespace.
|
|
223
|
+
* @returns {boolean} True if the filter is active or was successfully cleared.
|
|
224
|
+
*/
|
|
225
|
+
configureNodesFilter(nodeNames) {
|
|
226
|
+
this.#checkNotDestroyed();
|
|
227
|
+
|
|
228
|
+
if (nodeNames === undefined || nodeNames === null) {
|
|
229
|
+
this.#subscription.clearContentFilter();
|
|
230
|
+
return !this.#subscription.hasContentFilter();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (!Array.isArray(nodeNames)) {
|
|
234
|
+
throw new TypeValidationError('nodeNames', nodeNames, 'string[]', {
|
|
235
|
+
entityType: 'parameter event handler',
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (nodeNames.length === 0) {
|
|
240
|
+
this.#subscription.clearContentFilter();
|
|
241
|
+
return !this.#subscription.hasContentFilter();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const resolvedNodeNames = nodeNames.map((nodeName, index) => {
|
|
245
|
+
if (typeof nodeName !== 'string' || nodeName.trim() === '') {
|
|
246
|
+
throw new TypeValidationError(
|
|
247
|
+
`nodeNames[${index}]`,
|
|
248
|
+
nodeName,
|
|
249
|
+
'non-empty string',
|
|
250
|
+
{
|
|
251
|
+
entityType: 'parameter event handler',
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const resolvedNodeName = this.#resolvePath(nodeName.trim());
|
|
257
|
+
this.#validateFullyQualifiedNodePath(resolvedNodeName);
|
|
258
|
+
return resolvedNodeName;
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const contentFilter = {
|
|
262
|
+
expression: resolvedNodeNames
|
|
263
|
+
.map((_, index) => `node = %${index}`)
|
|
264
|
+
.join(' OR '),
|
|
265
|
+
parameters: resolvedNodeNames.map((nodeName) => `'${nodeName}'`),
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
this.#subscription.setContentFilter(contentFilter);
|
|
269
|
+
return this.#subscription.hasContentFilter();
|
|
270
|
+
}
|
|
271
|
+
|
|
213
272
|
/**
|
|
214
273
|
* Remove a previously added parameter callback.
|
|
215
274
|
*
|
|
@@ -450,6 +509,45 @@ class ParameterEventHandler {
|
|
|
450
509
|
return `${paramName}\0${nodeName}`;
|
|
451
510
|
}
|
|
452
511
|
|
|
512
|
+
/**
|
|
513
|
+
* Resolve a node path to the fully qualified name used in ParameterEvent.node.
|
|
514
|
+
* @private
|
|
515
|
+
*/
|
|
516
|
+
#resolvePath(nodePath) {
|
|
517
|
+
// Absolute node paths are already rooted. Relative names are resolved
|
|
518
|
+
// against the handler node namespace before building the content filter.
|
|
519
|
+
const unresolvedPath = nodePath.startsWith('/')
|
|
520
|
+
? nodePath
|
|
521
|
+
: `${this.#node.namespace().replace(/\/+$/, '')}/${nodePath}`;
|
|
522
|
+
|
|
523
|
+
// Collapse repeated separators for inputs like '/ns//node/' or 'nested//node'.
|
|
524
|
+
const resolvedPath = unresolvedPath.replace(/\/+/g, '/');
|
|
525
|
+
|
|
526
|
+
// Preserve the root namespace as '/' and strip trailing slashes everywhere
|
|
527
|
+
// else so the filter matches the canonical ParameterEvent.node format.
|
|
528
|
+
if (resolvedPath === '/') {
|
|
529
|
+
return resolvedPath;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return resolvedPath.replace(/\/+$/, '');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Validate a fully qualified node path before using it in a content filter.
|
|
537
|
+
* @private
|
|
538
|
+
*/
|
|
539
|
+
#validateFullyQualifiedNodePath(nodePath) {
|
|
540
|
+
const normalizedPath =
|
|
541
|
+
nodePath.length > 1 ? nodePath.replace(/\/+$/, '') : nodePath;
|
|
542
|
+
const separatorIndex = normalizedPath.lastIndexOf('/');
|
|
543
|
+
const nodeNamespace =
|
|
544
|
+
separatorIndex === 0 ? '/' : normalizedPath.slice(0, separatorIndex);
|
|
545
|
+
const nodeName = normalizedPath.slice(separatorIndex + 1);
|
|
546
|
+
|
|
547
|
+
validator.validateNamespace(nodeNamespace);
|
|
548
|
+
validator.validateNodeName(nodeName);
|
|
549
|
+
}
|
|
550
|
+
|
|
453
551
|
/**
|
|
454
552
|
* Check if the handler has been destroyed and throw if so.
|
|
455
553
|
* @private
|
package/lib/prebuilds.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const PREBUILD_PACKAGE_NAME = 'rclnodejs';
|
|
18
|
+
const SUPPORTED_PREBUILD_RUNTIMES = new Set(['node', 'electron']);
|
|
19
|
+
|
|
20
|
+
function detectPrebuildRuntime() {
|
|
21
|
+
if (process.env.npm_config_runtime === 'electron') {
|
|
22
|
+
return 'electron';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return process.versions.electron ? 'electron' : 'node';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getTaggedPrebuildFilename({
|
|
29
|
+
rosDistro,
|
|
30
|
+
ubuntuCodename,
|
|
31
|
+
arch,
|
|
32
|
+
runtime,
|
|
33
|
+
}) {
|
|
34
|
+
return `${rosDistro}-${ubuntuCodename}-${arch}-${runtime}-${PREBUILD_PACKAGE_NAME}.node`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getRuntimeFromGeneratedPrebuild(fileName) {
|
|
38
|
+
const runtime = fileName.split('.')[0];
|
|
39
|
+
return SUPPORTED_PREBUILD_RUNTIMES.has(runtime) ? runtime : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
detectPrebuildRuntime,
|
|
44
|
+
getRuntimeFromGeneratedPrebuild,
|
|
45
|
+
getTaggedPrebuildFilename,
|
|
46
|
+
PREBUILD_PACKAGE_NAME,
|
|
47
|
+
};
|