rclnodejs 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -68,13 +68,15 @@ class HandleManager {
68
68
  rcl_ret_t CollectReadyHandles(rcl_wait_set_t* wait_set);
69
69
  rcl_ret_t GetEntityCounts(size_t* subscriptions_size,
70
70
  size_t* guard_conditions_size, size_t* timers_size,
71
- size_t* clients_size, size_t* services_size);
71
+ size_t* clients_size, size_t* services_size,
72
+ size_t* events_size);
72
73
 
73
74
  uint32_t subscription_count() const { return subscriptions_.size(); }
74
75
  uint32_t service_count() const { return services_.size(); }
75
76
  uint32_t client_count() const { return clients_.size(); }
76
77
  uint32_t timer_count() const { return timers_.size(); }
77
78
  uint32_t guard_condition_count() const { return guard_conditions_.size(); }
79
+ uint32_t event_count() const { return events_.size(); }
78
80
  uv_rwlock_t* handle_rwlock() { return &sync_handles_rwlock_; }
79
81
 
80
82
  uint32_t ready_handles_count();
@@ -110,6 +112,7 @@ class HandleManager {
110
112
  std::vector<rclnodejs::RclHandle*> action_servers_;
111
113
  std::vector<rclnodejs::RclHandle*> action_clients_;
112
114
  std::vector<rclnodejs::RclHandle*> ready_handles_;
115
+ std::vector<rclnodejs::RclHandle*> events_;
113
116
 
114
117
  // Protects the handles.
115
118
  uv_rwlock_t sync_handles_rwlock_;
@@ -16,6 +16,7 @@
16
16
 
17
17
  #include <rcl/error_handling.h>
18
18
  #include <rcl/rcl.h>
19
+ #include <rcl_action/action_client.h>
19
20
  #include <rcl_action/rcl_action.h>
20
21
 
21
22
  #include <string>
@@ -126,28 +127,6 @@ Napi::Value ActionSendGoalRequest(const Napi::CallbackInfo& info) {
126
127
  return Napi::Number::New(env, static_cast<int32_t>(sequence_number));
127
128
  }
128
129
 
129
- Napi::Value ActionTakeCancelRequest(const Napi::CallbackInfo& info) {
130
- Napi::Env env = info.Env();
131
-
132
- RclHandle* action_server_handle =
133
- RclHandle::Unwrap(info[0].As<Napi::Object>());
134
- rcl_action_server_t* action_server =
135
- reinterpret_cast<rcl_action_server_t*>(action_server_handle->ptr());
136
- rmw_request_id_t* header =
137
- reinterpret_cast<rmw_request_id_t*>(malloc(sizeof(rmw_request_id_t)));
138
-
139
- void* taken_request = info[1].As<Napi::Buffer<char>>().Data();
140
- rcl_ret_t ret =
141
- rcl_action_take_cancel_request(action_server, header, taken_request);
142
- if (ret != RCL_RET_ACTION_SERVER_TAKE_FAILED) {
143
- auto js_obj = RclHandle::NewInstance(env, header, nullptr,
144
- [](void* ptr) { free(ptr); });
145
- return js_obj;
146
- }
147
-
148
- return env.Undefined();
149
- }
150
-
151
130
  Napi::Value ActionSendResultRequest(const Napi::CallbackInfo& info) {
152
131
  Napi::Env env = info.Env();
153
132
 
@@ -211,6 +190,97 @@ Napi::Value ActionTakeStatus(const Napi::CallbackInfo& info) {
211
190
  return env.Undefined();
212
191
  }
213
192
 
193
+ Napi::Value GetNumEntities(const Napi::CallbackInfo& info) {
194
+ Napi::Env env = info.Env();
195
+ RclHandle* action_client_handle =
196
+ RclHandle::Unwrap(info[0].As<Napi::Object>());
197
+ rcl_action_client_t* action_client =
198
+ reinterpret_cast<rcl_action_client_t*>(action_client_handle->ptr());
199
+
200
+ size_t num_subscriptions = 0u;
201
+ size_t num_guard_conditions = 0u;
202
+ size_t num_timers = 0u;
203
+ size_t num_clients = 0u;
204
+ size_t num_services = 0u;
205
+
206
+ rcl_ret_t ret;
207
+ ret = rcl_action_client_wait_set_get_num_entities(
208
+ action_client, &num_subscriptions, &num_guard_conditions, &num_timers,
209
+ &num_clients, &num_services);
210
+ if (RCL_RET_OK != ret) {
211
+ rcl_reset_error();
212
+ std::string error_text{
213
+ "Failed to get number of entities for 'rcl_action_client_t'"};
214
+ Napi::Error::New(env, error_text).ThrowAsJavaScriptException();
215
+ return env.Undefined();
216
+ }
217
+ Napi::Object entities = Napi::Object::New(env);
218
+ entities.Set("subscriptionsNumber",
219
+ Napi::Number::New(env, num_subscriptions));
220
+ entities.Set("guardConditionsNumber",
221
+ Napi::Number::New(env, num_guard_conditions));
222
+ entities.Set("timersNumber", Napi::Number::New(env, num_timers));
223
+ entities.Set("clientsNumber", Napi::Number::New(env, num_clients));
224
+ entities.Set("servicesNumber", Napi::Number::New(env, num_services));
225
+ return entities;
226
+ }
227
+
228
+ Napi::Value ActionSendCancelRequest(const Napi::CallbackInfo& info) {
229
+ Napi::Env env = info.Env();
230
+
231
+ RclHandle* action_client_handle =
232
+ RclHandle::Unwrap(info[0].As<Napi::Object>());
233
+ rcl_action_client_t* action_client =
234
+ reinterpret_cast<rcl_action_client_t*>(action_client_handle->ptr());
235
+ void* buffer = info[1].As<Napi::Buffer<char>>().Data();
236
+
237
+ int64_t sequence_number;
238
+ THROW_ERROR_IF_NOT_EQUAL(
239
+ rcl_action_send_cancel_request(action_client, buffer, &sequence_number),
240
+ RCL_RET_OK, rcl_get_error_string().str);
241
+
242
+ return Napi::Number::New(env, static_cast<int32_t>(sequence_number));
243
+ }
244
+
245
+ #if ROS_VERSION >= 2505 // ROS2 >= Kilted
246
+ Napi::Value ConfigureActionClientIntrospection(const Napi::CallbackInfo& info) {
247
+ Napi::Env env = info.Env();
248
+ RclHandle* action_client_handle =
249
+ RclHandle::Unwrap(info[0].As<Napi::Object>());
250
+ rcl_action_client_t* action_client =
251
+ reinterpret_cast<rcl_action_client_t*>(action_client_handle->ptr());
252
+ RclHandle* node_handle = RclHandle::Unwrap(info[1].As<Napi::Object>());
253
+ rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
254
+ rcl_clock_t* clock = reinterpret_cast<rcl_clock_t*>(
255
+ RclHandle::Unwrap(info[2].As<Napi::Object>())->ptr());
256
+
257
+ std::string action_name = info[3].As<Napi::String>().Utf8Value();
258
+ std::string package_name = info[4].As<Napi::String>().Utf8Value();
259
+ const rosidl_action_type_support_t* ts =
260
+ GetActionTypeSupport(package_name, action_name);
261
+ rcl_ret_t ret = RCL_RET_ERROR;
262
+ if (ts) {
263
+ rcl_publisher_options_t publisher_ops = rcl_publisher_get_default_options();
264
+ auto qos_profile = GetQoSProfile(info[5]);
265
+ if (qos_profile) {
266
+ publisher_ops.qos = *qos_profile;
267
+ }
268
+ rcl_service_introspection_state_t state =
269
+ static_cast<rcl_service_introspection_state_t>(
270
+ info[6].As<Napi::Number>().Uint32Value());
271
+ ret = rcl_action_client_configure_action_introspection(
272
+ action_client, node, clock, ts, publisher_ops, state);
273
+ if (ret == RCL_RET_OK) {
274
+ return env.Undefined();
275
+ }
276
+ }
277
+
278
+ Napi::Error::New(env, "failed to configure action client introspection")
279
+ .ThrowAsJavaScriptException();
280
+ return env.Undefined();
281
+ }
282
+ #endif // ROS_VERSION >= 2505
283
+
214
284
  Napi::Object InitActionClientBindings(Napi::Env env, Napi::Object exports) {
215
285
  exports.Set("actionCreateClient",
216
286
  Napi::Function::New(env, ActionCreateClient));
@@ -218,13 +288,18 @@ Napi::Object InitActionClientBindings(Napi::Env env, Napi::Object exports) {
218
288
  Napi::Function::New(env, ActionServerIsAvailable));
219
289
  exports.Set("actionSendGoalRequest",
220
290
  Napi::Function::New(env, ActionSendGoalRequest));
221
- exports.Set("actionTakeCancelRequest",
222
- Napi::Function::New(env, ActionTakeCancelRequest));
223
291
  exports.Set("actionSendResultRequest",
224
292
  Napi::Function::New(env, ActionSendResultRequest));
225
293
  exports.Set("actionTakeFeedback",
226
294
  Napi::Function::New(env, ActionTakeFeedback));
227
295
  exports.Set("actionTakeStatus", Napi::Function::New(env, ActionTakeStatus));
296
+ exports.Set("getNumEntities", Napi::Function::New(env, GetNumEntities));
297
+ exports.Set("actionSendCancelRequest",
298
+ Napi::Function::New(env, ActionSendCancelRequest));
299
+ #if ROS_VERSION >= 2505 // ROS2 >= Kilted
300
+ exports.Set("configureActionClientIntrospection",
301
+ Napi::Function::New(env, ConfigureActionClientIntrospection));
302
+ #endif // ROS_VERSION >= 2505
228
303
  return exports;
229
304
  }
230
305
 
@@ -16,6 +16,7 @@
16
16
 
17
17
  #include <rcl/error_handling.h>
18
18
  #include <rcl/rcl.h>
19
+ #include <rcl_action/action_server.h>
19
20
  #include <rcl_action/rcl_action.h>
20
21
 
21
22
  #include <string>
@@ -96,23 +97,6 @@ Napi::Value ActionCreateServer(const Napi::CallbackInfo& info) {
96
97
  }
97
98
  }
98
99
 
99
- Napi::Value ActionSendCancelRequest(const Napi::CallbackInfo& info) {
100
- Napi::Env env = info.Env();
101
-
102
- RclHandle* action_client_handle =
103
- RclHandle::Unwrap(info[0].As<Napi::Object>());
104
- rcl_action_client_t* action_client =
105
- reinterpret_cast<rcl_action_client_t*>(action_client_handle->ptr());
106
- void* buffer = info[1].As<Napi::Buffer<char>>().Data();
107
-
108
- int64_t sequence_number;
109
- THROW_ERROR_IF_NOT_EQUAL(
110
- rcl_action_send_cancel_request(action_client, buffer, &sequence_number),
111
- RCL_RET_OK, rcl_get_error_string().str);
112
-
113
- return Napi::Number::New(env, static_cast<int32_t>(sequence_number));
114
- }
115
-
116
100
  Napi::Value ActionTakeResultRequest(const Napi::CallbackInfo& info) {
117
101
  Napi::Env env = info.Env();
118
102
 
@@ -432,11 +416,71 @@ Napi::Value ActionServerGoalExists(const Napi::CallbackInfo& info) {
432
416
  return Napi::Boolean::New(env, exists);
433
417
  }
434
418
 
419
+ Napi::Value ActionTakeCancelRequest(const Napi::CallbackInfo& info) {
420
+ Napi::Env env = info.Env();
421
+
422
+ RclHandle* action_server_handle =
423
+ RclHandle::Unwrap(info[0].As<Napi::Object>());
424
+ rcl_action_server_t* action_server =
425
+ reinterpret_cast<rcl_action_server_t*>(action_server_handle->ptr());
426
+ rmw_request_id_t* header =
427
+ reinterpret_cast<rmw_request_id_t*>(malloc(sizeof(rmw_request_id_t)));
428
+
429
+ void* taken_request = info[1].As<Napi::Buffer<char>>().Data();
430
+ rcl_ret_t ret =
431
+ rcl_action_take_cancel_request(action_server, header, taken_request);
432
+ if (ret != RCL_RET_ACTION_SERVER_TAKE_FAILED) {
433
+ auto js_obj = RclHandle::NewInstance(env, header, nullptr,
434
+ [](void* ptr) { free(ptr); });
435
+ return js_obj;
436
+ }
437
+
438
+ return env.Undefined();
439
+ }
440
+
441
+ #if ROS_VERSION >= 2505 // ROS2 >= Kilted
442
+ Napi::Value ConfigureActionServerIntrospection(const Napi::CallbackInfo& info) {
443
+ Napi::Env env = info.Env();
444
+ RclHandle* action_server_handle =
445
+ RclHandle::Unwrap(info[0].As<Napi::Object>());
446
+ rcl_action_server_t* action_server =
447
+ reinterpret_cast<rcl_action_server_t*>(action_server_handle->ptr());
448
+ RclHandle* node_handle = RclHandle::Unwrap(info[1].As<Napi::Object>());
449
+ rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
450
+ rcl_clock_t* clock = reinterpret_cast<rcl_clock_t*>(
451
+ RclHandle::Unwrap(info[2].As<Napi::Object>())->ptr());
452
+
453
+ std::string action_name = info[3].As<Napi::String>().Utf8Value();
454
+ std::string package_name = info[4].As<Napi::String>().Utf8Value();
455
+ const rosidl_action_type_support_t* ts =
456
+ GetActionTypeSupport(package_name, action_name);
457
+
458
+ rcl_ret_t ret = RCL_RET_ERROR;
459
+ if (ts) {
460
+ rcl_publisher_options_t publisher_ops = rcl_publisher_get_default_options();
461
+ auto qos_profile = GetQoSProfile(info[5]);
462
+ if (qos_profile) {
463
+ publisher_ops.qos = *qos_profile;
464
+ }
465
+ rcl_service_introspection_state_t state =
466
+ static_cast<rcl_service_introspection_state_t>(
467
+ info[6].As<Napi::Number>().Uint32Value());
468
+ ret = rcl_action_server_configure_action_introspection(
469
+ action_server, node, clock, ts, publisher_ops, state);
470
+ if (ret == RCL_RET_OK) {
471
+ return env.Undefined();
472
+ }
473
+ }
474
+
475
+ Napi::Error::New(env, "failed to configure action server introspection")
476
+ .ThrowAsJavaScriptException();
477
+ return env.Undefined();
478
+ }
479
+ #endif // ROS_VERSION >= 2505
480
+
435
481
  Napi::Object InitActionServerBindings(Napi::Env env, Napi::Object exports) {
436
482
  exports.Set("actionCreateServer",
437
483
  Napi::Function::New(env, ActionCreateServer));
438
- exports.Set("actionSendCancelRequest",
439
- Napi::Function::New(env, ActionSendCancelRequest));
440
484
  exports.Set("actionTakeResultRequest",
441
485
  Napi::Function::New(env, ActionTakeResultRequest));
442
486
  exports.Set("actionTakeGoalRequest",
@@ -464,6 +508,12 @@ Napi::Object InitActionServerBindings(Napi::Env env, Napi::Object exports) {
464
508
  Napi::Function::New(env, ActionProcessCancelRequest));
465
509
  exports.Set("actionServerGoalExists",
466
510
  Napi::Function::New(env, ActionServerGoalExists));
511
+ exports.Set("actionTakeCancelRequest",
512
+ Napi::Function::New(env, ActionTakeCancelRequest));
513
+ #if ROS_VERSION >= 2505 // ROS2 >= Kilted
514
+ exports.Set("configureActionServerIntrospection",
515
+ Napi::Function::New(env, ConfigureActionServerIntrospection));
516
+ #endif // ROS_VERSION >= 2505
467
517
  return exports;
468
518
  }
469
519
 
@@ -0,0 +1,294 @@
1
+ // Copyright (c) 2025, The Robot Web Tools Contributors
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
+ #include "rcl_event_handle_bindings.h"
16
+
17
+ #include <rcl/error_handling.h>
18
+ #include <rcl/rcl.h>
19
+
20
+ #include "rcl_handle.h"
21
+
22
+ namespace {
23
+
24
+ typedef union event_callback_data {
25
+ // Subscription events
26
+ rmw_requested_deadline_missed_status_t requested_deadline_missed;
27
+ rmw_liveliness_changed_status_t liveliness_changed;
28
+ rmw_message_lost_status_t message_lost;
29
+ rmw_requested_qos_incompatible_event_status_t requested_incompatible_qos;
30
+ rmw_matched_status_t subscription_matched;
31
+ // Publisher events
32
+ rmw_offered_deadline_missed_status_t offered_deadline_missed;
33
+ rmw_liveliness_lost_status_t liveliness_lost;
34
+ rmw_offered_qos_incompatible_event_status_t offered_incompatible_qos;
35
+ rmw_matched_status_t publisher_matched;
36
+
37
+ rmw_incompatible_type_status_t incompatible_type;
38
+ } event_callback_data_t;
39
+
40
+ rcl_event_t* CreateEventHandle() {
41
+ rcl_event_t* event =
42
+ reinterpret_cast<rcl_event_t*>(malloc(sizeof(rcl_event_t)));
43
+ *event = rcl_get_zero_initialized_event();
44
+ return event;
45
+ }
46
+
47
+ Napi::Value CreateJSObjectForSubscriptionEvent(
48
+ Napi::Env env, rcl_subscription_event_type_t subscription_event_type,
49
+ const event_callback_data_t& data) {
50
+ Napi::Object obj = Napi::Object::New(env);
51
+ switch (subscription_event_type) {
52
+ case RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED: {
53
+ obj.Set(
54
+ "total_count",
55
+ Napi::Number::New(env, data.requested_deadline_missed.total_count));
56
+ obj.Set("total_count_change",
57
+ Napi::Number::New(
58
+ env, data.requested_deadline_missed.total_count_change));
59
+ break;
60
+ }
61
+ case RCL_SUBSCRIPTION_LIVELINESS_CHANGED: {
62
+ obj.Set("alive_count",
63
+ Napi::Number::New(env, data.liveliness_changed.alive_count));
64
+ obj.Set("not_alive_count",
65
+ Napi::Number::New(env, data.liveliness_changed.not_alive_count));
66
+ obj.Set(
67
+ "alive_count_change",
68
+ Napi::Number::New(env, data.liveliness_changed.alive_count_change));
69
+ obj.Set("not_alive_count_change",
70
+ Napi::Number::New(
71
+ env, data.liveliness_changed.not_alive_count_change));
72
+ break;
73
+ }
74
+ case RCL_SUBSCRIPTION_MESSAGE_LOST: {
75
+ obj.Set("total_count",
76
+ Napi::Number::New(env, data.message_lost.total_count));
77
+ obj.Set("total_count_change",
78
+ Napi::Number::New(env, data.message_lost.total_count_change));
79
+ break;
80
+ }
81
+ case RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS: {
82
+ obj.Set(
83
+ "total_count",
84
+ Napi::Number::New(env, data.requested_incompatible_qos.total_count));
85
+ obj.Set("total_count_change",
86
+ Napi::Number::New(
87
+ env, data.requested_incompatible_qos.total_count_change));
88
+ obj.Set("last_policy_kind",
89
+ Napi::Number::New(
90
+ env, data.requested_incompatible_qos.last_policy_kind));
91
+ break;
92
+ }
93
+ case RCL_SUBSCRIPTION_INCOMPATIBLE_TYPE: {
94
+ obj.Set("total_count",
95
+ Napi::Number::New(env, data.incompatible_type.total_count));
96
+ obj.Set(
97
+ "total_count_change",
98
+ Napi::Number::New(env, data.incompatible_type.total_count_change));
99
+ break;
100
+ }
101
+ case RCL_SUBSCRIPTION_MATCHED: {
102
+ obj.Set("total_count",
103
+ Napi::Number::New(env, data.subscription_matched.total_count));
104
+ obj.Set(
105
+ "total_count_change",
106
+ Napi::Number::New(env, data.subscription_matched.total_count_change));
107
+ obj.Set("current_count",
108
+ Napi::Number::New(env, data.subscription_matched.current_count));
109
+ obj.Set("current_count_change",
110
+ Napi::Number::New(
111
+ env, data.subscription_matched.current_count_change));
112
+ break;
113
+ }
114
+ default:
115
+ break;
116
+ }
117
+ return obj;
118
+ }
119
+
120
+ Napi::Value CreateJSObjectForPublisherEvent(
121
+ Napi::Env env, rcl_publisher_event_type_t publisher_event_type,
122
+ const event_callback_data_t& data) {
123
+ Napi::Object obj = Napi::Object::New(env);
124
+ switch (publisher_event_type) {
125
+ case RCL_PUBLISHER_OFFERED_DEADLINE_MISSED: {
126
+ obj.Set("total_count",
127
+ Napi::Number::New(env, data.offered_deadline_missed.total_count));
128
+ obj.Set("total_count_change",
129
+ Napi::Number::New(
130
+ env, data.offered_deadline_missed.total_count_change));
131
+ break;
132
+ }
133
+ case RCL_PUBLISHER_LIVELINESS_LOST: {
134
+ obj.Set("total_count",
135
+ Napi::Number::New(env, data.liveliness_lost.total_count));
136
+ obj.Set("total_count_change",
137
+ Napi::Number::New(env, data.liveliness_lost.total_count_change));
138
+ break;
139
+ }
140
+ case RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS: {
141
+ obj.Set(
142
+ "total_count",
143
+ Napi::Number::New(env, data.offered_incompatible_qos.total_count));
144
+ obj.Set("total_count_change",
145
+ Napi::Number::New(
146
+ env, data.offered_incompatible_qos.total_count_change));
147
+ obj.Set("last_policy_kind",
148
+ Napi::Number::New(
149
+ env, data.offered_incompatible_qos.last_policy_kind));
150
+ break;
151
+ }
152
+ case RCL_PUBLISHER_INCOMPATIBLE_TYPE: {
153
+ obj.Set("total_count",
154
+ Napi::Number::New(env, data.incompatible_type.total_count));
155
+ obj.Set(
156
+ "total_count_change",
157
+ Napi::Number::New(env, data.incompatible_type.total_count_change));
158
+ break;
159
+ }
160
+ case RCL_PUBLISHER_MATCHED: {
161
+ obj.Set("total_count",
162
+ Napi::Number::New(env, data.publisher_matched.total_count));
163
+ obj.Set(
164
+ "total_count_change",
165
+ Napi::Number::New(env, data.publisher_matched.total_count_change));
166
+ obj.Set("current_count",
167
+ Napi::Number::New(env, data.publisher_matched.current_count));
168
+ obj.Set(
169
+ "current_count_change",
170
+ Napi::Number::New(env, data.publisher_matched.current_count_change));
171
+ break;
172
+ }
173
+ default:
174
+ break;
175
+ }
176
+ return obj;
177
+ }
178
+
179
+ } // namespace
180
+
181
+ namespace rclnodejs {
182
+
183
+ Napi::Value CreateSubscriptionEventHandle(const Napi::CallbackInfo& info) {
184
+ Napi::Env env = info.Env();
185
+ RclHandle* subscription_handle =
186
+ RclHandle::Unwrap(info[0].As<Napi::Object>());
187
+ rcl_subscription_t* subscription =
188
+ reinterpret_cast<rcl_subscription_t*>(subscription_handle->ptr());
189
+ rcl_subscription_event_type_t event_type =
190
+ static_cast<rcl_subscription_event_type_t>(
191
+ info[1].As<Napi::Number>().Int32Value());
192
+
193
+ rcl_event_t* event = CreateEventHandle();
194
+ rcl_ret_t ret = rcl_subscription_event_init(event, subscription, event_type);
195
+
196
+ if (ret != RCL_RET_OK) {
197
+ Napi::Error::New(env, "failed to create subscription event")
198
+ .ThrowAsJavaScriptException();
199
+ rcl_reset_error();
200
+ free(event);
201
+ return env.Undefined();
202
+ }
203
+
204
+ auto js_obj = RclHandle::NewInstance(env, event, nullptr, [env](void* ptr) {
205
+ rcl_event_t* event = reinterpret_cast<rcl_event_t*>(ptr);
206
+ rcl_ret_t ret = rcl_event_fini(event);
207
+ if (ret != RCL_RET_OK) {
208
+ Napi::Error::New(env, rcl_get_error_string().str)
209
+ .ThrowAsJavaScriptException();
210
+ rcl_reset_error();
211
+ }
212
+ free(ptr);
213
+ });
214
+ return js_obj;
215
+ }
216
+
217
+ Napi::Value CreatePublisherEventHandle(const Napi::CallbackInfo& info) {
218
+ Napi::Env env = info.Env();
219
+ RclHandle* publisher_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
220
+ rcl_publisher_t* publisher =
221
+ reinterpret_cast<rcl_publisher_t*>(publisher_handle->ptr());
222
+ rcl_publisher_event_type_t event_type =
223
+ static_cast<rcl_publisher_event_type_t>(
224
+ info[1].As<Napi::Number>().Int32Value());
225
+
226
+ rcl_event_t* event = CreateEventHandle();
227
+ rcl_ret_t ret = rcl_publisher_event_init(event, publisher, event_type);
228
+
229
+ if (ret != RCL_RET_OK) {
230
+ Napi::Error::New(env, "failed to create publisher event")
231
+ .ThrowAsJavaScriptException();
232
+ rcl_reset_error();
233
+ free(event);
234
+ return env.Undefined();
235
+ }
236
+
237
+ auto js_obj = RclHandle::NewInstance(env, event, nullptr, [env](void* ptr) {
238
+ rcl_event_t* event = reinterpret_cast<rcl_event_t*>(ptr);
239
+ rcl_ret_t ret = rcl_event_fini(event);
240
+ if (ret != RCL_RET_OK) {
241
+ Napi::Error::New(env, rcl_get_error_string().str)
242
+ .ThrowAsJavaScriptException();
243
+ rcl_reset_error();
244
+ }
245
+ free(ptr);
246
+ });
247
+ return js_obj;
248
+ }
249
+
250
+ Napi::Value TakeEvent(const Napi::CallbackInfo& info) {
251
+ Napi::Env env = info.Env();
252
+ RclHandle* event_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
253
+ rcl_event_t* event = reinterpret_cast<rcl_event_t*>(event_handle->ptr());
254
+ auto event_type = info[1].As<Napi::Object>();
255
+
256
+ event_callback_data_t data;
257
+ rcl_ret_t ret;
258
+ if (event_type.Has("subscription_event_type")) {
259
+ rcl_subscription_event_type_t subscription_event_type =
260
+ static_cast<rcl_subscription_event_type_t>(
261
+ event_type.Get("subscription_event_type")
262
+ .As<Napi::Number>()
263
+ .Int32Value());
264
+ ret = rcl_take_event(event, &data);
265
+ if (RCL_RET_OK == ret) {
266
+ return CreateJSObjectForSubscriptionEvent(env, subscription_event_type,
267
+ data);
268
+ }
269
+ } else if (event_type.Has("publisher_event_type")) {
270
+ rcl_publisher_event_type_t publisher_event_type =
271
+ static_cast<rcl_publisher_event_type_t>(
272
+ event_type.Get("publisher_event_type")
273
+ .As<Napi::Number>()
274
+ .Int32Value());
275
+ ret = rcl_take_event(event, &data);
276
+ if (RCL_RET_OK == ret) {
277
+ return CreateJSObjectForPublisherEvent(env, publisher_event_type, data);
278
+ }
279
+ }
280
+
281
+ Napi::Error::New(env, "failed to take event").ThrowAsJavaScriptException();
282
+ return env.Undefined();
283
+ }
284
+
285
+ Napi::Object InitEventHandleBindings(Napi::Env env, Napi::Object exports) {
286
+ exports.Set("createSubscriptionEventHandle",
287
+ Napi::Function::New(env, CreateSubscriptionEventHandle));
288
+ exports.Set("createPublisherEventHandle",
289
+ Napi::Function::New(env, CreatePublisherEventHandle));
290
+ exports.Set("takeEvent", Napi::Function::New(env, TakeEvent));
291
+ return exports;
292
+ }
293
+
294
+ } // namespace rclnodejs
@@ -0,0 +1,26 @@
1
+ // Copyright (c) 2025, The Robot Web Tools Contributors
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
+ #ifndef SRC_RCL_EVENT_HANDLE_BINDINGS_H_
16
+ #define SRC_RCL_EVENT_HANDLE_BINDINGS_H_
17
+
18
+ #include <napi.h>
19
+
20
+ namespace rclnodejs {
21
+
22
+ Napi::Object InitEventHandleBindings(Napi::Env env, Napi::Object exports);
23
+
24
+ }
25
+
26
+ #endif // SRC_RCL_EVENT_HANDLE_BINDINGS_H_
@@ -360,6 +360,18 @@ Napi::Value GetLifecycleShutdownTransitionLabel(
360
360
  return Napi::String::New(env, rcl_lifecycle_shutdown_label);
361
361
  }
362
362
 
363
+ Napi::Value IsInitialized(const Napi::CallbackInfo& info) {
364
+ Napi::Env env = info.Env();
365
+ RclHandle* state_machine_handle =
366
+ RclHandle::Unwrap(info[0].As<Napi::Object>());
367
+ rcl_lifecycle_state_machine_t* state_machine =
368
+ reinterpret_cast<rcl_lifecycle_state_machine_t*>(
369
+ state_machine_handle->ptr());
370
+ const bool is_initialized =
371
+ RCL_RET_OK == rcl_lifecycle_state_machine_is_initialized(state_machine);
372
+ return Napi::Boolean::New(env, is_initialized);
373
+ }
374
+
363
375
  Napi::Object InitLifecycleBindings(Napi::Env env, Napi::Object exports) {
364
376
  exports.Set("createLifecycleStateMachine",
365
377
  Napi::Function::New(env, CreateLifecycleStateMachine));
@@ -383,6 +395,7 @@ Napi::Object InitLifecycleBindings(Napi::Env env, Napi::Object exports) {
383
395
  Napi::Function::New(env, GetLifecycleTransitionIdToLabel));
384
396
  exports.Set("getLifecycleShutdownTransitionLabel",
385
397
  Napi::Function::New(env, GetLifecycleShutdownTransitionLabel));
398
+ exports.Set("isInitialized", Napi::Function::New(env, IsInitialized));
386
399
  return exports;
387
400
  }
388
401