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

RobotWebTools / rclnodejs / 20742259610

06 Jan 2026 08:04AM UTC coverage: 80.626% (-2.2%) from 82.843%
20742259610

push

github

minggangw
Add ClockEvent support (#1354)

This PR adds comprehensive ClockEvent support to rclnodejs, enabling clock-based sleep functionality for STEADY_TIME, SYSTEM_TIME, and ROS_TIME clocks.

### New Features

**ClockEvent Class**
- Thread-safe event synchronization for clock-based waiting
- Support for steady, system, and ROS clock types
- Async worker pattern for non-blocking sleep operations
- Clock epoch synchronization between RCL and std::chrono

**Clock Sleep Methods**
- `Clock.sleepUntil(until, context)` - Sleep until absolute time point
- `Clock.sleepFor(duration, context)` - Sleep for specified duration
- Clock jump callbacks to wake on time changes
- Context-aware early wakeup on shutdown
- Detects ROS time activation/deactivation

**ClockChange Enum**
- `ROS_TIME_NO_CHANGE`, `ROS_TIME_ACTIVATED`, `ROS_TIME_DEACTIVATED`, `SYSTEM_TIME_NO_CHANGE`
- Used in clock jump callback notifications

### Critical Fixes

**BigInt Precision Loss Prevention**
- Added lossless conversion checks for nanosecond timestamps
- Prevents silent data corruption when converting BigInt to int64_t

**Missing Module Imports**
- Fixed missing Context, ClockEvent, and ClockChange imports in clock.js

### Test Coverage

- **test-clock-event.js** - Basic ClockEvent operations (4 tests)
- **test-clock-sleep.js** - Sleep methods for all clock types (11 tests)
  - Includes comprehensive ROS time active scenario with TimeSource + simulated clock messages
- **test-clock-change.js** - ClockChange enum and integration tests (11 tests)

**Results**: 1055 passing, 6 pending

### Files Changed

**Added**: 
- `src/clock_event.{cpp,hpp}`, clock_event.js, clock_change.js
- `types/clock_event.d.ts`, `types/clock_change.d.ts`
- `test/test-clock-{event,sleep,change}.js`

**Modified**:
- clock.js - Added sleep methods and imports
- `types/clock.d.ts`, index.d.ts - Added type definitions
- `binding.gyp`, `index.js`, `src/addon.cpp` - Registered bindings

**Impact**: +1397 lines, fully backward co... (continued)

1268 of 1753 branches covered (72.33%)

Branch coverage included in aggregate %.

40 of 42 new or added lines in 3 files covered. (95.24%)

122 existing lines in 10 files now uncovered.

2727 of 3202 relevant lines covered (85.17%)

465.68 hits per line

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

87.69
/lib/validator.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
const { TypeValidationError, NameValidationError } = require('./errors.js');
26✔
19

20
/**
21
 * An object - Representing a validator in ROS.
22
 * @exports validator
23
 */
24
let validator = {
26✔
25
  _createErrorFromValidation: function (result, nameValue, nameType) {
26
    return new NameValidationError(nameValue, nameType, result[0], result[1]);
10✔
27
  },
28

29
  /**
30
   * Validate a given topic or service name, and throw an error if invalid.
31
   * @param {string} topic - The name of topic/service. Must be fully-qualified and already expanded.
32
   * @returns {true} Always returns true if valid.
33
   * @throws {TypeValidationError} If topic is not a string.
34
   * @throws {NameValidationError} If the topic name is invalid.
35
   */
36
  validateFullTopicName(topic) {
37
    if (typeof topic !== 'string') {
11!
UNCOV
38
      throw new TypeValidationError('topic', topic, 'string');
×
39
    }
40

41
    let result = rclnodejs.validateFullTopicName(topic);
11✔
42
    if (result === null) {
11✔
43
      return true;
9✔
44
    }
45
    throw this._createErrorFromValidation(result, topic, 'topic');
2✔
46
  },
47

48
  /**
49
   * Check if a fully-qualified topic name is valid without throwing.
50
   * @param {string} topic - The name of topic/service. Must be fully-qualified and already expanded.
51
   * @returns {boolean} True if valid, false otherwise.
52
   */
53
  isValidFullTopicName(topic) {
54
    if (typeof topic !== 'string') {
8✔
55
      return false;
3✔
56
    }
57
    return rclnodejs.validateFullTopicName(topic) === null;
5✔
58
  },
59

60
  /**
61
   * Validate a given node name, and throw an error if invalid.
62
   * @param {string} name - The name of node.
63
   * @returns {true} Always returns true if valid.
64
   * @throws {TypeValidationError} If name is not a string.
65
   * @throws {NameValidationError} If the node name is invalid.
66
   */
67
  validateNodeName(name) {
68
    if (typeof name !== 'string') {
109!
UNCOV
69
      throw new TypeValidationError('name', name, 'string');
×
70
    }
71

72
    let result = rclnodejs.validateNodeName(name);
109✔
73
    if (result === null) {
109✔
74
      return true;
105✔
75
    }
76
    throw this._createErrorFromValidation(result, name, 'node');
4✔
77
  },
78

79
  /**
80
   * Check if a node name is valid without throwing.
81
   * @param {string} name - The name of node.
82
   * @returns {boolean} True if valid, false otherwise.
83
   */
84
  isValidNodeName(name) {
85
    if (typeof name !== 'string') {
7✔
86
      return false;
2✔
87
    }
88
    return rclnodejs.validateNodeName(name) === null;
5✔
89
  },
90

91
  /**
92
   * Validate a given topic or service name, and throw an error if invalid.
93
   * @param {string} topic - The name of topic/service. Does not have to be fully-qualified.
94
   * @returns {true} Always returns true if valid.
95
   * @throws {TypeValidationError} If topic is not a string.
96
   * @throws {NameValidationError} If the topic name is invalid.
97
   */
98
  validateTopicName(topic) {
99
    if (typeof topic !== 'string') {
5!
UNCOV
100
      throw new TypeValidationError('topic', topic, 'string');
×
101
    }
102

103
    let result = rclnodejs.validateTopicName(topic);
5✔
104
    if (result === null) {
5✔
105
      return true;
3✔
106
    }
107
    throw this._createErrorFromValidation(result, topic, 'topic');
2✔
108
  },
109

110
  /**
111
   * Check if a topic name is valid without throwing.
112
   * @param {string} topic - The name of topic/service. Does not have to be fully-qualified.
113
   * @returns {boolean} True if valid, false otherwise.
114
   */
115
  isValidTopicName(topic) {
116
    if (typeof topic !== 'string') {
8✔
117
      return false;
1✔
118
    }
119
    return rclnodejs.validateTopicName(topic) === null;
7✔
120
  },
121

122
  /**
123
   * Validate a given namespace, and throw an error if invalid.
124
   * @param {string} namespace - The namespace to be validated.
125
   * @returns {true} Always returns true if valid.
126
   * @throws {TypeValidationError} If namespace is not a string.
127
   * @throws {NameValidationError} If the namespace is invalid.
128
   */
129
  validateNamespace(namespace) {
130
    if (typeof namespace !== 'string') {
4!
UNCOV
131
      throw new TypeValidationError('namespace', namespace, 'string');
×
132
    }
133

134
    let result = rclnodejs.validateNamespace(namespace);
4✔
135
    if (result === null) {
4✔
136
      return true;
2✔
137
    }
138
    throw this._createErrorFromValidation(result, namespace, 'namespace');
2✔
139
  },
140

141
  /**
142
   * Check if a namespace is valid without throwing.
143
   * @param {string} namespace - The namespace to be validated.
144
   * @returns {boolean} True if valid, false otherwise.
145
   */
146
  isValidNamespace(namespace) {
147
    if (typeof namespace !== 'string') {
8✔
148
      return false;
2✔
149
    }
150
    return rclnodejs.validateNamespace(namespace) === null;
6✔
151
  },
152
};
153

154
module.exports = validator;
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