rclnodejs 1.1.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.
package/lib/publisher.js CHANGED
@@ -24,8 +24,12 @@ const Entity = require('./entity.js');
24
24
  */
25
25
 
26
26
  class Publisher extends Entity {
27
- constructor(handle, typeClass, topic, options) {
27
+ constructor(handle, typeClass, topic, options, node, eventCallbacks) {
28
28
  super(handle, typeClass, options);
29
+ if (node && eventCallbacks) {
30
+ this._events = eventCallbacks.createEventHandlers(this.handle);
31
+ node._events.push(...this._events);
32
+ }
29
33
  }
30
34
 
31
35
  /**
@@ -60,17 +64,24 @@ class Publisher extends Entity {
60
64
  debug(`Message of topic ${this.topic} has been published.`);
61
65
  }
62
66
 
63
- static createPublisher(nodeHandle, typeClass, topic, options) {
67
+ static createPublisher(node, typeClass, topic, options, eventCallbacks) {
64
68
  let type = typeClass.type();
65
69
  let handle = rclnodejs.createPublisher(
66
- nodeHandle,
70
+ node.handle,
67
71
  type.pkgName,
68
72
  type.subFolder,
69
73
  type.interfaceName,
70
74
  topic,
71
75
  options.qos
72
76
  );
73
- return new Publisher(handle, typeClass, topic, options);
77
+ return new Publisher(
78
+ handle,
79
+ typeClass,
80
+ topic,
81
+ options,
82
+ node,
83
+ eventCallbacks
84
+ );
74
85
  }
75
86
 
76
87
  /**
@@ -99,6 +110,22 @@ class Publisher extends Entity {
99
110
  waitForAllAcked(timeout) {
100
111
  return rclnodejs.waitForAllAcked(this._handle, timeout);
101
112
  }
113
+
114
+ /**
115
+ * Get the event handlers for this publisher.
116
+ * @returns {Array} The array of event handlers for this publisher.
117
+ */
118
+ get events() {
119
+ return this._events;
120
+ }
121
+
122
+ /**
123
+ * Set the event handlers for this publisher.
124
+ * @param {Array} events - The array of event handlers to be set for this publisher.
125
+ */
126
+ set events(events) {
127
+ this._events = events;
128
+ }
102
129
  }
103
130
 
104
131
  module.exports = Publisher;
@@ -29,11 +29,24 @@ const debug = require('debug')('rclnodejs:subscription');
29
29
  */
30
30
 
31
31
  class Subscription extends Entity {
32
- constructor(handle, typeClass, topic, options, callback) {
32
+ constructor(
33
+ handle,
34
+ typeClass,
35
+ topic,
36
+ options,
37
+ callback,
38
+ node,
39
+ eventCallbacks
40
+ ) {
33
41
  super(handle, typeClass, options);
34
42
  this._topic = topic;
35
43
  this._callback = callback;
36
44
  this._isRaw = options.isRaw || false;
45
+
46
+ if (node && eventCallbacks) {
47
+ this._events = eventCallbacks.createEventHandlers(this.handle);
48
+ node._events.push(...this._events);
49
+ }
37
50
  }
38
51
 
39
52
  processResponse(msg) {
@@ -45,7 +58,14 @@ class Subscription extends Entity {
45
58
  }
46
59
  }
47
60
 
48
- static createSubscription(nodeHandle, typeClass, topic, options, callback) {
61
+ static createSubscription(
62
+ node,
63
+ typeClass,
64
+ topic,
65
+ options,
66
+ callback,
67
+ eventCallbacks
68
+ ) {
49
69
  let type = typeClass.type();
50
70
 
51
71
  // convert contentFilter.parameters to a string[]
@@ -56,14 +76,22 @@ class Subscription extends Entity {
56
76
  }
57
77
 
58
78
  let handle = rclnodejs.createSubscription(
59
- nodeHandle,
79
+ node.handle,
60
80
  type.pkgName,
61
81
  type.subFolder,
62
82
  type.interfaceName,
63
83
  topic,
64
84
  options
65
85
  );
66
- return new Subscription(handle, typeClass, topic, options, callback);
86
+ return new Subscription(
87
+ handle,
88
+ typeClass,
89
+ topic,
90
+ options,
91
+ callback,
92
+ node,
93
+ eventCallbacks
94
+ );
67
95
  }
68
96
 
69
97
  /**
@@ -124,6 +152,22 @@ class Subscription extends Entity {
124
152
  get publisherCount() {
125
153
  return rclnodejs.getPublisherCount(this._handle);
126
154
  }
155
+
156
+ /**
157
+ * Get the event handlers for this subscription.
158
+ * @returns {Array} The array of event handlers for this subscription.
159
+ */
160
+ get events() {
161
+ return this._events;
162
+ }
163
+
164
+ /**
165
+ * Set the event handlers for this subscription.
166
+ * @param {Array} events - The array of event handlers for this subscription.
167
+ */
168
+ set events(events) {
169
+ this._events = events;
170
+ }
127
171
  }
128
172
 
129
173
  module.exports = Subscription;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rclnodejs",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "ROS2.0 JavaScript client with Node.js",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
@@ -1,4 +1,4 @@
1
- # rclnodejs ![GitHub Workflow Status](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-build-and-test.yml/badge.svg?branch=jazzy)
1
+ # rclnodejs [![Linux](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-x64-build-and-test.yml/badge.svg?branch=develop)](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-x64-build-and-test.yml?query=branch%3Adevelop)[![Linux](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-arm64-build-and-test.yml/badge.svg?branch=develop)](https://github.com/RobotWebTools/rclnodejs/actions/workflows/linux-arm64-build-and-test.yml?query=branch%3Adevelop)
2
2
 
3
3
  `rclnodejs` is a Node.js client for the Robot Operating System (ROS 2). It provides a simple and easy JavaScript API for ROS 2 programming. TypeScript declarations are included to support use of rclnodejs in TypeScript projects.
4
4
 
@@ -22,8 +22,8 @@ rclnodejs.init().then(() => {
22
22
 
23
23
  **ROS 2 SDK**
24
24
 
25
- - See the ROS 2 SDK [Installation Guide](https://docs.ros.org/en/jazzy/Installation.html) for details.
26
- - **DON'T FORGET TO [SOURCE THE ROS 2 STARTUP FILES](https://docs.ros.org/en/jazzy/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.html#source-the-setup-files)**
25
+ - See the ROS 2 SDK [Installation Guide](https://docs.ros.org/en/kilted/Installation.html) for details.
26
+ - **DON'T FORGET TO [SOURCE THE ROS 2 STARTUP FILES](https://docs.ros.org/en/kilted/Tutorials/Beginner-CLI-Tools/Configuring-ROS2-Environment.htmls)**
27
27
 
28
28
  ## Install rclnodejs
29
29
 
@@ -45,7 +45,7 @@ npm i rclnodejs@x.y.z
45
45
 
46
46
  | RCLNODEJS Version | Compatible ROS 2 LTS |
47
47
  | :----------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
48
- | latest version (currently [v1.1.0](https://github.com/RobotWebTools/rclnodejs/tree/1.1.0)) | [Kilted](https://github.com/RobotWebTools/rclnodejs/tree/kilted)<br>[Jazzy](https://github.com/RobotWebTools/rclnodejs/tree/jazzy)<br>[Humble](https://github.com/RobotWebTools/rclnodejs/tree/humble-hawksbill) |
48
+ | latest version (currently [v1.2.0](https://github.com/RobotWebTools/rclnodejs/tree/1.2.0)) | [Kilted](https://github.com/RobotWebTools/rclnodejs/tree/kilted)<br>[Jazzy](https://github.com/RobotWebTools/rclnodejs/tree/jazzy)<br>[Humble](https://github.com/RobotWebTools/rclnodejs/tree/humble-hawksbill) |
49
49
 
50
50
  ## Documentation
51
51
 
package/src/addon.cpp CHANGED
@@ -35,6 +35,7 @@
35
35
  #include "rcl_time_point_bindings.h"
36
36
  #include "rcl_timer_bindings.h"
37
37
  #if ROS_VERSION > 2205 // ROS2 > Humble
38
+ #include "rcl_event_handle_bindings.h"
38
39
  #include "rcl_type_description_service_bindings.h"
39
40
  #endif
40
41
  #include "rcl_utilities.h"
@@ -84,6 +85,7 @@ Napi::Object InitModule(Napi::Env env, Napi::Object exports) {
84
85
  rclnodejs::InitTimerBindings(env, exports);
85
86
  #if ROS_VERSION > 2205 // ROS2 > Humble
86
87
  rclnodejs::InitTypeDescriptionServiceBindings(env, exports);
88
+ rclnodejs::InitEventHandleBindings(env, exports);
87
89
  #endif
88
90
  rclnodejs::InitLifecycleBindings(env, exports);
89
91
  rclnodejs::ShadowNode::Init(env, exports);
package/src/executor.cpp CHANGED
@@ -190,10 +190,11 @@ RclResult Executor::WaitForReadyCallbacks(rcl_wait_set_t* wait_set,
190
190
  size_t num_timers = 0u;
191
191
  size_t num_clients = 0u;
192
192
  size_t num_services = 0u;
193
+ size_t num_events = 0u;
193
194
 
194
195
  rcl_ret_t get_entity_ret = handle_manager_->GetEntityCounts(
195
196
  &num_subscriptions, &num_guard_conditions, &num_timers, &num_clients,
196
- &num_services);
197
+ &num_services, &num_events);
197
198
  if (get_entity_ret != RCL_RET_OK) {
198
199
  std::string error_message = std::string("Failed to get entity counts: ") +
199
200
  std::string(rcl_get_error_string().str);
@@ -202,9 +203,7 @@ RclResult Executor::WaitForReadyCallbacks(rcl_wait_set_t* wait_set,
202
203
 
203
204
  rcl_ret_t resize_ret =
204
205
  rcl_wait_set_resize(wait_set, num_subscriptions, num_guard_conditions,
205
- num_timers, num_clients, num_services,
206
- // TODO(minggang): support events.
207
- 0u);
206
+ num_timers, num_clients, num_services, num_events);
208
207
  if (resize_ret != RCL_RET_OK) {
209
208
  std::string error_message = std::string("Failed to resize: ") +
210
209
  std::string(rcl_get_error_string().str);
@@ -40,7 +40,6 @@ HandleManager::~HandleManager() {
40
40
 
41
41
  void HandleManager::SynchronizeHandles(const Napi::Object& node) {
42
42
  Napi::HandleScope scope(node.Env());
43
-
44
43
  Napi::Value timers = node.Get("_timers");
45
44
  Napi::Value subscriptions = node.Get("_subscriptions");
46
45
  Napi::Value clients = node.Get("_clients");
@@ -48,6 +47,7 @@ void HandleManager::SynchronizeHandles(const Napi::Object& node) {
48
47
  Napi::Value guard_conditions = node.Get("_guards");
49
48
  Napi::Value action_clients = node.Get("_actionClients");
50
49
  Napi::Value action_servers = node.Get("_actionServers");
50
+ Napi::Value events = node.Get("_events");
51
51
 
52
52
  uint32_t sum = 0;
53
53
  is_synchronizing_.store(true);
@@ -66,6 +66,7 @@ void HandleManager::SynchronizeHandles(const Napi::Object& node) {
66
66
  &action_clients_);
67
67
  sum += SynchronizeHandlesByType(action_servers.As<Napi::Object>(),
68
68
  &action_servers_);
69
+ sum += SynchronizeHandlesByType(events.As<Napi::Object>(), &events_);
69
70
  }
70
71
  is_synchronizing_.store(false);
71
72
 
@@ -98,6 +99,7 @@ void HandleManager::ClearHandles() {
98
99
  guard_conditions_.clear();
99
100
  action_clients_.clear();
100
101
  action_servers_.clear();
102
+ events_.clear();
101
103
  }
102
104
 
103
105
  rcl_ret_t HandleManager::AddHandlesToWaitSet(rcl_wait_set_t* wait_set) {
@@ -152,6 +154,11 @@ rcl_ret_t HandleManager::AddHandlesToWaitSet(rcl_wait_set_t* wait_set) {
152
154
  if (ret != RCL_RET_OK) return ret;
153
155
  }
154
156
 
157
+ for (auto& event : events_) {
158
+ rcl_event_t* rcl_event = reinterpret_cast<rcl_event_t*>(event->ptr());
159
+ rcl_ret_t ret = rcl_wait_set_add_event(wait_set, rcl_event, nullptr);
160
+ if (ret != RCL_RET_OK) return ret;
161
+ }
155
162
  return RCL_RET_OK;
156
163
  }
157
164
 
@@ -169,6 +176,8 @@ rcl_ret_t HandleManager::CollectReadyHandles(rcl_wait_set_t* wait_set) {
169
176
  CollectReadyHandlesByType(wait_set->guard_conditions,
170
177
  wait_set->size_of_guard_conditions,
171
178
  guard_conditions_, &ready_handles);
179
+ CollectReadyHandlesByType(wait_set->events, wait_set->size_of_events, events_,
180
+ &ready_handles);
172
181
 
173
182
  rcl_ret_t ret = CollectReadyActionHandles(wait_set, &ready_handles);
174
183
  if (!ready_handles.empty()) {
@@ -184,7 +193,8 @@ rcl_ret_t HandleManager::GetEntityCounts(size_t* subscriptions_size,
184
193
  size_t* guard_conditions_size,
185
194
  size_t* timers_size,
186
195
  size_t* clients_size,
187
- size_t* services_size) {
196
+ size_t* services_size,
197
+ size_t* events_size) {
188
198
  size_t num_subscriptions = 0u;
189
199
  size_t num_guard_conditions = 0u;
190
200
  size_t num_timers = 0u;
@@ -230,6 +240,7 @@ rcl_ret_t HandleManager::GetEntityCounts(size_t* subscriptions_size,
230
240
  *timers_size += timer_count();
231
241
  *clients_size += client_count();
232
242
  *services_size += service_count();
243
+ *events_size += event_count();
233
244
 
234
245
  return RCL_RET_OK;
235
246
  }
@@ -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_;
@@ -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_
@@ -446,6 +446,10 @@ Napi::Value GetFullyQualifiedName(const Napi::CallbackInfo& info) {
446
446
  return Napi::String::New(env, fully_qualified_node_name);
447
447
  }
448
448
 
449
+ Napi::Value GetRMWImplementationIdentifier(const Napi::CallbackInfo& info) {
450
+ return Napi::String::New(info.Env(), rmw_get_implementation_identifier());
451
+ }
452
+
449
453
  Napi::Object InitNodeBindings(Napi::Env env, Napi::Object exports) {
450
454
  exports.Set("getParameterOverrides",
451
455
  Napi::Function::New(env, GetParameterOverrides));
@@ -468,6 +472,8 @@ Napi::Object InitNodeBindings(Napi::Env env, Napi::Object exports) {
468
472
  exports.Set("getNodeNames", Napi::Function::New(env, GetNodeNames));
469
473
  exports.Set("getFullyQualifiedName",
470
474
  Napi::Function::New(env, GetFullyQualifiedName));
475
+ exports.Set("getRMWImplementationIdentifier",
476
+ Napi::Function::New(env, GetRMWImplementationIdentifier));
471
477
  return exports;
472
478
  }
473
479