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

RobotWebTools / rclnodejs / 14751311325

30 Apr 2025 09:26AM UTC coverage: 84.794% (-0.2%) from 85.01%
14751311325

push

github

minggangw
Add missing methods for graph (#1110)

This PR adds new graph-related methods to expose publisher and subscription information by topic. 

- Introduces native bindings and corresponding JavaScript wrappers for retrieving publishers and subscriptions info.  
- Adds test coverage in both TypeScript and JavaScript test files to verify the new functionality.  
- Updates build configuration and dependency lists to support the added functionality.

Fix: #1111

708 of 928 branches covered (76.29%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

5 existing lines in 1 file now uncovered.

1740 of 1959 relevant lines covered (88.82%)

1010.45 hits per line

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

71.88
/lib/client.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('bindings')('rclnodejs');
78✔
18
const DistroUtils = require('./distro.js');
78✔
19
const Entity = require('./entity.js');
78✔
20
const debug = require('debug')('rclnodejs:client');
78✔
21

22
/**
23
 * @class - Class representing a Client in ROS
24
 * @hideconstructor
25
 */
26

27
class Client extends Entity {
28
  constructor(handle, nodeHandle, serviceName, typeClass, options) {
29
    super(handle, typeClass, options);
138✔
30
    this._nodeHandle = nodeHandle;
138✔
31
    this._serviceName = serviceName;
138✔
32
    this._sequenceNumberToCallbackMap = new Map();
138✔
33
  }
34

35
  /**
36
   * This callback is called when a resopnse is sent back from service
37
   * @callback ResponseCallback
38
   * @param {Object} response - The response sent from the service
39
   * @see [Client.sendRequest]{@link Client#sendRequest}
40
   * @see [Node.createService]{@link Node#createService}
41
   * @see {@link Client}
42
   * @see {@link Service}
43
   */
44

45
  /**
46
   * Send the request and will be notified asynchronously if receiving the repsonse.
47
   * @param {object} request - The request to be submitted.
48
   * @param {ResponseCallback} callback - Thc callback function for receiving the server response.
49
   * @return {undefined}
50
   * @see {@link ResponseCallback}
51
   */
52
  sendRequest(request, callback) {
53
    if (typeof callback !== 'function') {
99!
54
      throw new TypeError('Invalid argument');
×
55
    }
56

57
    let requestToSend =
58
      request instanceof this._typeClass.Request
99✔
59
        ? request
60
        : new this._typeClass.Request(request);
61

62
    let rawRequest = requestToSend.serialize();
93✔
63
    let sequenceNumber = rclnodejs.sendRequest(this._handle, rawRequest);
93✔
64
    debug(`Client has sent a ${this._serviceName} request.`);
93✔
65
    this._sequenceNumberToCallbackMap.set(sequenceNumber, callback);
93✔
66
  }
67

68
  processResponse(sequenceNumber, response) {
69
    if (this._sequenceNumberToCallbackMap.has(sequenceNumber)) {
90!
70
      debug(`Client has received ${this._serviceName} response from service.`);
90✔
71
      let callback = this._sequenceNumberToCallbackMap.get(sequenceNumber);
90✔
72
      this._sequenceNumberToCallbackMap.delete(sequenceNumber);
90✔
73
      callback(response.toPlainObject(this.typedArrayEnabled));
90✔
74
    } else {
75
      debug(
×
76
        `Client has received an unexpected ${this._serviceName} with sequence number ${sequenceNumber}.`
77
      );
78
    }
79
  }
80

81
  /**
82
   * Checks if the service is available.
83
   * @return {boolean} true if the service is available.
84
   */
85
  isServiceServerAvailable() {
86
    return rclnodejs.serviceServerIsAvailable(this._nodeHandle, this.handle);
60✔
87
  }
88

89
  /**
90
   * Wait until the service server is available or a timeout is reached. This
91
   * function polls for the service state so it may not return as soon as the
92
   * service is available.
93
   * @param {number} timeout The maximum amount of time to wait for, if timeout
94
   * is `undefined` or `< 0`, this will wait indefinitely.
95
   * @return {Promise<boolean>} true if the service is available.
96
   */
97
  async waitForService(timeout = undefined) {
27✔
98
    let deadline = Infinity;
60✔
99
    if (timeout !== undefined && timeout >= 0) {
60✔
100
      deadline = Date.now() + timeout;
33✔
101
    }
102
    let waitMs = 5;
60✔
103
    let serviceAvailable = this.isServiceServerAvailable();
60✔
104
    while (!serviceAvailable && Date.now() < deadline) {
60!
UNCOV
105
      waitMs *= 2;
×
UNCOV
106
      waitMs = Math.min(waitMs, 1000);
×
UNCOV
107
      if (timeout !== undefined && timeout >= -1) {
×
108
        waitMs = Math.min(waitMs, deadline - Date.now());
×
109
      }
UNCOV
110
      await new Promise((resolve) => setTimeout(resolve, waitMs));
×
UNCOV
111
      serviceAvailable = this.isServiceServerAvailable();
×
112
    }
113
    return serviceAvailable;
60✔
114
  }
115

116
  static createClient(nodeHandle, serviceName, typeClass, options) {
117
    let type = typeClass.type();
168✔
118
    let handle = rclnodejs.createClient(
168✔
119
      nodeHandle,
120
      serviceName,
121
      type.interfaceName,
122
      type.pkgName,
123
      options.qos
124
    );
125
    return new Client(handle, nodeHandle, serviceName, typeClass, options);
138✔
126
  }
127

128
  /**
129
   * @type {string}
130
   */
131
  get serviceName() {
132
    return rclnodejs.getClientServiceName(this._handle);
9✔
133
  }
134

135
  /**
136
   * Configure introspection.
137
   * @param {Clock} clock - Clock to use for service event timestamps
138
   * @param {QoS} qos - QoSProfile for the service event publisher
139
   * @param {ServiceIntrospectionState} introspectionState - State to set introspection to
140
   */
141
  configureIntrospection(clock, qos, introspectionState) {
142
    if (DistroUtils.getDistroId() <= DistroUtils.getDistroId('humble')) {
15!
143
      console.warn(
×
144
        'Service introspection is not supported by this versionof ROS 2'
145
      );
146
      return;
×
147
    }
148

149
    let type = this.typeClass.type();
15✔
150
    rclnodejs.configureServiceIntrospection(
15✔
151
      this.handle,
152
      this._nodeHandle,
153
      clock.handle,
154
      type.interfaceName,
155
      type.pkgName,
156
      qos,
157
      introspectionState,
158
      false
159
    );
160
  }
161
}
162

163
module.exports = Client;
78✔
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