rclnodejs 1.8.1 → 1.8.3

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.
Files changed (50) hide show
  1. package/index.js +33 -23
  2. package/lib/action/client.js +6 -0
  3. package/lib/action/server.js +1 -3
  4. package/lib/distro.js +2 -1
  5. package/lib/lifecycle.js +2 -1
  6. package/lib/lifecycle_publisher.js +2 -2
  7. package/lib/node.js +37 -11
  8. package/lib/parameter.js +6 -10
  9. package/lib/service.js +8 -4
  10. package/lib/time_source.js +3 -20
  11. package/package.json +4 -4
  12. package/prebuilds/linux-arm64/humble-jammy-arm64-rclnodejs.node +0 -0
  13. package/prebuilds/linux-arm64/jazzy-noble-arm64-rclnodejs.node +0 -0
  14. package/prebuilds/linux-arm64/kilted-noble-arm64-rclnodejs.node +0 -0
  15. package/prebuilds/linux-x64/humble-jammy-x64-rclnodejs.node +0 -0
  16. package/prebuilds/linux-x64/jazzy-noble-x64-rclnodejs.node +0 -0
  17. package/prebuilds/linux-x64/kilted-noble-x64-rclnodejs.node +0 -0
  18. package/rosidl_gen/generate_worker.js +3 -13
  19. package/rosidl_gen/idl_generator.js +210 -0
  20. package/rosidl_gen/index.js +3 -12
  21. package/rosidl_gen/packages.js +7 -4
  22. package/rosidl_gen/primitive_types.js +2 -2
  23. package/rosidl_parser/idl_parser.py +437 -0
  24. package/rosidl_parser/parser.py +2 -4
  25. package/rosidl_parser/rosidl_parser.js +27 -0
  26. package/src/executor.cpp +1 -0
  27. package/src/macros.h +2 -2
  28. package/src/rcl_action_client_bindings.cpp +18 -11
  29. package/src/rcl_action_server_bindings.cpp +24 -13
  30. package/src/rcl_bindings.cpp +1 -1
  31. package/src/rcl_client_bindings.cpp +13 -5
  32. package/src/rcl_context_bindings.cpp +7 -8
  33. package/src/rcl_guard_condition_bindings.cpp +12 -3
  34. package/src/rcl_lifecycle_bindings.cpp +53 -11
  35. package/src/rcl_node_bindings.cpp +11 -4
  36. package/src/rcl_publisher_bindings.cpp +12 -3
  37. package/src/rcl_service_bindings.cpp +12 -3
  38. package/src/rcl_subscription_bindings.cpp +24 -21
  39. package/src/rcl_timer_bindings.cpp +24 -9
  40. package/src/rcl_type_description_service_bindings.cpp +9 -1
  41. package/test_data_integrity.js +108 -0
  42. package/test_repro_exact.js +57 -0
  43. package/test_repro_hz.js +86 -0
  44. package/test_repro_pub.js +36 -0
  45. package/test_repro_stress.js +83 -0
  46. package/test_repro_sub.js +64 -0
  47. package/test_xproc_data.js +64 -0
  48. package/rosidl_convertor/README.md +0 -298
  49. package/rosidl_convertor/idl_convertor.js +0 -50
  50. package/rosidl_convertor/idl_convertor.py +0 -1250
@@ -76,10 +76,18 @@ Napi::Value ActionCreateServer(const Napi::CallbackInfo& info) {
76
76
  malloc(sizeof(rcl_action_server_t)));
77
77
  *action_server = rcl_action_get_zero_initialized_server();
78
78
 
79
- THROW_ERROR_IF_NOT_EQUAL(
80
- rcl_action_server_init(action_server, node, clock, ts,
81
- action_name.c_str(), &action_server_ops),
82
- RCL_RET_OK, rcl_get_error_string().str);
79
+ {
80
+ rcl_ret_t ret =
81
+ rcl_action_server_init(action_server, node, clock, ts,
82
+ action_name.c_str(), &action_server_ops);
83
+ if (RCL_RET_OK != ret) {
84
+ std::string error_msg = rcl_get_error_string().str;
85
+ rcl_reset_error();
86
+ free(action_server);
87
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
88
+ return env.Undefined();
89
+ }
90
+ }
83
91
  auto js_obj = RclHandle::NewInstance(
84
92
  env, action_server, node_handle, [node, env](void* ptr) {
85
93
  rcl_action_server_t* action_server =
@@ -118,6 +126,7 @@ Napi::Value ActionTakeResultRequest(const Napi::CallbackInfo& info) {
118
126
  return js_obj;
119
127
  }
120
128
 
129
+ free(header);
121
130
  return env.Undefined();
122
131
  }
123
132
 
@@ -140,6 +149,7 @@ Napi::Value ActionTakeGoalRequest(const Napi::CallbackInfo& info) {
140
149
  return js_obj;
141
150
  }
142
151
 
152
+ free(header);
143
153
  return env.Undefined();
144
154
  }
145
155
 
@@ -213,14 +223,14 @@ Napi::Value ActionTakeGoalResponse(const Napi::CallbackInfo& info) {
213
223
  free(header);
214
224
 
215
225
  if (ret != RCL_RET_OK && ret != RCL_RET_ACTION_CLIENT_TAKE_FAILED) {
226
+ std::string error_msg = rcl_get_error_string().str;
216
227
  rcl_reset_error();
217
- Napi::Error::New(env, rcl_get_error_string().str)
218
- .ThrowAsJavaScriptException();
228
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
219
229
  return env.Undefined();
220
230
  }
221
231
 
222
232
  if (ret != RCL_RET_ACTION_CLIENT_TAKE_FAILED) {
223
- return Napi::Number::New(env, static_cast<int32_t>(sequence_number));
233
+ return Napi::Number::New(env, static_cast<double>(sequence_number));
224
234
  }
225
235
  return env.Undefined();
226
236
  }
@@ -242,14 +252,14 @@ Napi::Value ActionTakeCancelResponse(const Napi::CallbackInfo& info) {
242
252
  free(header);
243
253
 
244
254
  if (ret != RCL_RET_OK && ret != RCL_RET_ACTION_CLIENT_TAKE_FAILED) {
255
+ std::string error_msg = rcl_get_error_string().str;
245
256
  rcl_reset_error();
246
- Napi::Error::New(env, rcl_get_error_string().str)
247
- .ThrowAsJavaScriptException();
257
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
248
258
  return env.Undefined();
249
259
  }
250
260
 
251
261
  if (ret != RCL_RET_ACTION_CLIENT_TAKE_FAILED) {
252
- return Napi::Number::New(env, static_cast<int32_t>(sequence_number));
262
+ return Napi::Number::New(env, static_cast<double>(sequence_number));
253
263
  }
254
264
  return env.Undefined();
255
265
  }
@@ -271,14 +281,14 @@ Napi::Value ActionTakeResultResponse(const Napi::CallbackInfo& info) {
271
281
  free(header);
272
282
 
273
283
  if (ret != RCL_RET_OK && ret != RCL_RET_ACTION_CLIENT_TAKE_FAILED) {
284
+ std::string error_msg = rcl_get_error_string().str;
274
285
  rcl_reset_error();
275
- Napi::Error::New(env, rcl_get_error_string().str)
276
- .ThrowAsJavaScriptException();
286
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
277
287
  return env.Undefined();
278
288
  }
279
289
 
280
290
  if (ret != RCL_RET_ACTION_CLIENT_TAKE_FAILED) {
281
- return Napi::Number::New(env, static_cast<int32_t>(sequence_number));
291
+ return Napi::Number::New(env, static_cast<double>(sequence_number));
282
292
  }
283
293
  return env.Undefined();
284
294
  }
@@ -455,6 +465,7 @@ Napi::Value ActionTakeCancelRequest(const Napi::CallbackInfo& info) {
455
465
  return js_obj;
456
466
  }
457
467
 
468
+ free(header);
458
469
  return env.Undefined();
459
470
  }
460
471
 
@@ -14,7 +14,7 @@
14
14
 
15
15
  #include "rcl_bindings.h"
16
16
 
17
- #include <node.h>
17
+ #include <node_version.h>
18
18
  #include <rcl/arguments.h>
19
19
  #include <rcl/rcl.h>
20
20
 
@@ -49,9 +49,17 @@ Napi::Value CreateClient(const Napi::CallbackInfo& info) {
49
49
  client_ops.qos = *qos_profile;
50
50
  }
51
51
 
52
- THROW_ERROR_IF_NOT_EQUAL(
53
- rcl_client_init(client, node, ts, service_name.c_str(), &client_ops),
54
- RCL_RET_OK, rcl_get_error_string().str);
52
+ {
53
+ rcl_ret_t ret =
54
+ rcl_client_init(client, node, ts, service_name.c_str(), &client_ops);
55
+ if (RCL_RET_OK != ret) {
56
+ std::string error_msg = rcl_get_error_string().str;
57
+ rcl_reset_error();
58
+ free(client);
59
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
60
+ return env.Undefined();
61
+ }
62
+ }
55
63
 
56
64
  auto js_obj = RclHandle::NewInstance(
57
65
  env, client, node_handle, [node, env](void* ptr) {
@@ -80,7 +88,7 @@ Napi::Value SendRequest(const Napi::CallbackInfo& info) {
80
88
  THROW_ERROR_IF_NOT_EQUAL(rcl_send_request(client, buffer, &sequence_number),
81
89
  RCL_RET_OK, rcl_get_error_string().str);
82
90
 
83
- return Napi::Number::New(env, static_cast<uint32_t>(sequence_number));
91
+ return Napi::Number::New(env, static_cast<double>(sequence_number));
84
92
  }
85
93
 
86
94
  Napi::Value RclTakeResponse(const Napi::CallbackInfo& info) {
@@ -96,7 +104,7 @@ Napi::Value RclTakeResponse(const Napi::CallbackInfo& info) {
96
104
  int64_t sequence_number = header.request_id.sequence_number;
97
105
 
98
106
  if (ret == RCL_RET_OK) {
99
- return Napi::Number::New(env, static_cast<uint32_t>(sequence_number));
107
+ return Napi::Number::New(env, static_cast<double>(sequence_number));
100
108
  }
101
109
 
102
110
  rcl_reset_error();
@@ -120,14 +120,13 @@ Napi::Value CreateContext(const Napi::CallbackInfo& info) {
120
120
  rcl_context_t* context =
121
121
  reinterpret_cast<rcl_context_t*>(malloc(sizeof(rcl_context_t)));
122
122
  *context = rcl_get_zero_initialized_context();
123
- auto js_obj =
124
- RclHandle::NewInstance(env, context, nullptr, [&env](void* ptr) {
125
- rcl_context_t* context = reinterpret_cast<rcl_context_t*>(ptr);
126
- rcl_ret_t ret = DestroyContext(env, context);
127
- free(ptr);
128
- THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
129
- rcl_get_error_string().str);
130
- });
123
+ auto js_obj = RclHandle::NewInstance(env, context, nullptr, [env](void* ptr) {
124
+ rcl_context_t* context = reinterpret_cast<rcl_context_t*>(ptr);
125
+ rcl_ret_t ret = DestroyContext(env, context);
126
+ free(ptr);
127
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
128
+ rcl_get_error_string().str);
129
+ });
131
130
 
132
131
  return js_obj;
133
132
  }
@@ -17,6 +17,8 @@
17
17
  #include <rcl/error_handling.h>
18
18
  #include <rcl/rcl.h>
19
19
 
20
+ #include <string>
21
+
20
22
  #include "macros.h"
21
23
  #include "rcl_handle.h"
22
24
  #include "rcl_utilities.h"
@@ -37,9 +39,16 @@ Napi::Value CreateGuardCondition(const Napi::CallbackInfo& info) {
37
39
  rcl_guard_condition_options_t gc_options =
38
40
  rcl_guard_condition_get_default_options();
39
41
 
40
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK,
41
- rcl_guard_condition_init(gc, context, gc_options),
42
- rcl_get_error_string().str);
42
+ {
43
+ rcl_ret_t ret = rcl_guard_condition_init(gc, context, gc_options);
44
+ if (RCL_RET_OK != ret) {
45
+ std::string error_msg = rcl_get_error_string().str;
46
+ rcl_reset_error();
47
+ free(gc);
48
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
49
+ return env.Undefined();
50
+ }
51
+ }
43
52
 
44
53
  auto handle = RclHandle::NewInstance(env, gc, nullptr, [env](void* ptr) {
45
54
  rcl_guard_condition_t* gc = reinterpret_cast<rcl_guard_condition_t*>(ptr);
@@ -71,16 +71,51 @@ Napi::Value CreateLifecycleStateMachine(const Napi::CallbackInfo& info) {
71
71
  const rosidl_service_type_support_t* gs =
72
72
  GetServiceTypeSupport("lifecycle_msgs", "GetState");
73
73
 
74
- #if ROS_VERSION >= 2105
74
+ #if ROS_VERSION >= 5000 // ROS2 Rolling
75
75
  rcl_lifecycle_state_machine_options_t options =
76
76
  rcl_lifecycle_get_default_state_machine_options();
77
77
  options.enable_com_interface = info[1].As<Napi::Boolean>().Value();
78
78
 
79
- THROW_ERROR_IF_NOT_EQUAL(
80
- RCL_RET_OK,
81
- rcl_lifecycle_state_machine_init(state_machine, node, pn, cs, gs, gas,
82
- gat, gtg, &options),
83
- rcl_get_error_string().str);
79
+ RclHandle* clock_handle = RclHandle::Unwrap(info[2].As<Napi::Object>());
80
+ rcl_clock_t* clock = reinterpret_cast<rcl_clock_t*>(clock_handle->ptr());
81
+
82
+ {
83
+ rcl_ret_t ret = rcl_lifecycle_state_machine_init(
84
+ state_machine, node, clock, pn, cs, gs, gas, gat, gtg, &options);
85
+ if (RCL_RET_OK != ret) {
86
+ std::string error_msg = rcl_get_error_string().str;
87
+ rcl_reset_error();
88
+ free(state_machine);
89
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
90
+ return env.Undefined();
91
+ }
92
+ }
93
+
94
+ auto js_obj = RclHandle::NewInstance(
95
+ env, state_machine, node_handle, [node, env](void* ptr) {
96
+ rcl_lifecycle_state_machine_t* state_machine =
97
+ reinterpret_cast<rcl_lifecycle_state_machine_t*>(ptr);
98
+ rcl_ret_t ret = rcl_lifecycle_state_machine_fini(state_machine, node);
99
+ free(ptr);
100
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
101
+ rcl_get_error_string().str);
102
+ });
103
+ #elif ROS_VERSION >= 2105
104
+ rcl_lifecycle_state_machine_options_t options =
105
+ rcl_lifecycle_get_default_state_machine_options();
106
+ options.enable_com_interface = info[1].As<Napi::Boolean>().Value();
107
+
108
+ {
109
+ rcl_ret_t ret = rcl_lifecycle_state_machine_init(
110
+ state_machine, node, pn, cs, gs, gas, gat, gtg, &options);
111
+ if (RCL_RET_OK != ret) {
112
+ std::string error_msg = rcl_get_error_string().str;
113
+ rcl_reset_error();
114
+ free(state_machine);
115
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
116
+ return env.Undefined();
117
+ }
118
+ }
84
119
 
85
120
  auto js_obj = RclHandle::NewInstance(
86
121
  env, state_machine, node_handle, [node, env](void* ptr) {
@@ -95,11 +130,18 @@ Napi::Value CreateLifecycleStateMachine(const Napi::CallbackInfo& info) {
95
130
  const rcl_node_options_t* node_options =
96
131
  reinterpret_cast<const rcl_node_options_t*>(rcl_node_get_options(node));
97
132
 
98
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK,
99
- rcl_lifecycle_state_machine_init(
100
- state_machine, node, pn, cs, gs, gas, gat, gtg,
101
- true, &node_options->allocator),
102
- rcl_get_error_string().str);
133
+ {
134
+ rcl_ret_t ret = rcl_lifecycle_state_machine_init(
135
+ state_machine, node, pn, cs, gs, gas, gat, gtg, true,
136
+ &node_options->allocator);
137
+ if (RCL_RET_OK != ret) {
138
+ std::string error_msg = rcl_get_error_string().str;
139
+ rcl_reset_error();
140
+ free(state_machine);
141
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
142
+ return env.Undefined();
143
+ }
144
+ }
103
145
 
104
146
  auto js_obj = RclHandle::NewInstance(
105
147
  env, state_machine, node_handle, [node, node_options, env](void* ptr) {
@@ -224,10 +224,17 @@ Napi::Value CreateNode(const Napi::CallbackInfo& info) {
224
224
  options.rosout_qos = *qos_profile;
225
225
  }
226
226
 
227
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK,
228
- rcl_node_init(node, node_name.c_str(),
229
- name_space.c_str(), context, &options),
230
- rcl_get_error_string().str);
227
+ {
228
+ rcl_ret_t ret = rcl_node_init(node, node_name.c_str(), name_space.c_str(),
229
+ context, &options);
230
+ if (RCL_RET_OK != ret) {
231
+ std::string error_msg = rcl_get_error_string().str;
232
+ rcl_reset_error();
233
+ free(node);
234
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
235
+ return env.Undefined();
236
+ }
237
+ }
231
238
 
232
239
  auto handle = RclHandle::NewInstance(env, node, nullptr, [env](void* ptr) {
233
240
  rcl_node_t* node = reinterpret_cast<rcl_node_t*>(ptr);
@@ -51,9 +51,17 @@ Napi::Value CreatePublisher(const Napi::CallbackInfo& info) {
51
51
  publisher_ops.qos = *qos_profile;
52
52
  }
53
53
 
54
- THROW_ERROR_IF_NOT_EQUAL(
55
- rcl_publisher_init(publisher, node, ts, topic.c_str(), &publisher_ops),
56
- RCL_RET_OK, rcl_get_error_string().str);
54
+ {
55
+ rcl_ret_t ret = rcl_publisher_init(publisher, node, ts, topic.c_str(),
56
+ &publisher_ops);
57
+ if (RCL_RET_OK != ret) {
58
+ std::string error_msg = rcl_get_error_string().str;
59
+ rcl_reset_error();
60
+ free(publisher);
61
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
62
+ return env.Undefined();
63
+ }
64
+ }
57
65
 
58
66
  auto js_obj = RclHandle::NewInstance(
59
67
  env, publisher, node_handle, [node, env](void* ptr) {
@@ -66,6 +74,7 @@ Napi::Value CreatePublisher(const Napi::CallbackInfo& info) {
66
74
 
67
75
  return js_obj;
68
76
  } else {
77
+ free(publisher);
69
78
  Napi::Error::New(env, GetErrorMessageAndClear())
70
79
  .ThrowAsJavaScriptException();
71
80
  return env.Undefined();
@@ -52,9 +52,17 @@ Napi::Value CreateService(const Napi::CallbackInfo& info) {
52
52
  service_ops.qos = *qos_profile;
53
53
  }
54
54
 
55
- THROW_ERROR_IF_NOT_EQUAL(
56
- rcl_service_init(service, node, ts, service_name.c_str(), &service_ops),
57
- RCL_RET_OK, rcl_get_error_string().str);
55
+ {
56
+ rcl_ret_t ret = rcl_service_init(service, node, ts, service_name.c_str(),
57
+ &service_ops);
58
+ if (RCL_RET_OK != ret) {
59
+ std::string error_msg = rcl_get_error_string().str;
60
+ rcl_reset_error();
61
+ free(service);
62
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
63
+ return env.Undefined();
64
+ }
65
+ }
58
66
  auto js_obj = RclHandle::NewInstance(
59
67
  env, service, node_handle, [node, env](void* ptr) {
60
68
  rcl_service_t* service = reinterpret_cast<rcl_service_t*>(ptr);
@@ -88,6 +96,7 @@ Napi::Value RclTakeRequest(const Napi::CallbackInfo& info) {
88
96
  return js_obj;
89
97
  }
90
98
 
99
+ free(header);
91
100
  return env.Undefined();
92
101
  }
93
102
 
@@ -19,6 +19,8 @@
19
19
 
20
20
  #include <cstdio>
21
21
  #include <memory>
22
+ #include <rcpputils/scope_exit.hpp>
23
+ // NOLINTNEXTLINE
22
24
  #include <string>
23
25
 
24
26
  #include "macros.h"
@@ -108,12 +110,6 @@ Napi::Value CreateSubscription(const Napi::CallbackInfo& info) {
108
110
  rcl_ret_t ret = rcl_subscription_options_set_content_filter_options(
109
111
  expression.c_str(), argc, (const char**)argv, &subscription_ops);
110
112
 
111
- if (ret != RCL_RET_OK) {
112
- std::string error_string = rcl_get_error_string().str;
113
- rcl_reset_error();
114
- Napi::Error::New(env, error_string).ThrowAsJavaScriptException();
115
- }
116
-
117
113
  if (argc) {
118
114
  for (int i = 0; i < argc; i++) {
119
115
  free(argv[i]);
@@ -122,7 +118,10 @@ Napi::Value CreateSubscription(const Napi::CallbackInfo& info) {
122
118
  }
123
119
 
124
120
  if (ret != RCL_RET_OK) {
121
+ std::string error_string = rcl_get_error_string().str;
122
+ rcl_reset_error();
125
123
  free(subscription);
124
+ Napi::Error::New(env, error_string).ThrowAsJavaScriptException();
126
125
  return env.Undefined();
127
126
  }
128
127
  }
@@ -137,8 +136,8 @@ Napi::Value CreateSubscription(const Napi::CallbackInfo& info) {
137
136
  if (ret != RCL_RET_OK) {
138
137
  std::string error_msg = rcl_get_error_string().str;
139
138
  rcl_reset_error();
140
- Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
141
139
  free(subscription);
140
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
142
141
  return env.Undefined();
143
142
  }
144
143
 
@@ -157,9 +156,9 @@ Napi::Value CreateSubscription(const Napi::CallbackInfo& info) {
157
156
 
158
157
  return js_obj;
159
158
  } else {
160
- Napi::Error::New(env, GetErrorMessageAndClear())
161
- .ThrowAsJavaScriptException();
159
+ std::string error_msg = GetErrorMessageAndClear();
162
160
  free(subscription);
161
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
163
162
  return env.Undefined();
164
163
  }
165
164
  }
@@ -194,10 +193,16 @@ Napi::Value RclTakeRaw(const Napi::CallbackInfo& info) {
194
193
  return env.Undefined();
195
194
  }
196
195
 
196
+ RCPPUTILS_SCOPE_EXIT({
197
+ rcl_ret_t fini_ret = rmw_serialized_message_fini(&msg);
198
+ if (fini_ret != RCL_RET_OK) {
199
+ rcl_reset_error();
200
+ }
201
+ });
202
+
197
203
  Napi::Buffer<char> buffer = Napi::Buffer<char>::Copy(
198
204
  env, reinterpret_cast<char*>(msg.buffer), msg.buffer_length);
199
- THROW_ERROR_IF_NOT_EQUAL(rmw_serialized_message_fini(&msg), RCL_RET_OK,
200
- "Failed to deallocate message buffer");
205
+
201
206
  return buffer;
202
207
  }
203
208
 
@@ -369,6 +374,14 @@ Napi::Value GetContentFilter(const Napi::CallbackInfo& info) {
369
374
  return env.Undefined();
370
375
  }
371
376
 
377
+ RCPPUTILS_SCOPE_EXIT({
378
+ rcl_ret_t fini_ret =
379
+ rcl_subscription_content_filter_options_fini(subscription, &options);
380
+ if (fini_ret != RCL_RET_OK) {
381
+ rcl_reset_error();
382
+ }
383
+ });
384
+
372
385
  // Create result object
373
386
  Napi::Object result = Napi::Object::New(env);
374
387
  result.Set(
@@ -387,16 +400,6 @@ Napi::Value GetContentFilter(const Napi::CallbackInfo& info) {
387
400
  }
388
401
  result.Set("parameters", parameters);
389
402
 
390
- // Cleanup
391
- rcl_ret_t fini_ret =
392
- rcl_subscription_content_filter_options_fini(subscription, &options);
393
- if (fini_ret != RCL_RET_OK) {
394
- std::string error_msg = rcl_get_error_string().str;
395
- rcl_reset_error();
396
- Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
397
- return env.Undefined();
398
- }
399
-
400
403
  return result;
401
404
  }
402
405
 
@@ -19,6 +19,7 @@
19
19
 
20
20
  #include <memory>
21
21
  #include <mutex>
22
+ #include <string>
22
23
  #include <unordered_map>
23
24
 
24
25
  #include "macros.h"
@@ -82,16 +83,30 @@ Napi::Value CreateTimer(const Napi::CallbackInfo& info) {
82
83
  *timer = rcl_get_zero_initialized_timer();
83
84
 
84
85
  #if ROS_VERSION > 2305 // After Iron.
85
- THROW_ERROR_IF_NOT_EQUAL(
86
- RCL_RET_OK,
87
- rcl_timer_init2(timer, clock, context, period_nsec, nullptr,
88
- rcl_get_default_allocator(), /*autostart=*/true),
89
- rcl_get_error_string().str);
86
+ {
87
+ rcl_ret_t ret = rcl_timer_init2(timer, clock, context, period_nsec, nullptr,
88
+ rcl_get_default_allocator(),
89
+ /*autostart=*/true);
90
+ if (RCL_RET_OK != ret) {
91
+ std::string error_msg = rcl_get_error_string().str;
92
+ rcl_reset_error();
93
+ free(timer);
94
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
95
+ return env.Undefined();
96
+ }
97
+ }
90
98
  #else
91
- THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK,
92
- rcl_timer_init(timer, clock, context, period_nsec,
93
- nullptr, rcl_get_default_allocator()),
94
- rcl_get_error_string().str);
99
+ {
100
+ rcl_ret_t ret = rcl_timer_init(timer, clock, context, period_nsec, nullptr,
101
+ rcl_get_default_allocator());
102
+ if (RCL_RET_OK != ret) {
103
+ std::string error_msg = rcl_get_error_string().str;
104
+ rcl_reset_error();
105
+ free(timer);
106
+ Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
107
+ return env.Undefined();
108
+ }
109
+ }
95
110
  #endif
96
111
 
97
112
  auto js_obj =
@@ -15,9 +15,12 @@
15
15
  #include "rcl_type_description_service_bindings.h"
16
16
 
17
17
  #include <napi.h>
18
+ #include <rcl/error_handling.h>
18
19
  #include <rcl/rcl.h>
19
20
  #include <rmw/types.h>
20
21
 
22
+ #include <string>
23
+
21
24
  #include "rcl_handle.h"
22
25
 
23
26
  namespace rclnodejs {
@@ -31,8 +34,13 @@ Napi::Value InitTypeDescriptionService(const Napi::CallbackInfo& info) {
31
34
  *service = rcl_get_zero_initialized_service();
32
35
  rcl_ret_t ret = rcl_node_type_description_service_init(service, node);
33
36
  if (RCL_RET_OK != ret) {
34
- Napi::Error::New(env, "Failed to initialize type description service")
37
+ std::string error_msg = rcl_get_error_string().str;
38
+ rcl_reset_error();
39
+ free(service);
40
+ Napi::Error::New(
41
+ env, "Failed to initialize type description service: " + error_msg)
35
42
  .ThrowAsJavaScriptException();
43
+ return env.Undefined();
36
44
  }
37
45
 
38
46
  auto service_handle =
@@ -0,0 +1,108 @@
1
+ // Data integrity + throughput test for subscription
2
+ 'use strict';
3
+
4
+ const rclnodejs = require('./index.js');
5
+
6
+ const PUBLISH_HZ = 50;
7
+ const TEST_DURATION_SEC = 10;
8
+
9
+ async function main() {
10
+ await rclnodejs.init();
11
+
12
+ const pubNode = new rclnodejs.Node('data_pub_node');
13
+ const subNode = new rclnodejs.Node('data_sub_node');
14
+
15
+ const publisher = pubNode.createPublisher(
16
+ 'std_msgs/msg/Float64MultiArray',
17
+ '/data_integrity_topic'
18
+ );
19
+
20
+ let msgCount = 0;
21
+ let errCount = 0;
22
+ let lastTs = null;
23
+ const hzSamples = [];
24
+ const errors = [];
25
+
26
+ subNode.createSubscription(
27
+ 'std_msgs/msg/Float64MultiArray',
28
+ '/data_integrity_topic',
29
+ (msg) => {
30
+ const now = Date.now();
31
+ msgCount++;
32
+
33
+ // --- Data validation ---
34
+ // Each published message has data = [seqNo, seqNo*1.5, seqNo*2.5]
35
+ // Verify structure and values
36
+ if (!msg || !msg.data) {
37
+ errCount++;
38
+ errors.push(`msg#${msgCount}: missing data field, got: ${JSON.stringify(msg)}`);
39
+ } else if (!Array.isArray(msg.data) && !(msg.data instanceof Float64Array)) {
40
+ errCount++;
41
+ errors.push(`msg#${msgCount}: data is not array-like, type=${typeof msg.data}`);
42
+ } else if (msg.data.length !== 3) {
43
+ errCount++;
44
+ errors.push(`msg#${msgCount}: expected 3 elements, got ${msg.data.length}`);
45
+ } else {
46
+ const seqNo = msg.data[0];
47
+ const expectedB = seqNo * 1.5;
48
+ const expectedC = seqNo * 2.5;
49
+
50
+ if (Math.abs(msg.data[1] - expectedB) > 1e-9) {
51
+ errCount++;
52
+ errors.push(
53
+ `msg#${msgCount}: data[1] expected ${expectedB}, got ${msg.data[1]}`
54
+ );
55
+ }
56
+ if (Math.abs(msg.data[2] - expectedC) > 1e-9) {
57
+ errCount++;
58
+ errors.push(
59
+ `msg#${msgCount}: data[2] expected ${expectedC}, got ${msg.data[2]}`
60
+ );
61
+ }
62
+ }
63
+
64
+ if (lastTs) {
65
+ hzSamples.push(1000 / (now - lastTs));
66
+ }
67
+ lastTs = now;
68
+ }
69
+ );
70
+
71
+ pubNode.spin();
72
+ subNode.spin();
73
+
74
+ let pubSeq = 0;
75
+ const pubInterval = setInterval(() => {
76
+ pubSeq++;
77
+ publisher.publish({ data: [pubSeq, pubSeq * 1.5, pubSeq * 2.5] });
78
+ }, 1000 / PUBLISH_HZ);
79
+
80
+ setTimeout(() => {
81
+ clearInterval(pubInterval);
82
+
83
+ console.log(`\n=== Data Integrity Test Results ===`);
84
+ console.log(`Published: ${pubSeq} messages at ${PUBLISH_HZ} Hz`);
85
+ console.log(`Received: ${msgCount} messages`);
86
+ console.log(`Data errors: ${errCount}`);
87
+
88
+ if (errors.length > 0) {
89
+ console.log(`\nFirst 10 errors:`);
90
+ errors.slice(0, 10).forEach((e) => console.log(` ${e}`));
91
+ }
92
+
93
+ if (hzSamples.length > 0) {
94
+ const avgHz = hzSamples.reduce((a, b) => a + b, 0) / hzSamples.length;
95
+ console.log(`\nAvg Hz: ${avgHz.toFixed(2)}`);
96
+ }
97
+
98
+ const pass = errCount === 0 && msgCount > 0;
99
+ console.log(`\nResult: ${pass ? 'PASS - all data correct' : 'FAIL'}`);
100
+
101
+ pubNode.stop();
102
+ subNode.stop();
103
+ rclnodejs.shutdown();
104
+ process.exit(pass ? 0 : 1);
105
+ }, TEST_DURATION_SEC * 1000);
106
+ }
107
+
108
+ main().catch(console.error);