Skip to content

Commit e76626c

Browse files
authored
Add support for guard conditions (#561)
Implements guard conditions in rclnodejs. In addition, now only entities that are ready to be handled will be executed. This was initially needed since guard conditions should only run when triggered, but was extended to all entities. The approach taken was passing all RclHandles that are ready to be executed back to the main process. Fix #315
1 parent 4b83bad commit e76626c

16 files changed

+364
-33
lines changed

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ let rcl = {
118118
node.init(nodeName, namespace);
119119
debug('Finish initializing node, name = %s and namespace = %s.', nodeName, namespace);
120120
node.handle = handle;
121+
node.context = context;
121122
this._nodes.push(node);
122123
return node;
123124
},

lib/guard_condition.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
'use strict';
14+
15+
const rclnodejs = require('bindings')('rclnodejs');
16+
const Entity = require('./entity.js');
17+
const Context = require('./context.js');
18+
19+
/**
20+
* @class - Class representing a guard condition in ROS
21+
* @hideconstructor
22+
*/
23+
24+
class GuardCondition extends Entity {
25+
constructor(handle, callback) {
26+
super(handle, null, null);
27+
28+
this._callback = callback;
29+
}
30+
31+
get callback() {
32+
return this._callback;
33+
}
34+
35+
static createGuardCondition(callback, context = Context.defaultContext()) {
36+
let handle = rclnodejs.createGuardCondition(context.handle());
37+
return new GuardCondition(handle, callback);
38+
}
39+
40+
/**
41+
* Triggers the guard condition.
42+
* @returns {undefined}
43+
*/
44+
trigger() {
45+
rclnodejs.triggerGuardCondition(this.handle);
46+
}
47+
}
48+
49+
module.exports = GuardCondition;

lib/node.js

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const QoS = require('./qos.js');
2424
const debug = require('debug')('rclnodejs:node');
2525
const loader = require('./interface_loader.js');
2626
const Context = require('./context.js');
27+
const GuardCondition = require('./guard_condition.js');
2728
/**
2829
* @class - Class representing a Node in ROS
2930
* @hideconstructor
@@ -36,6 +37,7 @@ class Node {
3637
this._clients = [];
3738
this._services = [];
3839
this._timers = [];
40+
this._guards = [];
3941
this._name = name;
4042

4143
if (namespace.length === 0) {
@@ -47,15 +49,22 @@ class Node {
4749
this.spinning = false;
4850
}
4951

50-
execute() {
51-
this._timers.forEach((timer) => {
52+
execute(handles) {
53+
let timersReady = this._timers.filter((timer) => handles.indexOf(timer.handle) !== -1);
54+
let guardsReady = this._guards.filter((guard) => handles.indexOf(guard.handle) !== -1);
55+
let subscriptionsReady = this._subscriptions.filter((subscription) =>
56+
handles.indexOf(subscription.handle) !== -1);
57+
let clientsReady = this._clients.filter((client) => handles.indexOf(client.handle) !== -1);
58+
let servicesReady = this._services.filter((service) => handles.indexOf(service.handle) !== -1);
59+
60+
timersReady.forEach((timer) => {
5261
if (timer.isReady()) {
5362
rclnodejs.callTimer(timer.handle);
5463
timer.callback();
5564
}
5665
});
5766

58-
this._subscriptions.forEach((subscription) => {
67+
subscriptionsReady.forEach((subscription) => {
5968
let Message = subscription.typeClass;
6069
let msg = new Message();
6170
let success = rclnodejs.rclTake(subscription.handle, msg.toRawROS());
@@ -65,7 +74,11 @@ class Node {
6574
Message.destoryRawROS(msg);
6675
});
6776

68-
this._clients.forEach((client) => {
77+
guardsReady.forEach((guard) => {
78+
guard.callback();
79+
});
80+
81+
clientsReady.forEach((client) => {
6982
let Response = client.typeClass.Response;
7083
let response = new Response();
7184
let success = rclnodejs.rclTakeResponse(client.handle, client.sequenceNumber, response.toRawROS());
@@ -75,7 +88,7 @@ class Node {
7588
Response.destoryRawROS(response);
7689
});
7790

78-
this._services.forEach((service) => {
91+
servicesReady.forEach((service) => {
7992
let Request = service.typeClass.Request;
8093
let request = new Request();
8194
let header = rclnodejs.rclTakeRequest(service.handle, this.handle, request.toRawROS());
@@ -313,6 +326,24 @@ class Node {
313326
return service;
314327
}
315328

329+
/**
330+
* Create a guard condition.
331+
* @param {Function} callback - The callback to be called when the guard condition is triggered.
332+
* @return {GuardCondition} - An instance of GuardCondition.
333+
*/
334+
createGuardCondition(callback) {
335+
if (typeof (callback) !== 'function') {
336+
throw new TypeError('Invalid argument');
337+
}
338+
339+
let guard = GuardCondition.createGuardCondition(callback, this.context);
340+
debug('Finish creating guard condition');
341+
this._guards.push(guard);
342+
this.syncHandles();
343+
344+
return guard;
345+
}
346+
316347
/**
317348
* Destroy all resource allocated by this node, including
318349
* <code>Timer</code>s/<code>Publisher</code>s/<code>Subscription</code>s
@@ -330,6 +361,7 @@ class Node {
330361
this._subscriptions = [];
331362
this._clients = [];
332363
this._services = [];
364+
this._guards = [];
333365
}
334366

335367
/**
@@ -392,6 +424,18 @@ class Node {
392424
this._destroyEntity(timer, this._timers);
393425
}
394426

427+
/**
428+
* Destroy a guard condition.
429+
* @param {GuardCondition} guard - The guard condition to be destroyed.
430+
* @return {undefined}
431+
*/
432+
destroyGuardCondition(guard) {
433+
if (!(guard instanceof GuardCondition)) {
434+
throw new TypeError('Invalid argument');
435+
}
436+
this._destroyEntity(guard, this._guards);
437+
}
438+
395439
/* Get the name of the node.
396440
* @return {string}
397441
*/

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"dtslint": "^2.0.2",
3737
"eslint": "^5.14.1",
3838
"mocha": "^6.0.1",
39+
"sinon": "^8.1.1",
3940
"tree-kill": "^1.2.1",
4041
"typescript": "^3.7.2"
4142
},

src/executor.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ void Executor::DoWork(uv_async_t* handle) {
8888
rcl_reset_error();
8989
g_exception_ptr = nullptr;
9090
}
91-
executor->delegate_->Execute();
91+
executor->delegate_->Execute(
92+
executor->handle_manager_->get_ready_handles());
9293
}
9394
}
9495

@@ -117,8 +118,8 @@ void Executor::Run(void* arg) {
117118
continue;
118119

119120
if (rcl_wait_set_resize(&wait_set, handle_manager->subscription_count(),
120-
// TODO(minggang): support guard conditions
121-
1u, handle_manager->timer_count(),
121+
handle_manager->guard_condition_count() + 1u,
122+
handle_manager->timer_count(),
122123
handle_manager->client_count(),
123124
handle_manager->service_count(),
124125
// TODO(minggang): support events.
@@ -147,6 +148,9 @@ void Executor::Run(void* arg) {
147148
wait_set.guard_conditions[0]) {
148149
executor->running_.store(false);
149150
}
151+
152+
handle_manager->CollectReadyHandles(&wait_set);
153+
150154
if (!uv_is_closing(
151155
reinterpret_cast<uv_handle_t*>(executor->async_))) {
152156
uv_async_send(executor->async_);

src/executor.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919

2020
#include <atomic>
2121
#include <exception>
22+
#include <vector>
23+
24+
#include "rcl_handle.hpp"
2225

2326
struct rcl_context_t;
2427

@@ -30,7 +33,8 @@ class Executor {
3033
public:
3134
class Delegate {
3235
public:
33-
virtual void Execute() = 0;
36+
virtual void Execute(
37+
const std::vector<rclnodejs::RclHandle *>& handles) = 0;
3438
virtual void CatchException(std::exception_ptr e_ptr) = 0;
3539
};
3640

src/handle_manager.cpp

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
#include <vector>
1818

19-
#include "rcl_handle.hpp"
2019
#include "spdlog/spdlog.h"
2120

2221
namespace rclnodejs {
@@ -47,45 +46,92 @@ void HandleManager::CollectHandles(const v8::Local<v8::Object> node) {
4746
Nan::Get(node, Nan::New("_clients").ToLocalChecked());
4847
Nan::MaybeLocal<v8::Value> services =
4948
Nan::Get(node, Nan::New("_services").ToLocalChecked());
49+
Nan::MaybeLocal<v8::Value> guard_conditions =
50+
Nan::Get(node, Nan::New("_guards").ToLocalChecked());
5051

5152
CollectHandlesByType(timers.ToLocalChecked()->ToObject(), &timers_);
5253
CollectHandlesByType(subscriptions.ToLocalChecked()->ToObject(),
5354
&subscriptions_);
5455
CollectHandlesByType(clients.ToLocalChecked()->ToObject(), &clients_);
5556
CollectHandlesByType(services.ToLocalChecked()->ToObject(), &services_);
57+
CollectHandlesByType(guard_conditions.ToLocalChecked()->ToObject(),
58+
&guard_conditions_);
5659
}
5760

5861
is_synchronizing_.store(false);
5962
uv_sem_post(&sem_);
6063

6164
SPDLOG_DEBUG(
6265
spdlog::get("rclnodejs"),
63-
"Add {0:d} timers, {1:d} subscriptions, {2:d} clients, {3:d} services.",
64-
timers_.size(), subscriptions_.size(), clients_.size(), services_.size());
66+
"Add {0:d} timers, {1:d} subscriptions, {2:d} clients, " +
67+
"{3:d} services, {4:d} guards.",
68+
timers_.size(),
69+
subscriptions_.size(),
70+
clients_.size(),
71+
services_.size(),
72+
guard_conditions_.size());
6573
}
6674

6775
bool HandleManager::AddHandlesToWaitSet(rcl_wait_set_t* wait_set) {
6876
for (auto& timer : timers_) {
69-
if (rcl_wait_set_add_timer(wait_set, timer, nullptr) != RCL_RET_OK)
77+
rcl_timer_t* rcl_timer = reinterpret_cast<rcl_timer_t*>(timer->ptr());
78+
if (rcl_wait_set_add_timer(wait_set, rcl_timer, nullptr) != RCL_RET_OK)
7079
return false;
7180
}
7281
for (auto& subscription : subscriptions_) {
73-
if (rcl_wait_set_add_subscription(wait_set, subscription, nullptr) !=
82+
rcl_subscription_t* rcl_subscription =
83+
reinterpret_cast<rcl_subscription_t*>(subscription->ptr());
84+
if (rcl_wait_set_add_subscription(wait_set, rcl_subscription, nullptr) !=
7485
RCL_RET_OK)
7586
return false;
7687
}
7788
for (auto& client : clients_) {
78-
if (rcl_wait_set_add_client(wait_set, client, nullptr) != RCL_RET_OK)
89+
rcl_client_t* rcl_client = reinterpret_cast<rcl_client_t*>(client->ptr());
90+
if (rcl_wait_set_add_client(wait_set, rcl_client, nullptr) != RCL_RET_OK)
7991
return false;
8092
}
8193
for (auto& service : services_) {
82-
if (rcl_wait_set_add_service(wait_set, service, nullptr) != RCL_RET_OK)
94+
rcl_service_t* rcl_service =
95+
reinterpret_cast<rcl_service_t*>(service->ptr());
96+
if (rcl_wait_set_add_service(wait_set, rcl_service, nullptr) != RCL_RET_OK)
97+
return false;
98+
}
99+
for (auto& guard_condition : guard_conditions_) {
100+
rcl_guard_condition_t* rcl_guard_condition =
101+
reinterpret_cast<rcl_guard_condition_t*>(guard_condition->ptr());
102+
if (rcl_wait_set_add_guard_condition(wait_set, rcl_guard_condition, nullptr)
103+
!= RCL_RET_OK)
83104
return false;
84105
}
85106

86107
return true;
87108
}
88109

110+
void HandleManager::CollectReadyHandles(rcl_wait_set_t* wait_set) {
111+
ready_handles_.clear();
112+
113+
CollectReadyHandlesByType(
114+
wait_set->subscriptions,
115+
wait_set->size_of_subscriptions,
116+
subscriptions_);
117+
CollectReadyHandlesByType(
118+
wait_set->clients,
119+
wait_set->size_of_clients,
120+
clients_);
121+
CollectReadyHandlesByType(
122+
wait_set->services,
123+
wait_set->size_of_services,
124+
services_);
125+
CollectReadyHandlesByType(
126+
wait_set->timers,
127+
wait_set->size_of_timers,
128+
timers_);
129+
CollectReadyHandlesByType(
130+
wait_set->guard_conditions,
131+
wait_set->size_of_guard_conditions,
132+
guard_conditions_);
133+
}
134+
89135
void HandleManager::ClearHandles() {
90136
timers_.clear();
91137
clients_.clear();
@@ -94,10 +140,9 @@ void HandleManager::ClearHandles() {
94140
guard_conditions_.clear();
95141
}
96142

97-
template <typename T>
98143
void HandleManager::CollectHandlesByType(
99144
const v8::Local<v8::Object>& typeObject,
100-
std::vector<const T*>* vec) {
145+
std::vector<rclnodejs::RclHandle*>* vec) {
101146
Nan::HandleScope scope;
102147

103148
if (typeObject->IsArray()) {
@@ -112,7 +157,23 @@ void HandleManager::CollectHandlesByType(
112157
rclnodejs::RclHandle* rcl_handle =
113158
rclnodejs::RclHandle::Unwrap<rclnodejs::RclHandle>(
114159
handle.ToLocalChecked()->ToObject());
115-
vec->push_back(reinterpret_cast<T*>(rcl_handle->ptr()));
160+
vec->push_back(rcl_handle);
161+
}
162+
}
163+
}
164+
165+
template<typename T>
166+
void HandleManager::CollectReadyHandlesByType(
167+
const T** struct_ptr,
168+
size_t size,
169+
const std::vector<rclnodejs::RclHandle*>& handles) {
170+
for (size_t idx = 0; idx < size; ++idx) {
171+
if (struct_ptr[idx]) {
172+
for (auto& handle : handles) {
173+
if (struct_ptr[idx] == handle->ptr()) {
174+
ready_handles_.push_back(handle);
175+
}
176+
}
116177
}
117178
}
118179
}

0 commit comments

Comments
 (0)