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
package/lib/node.js CHANGED
@@ -40,12 +40,18 @@ const {
40
40
  const ParameterService = require('./parameter_service.js');
41
41
  const ParameterClient = require('./parameter_client.js');
42
42
  const ParameterWatcher = require('./parameter_watcher.js');
43
+ const ParameterEventHandler = require('./parameter_event_handler.js');
43
44
  const Publisher = require('./publisher.js');
44
45
  const QoS = require('./qos.js');
45
46
  const Rates = require('./rate.js');
46
47
  const Service = require('./service.js');
47
48
  const Subscription = require('./subscription.js');
48
49
  const ObservableSubscription = require('./observable_subscription.js');
50
+ const MessageInfo = require('./message_info.js');
51
+ const {
52
+ declareQosParameters,
53
+ _resolveQoS,
54
+ } = require('./qos_overriding_options.js');
49
55
  const TimeSource = require('./time_source.js');
50
56
  const Timer = require('./timer.js');
51
57
  const TypeDescriptionService = require('./type_description_service.js');
@@ -148,13 +154,16 @@ class Node extends rclnodejs.ShadowNode {
148
154
  this._actionServers = [];
149
155
  this._parameterClients = [];
150
156
  this._parameterWatchers = [];
157
+ this._parameterEventHandlers = [];
151
158
  this._rateTimerServer = null;
152
159
  this._parameterDescriptors = new Map();
153
160
  this._parameters = new Map();
154
161
  this._parameterService = null;
155
162
  this._typeDescriptionService = null;
156
163
  this._parameterEventPublisher = null;
164
+ this._preSetParametersCallbacks = [];
157
165
  this._setParametersCallbacks = [];
166
+ this._postSetParametersCallbacks = [];
158
167
  this._logger = new Logging(rclnodejs.getNodeLoggerName(this.handle));
159
168
  this._spinning = false;
160
169
  this._enableRosout = options.enableRosout;
@@ -247,8 +256,14 @@ class Node extends rclnodejs.ShadowNode {
247
256
 
248
257
  timersReady.forEach((timer) => {
249
258
  if (timer.isReady()) {
250
- rclnodejs.callTimer(timer.handle);
251
- timer.callback();
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
+ }
252
267
  }
253
268
  });
254
269
 
@@ -269,9 +284,22 @@ class Node extends rclnodejs.ShadowNode {
269
284
  this._runWithMessageType(
270
285
  subscription.typeClass,
271
286
  (message, deserialize) => {
272
- let success = rclnodejs.rclTake(subscription.handle, message);
273
- if (success) {
274
- subscription.processResponse(deserialize());
287
+ if (subscription.wantsMessageInfo) {
288
+ let rawInfo = rclnodejs.rclTakeWithInfo(
289
+ subscription.handle,
290
+ message
291
+ );
292
+ if (rawInfo) {
293
+ subscription.processResponse(
294
+ deserialize(),
295
+ new MessageInfo(rawInfo)
296
+ );
297
+ }
298
+ } else {
299
+ let success = rclnodejs.rclTake(subscription.handle, message);
300
+ if (success) {
301
+ subscription.processResponse(deserialize());
302
+ }
275
303
  }
276
304
  }
277
305
  );
@@ -606,15 +634,43 @@ class Node extends rclnodejs.ShadowNode {
606
634
  /**
607
635
  * Create a Timer.
608
636
  * @param {bigint} period - The number representing period in nanoseconds.
609
- * @param {function} callback - The callback to be called when timeout.
610
- * @param {Clock} [clock] - The clock which the timer gets time from.
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.
611
643
  * @return {Timer} - An instance of Timer.
612
644
  */
613
- createTimer(period, callback, clock = null) {
614
- if (arguments.length === 3 && !(arguments[2] instanceof Clock)) {
615
- clock = null;
616
- } else if (arguments.length === 4) {
617
- clock = arguments[3];
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
+ });
618
674
  }
619
675
 
620
676
  if (typeof period !== 'bigint') {
@@ -627,12 +683,27 @@ class Node extends rclnodejs.ShadowNode {
627
683
  nodeName: this.name(),
628
684
  });
629
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
+ }
630
699
 
631
700
  const timerClock = clock || this._clock;
701
+ const autostart = options.autostart ?? true;
632
702
  let timerHandle = rclnodejs.createTimer(
633
703
  timerClock.handle,
634
704
  this.context.handle,
635
- period
705
+ period,
706
+ autostart
636
707
  );
637
708
  let timer = new Timer(timerHandle, period, callback);
638
709
  debug('Finish creating timer, period = %d.', period);
@@ -689,6 +760,10 @@ class Node extends rclnodejs.ShadowNode {
689
760
  * @param {object} options - The options argument used to parameterize the publisher.
690
761
  * @param {boolean} options.enableTypedArray - The topic will use TypedArray if necessary, default: true.
691
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.
692
767
  * @param {PublisherEventCallbacks} eventCallbacks - The event callbacks for the publisher.
693
768
  * @return {Publisher} - An instance of Publisher.
694
769
  */
@@ -736,6 +811,21 @@ class Node extends rclnodejs.ShadowNode {
736
811
  );
737
812
  }
738
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
+
739
829
  let publisher = publisherClass.createPublisher(
740
830
  this,
741
831
  typeClass,
@@ -779,6 +869,10 @@ class Node extends rclnodejs.ShadowNode {
779
869
  * @param {string[]} [options.contentFilter.parameters=undefined] - Array of strings that give values to
780
870
  * the ‘parameters’ (i.e., "%n" tokens) in the filter_expression. The number of supplied parameters must
781
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.
782
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.
783
877
  * @param {SubscriptionEventCallbacks} eventCallbacks - The event callbacks for the subscription.
784
878
  * @return {Subscription} - An instance of Subscription.
@@ -832,6 +926,21 @@ class Node extends rclnodejs.ShadowNode {
832
926
  );
833
927
  }
834
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
+
835
944
  let subscription = Subscription.createSubscription(
836
945
  this,
837
946
  typeClass,
@@ -1082,6 +1191,7 @@ class Node extends rclnodejs.ShadowNode {
1082
1191
 
1083
1192
  this._parameterClients.forEach((paramClient) => paramClient.destroy());
1084
1193
  this._parameterWatchers.forEach((watcher) => watcher.destroy());
1194
+ this._parameterEventHandlers.forEach((handler) => handler.destroy());
1085
1195
 
1086
1196
  this.context.onNodeDestroyed(this);
1087
1197
 
@@ -1102,6 +1212,7 @@ class Node extends rclnodejs.ShadowNode {
1102
1212
  this._actionServers = [];
1103
1213
  this._parameterClients = [];
1104
1214
  this._parameterWatchers = [];
1215
+ this._parameterEventHandlers = [];
1105
1216
 
1106
1217
  if (this._rateTimerServer) {
1107
1218
  this._rateTimerServer.shutdown();
@@ -1214,6 +1325,38 @@ class Node extends rclnodejs.ShadowNode {
1214
1325
  watcher.destroy();
1215
1326
  }
1216
1327
 
1328
+ /**
1329
+ * Create a ParameterEventHandler that monitors parameter changes on any node.
1330
+ *
1331
+ * Unlike {@link ParameterWatcher} which watches specific parameters on a single
1332
+ * remote node, ParameterEventHandler can register callbacks for parameters on
1333
+ * any node in the ROS 2 graph by subscribing to /parameter_events.
1334
+ *
1335
+ * @param {object} [options] - Options for the handler
1336
+ * @param {object} [options.qos] - QoS profile for the parameter_events subscription
1337
+ * @return {ParameterEventHandler} - An instance of ParameterEventHandler
1338
+ * @see {@link ParameterEventHandler}
1339
+ */
1340
+ createParameterEventHandler(options = {}) {
1341
+ const handler = new ParameterEventHandler(this, options);
1342
+ debug('Created ParameterEventHandler on node=%s', this.name());
1343
+ this._parameterEventHandlers.push(handler);
1344
+ return handler;
1345
+ }
1346
+
1347
+ /**
1348
+ * Destroy a ParameterEventHandler.
1349
+ * @param {ParameterEventHandler} handler - The handler to be destroyed.
1350
+ * @return {undefined}
1351
+ */
1352
+ destroyParameterEventHandler(handler) {
1353
+ if (!(handler instanceof ParameterEventHandler)) {
1354
+ throw new TypeError('Invalid argument');
1355
+ }
1356
+ this._removeEntityFromArray(handler, this._parameterEventHandlers);
1357
+ handler.destroy();
1358
+ }
1359
+
1217
1360
  /**
1218
1361
  * Destroy a Timer.
1219
1362
  * @param {Timer} timer - The Timer to be destroyed.
@@ -1965,6 +2108,29 @@ class Node extends rclnodejs.ShadowNode {
1965
2108
  * @return {SetParameterResult} - A single collective result.
1966
2109
  */
1967
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
1968
2134
  let result = this._validateParameters(parameters, declareParameterMode);
1969
2135
  if (!result.successful) {
1970
2136
  return result;
@@ -2034,6 +2200,11 @@ class Node extends rclnodejs.ShadowNode {
2034
2200
  // Publish ParameterEvent.
2035
2201
  this._parameterEventPublisher.publish(parameterEvent);
2036
2202
 
2203
+ // POST callbacks — for side effects after successful set
2204
+ for (const callback of this._postSetParametersCallbacks) {
2205
+ callback(parameters);
2206
+ }
2207
+
2037
2208
  return {
2038
2209
  successful: true,
2039
2210
  reason: '',
@@ -2078,6 +2249,82 @@ class Node extends rclnodejs.ShadowNode {
2078
2249
  }
2079
2250
  }
2080
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
+
2081
2328
  /**
2082
2329
  * Get the fully qualified name of the node.
2083
2330
  *