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

RobotWebTools / rclnodejs / 26429200312

26 May 2026 02:41AM UTC coverage: 85.461% (-0.01%) from 85.474%
26429200312

Pull #1520

github

web-flow
Merge 74c971c93 into d78f9aaf7
Pull Request #1520: Add publisher/subscription_event_type_is_supported

1838 of 2335 branches covered (78.72%)

Branch coverage included in aggregate %.

8 of 10 new or added lines in 1 file covered. (80.0%)

10 existing lines in 1 file now uncovered.

3617 of 4048 relevant lines covered (89.35%)

395.64 hits per line

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

87.77
/lib/event_handler.js
1
// Copyright (c) 2025, The Robot Web Tools Contributors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
'use strict';
16

17
const rclnodejs = require('./native_loader.js');
27✔
18
const DistroUtils = require('./distro.js');
27✔
19
const { OperationError } = require('./errors.js');
27✔
20
const Entity = require('./entity.js');
27✔
21

22
/**
23
 * Enumeration for PublisherEventCallbacks event types.
24
 * @enum {number}
25
 */
26
const PublisherEventType = {
27✔
27
  /** @member {number} */
28
  PUBLISHER_OFFERED_DEADLINE_MISSED: 0,
29
  /** @member {number} */
30
  PUBLISHER_LIVELINESS_LOST: 1,
31
  /** @member {number} */
32
  PUBLISHER_OFFERED_INCOMPATIBLE_QOS: 2,
33
  /** @member {number} */
34
  PUBLISHER_INCOMPATIBLE_TYPE: 3,
35
  /** @member {number} */
36
  PUBLISHER_MATCHED: 4,
37
};
38

39
/**
40
 * Enumeration for SubscriptionEventCallbacks event types.
41
 * @enum {number}
42
 */
43
const SubscriptionEventType = {
27✔
44
  /** @member {number} */
45
  SUBSCRIPTION_REQUESTED_DEADLINE_MISSED: 0,
46
  /** @member {number} */
47
  SUBSCRIPTION_LIVELINESS_CHANGED: 1,
48
  /** @member {number} */
49
  SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS: 2,
50
  /** @member {number} */
51
  SUBSCRIPTION_MESSAGE_LOST: 3,
52
  /** @member {number} */
53
  SUBSCRIPTION_INCOMPATIBLE_TYPE: 4,
54
  /** @member {number} */
55
  SUBSCRIPTION_MATCHED: 5,
56
};
57

58
/**
59
 * Check if a publisher event type is supported by the active RMW implementation.
60
 *
61
 * Only available in ROS 2 Rolling and later, where the underlying rcl API
62
 * (`rcl_publisher_event_type_is_supported`) is provided.
63
 *
64
 * @param {number} eventType - A {@link PublisherEventType} value.
65
 * @return {boolean} True if the event type is supported by the active RMW
66
 *   implementation, false otherwise.
67
 * @throws {OperationError} if invoked on a ROS distro older than Rolling, or
68
 *   if eventType is not a valid {@link PublisherEventType} value.
69
 */
70
function isPublisherEventTypeSupported(eventType) {
71
  if (typeof rclnodejs.isPublisherEventTypeSupported !== 'function') {
8!
NEW
72
    throw new OperationError(
×
73
      'isPublisherEventTypeSupported is only available in ROS 2 Rolling and later',
74
      {
75
        code: 'UNSUPPORTED_ROS_VERSION',
76
        entityType: 'publisher event type',
77
        details: {
78
          requiredVersion: 'rolling',
79
          currentVersion: DistroUtils.getDistroId(),
80
        },
81
      }
82
    );
83
  }
84
  if (
8✔
85
    typeof eventType !== 'number' ||
15✔
86
    !Object.values(PublisherEventType).includes(eventType)
87
  ) {
88
    throw new OperationError(`Invalid PublisherEventType value: ${eventType}`, {
2✔
89
      code: 'INVALID_ARGUMENT',
90
      entityType: 'publisher event type',
91
    });
92
  }
93
  return rclnodejs.isPublisherEventTypeSupported(eventType);
6✔
94
}
95

96
/**
97
 * Check if a subscription event type is supported by the active RMW implementation.
98
 *
99
 * Only available in ROS 2 Rolling and later, where the underlying rcl API
100
 * (`rcl_subscription_event_type_is_supported`) is provided.
101
 *
102
 * @param {number} eventType - A {@link SubscriptionEventType} value.
103
 * @return {boolean} True if the event type is supported by the active RMW
104
 *   implementation, false otherwise.
105
 * @throws {OperationError} if invoked on a ROS distro older than Rolling, or
106
 *   if eventType is not a valid {@link SubscriptionEventType} value.
107
 */
108
function isSubscriptionEventTypeSupported(eventType) {
109
  if (typeof rclnodejs.isSubscriptionEventTypeSupported !== 'function') {
9!
NEW
110
    throw new OperationError(
×
111
      'isSubscriptionEventTypeSupported is only available in ROS 2 Rolling and later',
112
      {
113
        code: 'UNSUPPORTED_ROS_VERSION',
114
        entityType: 'subscription event type',
115
        details: {
116
          requiredVersion: 'rolling',
117
          currentVersion: DistroUtils.getDistroId(),
118
        },
119
      }
120
    );
121
  }
122
  if (
9✔
123
    typeof eventType !== 'number' ||
17✔
124
    !Object.values(SubscriptionEventType).includes(eventType)
125
  ) {
126
    throw new OperationError(
2✔
127
      `Invalid SubscriptionEventType value: ${eventType}`,
128
      {
129
        code: 'INVALID_ARGUMENT',
130
        entityType: 'subscription event type',
131
      }
132
    );
133
  }
134
  return rclnodejs.isSubscriptionEventTypeSupported(eventType);
7✔
135
}
136

137
class EventHandler extends Entity {
138
  constructor(handle, callback, eventType, eventTypeName) {
139
    super(handle, null, null);
14✔
140
    this._callback = callback;
14✔
141
    this._eventType = eventType;
14✔
142
    this._eventTypeName = eventTypeName;
14✔
143
  }
144

145
  takeData() {
146
    const data = rclnodejs.takeEvent(this._handle, {
11✔
147
      [this._eventTypeName]: this._eventType,
148
    });
149
    if (this._callback) {
11!
150
      this._callback(data);
11✔
151
    }
152
  }
153
}
154

155
/**
156
 * @class - Class representing a ROS 2 PublisherEventCallbacks
157
 * @hideconstructor
158
 */
159
class PublisherEventCallbacks {
160
  constructor() {
161
    if (DistroUtils.getDistroId() < DistroUtils.getDistroId('jazzy')) {
7✔
162
      throw new OperationError(
1✔
163
        'PublisherEventCallbacks is only available in ROS 2 Jazzy and later',
164
        {
165
          code: 'UNSUPPORTED_ROS_VERSION',
166
          entityType: 'publisher event callbacks',
167
          details: {
168
            requiredVersion: 'jazzy',
169
            currentVersion: DistroUtils.getDistroId(),
170
          },
171
        }
172
      );
173
    }
174
    this._deadline = null;
6✔
175
    this._incompatible_qos = null;
6✔
176
    this._liveliness = null;
6✔
177
    this._incompatible_type = null;
6✔
178
    this._matched = null;
6✔
179
    this._eventHandlers = [];
6✔
180
  }
181

182
  /**
183
   * Set deadline missed callback.
184
   * @param {function} callback - The callback function to be called.
185
   */
186
  set deadline(callback) {
187
    this._deadline = callback;
4✔
188
  }
189

190
  /**
191
   * Get deadline missed callback.
192
   * @return {function} - The callback function.
193
   */
194
  get deadline() {
195
    return this._deadline;
1✔
196
  }
197

198
  /**
199
   * Set incompatible QoS callback.
200
   * @param {function} callback - The callback function to be called.
201
   */
202
  set incompatibleQos(callback) {
203
    this._incompatible_qos = callback;
2✔
204
  }
205

206
  /**
207
   * Get incompatible QoS callback.
208
   * @return {function} - The callback function.
209
   */
210
  get incompatibleQos() {
211
    return this._incompatible_qos;
1✔
212
  }
213

214
  /**
215
   * Set liveliness lost callback.
216
   * @param {function} callback - The callback function to be called.
217
   */
218
  set liveliness(callback) {
219
    this._liveliness = callback;
2✔
220
  }
221

222
  /**
223
   * Get liveliness lost callback.
224
   * @return {function} - The callback function.
225
   */
226
  get liveliness() {
227
    return this._liveliness;
1✔
228
  }
229

230
  /**
231
   * Set incompatible type callback.
232
   * @param {function} callback - The callback function to be called.
233
   */
234
  set incompatibleType(callback) {
235
    this._incompatible_type = callback;
1✔
236
  }
237

238
  /**
239
   * Get incompatible type callback.
240
   * @return {function} - The callback function.
241
   */
242
  get incompatibleType() {
243
    return this._incompatible_type;
1✔
244
  }
245

246
  /**
247
   * Set matched callback.
248
   * @param {function} callback - The callback function to be called.
249
   */
250
  set matched(callback) {
251
    this._matched = callback;
2✔
252
  }
253

254
  /**
255
   * Get matched callback.
256
   * @return {function} - The callback function.
257
   */
258
  get matched() {
259
    return this._matched;
1✔
260
  }
261

262
  createEventHandlers(publisherHandle) {
263
    if (this._deadline) {
4✔
264
      const deadlineHandle = rclnodejs.createPublisherEventHandle(
3✔
265
        publisherHandle,
266
        PublisherEventType.PUBLISHER_OFFERED_DEADLINE_MISSED
267
      );
268
      this._eventHandlers.push(
3✔
269
        new EventHandler(
270
          deadlineHandle,
271
          this._deadline,
272
          PublisherEventType.PUBLISHER_OFFERED_DEADLINE_MISSED,
273
          'publisher_event_type'
274
        )
275
      );
276
    }
277

278
    if (this._incompatible_qos) {
4✔
279
      const incompatibleQosHandle = rclnodejs.createPublisherEventHandle(
1✔
280
        publisherHandle,
281
        PublisherEventType.PUBLISHER_OFFERED_INCOMPATIBLE_QOS
282
      );
283
      this._eventHandlers.push(
1✔
284
        new EventHandler(
285
          incompatibleQosHandle,
286
          this._incompatible_qos,
287
          PublisherEventType.PUBLISHER_OFFERED_INCOMPATIBLE_QOS,
288
          'publisher_event_type'
289
        )
290
      );
291
    }
292

293
    if (this._liveliness) {
4✔
294
      const livelinessHandle = rclnodejs.createPublisherEventHandle(
1✔
295
        publisherHandle,
296
        PublisherEventType.PUBLISHER_LIVELINESS_LOST
297
      );
298
      this._eventHandlers.push(
1✔
299
        new EventHandler(
300
          livelinessHandle,
301
          this._liveliness,
302
          PublisherEventType.PUBLISHER_LIVELINESS_LOST,
303
          'publisher_event_type'
304
        )
305
      );
306
    }
307

308
    if (this._incompatible_type) {
4!
UNCOV
309
      const incompatibleTypeHandle = rclnodejs.createPublisherEventHandle(
×
310
        publisherHandle,
311
        PublisherEventType.PUBLISHER_INCOMPATIBLE_TYPE
312
      );
UNCOV
313
      this._eventHandlers.push(
×
314
        new EventHandler(
315
          incompatibleTypeHandle,
316
          this._incompatible_type,
317
          PublisherEventType.PUBLISHER_INCOMPATIBLE_TYPE,
318
          'publisher_event_type'
319
        )
320
      );
321
    }
322

323
    if (this._matched) {
4✔
324
      const matchedHandle = rclnodejs.createPublisherEventHandle(
1✔
325
        publisherHandle,
326
        PublisherEventType.PUBLISHER_MATCHED
327
      );
328
      this._eventHandlers.push(
1✔
329
        new EventHandler(
330
          matchedHandle,
331
          this._matched,
332
          PublisherEventType.PUBLISHER_MATCHED,
333
          'publisher_event_type'
334
        )
335
      );
336
    }
337

338
    return this._eventHandlers;
4✔
339
  }
340

341
  get eventHandlers() {
342
    return this._eventHandlers;
2✔
343
  }
344
}
345

346
/**
347
 * @class - Class representing a ROS 2 SubscriptionEventCallbacks
348
 * @hideconstructor
349
 */
350
class SubscriptionEventCallbacks {
351
  constructor() {
352
    if (DistroUtils.getDistroId() < DistroUtils.getDistroId('jazzy')) {
8✔
353
      throw new OperationError(
1✔
354
        'SubscriptionEventCallbacks is only available in ROS 2 Jazzy and later',
355
        {
356
          code: 'UNSUPPORTED_ROS_VERSION',
357
          entityType: 'subscription event callbacks',
358
          details: {
359
            requiredVersion: 'jazzy',
360
            currentVersion: DistroUtils.getDistroId(),
361
          },
362
        }
363
      );
364
    }
365
    this._deadline = null;
7✔
366
    this._incompatible_qos = null;
7✔
367
    this._liveliness = null;
7✔
368
    this._message_lost = null;
7✔
369
    this._incompatible_type = null;
7✔
370
    this._matched = null;
7✔
371
    this._eventHandlers = [];
7✔
372
  }
373

374
  /**
375
   * Set the callback for deadline missed event.
376
   * @param {function} callback - The callback function to be called.
377
   */
378
  set deadline(callback) {
379
    this._deadline = callback;
3✔
380
  }
381

382
  /**
383
   * Get the callback for deadline missed event.
384
   * @return {function} - The callback function.
385
   */
386
  get deadline() {
UNCOV
387
    return this._deadline;
×
388
  }
389

390
  /**
391
   * Set the callback for incompatible QoS event.
392
   * @param {function} callback - The callback function to be called.
393
   */
394
  set incompatibleQos(callback) {
395
    this._incompatible_qos = callback;
1✔
396
  }
397

398
  /**
399
   * Get the callback for incompatible QoS event.
400
   * @return {function} - The callback function.
401
   */
402
  get incompatibleQos() {
UNCOV
403
    return this._incompatible_qos;
×
404
  }
405

406
  /**
407
   * Set the callback for liveliness changed event.
408
   * @param {function} callback - The callback function to be called.
409
   */
410
  set liveliness(callback) {
411
    this._liveliness = callback;
2✔
412
  }
413

414
  /**
415
   * Get the callback for liveliness changed event.
416
   * @return {function} - The callback function.
417
   */
418
  get liveliness() {
UNCOV
419
    return this._liveliness;
×
420
  }
421

422
  /**
423
   * Set the callback for message lost event.
424
   * @param {function} callback - The callback function to be called.
425
   */
426
  set messageLost(callback) {
427
    this._message_lost = callback;
2✔
428
  }
429

430
  /**
431
   * Get the callback for message lost event.
432
   * @return {function} - The callback function.
433
   */
434
  get messageLost() {
435
    return this._message_lost;
1✔
436
  }
437

438
  /**
439
   * Set the callback for incompatible type event.
440
   * @param {function} callback - The callback function to be called.
441
   */
442
  set incompatibleType(callback) {
UNCOV
443
    this._incompatible_type = callback;
×
444
  }
445

446
  /**
447
   * Get the callback for incompatible type event.
448
   * @return {function} - The callback function.
449
   */
450
  get incompatibleType() {
UNCOV
451
    return this._incompatible_type;
×
452
  }
453

454
  /**
455
   * Set the callback for matched event.
456
   * @param {function} callback - The callback function to be called.
457
   */
458
  set matched(callback) {
459
    this._matched = callback;
1✔
460
  }
461

462
  /**
463
   * Get the callback for matched event.
464
   * @return {function} - The callback function.
465
   */
466
  get matched() {
UNCOV
467
    return this._matched;
×
468
  }
469

470
  createEventHandlers(subscriptionHandle) {
471
    if (this._deadline) {
5✔
472
      const deadlineHandle = rclnodejs.createSubscriptionEventHandle(
3✔
473
        subscriptionHandle,
474
        SubscriptionEventType.SUBSCRIPTION_REQUESTED_DEADLINE_MISSED
475
      );
476
      this._eventHandlers.push(
3✔
477
        new EventHandler(
478
          deadlineHandle,
479
          this._deadline,
480
          SubscriptionEventType.SUBSCRIPTION_REQUESTED_DEADLINE_MISSED,
481
          'subscription_event_type'
482
        )
483
      );
484
    }
485

486
    if (this._incompatible_qos) {
5✔
487
      const incompatibleQosHandle = rclnodejs.createSubscriptionEventHandle(
1✔
488
        subscriptionHandle,
489
        SubscriptionEventType.SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS
490
      );
491
      this._eventHandlers.push(
1✔
492
        new EventHandler(
493
          incompatibleQosHandle,
494
          this._incompatible_qos,
495
          SubscriptionEventType.SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS,
496
          'subscription_event_type'
497
        )
498
      );
499
    }
500

501
    if (this._liveliness) {
5✔
502
      const livelinessHandle = rclnodejs.createSubscriptionEventHandle(
2✔
503
        subscriptionHandle,
504
        SubscriptionEventType.SUBSCRIPTION_LIVELINESS_CHANGED
505
      );
506
      this._eventHandlers.push(
2✔
507
        new EventHandler(
508
          livelinessHandle,
509
          this._liveliness,
510
          SubscriptionEventType.SUBSCRIPTION_LIVELINESS_CHANGED,
511
          'subscription_event_type'
512
        )
513
      );
514
    }
515

516
    if (this._message_lost) {
5✔
517
      const messageLostHandle = rclnodejs.createSubscriptionEventHandle(
1✔
518
        subscriptionHandle,
519
        SubscriptionEventType.SUBSCRIPTION_MESSAGE_LOST
520
      );
521
      this._eventHandlers.push(
1✔
522
        new EventHandler(
523
          messageLostHandle,
524
          this._message_lost,
525
          SubscriptionEventType.SUBSCRIPTION_MESSAGE_LOST,
526
          'subscription_event_type'
527
        )
528
      );
529
    }
530

531
    if (this._incompatible_type) {
5!
UNCOV
532
      const incompatibleTypeHandle = rclnodejs.createSubscriptionEventHandle(
×
533
        subscriptionHandle,
534
        SubscriptionEventType.SUBSCRIPTION_INCOMPATIBLE_TYPE
535
      );
UNCOV
536
      this._eventHandlers.push(
×
537
        new EventHandler(
538
          incompatibleTypeHandle,
539
          this._incompatible_type,
540
          SubscriptionEventType.SUBSCRIPTION_INCOMPATIBLE_TYPE,
541
          'subscription_event_type'
542
        )
543
      );
544
    }
545

546
    if (this._matched) {
5✔
547
      const matchedHandle = rclnodejs.createSubscriptionEventHandle(
1✔
548
        subscriptionHandle,
549
        SubscriptionEventType.SUBSCRIPTION_MATCHED
550
      );
551
      this._eventHandlers.push(
1✔
552
        new EventHandler(
553
          matchedHandle,
554
          this._matched,
555
          SubscriptionEventType.SUBSCRIPTION_MATCHED,
556
          'subscription_event_type'
557
        )
558
      );
559
    }
560

561
    return this._eventHandlers;
5✔
562
  }
563
}
564

565
module.exports = {
27✔
566
  PublisherEventCallbacks,
567
  PublisherEventType,
568
  SubscriptionEventCallbacks,
569
  SubscriptionEventType,
570
  isPublisherEventTypeSupported,
571
  isSubscriptionEventTypeSupported,
572
};
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