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

RobotWebTools / rclnodejs / 22702850080

05 Mar 2026 04:49AM UTC coverage: 85.853% (+0.4%) from 85.481%
22702850080

Pull #1410

github

web-flow
Merge 9122fb393 into 55ec8f0cd
Pull Request #1410: Fix: resolve logic bugs, resource leaks, and error handling issues in JavaScript lib/

1384 of 1750 branches covered (79.09%)

Branch coverage included in aggregate %.

12 of 13 new or added lines in 6 files covered. (92.31%)

4 existing lines in 2 files now uncovered.

2864 of 3198 relevant lines covered (89.56%)

460.67 hits per line

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

93.88
/lib/time_source.js
1
// Copyright (c) 2018 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 { Clock, ROSClock } = require('./clock.js');
26✔
19
const { ClockType } = Clock;
26✔
20
const { Parameter, ParameterType } = require('./parameter.js');
26✔
21
const Time = require('./time.js');
26✔
22
const { TypeValidationError, OperationError } = require('./errors.js');
26✔
23

24
const USE_SIM_TIME_PARAM = 'use_sim_time';
26✔
25
const CLOCK_TOPIC = '/clock';
26✔
26

27
/**
28
 * @class - Class representing a TimeSource in ROS
29
 */
30

31
class TimeSource {
32
  /**
33
   * Create a TimeSource.
34
   * @param {Node} node - The node to be attached.
35
   */
36
  constructor(node) {
37
    this._node = node;
819✔
38
    this._associatedClocks = [];
819✔
39
    this._clockSubscription = undefined;
819✔
40
    this._lastTimeSet = new Time(0n, 0n, ClockType.ROS_TIME);
819✔
41
    this._isRosTimeActive = false;
819✔
42

43
    if (this._node) {
819✔
44
      this.attachNode(this._node);
817✔
45
    }
46
  }
47

48
  /**
49
   * Return status that whether the ROS time is active.
50
   * @name TimeSource#get:isRosTimeActive
51
   * @function
52
   * @return {boolean} Return true if the time is active, otherwise return false.
53
   */
54

55
  get isRosTimeActive() {
56
    return this._isRosTimeActive;
831✔
57
  }
58

59
  /**
60
   * Set the status of time.
61
   * @param {boolean} enabled - Set the ROS time to be active if enabled is true.
62
   * @name TimeSource#set:isRosTimeActive
63
   * @function
64
   * @return {undefined}
65
   */
66

67
  set isRosTimeActive(enabled) {
68
    if (this._isRosTimeActive === enabled) return;
6✔
69

70
    this._isRosTimeActive = enabled;
5✔
71
    this._associatedClocks.forEach((clock) => {
5✔
72
      clock.isRosTimeActive = enabled;
×
73
    });
74
    if (enabled) {
5✔
75
      this._subscribeToClockTopic();
4✔
76
    }
77
  }
78

79
  /**
80
   * Attach the clock to a Node object.
81
   * @param {Node} node - The node to be attached.
82
   * @return {undefined}
83
   */
84
  attachNode(node) {
85
    if (!(node instanceof rclnodejs.ShadowNode)) {
822✔
86
      throw new TypeValidationError('node', node, 'Node', {
1✔
87
        entityType: 'time source',
88
      });
89
    }
90

91
    if (this._node) {
821✔
92
      this.detachNode();
818✔
93
    }
94

95
    this._node = node;
821✔
96

97
    if (!node.hasParameter(USE_SIM_TIME_PARAM)) {
821✔
98
      node.declareParameter(
802✔
99
        new Parameter(USE_SIM_TIME_PARAM, ParameterType.PARAMETER_BOOL, false)
100
      );
101
    }
102

103
    const useSimTimeParam = node.getParameter(USE_SIM_TIME_PARAM);
821✔
104
    if (useSimTimeParam.type !== ParameterType.PARAMETER_NOT_SET) {
821!
105
      if (useSimTimeParam.type === ParameterType.PARAMETER_BOOL) {
821✔
106
        this._isRosTimeActive = useSimTimeParam.value;
819✔
107
      } else {
108
        node
2✔
109
          .getLogger()
110
          .error(
111
            `Invalid type for parameter ${USE_SIM_TIME_PARAM} ${useSimTimeParam.type} should be bool`
112
          );
113
      }
114
    } else {
UNCOV
115
      node
×
116
        .getLogger()
117
        .debug(
118
          `${USE_SIM_TIME_PARAM}' parameter not set, using wall time by default`
119
        );
120
    }
121

122
    if (this.isRosTimeActive) {
821✔
123
      this._subscribeToClockTopic();
6✔
124
    }
125

126
    node.addOnSetParametersCallback(this.onParameterEvent.bind(this));
821✔
127
  }
128

129
  /**
130
   * Detach the node which the clock have attached.
131
   * @return {undefined}
132
   */
133
  detachNode() {
134
    if (this._clockSubscription) {
821✔
135
      if (!this._node) {
3✔
136
        throw new OperationError(
1✔
137
          'Unable to destroy previously created clock subscription',
138
          {
139
            code: 'NO_NODE_ATTACHED',
140
            entityType: 'time source',
141
          }
142
        );
143
      }
144
      this._node.destroySubscription(this._clockSubscription);
2✔
145
    }
146
    this._clockSubscription = undefined;
820✔
147
    this._node = undefined;
820✔
148
  }
149

150
  /**
151
   * Attach the clock to a TimeSource object.
152
   * @param {Clock} clock - The node to be attached.
153
   * @return {undefined}
154
   */
155
  attachClock(clock) {
156
    if (!(clock instanceof ROSClock)) {
815✔
157
      throw new TypeValidationError('clock', clock, 'ROSClock', {
2✔
158
        entityType: 'time source',
159
      });
160
    }
161
    clock.rosTimeOverride = this._lastTimeSet;
813✔
162
    clock.isRosTimeActive = this._isRosTimeActive;
813✔
163
    this._associatedClocks.push(clock);
813✔
164
  }
165

166
  _clockCallback(msg) {
167
    this._lastTimeSet = Time.fromMsg(msg.clock);
10✔
168
    this._associatedClocks.forEach((clock) => {
10✔
169
      clock.rosTimeOverride = this._lastTimeSet;
10✔
170
    });
171
  }
172

173
  _subscribeToClockTopic() {
174
    if (!this._clockSubscription && this._node) {
10!
175
      this._clockSubscription = this._node.createSubscription(
10✔
176
        'rosgraph_msgs/msg/Clock',
177
        CLOCK_TOPIC,
178
        this._clockCallback.bind(this)
179
      );
180
    }
181
  }
182

183
  onParameterEvent(parameters = []) {
×
184
    for (const parameter of parameters) {
1,422✔
185
      if (parameter.name === USE_SIM_TIME_PARAM) {
1,422✔
186
        if (parameter.type === ParameterType.PARAMETER_BOOL) {
2✔
187
          this.isRosTimeActive = parameter.value;
1✔
188
        } else if (this._node) {
1!
189
          this._node
1✔
190
            .getLogger()
191
            .error(
192
              `${USE_SIM_TIME_PARAM} parameter set to something besides a bool`
193
            );
194
        }
195

196
        break;
2✔
197
      }
198
    }
199

200
    return {
1,422✔
201
      successful: true,
202
      reason: '',
203
    };
204
  }
205
}
206

207
module.exports = TimeSource;
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