Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions lib/action/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,19 @@ class ActionClient extends Entity {

this._node._destroyEntity(this, this._node._actionClients);
}

/**
* Get the number of wait set entities that make up an action entity.
* @return {object} - An object containing the number of various entities.
* @property {number} subscriptionsNumber - The number of subscriptions.
* @property {number} guardConditionsNumber - The number of guard conditions.
* @property {number} timersNumber - The number of timers.
* @property {number} clientsNumber - The number of clients.
* @property {number} servicesNumber - The number of services.
*/
getNumEntities() {
return rclnodejs.getNumEntities(this.handle);
}
}

module.exports = ActionClient;
79 changes: 55 additions & 24 deletions src/rcl_action_client_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,28 +126,6 @@ Napi::Value ActionSendGoalRequest(const Napi::CallbackInfo& info) {
return Napi::Number::New(env, static_cast<int32_t>(sequence_number));
}

Napi::Value ActionTakeCancelRequest(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

RclHandle* action_server_handle =
RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_action_server_t* action_server =
reinterpret_cast<rcl_action_server_t*>(action_server_handle->ptr());
rmw_request_id_t* header =
reinterpret_cast<rmw_request_id_t*>(malloc(sizeof(rmw_request_id_t)));

void* taken_request = info[1].As<Napi::Buffer<char>>().Data();
rcl_ret_t ret =
rcl_action_take_cancel_request(action_server, header, taken_request);
if (ret != RCL_RET_ACTION_SERVER_TAKE_FAILED) {
auto js_obj = RclHandle::NewInstance(env, header, nullptr,
[](void* ptr) { free(ptr); });
return js_obj;
}

return env.Undefined();
}

Napi::Value ActionSendResultRequest(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

Expand Down Expand Up @@ -211,20 +189,73 @@ Napi::Value ActionTakeStatus(const Napi::CallbackInfo& info) {
return env.Undefined();
}

Napi::Value GetNumEntities(const Napi::CallbackInfo& info) {
Copy link

Copilot AI May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Add a comment outlining the mapping between native entity counts and the JavaScript object keys to improve maintainability and assist future developers in understanding the conversion.

Copilot uses AI. Check for mistakes.

Napi::Env env = info.Env();
RclHandle* action_client_handle =
RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_action_client_t* action_client =
reinterpret_cast<rcl_action_client_t*>(action_client_handle->ptr());

size_t num_subscriptions = 0u;
size_t num_guard_conditions = 0u;
size_t num_timers = 0u;
size_t num_clients = 0u;
size_t num_services = 0u;

rcl_ret_t ret;
ret = rcl_action_client_wait_set_get_num_entities(
action_client, &num_subscriptions, &num_guard_conditions, &num_timers,
&num_clients, &num_services);
if (RCL_RET_OK != ret) {
rcl_reset_error();
std::string error_text{
"Failed to get number of entities for 'rcl_action_client_t'"};
Comment on lines +210 to +212
Copy link

Copilot AI May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider including the actual error code value in the thrown error message to provide better context for debugging failures from rcl_action_client_wait_set_get_num_entities.

Suggested change
rcl_reset_error();
std::string error_text{
"Failed to get number of entities for 'rcl_action_client_t'"};
std::string error_text = "Failed to get number of entities for 'rcl_action_client_t'. Error code: " + std::to_string(ret);
rcl_reset_error();

Copilot uses AI. Check for mistakes.

Napi::Error::New(env, error_text).ThrowAsJavaScriptException();
return env.Undefined();
}
Napi::Object entities = Napi::Object::New(env);
entities.Set("subscriptionsNumber",
Napi::Number::New(env, num_subscriptions));
entities.Set("guardConditionsNumber",
Napi::Number::New(env, num_guard_conditions));
entities.Set("timersNumber", Napi::Number::New(env, num_timers));
entities.Set("clientsNumber", Napi::Number::New(env, num_clients));
entities.Set("servicesNumber", Napi::Number::New(env, num_services));
return entities;
}

Napi::Value ActionSendCancelRequest(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

RclHandle* action_client_handle =
RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_action_client_t* action_client =
reinterpret_cast<rcl_action_client_t*>(action_client_handle->ptr());
void* buffer = info[1].As<Napi::Buffer<char>>().Data();

int64_t sequence_number;
THROW_ERROR_IF_NOT_EQUAL(
rcl_action_send_cancel_request(action_client, buffer, &sequence_number),
RCL_RET_OK, rcl_get_error_string().str);

return Napi::Number::New(env, static_cast<int32_t>(sequence_number));
}

Napi::Object InitActionClientBindings(Napi::Env env, Napi::Object exports) {
exports.Set("actionCreateClient",
Napi::Function::New(env, ActionCreateClient));
exports.Set("actionServerIsAvailable",
Napi::Function::New(env, ActionServerIsAvailable));
exports.Set("actionSendGoalRequest",
Napi::Function::New(env, ActionSendGoalRequest));
exports.Set("actionTakeCancelRequest",
Napi::Function::New(env, ActionTakeCancelRequest));
exports.Set("actionSendResultRequest",
Napi::Function::New(env, ActionSendResultRequest));
exports.Set("actionTakeFeedback",
Napi::Function::New(env, ActionTakeFeedback));
exports.Set("actionTakeStatus", Napi::Function::New(env, ActionTakeStatus));
exports.Set("getNumEntities", Napi::Function::New(env, GetNumEntities));
exports.Set("actionSendCancelRequest",
Napi::Function::New(env, ActionSendCancelRequest));
return exports;
}

Expand Down
43 changes: 24 additions & 19 deletions src/rcl_action_server_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,23 +96,6 @@ Napi::Value ActionCreateServer(const Napi::CallbackInfo& info) {
}
}

Napi::Value ActionSendCancelRequest(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

RclHandle* action_client_handle =
RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_action_client_t* action_client =
reinterpret_cast<rcl_action_client_t*>(action_client_handle->ptr());
void* buffer = info[1].As<Napi::Buffer<char>>().Data();

int64_t sequence_number;
THROW_ERROR_IF_NOT_EQUAL(
rcl_action_send_cancel_request(action_client, buffer, &sequence_number),
RCL_RET_OK, rcl_get_error_string().str);

return Napi::Number::New(env, static_cast<int32_t>(sequence_number));
}

Napi::Value ActionTakeResultRequest(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

Expand Down Expand Up @@ -432,11 +415,31 @@ Napi::Value ActionServerGoalExists(const Napi::CallbackInfo& info) {
return Napi::Boolean::New(env, exists);
}

Napi::Value ActionTakeCancelRequest(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();

RclHandle* action_server_handle =
RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_action_server_t* action_server =
reinterpret_cast<rcl_action_server_t*>(action_server_handle->ptr());
rmw_request_id_t* header =
reinterpret_cast<rmw_request_id_t*>(malloc(sizeof(rmw_request_id_t)));

void* taken_request = info[1].As<Napi::Buffer<char>>().Data();
rcl_ret_t ret =
rcl_action_take_cancel_request(action_server, header, taken_request);
if (ret != RCL_RET_ACTION_SERVER_TAKE_FAILED) {
auto js_obj = RclHandle::NewInstance(env, header, nullptr,
[](void* ptr) { free(ptr); });
return js_obj;
}

return env.Undefined();
}

Napi::Object InitActionServerBindings(Napi::Env env, Napi::Object exports) {
exports.Set("actionCreateServer",
Napi::Function::New(env, ActionCreateServer));
exports.Set("actionSendCancelRequest",
Napi::Function::New(env, ActionSendCancelRequest));
exports.Set("actionTakeResultRequest",
Napi::Function::New(env, ActionTakeResultRequest));
exports.Set("actionTakeGoalRequest",
Expand Down Expand Up @@ -464,6 +467,8 @@ Napi::Object InitActionServerBindings(Napi::Env env, Napi::Object exports) {
Napi::Function::New(env, ActionProcessCancelRequest));
exports.Set("actionServerGoalExists",
Napi::Function::New(env, ActionServerGoalExists));
exports.Set("actionTakeCancelRequest",
Napi::Function::New(env, ActionTakeCancelRequest));
return exports;
}

Expand Down
12 changes: 12 additions & 0 deletions test/test-action-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,16 @@ describe('rclnodejs action client', function () {

client.destroy();
});

it('Test getNumEntities', function () {
let client = new rclnodejs.ActionClient(node, fibonacci, 'fibonacci');
const numEntities = client.getNumEntities();
assert.strictEqual(numEntities.subscriptionsNumber, 2);
assert.strictEqual(numEntities.guardConditionsNumber, 0);
assert.strictEqual(numEntities.timersNumber, 0);
assert.strictEqual(numEntities.clientsNumber, 3);
assert.strictEqual(numEntities.servicesNumber, 0);

client.destroy();
});
});
1 change: 1 addition & 0 deletions test/types/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ goalHandlePromise.then((goalHandle) => {
expectType<boolean>(goalHandle.isCanceled());
expectType<boolean>(goalHandle.isAborted());
});
expectType<object>(actionClient.getNumEntities());

// ---- ActionServer -----
const actionServer = new rclnodejs.ActionServer(
Expand Down
6 changes: 6 additions & 0 deletions types/action_client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,11 @@ declare module 'rclnodejs' {
* Destroy the underlying action client handle.
*/
destroy(): void;

/**
* Get the number of wait set entities that make up an action entity.
* @return - The number of subscriptions, guard_conditions, timers, and clients and services.
*/
getNumEntities(): object;
}
}
Loading