Skip to content

Commit 4c6b4a7

Browse files
koonpengMinggang Wang
authored andcommitted
Add waitForService to Client class
This patch adds waitForService interface to wait for a specific service if it's not available yet. Also, isServiceServerAvailable is added to inquiry the current availability of a service. Fix #512 * add pollForService method to service client * use Nan::New instead of v8::Boolean::New * jsdoc * rename pollForService to waitForService * add to contributors
1 parent e40a663 commit 4c6b4a7

File tree

5 files changed

+68
-7
lines changed

5 files changed

+68
-7
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ project. The next step is to send a pull request (PR) for review. The PR will be
151151
Special thanks to the people who contribute.
152152

153153
* [martins-mozeiko](https://github.com/martins-mozeiko)
154+
* [Teo Koon Peng](https://github.com/koonpeng)
154155

155156
## License
156157

example/client-example.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,17 @@ rclnodejs.init().then(() => {
2525
b: Math.floor(Math.random() * 100),
2626
};
2727

28-
console.log(`Sending: ${typeof request}`, request);
29-
client.sendRequest(request, (response) => {
30-
console.log(`Result: ${typeof response}`, response);
31-
rclnodejs.shutdown();
28+
client.waitForService(1000).then(result => {
29+
if (!result) {
30+
console.log('Error: service not available');
31+
rclnodejs.shutdown();
32+
return;
33+
}
34+
console.log(`Sending: ${typeof request}`, request);
35+
client.sendRequest(request, (response) => {
36+
console.log(`Result: ${typeof response}`, response);
37+
rclnodejs.shutdown();
38+
});
3239
});
3340

3441
rclnodejs.spin(node);

lib/client.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ const debug = require('debug')('rclnodejs:client');
2424
*/
2525

2626
class Client extends Entity {
27-
constructor(handle, serviceName, typeClass, options) {
27+
constructor(handle, nodeHandle, serviceName, typeClass, options) {
2828
super(handle, typeClass, options);
29+
this._nodeHandle = nodeHandle;
2930
this._serviceName = serviceName;
3031
this._sequenceNumber = 0;
3132
}
@@ -72,10 +73,45 @@ class Client extends Entity {
7273
this._callback(this._response.toPlainObject(this.typedArrayEnabled));
7374
}
7475

76+
/**
77+
* Checks if the service is available.
78+
* @return {boolean} true if the service is available.
79+
*/
80+
isServiceServerAvailable() {
81+
return rclnodejs.serviceServerIsAvailable(this._nodeHandle, this.handle);
82+
}
83+
84+
/**
85+
* Wait until the service server is available or a timeout is reached. This
86+
* function polls for the service state so it may not return as soon as the
87+
* service is available.
88+
* @param {number} timeout The maximum amount of time to wait for, if timeout
89+
* is `undefined` or `< 0`, this will wait indefinitely.
90+
* @return {Promise<boolean>} true if the service is available.
91+
*/
92+
async waitForService(timeout = undefined) {
93+
let deadline = Infinity;
94+
if (timeout !== undefined && timeout >= 0) {
95+
deadline = Date.now() + timeout;
96+
}
97+
let waitMs = 5;
98+
let serviceAvailable = this.isServiceServerAvailable();
99+
while (!serviceAvailable && Date.now() < deadline) {
100+
waitMs *= 2;
101+
waitMs = Math.min(waitMs, 1000);
102+
if (timeout !== undefined && timeout >= -1) {
103+
waitMs = Math.min(waitMs, deadline - Date.now());
104+
}
105+
await new Promise(resolve => setTimeout(resolve, waitMs));
106+
serviceAvailable = this.isServiceServerAvailable();
107+
}
108+
return serviceAvailable;
109+
}
110+
75111
static createClient(nodeHandle, serviceName, typeClass, options) {
76112
let type = typeClass.type();
77113
let handle = rclnodejs.createClient(nodeHandle, serviceName, type.interfaceName, type.pkgName, options.qos);
78-
return new Client(handle, serviceName, typeClass, options);
114+
return new Client(handle, nodeHandle, serviceName, typeClass, options);
79115
}
80116

81117
/**

src/rcl_bindings.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,22 @@ NAN_METHOD(GetServiceNamesAndTypes) {
12211221
info.GetReturnValue().Set(result_list);
12221222
}
12231223

1224+
NAN_METHOD(ServiceServerIsAvailable) {
1225+
RclHandle* node_handle = RclHandle::Unwrap<RclHandle>(info[0]->ToObject());
1226+
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
1227+
RclHandle* client_handle = RclHandle::Unwrap<RclHandle>(info[1]->ToObject());
1228+
rcl_client_t* client = reinterpret_cast<rcl_client_t*>(client_handle->ptr());
1229+
1230+
bool is_available;
1231+
THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK,
1232+
rcl_service_server_is_available(
1233+
node, client, &is_available),
1234+
"Failed to get service state.");
1235+
1236+
v8::Local<v8::Boolean> result = Nan::New<v8::Boolean>(is_available);
1237+
info.GetReturnValue().Set(result);
1238+
}
1239+
12241240
uint32_t GetBindingMethodsCount(BindingMethod* methods) {
12251241
uint32_t count = 0;
12261242
while (methods[count].function) {
@@ -1284,6 +1300,7 @@ BindingMethod binding_methods[] = {
12841300
{"getServiceNamesAndTypesByNode", GetServiceNamesAndTypesByNode},
12851301
{"getTopicNamesAndTypes", GetTopicNamesAndTypes},
12861302
{"getServiceNamesAndTypes", GetServiceNamesAndTypes},
1303+
{"serviceServerIsAvailable", ServiceServerIsAvailable},
12871304
{"", nullptr}};
12881305

12891306
} // namespace rclnodejs

test/client_setup.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ rclnodejs.init().then(function() {
2626
b: 2,
2727
};
2828
var publisher = node.createPublisher(Int8, 'back_add_two_ints');
29-
var timer = node.createTimer(100, () => {
29+
client.waitForService().then(() => {
3030
client.sendRequest(request, (response) => {
3131
publisher.publish(response.sum);
3232
});

0 commit comments

Comments
 (0)