• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RobotWebTools / rclnodejs / 18931458458

30 Oct 2025 06:01AM UTC coverage: 81.767% (-1.4%) from 83.209%
18931458458

push

github

minggangw
Pump to 1.6.0 (#1316)

858 of 1164 branches covered (73.71%)

Branch coverage included in aggregate %.

2057 of 2401 relevant lines covered (85.67%)

466.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.11
/lib/node.js
1
// Copyright (c) 2017 Intel Corporation. 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 rclnodejs = require('./native_loader.js');
26✔
18

19
const ActionInterfaces = require('./action/interfaces.js');
26✔
20
const Client = require('./client.js');
26✔
21
const Clock = require('./clock.js');
26✔
22
const Context = require('./context.js');
26✔
23
const debug = require('debug')('rclnodejs:node');
26✔
24
const DistroUtils = require('./distro.js');
26✔
25
const GuardCondition = require('./guard_condition.js');
26✔
26
const loader = require('./interface_loader.js');
26✔
27
const Logging = require('./logging.js');
26✔
28
const NodeOptions = require('./node_options.js');
26✔
29
const {
30
  ParameterType,
31
  Parameter,
32
  ParameterDescriptor,
33
} = require('./parameter.js');
26✔
34
const { isValidSerializationMode } = require('./message_serialization.js');
26✔
35
const ParameterService = require('./parameter_service.js');
26✔
36
const Publisher = require('./publisher.js');
26✔
37
const QoS = require('./qos.js');
26✔
38
const Rates = require('./rate.js');
26✔
39
const Service = require('./service.js');
26✔
40
const Subscription = require('./subscription.js');
26✔
41
const TimeSource = require('./time_source.js');
26✔
42
const Timer = require('./timer.js');
26✔
43
const TypeDescriptionService = require('./type_description_service.js');
26✔
44
const Entity = require('./entity.js');
26✔
45
const { SubscriptionEventCallbacks } = require('../lib/event_handler.js');
26✔
46
const { PublisherEventCallbacks } = require('../lib/event_handler.js');
26✔
47
const { validateFullTopicName } = require('./validator.js');
26✔
48

49
// Parameter event publisher constants
50
const PARAMETER_EVENT_MSG_TYPE = 'rcl_interfaces/msg/ParameterEvent';
26✔
51
const PARAMETER_EVENT_TOPIC = 'parameter_events';
26✔
52

53
/**
54
 * @class - Class representing a Node in ROS
55
 */
56

57
class Node extends rclnodejs.ShadowNode {
58
  /**
59
   * Create a ROS2Node.
60
   * model using the {@link https://github.com/ros2/rcl/tree/master/rcl_lifecycle|ros2 client library (rcl) lifecyle api}.
61
   * @param {string} nodeName - The name used to register in ROS.
62
   * @param {string} [namespace=''] - The namespace used in ROS.
63
   * @param {Context} [context=Context.defaultContext()] - The context to create the node in.
64
   * @param {NodeOptions} [options=NodeOptions.defaultOptions] - The options to configure the new node behavior.
65
   * @throws {Error} If the given context is not registered.
66
   */
67
  constructor(
68
    nodeName,
69
    namespace = '',
31✔
70
    context = Context.defaultContext(),
66✔
71
    options = NodeOptions.defaultOptions,
68✔
72
    args = [],
87✔
73
    useGlobalArguments = true
87✔
74
  ) {
75
    super();
622✔
76

77
    if (typeof nodeName !== 'string' || typeof namespace !== 'string') {
622✔
78
      throw new TypeError('Invalid argument.');
22✔
79
    }
80

81
    this._init(nodeName, namespace, options, context, args, useGlobalArguments);
600✔
82
    debug(
591✔
83
      'Finish initializing node, name = %s and namespace = %s.',
84
      nodeName,
85
      namespace
86
    );
87
  }
88

89
  _init(name, namespace, options, context, args, useGlobalArguments) {
90
    this.handle = rclnodejs.createNode(
600✔
91
      name,
92
      namespace,
93
      context.handle,
94
      args,
95
      useGlobalArguments
96
    );
97
    Object.defineProperty(this, 'handle', {
591✔
98
      configurable: false,
99
      writable: false,
100
    }); // make read-only
101

102
    this._context = context;
591✔
103
    this.context.onNodeCreated(this);
591✔
104

105
    this._publishers = [];
591✔
106
    this._subscriptions = [];
591✔
107
    this._clients = [];
591✔
108
    this._services = [];
591✔
109
    this._timers = [];
591✔
110
    this._guards = [];
591✔
111
    this._events = [];
591✔
112
    this._actionClients = [];
591✔
113
    this._actionServers = [];
591✔
114
    this._rateTimerServer = null;
591✔
115
    this._parameterDescriptors = new Map();
591✔
116
    this._parameters = new Map();
591✔
117
    this._parameterService = null;
591✔
118
    this._typeDescriptionService = null;
591✔
119
    this._parameterEventPublisher = null;
591✔
120
    this._setParametersCallbacks = [];
591✔
121
    this._logger = new Logging(rclnodejs.getNodeLoggerName(this.handle));
591✔
122
    this._spinning = false;
591✔
123

124
    this._parameterEventPublisher = this.createPublisher(
591✔
125
      PARAMETER_EVENT_MSG_TYPE,
126
      PARAMETER_EVENT_TOPIC
127
    );
128

129
    // initialize _parameterOverrides from parameters defined on the commandline
130
    this._parameterOverrides = this._getNativeParameterOverrides();
591✔
131

132
    // override cli parameterOverrides with those specified in options
133
    if (options.parameterOverrides.length > 0) {
591✔
134
      for (const parameter of options.parameterOverrides) {
8✔
135
        if ((!parameter) instanceof Parameter) {
13!
136
          throw new TypeError(
×
137
            'Parameter-override must be an instance of Parameter.'
138
          );
139
        }
140
        this._parameterOverrides.set(parameter.name, parameter);
13✔
141
      }
142
    }
143

144
    // initialize _parameters from parameterOverrides
145
    if (options.automaticallyDeclareParametersFromOverrides) {
591✔
146
      for (const parameter of this._parameterOverrides.values()) {
5✔
147
        parameter.validate();
10✔
148
        const descriptor = ParameterDescriptor.fromParameter(parameter);
10✔
149
        this._parameters.set(parameter.name, parameter);
10✔
150
        this._parameterDescriptors.set(parameter.name, descriptor);
10✔
151
      }
152
    }
153

154
    // Clock that has support for ROS time.
155
    // Note: parameter overrides and parameter event publisher need to be ready at this point
156
    // to be able to declare 'use_sim_time' if it was not declared yet.
157
    this._clock = new Clock.ROSClock();
591✔
158
    this._timeSource = new TimeSource(this);
591✔
159
    this._timeSource.attachClock(this._clock);
591✔
160

161
    if (options.startParameterServices) {
591✔
162
      this._parameterService = new ParameterService(this);
585✔
163
      this._parameterService.start();
585✔
164
    }
165

166
    if (
591!
167
      DistroUtils.getDistroId() >= DistroUtils.getDistroId('jazzy') &&
1,182✔
168
      options.startTypeDescriptionService
169
    ) {
170
      this._typeDescriptionService = new TypeDescriptionService(this);
591✔
171
      this._typeDescriptionService.start();
591✔
172
    }
173
  }
174

175
  execute(handles) {
176
    let timersReady = this._timers.filter((timer) =>
8,629✔
177
      handles.includes(timer.handle)
7,950✔
178
    );
179
    let guardsReady = this._guards.filter((guard) =>
8,629✔
180
      handles.includes(guard.handle)
3✔
181
    );
182
    let subscriptionsReady = this._subscriptions.filter((subscription) =>
8,629✔
183
      handles.includes(subscription.handle)
465✔
184
    );
185
    let clientsReady = this._clients.filter((client) =>
8,629✔
186
      handles.includes(client.handle)
100✔
187
    );
188
    let servicesReady = this._services.filter((service) =>
8,629✔
189
      handles.includes(service.handle)
42,525✔
190
    );
191
    let actionClientsReady = this._actionClients.filter((actionClient) =>
8,629✔
192
      handles.includes(actionClient.handle)
156✔
193
    );
194
    let actionServersReady = this._actionServers.filter((actionServer) =>
8,629✔
195
      handles.includes(actionServer.handle)
156✔
196
    );
197
    let eventsReady = this._events.filter((event) =>
8,629✔
198
      handles.includes(event.handle)
4✔
199
    );
200

201
    timersReady.forEach((timer) => {
8,629✔
202
      if (timer.isReady()) {
7,939✔
203
        rclnodejs.callTimer(timer.handle);
7,892✔
204
        timer.callback();
7,892✔
205
      }
206
    });
207

208
    eventsReady.forEach((event) => {
8,629✔
209
      event.takeData();
4✔
210
    });
211

212
    for (const subscription of subscriptionsReady) {
8,629✔
213
      if (subscription.isDestroyed()) continue;
424✔
214
      if (subscription.isRaw) {
413✔
215
        let rawMessage = rclnodejs.rclTakeRaw(subscription.handle);
1✔
216
        if (rawMessage) {
1!
217
          subscription.processResponse(rawMessage);
1✔
218
        }
219
        continue;
1✔
220
      }
221

222
      this._runWithMessageType(
412✔
223
        subscription.typeClass,
224
        (message, deserialize) => {
225
          let success = rclnodejs.rclTake(subscription.handle, message);
412✔
226
          if (success) {
412✔
227
            subscription.processResponse(deserialize());
355✔
228
          }
229
        }
230
      );
231
    }
232

233
    for (const guard of guardsReady) {
8,629✔
234
      if (guard.isDestroyed()) continue;
3!
235

236
      guard.callback();
3✔
237
    }
238

239
    for (const client of clientsReady) {
8,629✔
240
      if (client.isDestroyed()) continue;
51!
241
      this._runWithMessageType(
51✔
242
        client.typeClass.Response,
243
        (message, deserialize) => {
244
          let sequenceNumber = rclnodejs.rclTakeResponse(
51✔
245
            client.handle,
246
            message
247
          );
248
          if (sequenceNumber !== undefined) {
51✔
249
            client.processResponse(sequenceNumber, deserialize());
31✔
250
          }
251
        }
252
      );
253
    }
254

255
    for (const service of servicesReady) {
8,629✔
256
      if (service.isDestroyed()) continue;
66!
257
      this._runWithMessageType(
66✔
258
        service.typeClass.Request,
259
        (message, deserialize) => {
260
          let header = rclnodejs.rclTakeRequest(
66✔
261
            service.handle,
262
            this.handle,
263
            message
264
          );
265
          if (header) {
66✔
266
            service.processRequest(header, deserialize());
34✔
267
          }
268
        }
269
      );
270
    }
271

272
    for (const actionClient of actionClientsReady) {
8,629✔
273
      if (actionClient.isDestroyed()) continue;
73!
274

275
      const properties = actionClient.handle.properties;
73✔
276

277
      if (properties.isGoalResponseReady) {
73✔
278
        this._runWithMessageType(
37✔
279
          actionClient.typeClass.impl.SendGoalService.Response,
280
          (message, deserialize) => {
281
            let sequence = rclnodejs.actionTakeGoalResponse(
37✔
282
              actionClient.handle,
283
              message
284
            );
285
            if (sequence != undefined) {
37✔
286
              actionClient.processGoalResponse(sequence, deserialize());
30✔
287
            }
288
          }
289
        );
290
      }
291

292
      if (properties.isCancelResponseReady) {
73✔
293
        this._runWithMessageType(
7✔
294
          actionClient.typeClass.impl.CancelGoal.Response,
295
          (message, deserialize) => {
296
            let sequence = rclnodejs.actionTakeCancelResponse(
7✔
297
              actionClient.handle,
298
              message
299
            );
300
            if (sequence != undefined) {
7✔
301
              actionClient.processCancelResponse(sequence, deserialize());
4✔
302
            }
303
          }
304
        );
305
      }
306

307
      if (properties.isResultResponseReady) {
73✔
308
        this._runWithMessageType(
15✔
309
          actionClient.typeClass.impl.GetResultService.Response,
310
          (message, deserialize) => {
311
            let sequence = rclnodejs.actionTakeResultResponse(
15✔
312
              actionClient.handle,
313
              message
314
            );
315
            if (sequence != undefined) {
15!
316
              actionClient.processResultResponse(sequence, deserialize());
15✔
317
            }
318
          }
319
        );
320
      }
321

322
      if (properties.isFeedbackReady) {
73✔
323
        this._runWithMessageType(
7✔
324
          actionClient.typeClass.impl.FeedbackMessage,
325
          (message, deserialize) => {
326
            let success = rclnodejs.actionTakeFeedback(
7✔
327
              actionClient.handle,
328
              message
329
            );
330
            if (success) {
7✔
331
              actionClient.processFeedbackMessage(deserialize());
5✔
332
            }
333
          }
334
        );
335
      }
336

337
      if (properties.isStatusReady) {
73✔
338
        this._runWithMessageType(
36✔
339
          actionClient.typeClass.impl.GoalStatusArray,
340
          (message, deserialize) => {
341
            let success = rclnodejs.actionTakeStatus(
36✔
342
              actionClient.handle,
343
              message
344
            );
345
            if (success) {
36✔
346
              actionClient.processStatusMessage(deserialize());
32✔
347
            }
348
          }
349
        );
350
      }
351
    }
352

353
    for (const actionServer of actionServersReady) {
8,629✔
354
      if (actionServer.isDestroyed()) continue;
92!
355

356
      const properties = actionServer.handle.properties;
92✔
357

358
      if (properties.isGoalRequestReady) {
92✔
359
        this._runWithMessageType(
30✔
360
          actionServer.typeClass.impl.SendGoalService.Request,
361
          (message, deserialize) => {
362
            const result = rclnodejs.actionTakeGoalRequest(
30✔
363
              actionServer.handle,
364
              message
365
            );
366
            if (result) {
30!
367
              actionServer.processGoalRequest(result, deserialize());
30✔
368
            }
369
          }
370
        );
371
      }
372

373
      if (properties.isCancelRequestReady) {
92✔
374
        this._runWithMessageType(
5✔
375
          actionServer.typeClass.impl.CancelGoal.Request,
376
          (message, deserialize) => {
377
            const result = rclnodejs.actionTakeCancelRequest(
5✔
378
              actionServer.handle,
379
              message
380
            );
381
            if (result) {
5✔
382
              actionServer.processCancelRequest(result, deserialize());
4✔
383
            }
384
          }
385
        );
386
      }
387

388
      if (properties.isResultRequestReady) {
92✔
389
        this._runWithMessageType(
23✔
390
          actionServer.typeClass.impl.GetResultService.Request,
391
          (message, deserialize) => {
392
            const result = rclnodejs.actionTakeResultRequest(
23✔
393
              actionServer.handle,
394
              message
395
            );
396
            if (result) {
23✔
397
              actionServer.processResultRequest(result, deserialize());
15✔
398
            }
399
          }
400
        );
401
      }
402

403
      if (properties.isGoalExpired) {
92✔
404
        let GoalInfoArray = ActionInterfaces.GoalInfo.ArrayType;
4✔
405
        let message = new GoalInfoArray(actionServer._goalHandles.size);
4✔
406
        let count = rclnodejs.actionExpireGoals(
4✔
407
          actionServer.handle,
408
          actionServer._goalHandles.size,
409
          message._refArray.buffer
410
        );
411
        if (count > 0) {
4✔
412
          actionServer.processGoalExpired(message, count);
3✔
413
        }
414
        GoalInfoArray.freeArray(message);
4✔
415
      }
416
    }
417

418
    // At this point it is safe to clear the cache of any
419
    // destroyed entity references
420
    Entity._gcHandles();
8,629✔
421
  }
422

423
  /**
424
   * Determine if this node is spinning.
425
   * @returns {boolean} - true when spinning; otherwise returns false.
426
   */
427
  get spinning() {
428
    return this._spinning;
4,056✔
429
  }
430

431
  /**
432
   * Trigger the event loop to continuously check for and route.
433
   * incoming events.
434
   * @param {Node} node - The node to be spun up.
435
   * @param {number} [timeout=10] - Timeout to wait in milliseconds. Block forever if negative. Don't wait if 0.
436
   * @throws {Error} If the node is already spinning.
437
   * @return {undefined}
438
   */
439
  spin(timeout = 10) {
15✔
440
    if (this.spinning) {
432!
441
      throw new Error('The node is already spinning.');
×
442
    }
443
    this.start(this.context.handle, timeout);
432✔
444
    this._spinning = true;
432✔
445
  }
446

447
  /**
448
   * Use spin().
449
   * @deprecated, since 0.18.0
450
   */
451
  startSpinning(timeout) {
452
    this.spin(timeout);
×
453
  }
454

455
  /**
456
   * Terminate spinning - no further events will be received.
457
   * @returns {undefined}
458
   */
459
  stop() {
460
    super.stop();
432✔
461
    this._spinning = false;
432✔
462
  }
463

464
  /**
465
   * Terminate spinning - no further events will be received.
466
   * @returns {undefined}
467
   * @deprecated since 0.18.0, Use stop().
468
   */
469
  stopSpinning() {
470
    super.stop();
×
471
    this._spinning = false;
×
472
  }
473

474
  /**
475
   * Spin the node and trigger the event loop to check for one incoming event. Thereafter the node
476
   * will not received additional events until running additional calls to spin() or spinOnce().
477
   * @param {Node} node - The node to be spun.
478
   * @param {number} [timeout=10] - Timeout to wait in milliseconds. Block forever if negative. Don't wait if 0.
479
   * @throws {Error} If the node is already spinning.
480
   * @return {undefined}
481
   */
482
  spinOnce(timeout = 10) {
2✔
483
    if (this.spinning) {
3,009✔
484
      throw new Error('The node is already spinning.');
2✔
485
    }
486
    super.spinOnce(this.context.handle, timeout);
3,007✔
487
  }
488

489
  _removeEntityFromArray(entity, array) {
490
    let index = array.indexOf(entity);
136✔
491
    if (index > -1) {
136✔
492
      array.splice(index, 1);
134✔
493
    }
494
  }
495

496
  _destroyEntity(entity, array, syncHandles = true) {
137✔
497
    if (entity['isDestroyed'] && entity.isDestroyed()) return;
144✔
498

499
    this._removeEntityFromArray(entity, array);
136✔
500
    if (syncHandles) {
136✔
501
      this.syncHandles();
131✔
502
    }
503

504
    if (entity['_destroy']) {
136✔
505
      entity._destroy();
130✔
506
    } else {
507
      // guards and timers
508
      entity.handle.release();
6✔
509
    }
510
  }
511

512
  _validateOptions(options) {
513
    if (
5,646✔
514
      options !== undefined &&
5,712✔
515
      (options === null || typeof options !== 'object')
516
    ) {
517
      throw new TypeError('Invalid argument of options');
20✔
518
    }
519

520
    if (options === undefined) {
5,626✔
521
      return Node.getDefaultOptions();
5,603✔
522
    }
523

524
    if (options.enableTypedArray === undefined) {
23✔
525
      options = Object.assign(options, { enableTypedArray: true });
11✔
526
    }
527

528
    if (options.qos === undefined) {
23✔
529
      options = Object.assign(options, { qos: QoS.profileDefault });
12✔
530
    }
531

532
    if (options.isRaw === undefined) {
23✔
533
      options = Object.assign(options, { isRaw: false });
13✔
534
    }
535

536
    if (options.serializationMode === undefined) {
23✔
537
      options = Object.assign(options, { serializationMode: 'default' });
7✔
538
    } else if (!isValidSerializationMode(options.serializationMode)) {
16✔
539
      throw new TypeError(
1✔
540
        `Invalid serializationMode: ${options.serializationMode}. Valid modes are: 'default', 'plain', 'json'`
541
      );
542
    }
543

544
    return options;
22✔
545
  }
546

547
  /**
548
   * Create a Timer.
549
   * @param {bigint} period - The number representing period in nanoseconds.
550
   * @param {function} callback - The callback to be called when timeout.
551
   * @param {Clock} [clock] - The clock which the timer gets time from.
552
   * @return {Timer} - An instance of Timer.
553
   */
554
  createTimer(period, callback, clock = null) {
58✔
555
    if (arguments.length === 3 && !(arguments[2] instanceof Clock)) {
58!
556
      clock = null;
×
557
    } else if (arguments.length === 4) {
58!
558
      clock = arguments[3];
×
559
    }
560

561
    if (typeof period !== 'bigint' || typeof callback !== 'function') {
58✔
562
      throw new TypeError('Invalid argument');
2✔
563
    }
564

565
    const timerClock = clock || this._clock;
56✔
566
    let timerHandle = rclnodejs.createTimer(
56✔
567
      timerClock.handle,
568
      this.context.handle,
569
      period
570
    );
571
    let timer = new Timer(timerHandle, period, callback);
56✔
572
    debug('Finish creating timer, period = %d.', period);
56✔
573
    this._timers.push(timer);
56✔
574
    this.syncHandles();
56✔
575

576
    return timer;
56✔
577
  }
578

579
  /**
580
   * Create a Rate.
581
   *
582
   * @param {number} hz - The frequency of the rate timer; default is 1 hz.
583
   * @returns {Promise<Rate>} - Promise resolving to new instance of Rate.
584
   */
585
  async createRate(hz = 1) {
4✔
586
    if (typeof hz !== 'number') {
9!
587
      throw new TypeError('Invalid argument');
×
588
    }
589

590
    const MAX_RATE_HZ_IN_MILLISECOND = 1000.0;
9✔
591
    if (hz <= 0.0 || hz > MAX_RATE_HZ_IN_MILLISECOND) {
9✔
592
      throw new RangeError(
2✔
593
        `Hz must be between 0.0 and ${MAX_RATE_HZ_IN_MILLISECOND}`
594
      );
595
    }
596

597
    // lazy initialize rateTimerServer
598
    if (!this._rateTimerServer) {
7✔
599
      this._rateTimerServer = new Rates.RateTimerServer(this);
5✔
600
      await this._rateTimerServer.init();
5✔
601
    }
602

603
    const period = Math.round(1000 / hz);
7✔
604
    const timer = this._rateTimerServer.createTimer(BigInt(period) * 1000000n);
7✔
605
    const rate = new Rates.Rate(hz, timer);
7✔
606

607
    return rate;
7✔
608
  }
609

610
  /**
611
   * Create a Publisher.
612
   * @param {function|string|object} typeClass - The ROS message class,
613
        OR a string representing the message class, e.g. 'std_msgs/msg/String',
614
        OR an object representing the message class, e.g. {package: 'std_msgs', type: 'msg', name: 'String'}
615
   * @param {string} topic - The name of the topic.
616
   * @param {object} options - The options argument used to parameterize the publisher.
617
   * @param {boolean} options.enableTypedArray - The topic will use TypedArray if necessary, default: true.
618
   * @param {QoS} options.qos - ROS Middleware "quality of service" settings for the publisher, default: QoS.profileDefault.
619
   * @param {PublisherEventCallbacks} eventCallbacks - The event callbacks for the publisher.
620
   * @return {Publisher} - An instance of Publisher.
621
   */
622
  createPublisher(typeClass, topic, options, eventCallbacks) {
623
    return this._createPublisher(
952✔
624
      typeClass,
625
      topic,
626
      options,
627
      Publisher,
628
      eventCallbacks
629
    );
630
  }
631

632
  _createPublisher(typeClass, topic, options, publisherClass, eventCallbacks) {
633
    if (typeof typeClass === 'string' || typeof typeClass === 'object') {
955✔
634
      typeClass = loader.loadInterface(typeClass);
931✔
635
    }
636
    options = this._validateOptions(options);
948✔
637

638
    if (
948✔
639
      typeof typeClass !== 'function' ||
2,818✔
640
      typeof topic !== 'string' ||
641
      (eventCallbacks && !(eventCallbacks instanceof PublisherEventCallbacks))
642
    ) {
643
      throw new TypeError('Invalid argument');
20✔
644
    }
645

646
    let publisher = publisherClass.createPublisher(
928✔
647
      this,
648
      typeClass,
649
      topic,
650
      options,
651
      eventCallbacks
652
    );
653
    debug('Finish creating publisher, topic = %s.', topic);
918✔
654
    this._publishers.push(publisher);
918✔
655
    return publisher;
918✔
656
  }
657

658
  /**
659
   * This callback is called when a message is published
660
   * @callback SubscriptionCallback
661
   * @param {Object} message - The message published
662
   * @see [Node.createSubscription]{@link Node#createSubscription}
663
   * @see [Node.createPublisher]{@link Node#createPublisher}
664
   * @see {@link Publisher}
665
   * @see {@link Subscription}
666
   */
667

668
  /**
669
   * Create a Subscription with optional content-filtering.
670
   * @param {function|string|object} typeClass - The ROS message class,
671
        OR a string representing the message class, e.g. 'std_msgs/msg/String',
672
        OR an object representing the message class, e.g. {package: 'std_msgs', type: 'msg', name: 'String'}
673
   * @param {string} topic - The name of the topic.
674
   * @param {object} options - The options argument used to parameterize the subscription.
675
   * @param {boolean} options.enableTypedArray - The topic will use TypedArray if necessary, default: true.
676
   * @param {QoS} options.qos - ROS Middleware "quality of service" settings for the subscription, default: QoS.profileDefault.
677
   * @param {boolean} options.isRaw - The topic is serialized when true, default: false.
678
   * @param {string} [options.serializationMode='default'] - Controls message serialization format:
679
   *  'default': Use native rclnodejs behavior (respects enableTypedArray setting),
680
   *  'plain': Convert TypedArrays to regular arrays,
681
   *  'json': Fully JSON-safe (handles TypedArrays, BigInt, etc.).
682
   * @param {object} [options.contentFilter=undefined] - The content-filter, default: undefined.
683
   *  Confirm that your RMW supports content-filtered topics before use. 
684
   * @param {string} options.contentFilter.expression - Specifies the criteria to select the data samples of
685
   *  interest. It is similar to the WHERE part of an SQL clause.
686
   * @param {string[]} [options.contentFilter.parameters=undefined] - Array of strings that give values to
687
   *  the ‘parameters’ (i.e., "%n" tokens) in the filter_expression. The number of supplied parameters must
688
   *  fit with the requested values in the filter_expression (i.e., the number of %n tokens). default: undefined.
689
   * @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.
690
   * @param {SubscriptionEventCallbacks} eventCallbacks - The event callbacks for the subscription.
691
   * @return {Subscription} - An instance of Subscription.
692
   * @throws {ERROR} - May throw an RMW error if content-filter is malformed. 
693
   * @see {@link SubscriptionCallback}
694
   * @see {@link https://www.omg.org/spec/DDS/1.4/PDF|Content-filter details at DDS 1.4 specification, Annex B}
695
   */
696
  createSubscription(typeClass, topic, options, callback, eventCallbacks) {
697
    if (typeof typeClass === 'string' || typeof typeClass === 'object') {
386✔
698
      typeClass = loader.loadInterface(typeClass);
377✔
699
    }
700

701
    if (typeof options === 'function') {
379✔
702
      callback = options;
339✔
703
      options = undefined;
339✔
704
    }
705
    options = this._validateOptions(options);
379✔
706

707
    if (
369✔
708
      typeof typeClass !== 'function' ||
1,456✔
709
      typeof topic !== 'string' ||
710
      typeof callback !== 'function' ||
711
      (eventCallbacks &&
712
        !(eventCallbacks instanceof SubscriptionEventCallbacks))
713
    ) {
714
      throw new TypeError('Invalid argument');
10✔
715
    }
716

717
    let subscription = Subscription.createSubscription(
359✔
718
      this,
719
      typeClass,
720
      topic,
721
      options,
722
      callback,
723
      eventCallbacks
724
    );
725
    debug('Finish creating subscription, topic = %s.', topic);
348✔
726
    this._subscriptions.push(subscription);
348✔
727
    this.syncHandles();
348✔
728

729
    return subscription;
348✔
730
  }
731

732
  /**
733
   * Create a Client.
734
   * @param {function|string|object} typeClass - The ROS message class,
735
        OR a string representing the message class, e.g. 'std_msgs/msg/String',
736
        OR an object representing the message class, e.g. {package: 'std_msgs', type: 'msg', name: 'String'}
737
   * @param {string} serviceName - The service name to request.
738
   * @param {object} options - The options argument used to parameterize the client.
739
   * @param {boolean} options.enableTypedArray - The response will use TypedArray if necessary, default: true.
740
   * @param {QoS} options.qos - ROS Middleware "quality of service" settings for the client, default: QoS.profileDefault.
741
   * @return {Client} - An instance of Client.
742
   */
743
  createClient(typeClass, serviceName, options) {
744
    if (typeof typeClass === 'string' || typeof typeClass === 'object') {
85✔
745
      typeClass = loader.loadInterface(typeClass);
75✔
746
    }
747
    options = this._validateOptions(options);
78✔
748

749
    if (typeof typeClass !== 'function' || typeof serviceName !== 'string') {
78✔
750
      throw new TypeError('Invalid argument');
20✔
751
    }
752

753
    let client = Client.createClient(
58✔
754
      this.handle,
755
      serviceName,
756
      typeClass,
757
      options
758
    );
759
    debug('Finish creating client, service = %s.', serviceName);
48✔
760
    this._clients.push(client);
48✔
761
    this.syncHandles();
48✔
762

763
    return client;
48✔
764
  }
765

766
  /**
767
   * This callback is called when a request is sent to service
768
   * @callback RequestCallback
769
   * @param {Object} request - The request sent to the service
770
   * @param {Response} response - The response to client.
771
        Use [response.send()]{@link Response#send} to send response object to client
772
   * @return {undefined}
773
   * @see [Node.createService]{@link Node#createService}
774
   * @see [Client.sendRequest]{@link Client#sendRequest}
775
   * @see {@link Client}
776
   * @see {@link Service}
777
   * @see {@link Response#send}
778
   */
779

780
  /**
781
   * Create a Service.
782
   * @param {function|string|object} typeClass - The ROS message class,
783
        OR a string representing the message class, e.g. 'std_msgs/msg/String',
784
        OR an object representing the message class, e.g. {package: 'std_msgs', type: 'msg', name: 'String'}
785
   * @param {string} serviceName - The service name to offer.
786
   * @param {object} options - The options argument used to parameterize the service.
787
   * @param {boolean} options.enableTypedArray - The request will use TypedArray if necessary, default: true.
788
   * @param {QoS} options.qos - ROS Middleware "quality of service" settings for the service, default: QoS.profileDefault.
789
   * @param {RequestCallback} callback - The callback to be called when receiving request.
790
   * @return {Service} - An instance of Service.
791
   * @see {@link RequestCallback}
792
   */
793
  createService(typeClass, serviceName, options, callback) {
794
    if (typeof typeClass === 'string' || typeof typeClass === 'object') {
3,581✔
795
      typeClass = loader.loadInterface(typeClass);
3,571✔
796
    }
797

798
    if (typeof options === 'function') {
3,574✔
799
      callback = options;
3,551✔
800
      options = undefined;
3,551✔
801
    }
802
    options = this._validateOptions(options);
3,574✔
803

804
    if (
3,564✔
805
      typeof typeClass !== 'function' ||
10,678✔
806
      typeof serviceName !== 'string' ||
807
      typeof callback !== 'function'
808
    ) {
809
      throw new TypeError('Invalid argument');
10✔
810
    }
811

812
    let service = Service.createService(
3,554✔
813
      this.handle,
814
      serviceName,
815
      typeClass,
816
      options,
817
      callback
818
    );
819
    debug('Finish creating service, service = %s.', serviceName);
3,544✔
820
    this._services.push(service);
3,544✔
821
    this.syncHandles();
3,544✔
822

823
    return service;
3,544✔
824
  }
825

826
  /**
827
   * Create a guard condition.
828
   * @param {Function} callback - The callback to be called when the guard condition is triggered.
829
   * @return {GuardCondition} - An instance of GuardCondition.
830
   */
831
  createGuardCondition(callback) {
832
    if (typeof callback !== 'function') {
3!
833
      throw new TypeError('Invalid argument');
×
834
    }
835

836
    let guard = GuardCondition.createGuardCondition(callback, this.context);
3✔
837
    debug('Finish creating guard condition');
3✔
838
    this._guards.push(guard);
3✔
839
    this.syncHandles();
3✔
840

841
    return guard;
3✔
842
  }
843

844
  /**
845
   * Destroy all resource allocated by this node, including
846
   * <code>Timer</code>s/<code>Publisher</code>s/<code>Subscription</code>s
847
   * /<code>Client</code>s/<code>Service</code>s
848
   * @return {undefined}
849
   */
850
  destroy() {
851
    if (this.spinning) {
613✔
852
      this.stop();
429✔
853
    }
854

855
    // Action servers/clients require manual destruction due to circular reference with goal handles.
856
    this._actionClients.forEach((actionClient) => actionClient.destroy());
613✔
857
    this._actionServers.forEach((actionServer) => actionServer.destroy());
613✔
858

859
    this.context.onNodeDestroyed(this);
613✔
860

861
    this.handle.release();
613✔
862
    this._clock = null;
613✔
863
    this._timers = [];
613✔
864
    this._publishers = [];
613✔
865
    this._subscriptions = [];
613✔
866
    this._clients = [];
613✔
867
    this._services = [];
613✔
868
    this._guards = [];
613✔
869
    this._actionClients = [];
613✔
870
    this._actionServers = [];
613✔
871

872
    if (this._rateTimerServer) {
613✔
873
      this._rateTimerServer.shutdown();
5✔
874
      this._rateTimerServer = null;
5✔
875
    }
876
  }
877

878
  /**
879
   * Destroy a Publisher.
880
   * @param {Publisher} publisher - The Publisher to be destroyed.
881
   * @return {undefined}
882
   */
883
  destroyPublisher(publisher) {
884
    if (!(publisher instanceof Publisher)) {
9✔
885
      throw new TypeError('Invalid argument');
2✔
886
    }
887
    if (publisher.events) {
7!
888
      publisher.events.forEach((event) => {
×
889
        this._destroyEntity(event, this._events);
×
890
      });
891
      publisher.events = [];
×
892
    }
893
    this._destroyEntity(publisher, this._publishers, false);
7✔
894
  }
895

896
  /**
897
   * Destroy a Subscription.
898
   * @param {Subscription} subscription - The Subscription to be destroyed.
899
   * @return {undefined}
900
   */
901
  destroySubscription(subscription) {
902
    if (!(subscription instanceof Subscription)) {
41✔
903
      throw new TypeError('Invalid argument');
2✔
904
    }
905
    if (subscription.events) {
39✔
906
      subscription.events.forEach((event) => {
1✔
907
        this._destroyEntity(event, this._events);
1✔
908
      });
909
      subscription.events = [];
1✔
910
    }
911

912
    this._destroyEntity(subscription, this._subscriptions);
39✔
913
  }
914

915
  /**
916
   * Destroy a Client.
917
   * @param {Client} client - The Client to be destroyed.
918
   * @return {undefined}
919
   */
920
  destroyClient(client) {
921
    if (!(client instanceof Client)) {
8✔
922
      throw new TypeError('Invalid argument');
2✔
923
    }
924
    this._destroyEntity(client, this._clients);
6✔
925
  }
926

927
  /**
928
   * Destroy a Service.
929
   * @param {Service} service - The Service to be destroyed.
930
   * @return {undefined}
931
   */
932
  destroyService(service) {
933
    if (!(service instanceof Service)) {
8✔
934
      throw new TypeError('Invalid argument');
2✔
935
    }
936
    this._destroyEntity(service, this._services);
6✔
937
  }
938

939
  /**
940
   * Destroy a Timer.
941
   * @param {Timer} timer - The Timer to be destroyed.
942
   * @return {undefined}
943
   */
944
  destroyTimer(timer) {
945
    if (!(timer instanceof Timer)) {
8✔
946
      throw new TypeError('Invalid argument');
2✔
947
    }
948
    this._destroyEntity(timer, this._timers);
6✔
949
  }
950

951
  /**
952
   * Destroy a guard condition.
953
   * @param {GuardCondition} guard - The guard condition to be destroyed.
954
   * @return {undefined}
955
   */
956
  destroyGuardCondition(guard) {
957
    if (!(guard instanceof GuardCondition)) {
3!
958
      throw new TypeError('Invalid argument');
×
959
    }
960
    this._destroyEntity(guard, this._guards);
3✔
961
  }
962

963
  /**
964
   * Get the name of the node.
965
   * @return {string}
966
   */
967
  name() {
968
    return rclnodejs.getNodeName(this.handle);
3,014✔
969
  }
970

971
  /**
972
   * Get the namespace of the node.
973
   * @return {string}
974
   */
975
  namespace() {
976
    return rclnodejs.getNamespace(this.handle);
2,467✔
977
  }
978

979
  /**
980
   * Get the context in which this node was created.
981
   * @return {Context}
982
   */
983
  get context() {
984
    return this._context;
5,293✔
985
  }
986

987
  /**
988
   * Get the nodes logger.
989
   * @returns {Logger} - The logger for the node.
990
   */
991
  getLogger() {
992
    return this._logger;
141✔
993
  }
994

995
  /**
996
   * Get the clock used by the node.
997
   * @returns {Clock} - The nodes clock.
998
   */
999
  getClock() {
1000
    return this._clock;
86✔
1001
  }
1002

1003
  /**
1004
   * Get the current time using the node's clock.
1005
   * @returns {Time} - The current time.
1006
   */
1007
  now() {
1008
    return this.getClock().now();
2✔
1009
  }
1010

1011
  /**
1012
   * Get the list of published topics discovered by the provided node for the remote node name.
1013
   * @param {string} nodeName - The name of the node.
1014
   * @param {string} namespace - The name of the namespace.
1015
   * @param {boolean} noDemangle - If true topic names and types returned will not be demangled, default: false.
1016
   * @return {Array<{name: string, types: Array<string>}>} - An array of the names and types.
1017
   */
1018
  getPublisherNamesAndTypesByNode(nodeName, namespace, noDemangle = false) {
×
1019
    return rclnodejs.getPublisherNamesAndTypesByNode(
×
1020
      this.handle,
1021
      nodeName,
1022
      namespace,
1023
      noDemangle
1024
    );
1025
  }
1026

1027
  /**
1028
   * Get the list of published topics discovered by the provided node for the remote node name.
1029
   * @param {string} nodeName - The name of the node.
1030
   * @param {string} namespace - The name of the namespace.
1031
   * @param {boolean} noDemangle - If true topic names and types returned will not be demangled, default: false.
1032
   * @return {Array<{name: string, types: Array<string>}>} - An array of the names and types.
1033
   */
1034
  getSubscriptionNamesAndTypesByNode(nodeName, namespace, noDemangle = false) {
×
1035
    return rclnodejs.getSubscriptionNamesAndTypesByNode(
×
1036
      this.handle,
1037
      nodeName,
1038
      namespace,
1039
      noDemangle
1040
    );
1041
  }
1042

1043
  /**
1044
   * Get service names and types for which a remote node has servers.
1045
   * @param {string} nodeName - The name of the node.
1046
   * @param {string} namespace - The name of the namespace.
1047
   * @return {Array<{name: string, types: Array<string>}>} - An array of the names and types.
1048
   */
1049
  getServiceNamesAndTypesByNode(nodeName, namespace) {
1050
    return rclnodejs.getServiceNamesAndTypesByNode(
×
1051
      this.handle,
1052
      nodeName,
1053
      namespace
1054
    );
1055
  }
1056

1057
  /**
1058
   * Get service names and types for which a remote node has clients.
1059
   * @param {string} nodeName - The name of the node.
1060
   * @param {string} namespace - The name of the namespace.
1061
   * @return {Array<{name: string, types: Array<string>}>} - An array of the names and types.
1062
   */
1063
  getClientNamesAndTypesByNode(nodeName, namespace) {
1064
    return rclnodejs.getClientNamesAndTypesByNode(
×
1065
      this.handle,
1066
      nodeName,
1067
      namespace
1068
    );
1069
  }
1070

1071
  /**
1072
   * Get the list of topics discovered by the provided node.
1073
   * @param {boolean} noDemangle - If true topic names and types returned will not be demangled, default: false.
1074
   * @return {Array<{name: string, types: Array<string>}>} - An array of the names and types.
1075
   */
1076
  getTopicNamesAndTypes(noDemangle = false) {
×
1077
    return rclnodejs.getTopicNamesAndTypes(this.handle, noDemangle);
×
1078
  }
1079

1080
  /**
1081
   * Get the list of services discovered by the provided node.
1082
   * @return {Array<{name: string, types: Array<string>}>} - An array of the names and types.
1083
   */
1084
  getServiceNamesAndTypes() {
1085
    return rclnodejs.getServiceNamesAndTypes(this.handle);
2✔
1086
  }
1087

1088
  /**
1089
   * Return a list of publishers on a given topic.
1090
   *
1091
   * The returned parameter is a list of TopicEndpointInfo objects, where each will contain
1092
   * the node name, node namespace, topic type, topic endpoint's GID, and its QoS profile.
1093
   *
1094
   * When the `no_mangle` parameter is `true`, the provided `topic` should be a valid
1095
   * topic name for the middleware (useful when combining ROS with native middleware (e.g. DDS)
1096
   * apps).  When the `no_mangle` parameter is `false`, the provided `topic` should
1097
   * follow ROS topic name conventions.
1098
   *
1099
   * `topic` may be a relative, private, or fully qualified topic name.
1100
   *  A relative or private topic will be expanded using this node's namespace and name.
1101
   *  The queried `topic` is not remapped.
1102
   *
1103
   * @param {string} topic - The topic on which to find the publishers.
1104
   * @param {boolean} [noDemangle=false] - If `true`, `topic` needs to be a valid middleware topic
1105
   *                               name, otherwise it should be a valid ROS topic name. Defaults to `false`.
1106
   * @returns {Array} - list of publishers
1107
   */
1108
  getPublishersInfoByTopic(topic, noDemangle = false) {
×
1109
    return rclnodejs.getPublishersInfoByTopic(
3✔
1110
      this.handle,
1111
      this._getValidatedTopic(topic, noDemangle),
1112
      noDemangle
1113
    );
1114
  }
1115

1116
  /**
1117
   * Return a list of subscriptions on a given topic.
1118
   *
1119
   * The returned parameter is a list of TopicEndpointInfo objects, where each will contain
1120
   * the node name, node namespace, topic type, topic endpoint's GID, and its QoS profile.
1121
   *
1122
   * When the `no_mangle` parameter is `true`, the provided `topic` should be a valid
1123
   * topic name for the middleware (useful when combining ROS with native middleware (e.g. DDS)
1124
   * apps).  When the `no_mangle` parameter is `false`, the provided `topic` should
1125
   * follow ROS topic name conventions.
1126
   *
1127
   * `topic` may be a relative, private, or fully qualified topic name.
1128
   *  A relative or private topic will be expanded using this node's namespace and name.
1129
   *  The queried `topic` is not remapped.
1130
   *
1131
   * @param {string} topic - The topic on which to find the subscriptions.
1132
   * @param {boolean} [noDemangle=false] -  If `true`, `topic` needs to be a valid middleware topic
1133
                                    name, otherwise it should be a valid ROS topic name. Defaults to `false`.
1134
   * @returns {Array} - list of subscriptions
1135
   */
1136
  getSubscriptionsInfoByTopic(topic, noDemangle = false) {
×
1137
    return rclnodejs.getSubscriptionsInfoByTopic(
2✔
1138
      this.handle,
1139
      this._getValidatedTopic(topic, noDemangle),
1140
      noDemangle
1141
    );
1142
  }
1143

1144
  /**
1145
   * Get the list of nodes discovered by the provided node.
1146
   * @return {Array<string>} - An array of the names.
1147
   */
1148
  getNodeNames() {
1149
    return this.getNodeNamesAndNamespaces().map((item) => item.name);
41✔
1150
  }
1151

1152
  /**
1153
   * Get the list of nodes and their namespaces discovered by the provided node.
1154
   * @return {Array<{name: string, namespace: string}>} An array of the names and namespaces.
1155
   */
1156
  getNodeNamesAndNamespaces() {
1157
    return rclnodejs.getNodeNames(this.handle, /*getEnclaves=*/ false);
17✔
1158
  }
1159

1160
  /**
1161
   * Get the list of nodes and their namespaces with enclaves discovered by the provided node.
1162
   * @return {Array<{name: string, namespace: string, enclave: string}>} An array of the names, namespaces and enclaves.
1163
   */
1164
  getNodeNamesAndNamespacesWithEnclaves() {
1165
    return rclnodejs.getNodeNames(this.handle, /*getEnclaves=*/ true);
1✔
1166
  }
1167

1168
  /**
1169
   * Return the number of publishers on a given topic.
1170
   * @param {string} topic - The name of the topic.
1171
   * @returns {number} - Number of publishers on the given topic.
1172
   */
1173
  countPublishers(topic) {
1174
    let expandedTopic = rclnodejs.expandTopicName(
6✔
1175
      topic,
1176
      this.name(),
1177
      this.namespace()
1178
    );
1179
    rclnodejs.validateTopicName(expandedTopic);
6✔
1180

1181
    return rclnodejs.countPublishers(this.handle, expandedTopic);
6✔
1182
  }
1183

1184
  /**
1185
   * Return the number of subscribers on a given topic.
1186
   * @param {string} topic - The name of the topic.
1187
   * @returns {number} - Number of subscribers on the given topic.
1188
   */
1189
  countSubscribers(topic) {
1190
    let expandedTopic = rclnodejs.expandTopicName(
6✔
1191
      topic,
1192
      this.name(),
1193
      this.namespace()
1194
    );
1195
    rclnodejs.validateTopicName(expandedTopic);
6✔
1196

1197
    return rclnodejs.countSubscribers(this.handle, expandedTopic);
6✔
1198
  }
1199

1200
  /**
1201
   * Get the number of clients on a given service name.
1202
   * @param {string} serviceName - the service name
1203
   * @returns {Number}
1204
   */
1205
  countClients(serviceName) {
1206
    if (DistroUtils.getDistroId() <= DistroUtils.getDistroId('humble')) {
2!
1207
      console.warn('countClients is not supported by this version of ROS 2');
×
1208
      return null;
×
1209
    }
1210
    return rclnodejs.countClients(this.handle, serviceName);
2✔
1211
  }
1212

1213
  /**
1214
   * Get the number of services on a given service name.
1215
   * @param {string} serviceName - the service name
1216
   * @returns {Number}
1217
   */
1218
  countServices(serviceName) {
1219
    if (DistroUtils.getDistroId() <= DistroUtils.getDistroId('humble')) {
1!
1220
      console.warn('countServices is not supported by this version of ROS 2');
×
1221
      return null;
×
1222
    }
1223
    return rclnodejs.countServices(this.handle, serviceName);
1✔
1224
  }
1225

1226
  /**
1227
   * Get the list of parameter-overrides found on the commandline and
1228
   * in the NodeOptions.parameter_overrides property.
1229
   *
1230
   * @return {Array<Parameter>} - An array of Parameters.
1231
   */
1232
  getParameterOverrides() {
1233
    return Array.from(this._parameterOverrides.values());
8✔
1234
  }
1235

1236
  /**
1237
   * Declare a parameter.
1238
   *
1239
   * Internally, register a parameter and it's descriptor.
1240
   * If a parameter-override exists, it's value will replace that of the parameter
1241
   * unless ignoreOverride is true.
1242
   * If the descriptor is undefined, then a ParameterDescriptor will be inferred
1243
   * from the parameter's state.
1244
   *
1245
   * If a parameter by the same name has already been declared then an Error is thrown.
1246
   * A parameter must be undeclared before attempting to redeclare it.
1247
   *
1248
   * @param {Parameter} parameter - Parameter to declare.
1249
   * @param {ParameterDescriptor} [descriptor] - Optional descriptor for parameter.
1250
   * @param {boolean} [ignoreOveride] - When true disregard any parameter-override that may be present.
1251
   * @return {Parameter} - The newly declared parameter.
1252
   */
1253
  declareParameter(parameter, descriptor, ignoreOveride = false) {
1,211✔
1254
    const parameters = this.declareParameters(
1,212✔
1255
      [parameter],
1256
      descriptor ? [descriptor] : [],
1,212✔
1257
      ignoreOveride
1258
    );
1259
    return parameters.length == 1 ? parameters[0] : null;
1,212!
1260
  }
1261

1262
  /**
1263
   * Declare a list of parameters.
1264
   *
1265
   * Internally register parameters with their corresponding descriptor one by one
1266
   * in the order they are provided. This is an atomic operation. If an error
1267
   * occurs the process halts and no further parameters are declared.
1268
   * Parameters that have already been processed are undeclared.
1269
   *
1270
   * While descriptors is an optional parameter, when provided there must be
1271
   * a descriptor for each parameter; otherwise an Error is thrown.
1272
   * If descriptors is not provided then a descriptor will be inferred
1273
   * from each parameter's state.
1274
   *
1275
   * When a parameter-override is available, the parameter's value
1276
   * will be replaced with that of the parameter-override unless ignoreOverrides
1277
   * is true.
1278
   *
1279
   * If a parameter by the same name has already been declared then an Error is thrown.
1280
   * A parameter must be undeclared before attempting to redeclare it.
1281
   *
1282
   * Prior to declaring the parameters each SetParameterEventCallback registered
1283
   * using setOnParameterEventCallback() is called in succession with the parameters
1284
   * list. Any SetParameterEventCallback that retuns does not return a successful
1285
   * result will cause the entire operation to terminate with no changes to the
1286
   * parameters. When all SetParameterEventCallbacks return successful then the
1287
   * list of parameters is updated.
1288
   *
1289
   * @param {Parameter[]} parameters - The parameters to declare.
1290
   * @param {ParameterDescriptor[]} [descriptors] - Optional descriptors,
1291
   *    a 1-1 correspondence with parameters.
1292
   * @param {boolean} ignoreOverrides - When true, parameter-overrides are
1293
   *    not considered, i.e.,ignored.
1294
   * @return {Parameter[]} - The declared parameters.
1295
   */
1296
  declareParameters(parameters, descriptors = [], ignoreOverrides = false) {
×
1297
    if (!Array.isArray(parameters)) {
1,212!
1298
      throw new TypeError('Invalid parameter: expected array of Parameter');
×
1299
    }
1300
    if (!Array.isArray(descriptors)) {
1,212!
1301
      throw new TypeError(
×
1302
        'Invalid parameters: expected array of ParameterDescriptor'
1303
      );
1304
    }
1305
    if (descriptors.length > 0 && parameters.length !== descriptors.length) {
1,212!
1306
      throw new TypeError(
×
1307
        'Each parameter must have a cooresponding ParameterDescriptor'
1308
      );
1309
    }
1310

1311
    const declaredDescriptors = [];
1,212✔
1312
    const declaredParameters = [];
1,212✔
1313
    const declaredParameterCollisions = [];
1,212✔
1314
    for (let i = 0; i < parameters.length; i++) {
1,212✔
1315
      let parameter =
1316
        !ignoreOverrides && this._parameterOverrides.has(parameters[i].name)
1,212✔
1317
          ? this._parameterOverrides.get(parameters[i].name)
1318
          : parameters[i];
1319

1320
      // stop processing parameters that have already been declared
1321
      if (this._parameters.has(parameter.name)) {
1,212!
1322
        declaredParameterCollisions.push(parameter);
×
1323
        continue;
×
1324
      }
1325

1326
      // create descriptor for parameter if not provided
1327
      let descriptor =
1328
        descriptors.length > 0
1,212✔
1329
          ? descriptors[i]
1330
          : ParameterDescriptor.fromParameter(parameter);
1331

1332
      descriptor.validate();
1,212✔
1333

1334
      declaredDescriptors.push(descriptor);
1,212✔
1335
      declaredParameters.push(parameter);
1,212✔
1336
    }
1337

1338
    if (declaredParameterCollisions.length > 0) {
1,212!
1339
      const errorMsg =
1340
        declaredParameterCollisions.length == 1
×
1341
          ? `Parameter(${declaredParameterCollisions[0]}) already declared.`
1342
          : `Multiple parameters already declared, e.g., Parameter(${declaredParameterCollisions[0]}).`;
1343
      throw new Error(errorMsg);
×
1344
    }
1345

1346
    // register descriptor
1347
    for (const descriptor of declaredDescriptors) {
1,212✔
1348
      this._parameterDescriptors.set(descriptor.name, descriptor);
1,212✔
1349
    }
1350

1351
    const result = this._setParametersAtomically(declaredParameters, true);
1,212✔
1352
    if (!result.successful) {
1,212!
1353
      // unregister descriptors
1354
      for (const descriptor of declaredDescriptors) {
×
1355
        this._parameterDescriptors.delete(descriptor.name);
×
1356
      }
1357

1358
      throw new Error(result.reason);
×
1359
    }
1360

1361
    return this.getParameters(declaredParameters.map((param) => param.name));
1,212✔
1362
  }
1363

1364
  /**
1365
   * Undeclare a parameter.
1366
   *
1367
   * Readonly parameters can not be undeclared or updated.
1368
   * @param {string} name - Name of parameter to undeclare.
1369
   * @return {undefined} -
1370
   */
1371
  undeclareParameter(name) {
1372
    if (!this.hasParameter(name)) return;
1!
1373

1374
    const descriptor = this.getParameterDescriptor(name);
1✔
1375
    if (descriptor.readOnly) {
1!
1376
      throw new Error(
×
1377
        `${name} parameter is read-only and can not be undeclared`
1378
      );
1379
    }
1380

1381
    this._parameters.delete(name);
1✔
1382
    this._parameterDescriptors.delete(name);
1✔
1383
  }
1384

1385
  /**
1386
   * Determine if a parameter has been declared.
1387
   * @param {string} name - name of parameter
1388
   * @returns {boolean} - Return true if parameter is declared; false otherwise.
1389
   */
1390
  hasParameter(name) {
1391
    return this._parameters.has(name);
3,600✔
1392
  }
1393

1394
  /**
1395
   * Get a declared parameter by name.
1396
   *
1397
   * If unable to locate a declared parameter then a
1398
   * parameter with type == PARAMETER_NOT_SET is returned.
1399
   *
1400
   * @param {string} name - The name of the parameter.
1401
   * @return {Parameter} - The parameter.
1402
   */
1403
  getParameter(name) {
1404
    return this.getParameters([name])[0];
1,194✔
1405
  }
1406

1407
  /**
1408
   * Get a list of parameters.
1409
   *
1410
   * Find and return the declared parameters.
1411
   * If no names are provided return all declared parameters.
1412
   *
1413
   * If unable to locate a declared parameter then a
1414
   * parameter with type == PARAMETER_NOT_SET is returned in
1415
   * it's place.
1416
   *
1417
   * @param {string[]} [names] - The names of the declared parameters
1418
   *    to find or null indicating to return all declared parameters.
1419
   * @return {Parameter[]} - The parameters.
1420
   */
1421
  getParameters(names = []) {
9✔
1422
    let params = [];
2,417✔
1423

1424
    if (names.length == 0) {
2,417✔
1425
      // get all parameters
1426
      params = [...this._parameters.values()];
9✔
1427
      return params;
9✔
1428
    }
1429

1430
    for (const name of names) {
2,408✔
1431
      const param = this.hasParameter(name)
2,409!
1432
        ? this._parameters.get(name)
1433
        : new Parameter(name, ParameterType.PARAMETER_NOT_SET);
1434

1435
      params.push(param);
2,409✔
1436
    }
1437

1438
    return params;
2,408✔
1439
  }
1440

1441
  /**
1442
   * Get the types of given parameters.
1443
   *
1444
   * Return the types of given parameters.
1445
   *
1446
   * @param {string[]} [names] - The names of the declared parameters.
1447
   * @return {Uint8Array} - The types.
1448
   */
1449
  getParameterTypes(names = []) {
×
1450
    let types = [];
1✔
1451

1452
    for (const name of names) {
1✔
1453
      const descriptor = this._parameterDescriptors.get(name);
3✔
1454
      if (descriptor) {
3!
1455
        types.push(descriptor.type);
3✔
1456
      }
1457
    }
1458
    return types;
1✔
1459
  }
1460

1461
  /**
1462
   * Get the names of all declared parameters.
1463
   *
1464
   * @return {Array<string>} - The declared parameter names or empty array if
1465
   *    no parameters have been declared.
1466
   */
1467
  getParameterNames() {
1468
    return this.getParameters().map((param) => param.name);
20✔
1469
  }
1470

1471
  /**
1472
   * Determine if a parameter descriptor exists.
1473
   *
1474
   * @param {string} name - The name of a descriptor to for.
1475
   * @return {boolean} - true if a descriptor has been declared; otherwise false.
1476
   */
1477
  hasParameterDescriptor(name) {
1478
    return !!this.getParameterDescriptor(name);
1,218✔
1479
  }
1480

1481
  /**
1482
   * Get a declared parameter descriptor by name.
1483
   *
1484
   * If unable to locate a declared parameter descriptor then a
1485
   * descriptor with type == PARAMETER_NOT_SET is returned.
1486
   *
1487
   * @param {string} name - The name of the parameter descriptor to find.
1488
   * @return {ParameterDescriptor} - The parameter descriptor.
1489
   */
1490
  getParameterDescriptor(name) {
1491
    return this.getParameterDescriptors([name])[0];
2,437✔
1492
  }
1493

1494
  /**
1495
   * Find a list of declared ParameterDescriptors.
1496
   *
1497
   * If no names are provided return all declared descriptors.
1498
   *
1499
   * If unable to locate a declared descriptor then a
1500
   * descriptor with type == PARAMETER_NOT_SET is returned in
1501
   * it's place.
1502
   *
1503
   * @param {string[]} [names] - The names of the declared parameter
1504
   *    descriptors to find or null indicating to return all declared descriptors.
1505
   * @return {ParameterDescriptor[]} - The parameter descriptors.
1506
   */
1507
  getParameterDescriptors(names = []) {
×
1508
    let descriptors = [];
2,438✔
1509

1510
    if (names.length == 0) {
2,438!
1511
      // get all parameters
1512
      descriptors = [...this._parameterDescriptors.values()];
×
1513
      return descriptors;
×
1514
    }
1515

1516
    for (const name of names) {
2,438✔
1517
      let descriptor = this._parameterDescriptors.get(name);
2,439✔
1518
      if (!descriptor) {
2,439!
1519
        descriptor = new ParameterDescriptor(
×
1520
          name,
1521
          ParameterType.PARAMETER_NOT_SET
1522
        );
1523
      }
1524
      descriptors.push(descriptor);
2,439✔
1525
    }
1526

1527
    return descriptors;
2,438✔
1528
  }
1529

1530
  /**
1531
   * Replace a declared parameter.
1532
   *
1533
   * The parameter being replaced must be a declared parameter who's descriptor
1534
   * is not readOnly; otherwise an Error is thrown.
1535
   *
1536
   * @param {Parameter} parameter - The new parameter.
1537
   * @return {rclnodejs.rcl_interfaces.msg.SetParameterResult} - The result of the operation.
1538
   */
1539
  setParameter(parameter) {
1540
    const results = this.setParameters([parameter]);
4✔
1541
    return results[0];
4✔
1542
  }
1543

1544
  /**
1545
   * Replace a list of declared parameters.
1546
   *
1547
   * Declared parameters are replaced in the order they are provided and
1548
   * a ParameterEvent is published for each individual parameter change.
1549
   *
1550
   * Prior to setting the parameters each SetParameterEventCallback registered
1551
   * using setOnParameterEventCallback() is called in succession with the parameters
1552
   * list. Any SetParameterEventCallback that retuns does not return a successful
1553
   * result will cause the entire operation to terminate with no changes to the
1554
   * parameters. When all SetParameterEventCallbacks return successful then the
1555
   * list of parameters is updated.
1556
   *
1557
   * If an error occurs, the process is stopped and returned. Parameters
1558
   * set before an error remain unchanged.
1559
   *
1560
   * @param {Parameter[]} parameters - The parameters to set.
1561
   * @return {rclnodejs.rcl_interfaces.msg.SetParameterResult[]} - A list of SetParameterResult, one for each parameter that was set.
1562
   */
1563
  setParameters(parameters = []) {
×
1564
    return parameters.map((parameter) =>
5✔
1565
      this.setParametersAtomically([parameter])
5✔
1566
    );
1567
  }
1568

1569
  /**
1570
   * Repalce a list of declared parameters atomically.
1571
   *
1572
   * Declared parameters are replaced in the order they are provided.
1573
   * A single ParameterEvent is published collectively for all changed
1574
   * parameters.
1575
   *
1576
   * Prior to setting the parameters each SetParameterEventCallback registered
1577
   * using setOnParameterEventCallback() is called in succession with the parameters
1578
   * list. Any SetParameterEventCallback that retuns does not return a successful
1579
   * result will cause the entire operation to terminate with no changes to the
1580
   * parameters. When all SetParameterEventCallbacks return successful then the
1581
   * list of parameters is updated.d
1582
   *
1583
   * If an error occurs, the process stops immediately. All parameters updated to
1584
   * the point of the error are reverted to their previous state.
1585
   *
1586
   * @param {Parameter[]} parameters - The parameters to set.
1587
   * @return {rclnodejs.rcl_interfaces.msg.SetParameterResult} - describes the result of setting 1 or more parameters.
1588
   */
1589
  setParametersAtomically(parameters = []) {
×
1590
    return this._setParametersAtomically(parameters);
6✔
1591
  }
1592

1593
  /**
1594
   * Internal method for updating parameters atomically.
1595
   *
1596
   * Prior to setting the parameters each SetParameterEventCallback registered
1597
   * using setOnParameterEventCallback() is called in succession with the parameters
1598
   * list. Any SetParameterEventCallback that retuns does not return a successful
1599
   * result will cause the entire operation to terminate with no changes to the
1600
   * parameters. When all SetParameterEventCallbacks return successful then the
1601
   * list of parameters is updated.
1602
   *
1603
   * @param {Paramerter[]} parameters - The parameters to update.
1604
   * @param {boolean} declareParameterMode - When true parameters are being declared;
1605
   *    otherwise they are being changed.
1606
   * @return {SetParameterResult} - A single collective result.
1607
   */
1608
  _setParametersAtomically(parameters = [], declareParameterMode = false) {
6!
1609
    let result = this._validateParameters(parameters, declareParameterMode);
1,218✔
1610
    if (!result.successful) {
1,218!
1611
      return result;
×
1612
    }
1613

1614
    // give all SetParametersCallbacks a chance to veto this change
1615
    for (const callback of this._setParametersCallbacks) {
1,218✔
1616
      result = callback(parameters);
633✔
1617
      if (!result.successful) {
633✔
1618
        // a callback has vetoed a parameter change
1619
        return result;
1✔
1620
      }
1621
    }
1622

1623
    // collectively track updates to parameters for use
1624
    // when publishing a ParameterEvent
1625
    const newParameters = [];
1,217✔
1626
    const changedParameters = [];
1,217✔
1627
    const deletedParameters = [];
1,217✔
1628

1629
    for (const parameter of parameters) {
1,217✔
1630
      if (parameter.type == ParameterType.PARAMETER_NOT_SET) {
1,217✔
1631
        this.undeclareParameter(parameter.name);
1✔
1632
        deletedParameters.push(parameter);
1✔
1633
      } else {
1634
        this._parameters.set(parameter.name, parameter);
1,216✔
1635
        if (declareParameterMode) {
1,216✔
1636
          newParameters.push(parameter);
1,212✔
1637
        } else {
1638
          changedParameters.push(parameter);
4✔
1639
        }
1640
      }
1641
    }
1642

1643
    // create ParameterEvent
1644
    const parameterEvent = new (loader.loadInterface(
1,217✔
1645
      PARAMETER_EVENT_MSG_TYPE
1646
    ))();
1647

1648
    const { seconds, nanoseconds } = this._clock.now().secondsAndNanoseconds;
1,217✔
1649
    parameterEvent.stamp = {
1,217✔
1650
      sec: Number(seconds),
1651
      nanosec: Number(nanoseconds),
1652
    };
1653

1654
    parameterEvent.node =
1,217✔
1655
      this.namespace() === '/'
1,217✔
1656
        ? this.namespace() + this.name()
1657
        : this.namespace() + '/' + this.name();
1658

1659
    if (newParameters.length > 0) {
1,217✔
1660
      parameterEvent['new_parameters'] = newParameters.map((parameter) =>
1,212✔
1661
        parameter.toParameterMessage()
1,212✔
1662
      );
1663
    }
1664
    if (changedParameters.length > 0) {
1,217✔
1665
      parameterEvent['changed_parameters'] = changedParameters.map(
4✔
1666
        (parameter) => parameter.toParameterMessage()
4✔
1667
      );
1668
    }
1669
    if (deletedParameters.length > 0) {
1,217✔
1670
      parameterEvent['deleted_parameters'] = deletedParameters.map(
1✔
1671
        (parameter) => parameter.toParameterMessage()
1✔
1672
      );
1673
    }
1674

1675
    // Publish ParameterEvent.
1676
    this._parameterEventPublisher.publish(parameterEvent);
1,217✔
1677

1678
    return {
1,217✔
1679
      successful: true,
1680
      reason: '',
1681
    };
1682
  }
1683

1684
  /**
1685
   * This callback is called when declaring a parameter or setting a parameter.
1686
   * The callback is provided a list of parameters and returns a SetParameterResult
1687
   * to indicate approval or veto of the operation.
1688
   *
1689
   * @callback SetParametersCallback
1690
   * @param {Parameter[]} parameters - The message published
1691
   * @returns {rcl_interfaces.msg.SetParameterResult} -
1692
   *
1693
   * @see [Node.addOnSetParametersCallback]{@link Node#addOnSetParametersCallback}
1694
   * @see [Node.removeOnSetParametersCallback]{@link Node#removeOnSetParametersCallback}
1695
   */
1696

1697
  /**
1698
   * Add a callback to the front of the list of callbacks invoked for parameter declaration
1699
   * and setting. No checks are made for duplicate callbacks.
1700
   *
1701
   * @param {SetParametersCallback} callback - The callback to add.
1702
   * @returns {undefined}
1703
   */
1704
  addOnSetParametersCallback(callback) {
1705
    this._setParametersCallbacks.unshift(callback);
599✔
1706
  }
1707

1708
  /**
1709
   * Remove a callback from the list of SetParametersCallbacks.
1710
   * If the callback is not found the process is a nop.
1711
   *
1712
   * @param {SetParametersCallback} callback - The callback to be removed
1713
   * @returns {undefined}
1714
   */
1715
  removeOnSetParametersCallback(callback) {
1716
    const idx = this._setParametersCallbacks.indexOf(callback);
2✔
1717
    if (idx > -1) {
2!
1718
      this._setParametersCallbacks.splice(idx, 1);
2✔
1719
    }
1720
  }
1721

1722
  /**
1723
   * Get the fully qualified name of the node.
1724
   *
1725
   * @returns {string} - String containing the fully qualified name of the node.
1726
   */
1727
  getFullyQualifiedName() {
1728
    return rclnodejs.getFullyQualifiedName(this.handle);
1✔
1729
  }
1730

1731
  /**
1732
   * Get the RMW implementation identifier
1733
   * @returns {string} - The RMW implementation identifier.
1734
   */
1735
  getRMWImplementationIdentifier() {
1736
    return rclnodejs.getRMWImplementationIdentifier();
1✔
1737
  }
1738

1739
  /**
1740
   * Return a topic name expanded and remapped.
1741
   * @param {string} topicName - Topic name to be expanded and remapped.
1742
   * @param {boolean} [onlyExpand=false] - If `true`, remapping rules won't be applied.
1743
   * @returns {string} - A fully qualified topic name.
1744
   */
1745
  resolveTopicName(topicName, onlyExpand = false) {
2✔
1746
    if (typeof topicName !== 'string') {
3!
1747
      throw new TypeError('Invalid argument: expected string');
×
1748
    }
1749
    return rclnodejs.resolveName(
3✔
1750
      this.handle,
1751
      topicName,
1752
      onlyExpand,
1753
      /*isService=*/ false
1754
    );
1755
  }
1756

1757
  /**
1758
   * Return a service name expanded and remapped.
1759
   * @param {string} service - Service name to be expanded and remapped.
1760
   * @param {boolean} [onlyExpand=false] - If `true`, remapping rules won't be applied.
1761
   * @returns {string} - A fully qualified service name.
1762
   */
1763
  resolveServiceName(service, onlyExpand = false) {
2✔
1764
    if (typeof service !== 'string') {
3!
1765
      throw new TypeError('Invalid argument: expected string');
×
1766
    }
1767
    return rclnodejs.resolveName(
3✔
1768
      this.handle,
1769
      service,
1770
      onlyExpand,
1771
      /*isService=*/ true
1772
    );
1773
  }
1774

1775
  // returns on 1st error or result {successful, reason}
1776
  _validateParameters(parameters = [], declareParameterMode = false) {
×
1777
    for (const parameter of parameters) {
1,218✔
1778
      // detect invalid parameter
1779
      try {
1,218✔
1780
        parameter.validate();
1,218✔
1781
      } catch {
1782
        return {
×
1783
          successful: false,
1784
          reason: `Invalid ${parameter.name}`,
1785
        };
1786
      }
1787

1788
      // detect undeclared parameter
1789
      if (!this.hasParameterDescriptor(parameter.name)) {
1,218!
1790
        return {
×
1791
          successful: false,
1792
          reason: `Parameter ${parameter.name} has not been declared`,
1793
        };
1794
      }
1795

1796
      // detect readonly parameter that can not be updated
1797
      const descriptor = this.getParameterDescriptor(parameter.name);
1,218✔
1798
      if (!declareParameterMode && descriptor.readOnly) {
1,218!
1799
        return {
×
1800
          successful: false,
1801
          reason: `Parameter ${parameter.name} is readonly`,
1802
        };
1803
      }
1804

1805
      // validate parameter against descriptor if not an undeclare action
1806
      if (parameter.type != ParameterType.PARAMETER_NOT_SET) {
1,218✔
1807
        try {
1,217✔
1808
          descriptor.validateParameter(parameter);
1,217✔
1809
        } catch {
1810
          return {
×
1811
            successful: false,
1812
            reason: `Parameter ${parameter.name} does not  readonly`,
1813
          };
1814
        }
1815
      }
1816
    }
1817

1818
    return {
1,218✔
1819
      successful: true,
1820
      reason: null,
1821
    };
1822
  }
1823

1824
  // Get a Map(nodeName->Parameter[]) of CLI parameter args that
1825
  // apply to 'this' node, .e.g., -p mynode:foo:=bar -p hello:=world
1826
  _getNativeParameterOverrides() {
1827
    const overrides = new Map();
591✔
1828

1829
    // Get native parameters from rcl context->global_arguments.
1830
    // rclnodejs returns an array of objects, 1 for each node e.g., -p my_node:foo:=bar,
1831
    // and a node named '/**' for global parameter rules,
1832
    // i.e., does not include a node identifier, e.g., -p color:=red
1833
    // {
1834
    //   name: string // node name
1835
    //   parameters[] = {
1836
    //     name: string
1837
    //     type: uint
1838
    //     value: object
1839
    // }
1840
    const cliParamOverrideData = rclnodejs.getParameterOverrides(
591✔
1841
      this.context.handle
1842
    );
1843

1844
    // convert native CLI parameterOverrides to Map<nodeName,Array<ParameterOverride>>
1845
    const cliParamOverrides = new Map();
591✔
1846
    if (cliParamOverrideData) {
591✔
1847
      for (let nodeParamData of cliParamOverrideData) {
8✔
1848
        const nodeName = nodeParamData.name;
12✔
1849
        const nodeParamOverrides = [];
12✔
1850
        for (let paramData of nodeParamData.parameters) {
12✔
1851
          const paramOverride = new Parameter(
17✔
1852
            paramData.name,
1853
            paramData.type,
1854
            paramData.value
1855
          );
1856
          nodeParamOverrides.push(paramOverride);
17✔
1857
        }
1858
        cliParamOverrides.set(nodeName, nodeParamOverrides);
12✔
1859
      }
1860
    }
1861

1862
    // collect global CLI global parameters, name == /**
1863
    let paramOverrides = cliParamOverrides.get('/**'); // array of ParameterOverrides
591✔
1864
    if (paramOverrides) {
591✔
1865
      for (const parameter of paramOverrides) {
5✔
1866
        overrides.set(parameter.name, parameter);
6✔
1867
      }
1868
    }
1869

1870
    // merge CLI node parameterOverrides with global parameterOverrides, replace existing
1871
    paramOverrides = cliParamOverrides.get(this.name()); // array of ParameterOverrides
591✔
1872
    if (paramOverrides) {
591✔
1873
      for (const parameter of paramOverrides) {
5✔
1874
        overrides.set(parameter.name, parameter);
7✔
1875
      }
1876
    }
1877

1878
    return overrides;
591✔
1879
  }
1880

1881
  /**
1882
   * Invokes the callback with a raw message of the given type. After the callback completes
1883
   * the message will be destroyed.
1884
   * @param {function} Type - Message type to create.
1885
   * @param {function} callback - Callback to invoke. First parameter will be the raw message,
1886
   * and the second is a function to retrieve the deserialized message.
1887
   * @returns {undefined}
1888
   */
1889
  _runWithMessageType(Type, callback) {
1890
    let message = new Type();
689✔
1891

1892
    callback(message.toRawROS(), () => {
689✔
1893
      let result = new Type();
555✔
1894
      result.deserialize(message.refObject);
555✔
1895

1896
      return result;
555✔
1897
    });
1898

1899
    Type.destroyRawROS(message);
689✔
1900
  }
1901

1902
  _addActionClient(actionClient) {
1903
    this._actionClients.push(actionClient);
41✔
1904
    this.syncHandles();
41✔
1905
  }
1906

1907
  _addActionServer(actionServer) {
1908
    this._actionServers.push(actionServer);
41✔
1909
    this.syncHandles();
41✔
1910
  }
1911

1912
  _getValidatedTopic(topicName, noDemangle) {
1913
    if (noDemangle) {
5!
1914
      return topicName;
×
1915
    }
1916
    const fqTopicName = rclnodejs.expandTopicName(
5✔
1917
      topicName,
1918
      this.name(),
1919
      this.namespace()
1920
    );
1921
    validateFullTopicName(fqTopicName);
5✔
1922
    return rclnodejs.remapTopicName(this.handle, fqTopicName);
5✔
1923
  }
1924
}
1925

1926
/**
1927
 * Create an Options instance initialized with default values.
1928
 * @returns {Options} - The new initialized instance.
1929
 * @static
1930
 * @example
1931
 * {
1932
 *   enableTypedArray: true,
1933
 *   isRaw: false,
1934
 *   qos: QoS.profileDefault,
1935
 *   contentFilter: undefined,
1936
 *   serializationMode: 'default',
1937
 * }
1938
 */
1939
Node.getDefaultOptions = function () {
26✔
1940
  return {
5,613✔
1941
    enableTypedArray: true,
1942
    isRaw: false,
1943
    qos: QoS.profileDefault,
1944
    contentFilter: undefined,
1945
    serializationMode: 'default',
1946
  };
1947
};
1948

1949
module.exports = Node;
26✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc