rclnodejs 1.6.0 → 1.8.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.
Files changed (84) hide show
  1. package/binding.gyp +2 -0
  2. package/index.js +152 -0
  3. package/lib/action/client.js +109 -10
  4. package/lib/action/deferred.js +8 -2
  5. package/lib/action/server.js +10 -1
  6. package/lib/action/uuid.js +4 -1
  7. package/lib/client.js +218 -4
  8. package/lib/clock.js +182 -1
  9. package/lib/clock_change.js +49 -0
  10. package/lib/clock_event.js +88 -0
  11. package/lib/context.js +12 -2
  12. package/lib/duration.js +37 -12
  13. package/lib/errors.js +621 -0
  14. package/lib/event_handler.js +21 -4
  15. package/lib/interface_loader.js +52 -12
  16. package/lib/lifecycle.js +8 -2
  17. package/lib/logging.js +90 -3
  18. package/lib/message_introspector.js +123 -0
  19. package/lib/message_serialization.js +10 -2
  20. package/lib/message_validation.js +512 -0
  21. package/lib/native_loader.js +9 -4
  22. package/lib/node.js +403 -50
  23. package/lib/node_options.js +40 -1
  24. package/lib/observable_subscription.js +105 -0
  25. package/lib/parameter.js +172 -35
  26. package/lib/parameter_client.js +506 -0
  27. package/lib/parameter_watcher.js +309 -0
  28. package/lib/publisher.js +56 -1
  29. package/lib/qos.js +79 -5
  30. package/lib/rate.js +6 -1
  31. package/lib/serialization.js +7 -2
  32. package/lib/subscription.js +8 -0
  33. package/lib/time.js +136 -21
  34. package/lib/time_source.js +13 -4
  35. package/lib/timer.js +42 -0
  36. package/lib/utils.js +27 -1
  37. package/lib/validator.js +74 -19
  38. package/package.json +4 -2
  39. package/prebuilds/linux-arm64/humble-jammy-arm64-rclnodejs.node +0 -0
  40. package/prebuilds/linux-arm64/jazzy-noble-arm64-rclnodejs.node +0 -0
  41. package/prebuilds/linux-arm64/kilted-noble-arm64-rclnodejs.node +0 -0
  42. package/prebuilds/linux-x64/humble-jammy-x64-rclnodejs.node +0 -0
  43. package/prebuilds/linux-x64/jazzy-noble-x64-rclnodejs.node +0 -0
  44. package/prebuilds/linux-x64/kilted-noble-x64-rclnodejs.node +0 -0
  45. package/rosidl_gen/message_translator.js +0 -61
  46. package/scripts/config.js +1 -0
  47. package/src/addon.cpp +2 -0
  48. package/src/clock_event.cpp +268 -0
  49. package/src/clock_event.hpp +62 -0
  50. package/src/macros.h +2 -4
  51. package/src/rcl_action_server_bindings.cpp +21 -3
  52. package/src/rcl_bindings.cpp +59 -0
  53. package/src/rcl_context_bindings.cpp +5 -0
  54. package/src/rcl_graph_bindings.cpp +73 -0
  55. package/src/rcl_logging_bindings.cpp +158 -0
  56. package/src/rcl_node_bindings.cpp +14 -2
  57. package/src/rcl_publisher_bindings.cpp +12 -0
  58. package/src/rcl_service_bindings.cpp +7 -6
  59. package/src/rcl_subscription_bindings.cpp +51 -14
  60. package/src/rcl_time_point_bindings.cpp +135 -0
  61. package/src/rcl_timer_bindings.cpp +140 -0
  62. package/src/rcl_utilities.cpp +103 -2
  63. package/src/rcl_utilities.h +7 -1
  64. package/types/action_client.d.ts +27 -2
  65. package/types/base.d.ts +6 -0
  66. package/types/client.d.ts +65 -1
  67. package/types/clock.d.ts +86 -0
  68. package/types/clock_change.d.ts +27 -0
  69. package/types/clock_event.d.ts +51 -0
  70. package/types/errors.d.ts +496 -0
  71. package/types/index.d.ts +10 -0
  72. package/types/logging.d.ts +32 -0
  73. package/types/message_introspector.d.ts +75 -0
  74. package/types/message_validation.d.ts +183 -0
  75. package/types/node.d.ts +107 -0
  76. package/types/node_options.d.ts +13 -0
  77. package/types/observable_subscription.d.ts +39 -0
  78. package/types/parameter_client.d.ts +252 -0
  79. package/types/parameter_watcher.d.ts +104 -0
  80. package/types/publisher.d.ts +28 -1
  81. package/types/qos.d.ts +18 -0
  82. package/types/subscription.d.ts +6 -0
  83. package/types/timer.d.ts +18 -0
  84. package/types/validator.d.ts +86 -0
@@ -17,12 +17,48 @@
17
17
  #include <rcl/error_handling.h>
18
18
  #include <rcl/rcl.h>
19
19
 
20
+ #include <memory>
21
+ #include <mutex>
22
+ #include <unordered_map>
23
+
20
24
  #include "macros.h"
21
25
  #include "rcl_handle.h"
22
26
  #include "rcl_utilities.h"
23
27
 
24
28
  namespace rclnodejs {
25
29
 
30
+ struct TimerContext {
31
+ Napi::ThreadSafeFunction on_reset_callback;
32
+ };
33
+
34
+ static std::unordered_map<rcl_timer_t*, std::shared_ptr<TimerContext>>
35
+ g_timer_contexts;
36
+ static std::mutex g_timer_contexts_mutex;
37
+
38
+ void TimerOnResetCallbackTrampoline(const void* user_data,
39
+ size_t number_of_events) {
40
+ const rcl_timer_t* timer = static_cast<const rcl_timer_t*>(user_data);
41
+ std::shared_ptr<TimerContext> context;
42
+
43
+ {
44
+ std::lock_guard<std::mutex> lock(g_timer_contexts_mutex);
45
+ auto it = g_timer_contexts.find(const_cast<rcl_timer_t*>(timer));
46
+ if (it != g_timer_contexts.end()) {
47
+ context = it->second;
48
+ }
49
+ }
50
+
51
+ if (context) {
52
+ auto callback = [](Napi::Env env, Napi::Function js_callback,
53
+ size_t* events) {
54
+ js_callback.Call({Napi::Number::New(env, *events)});
55
+ delete events;
56
+ };
57
+ size_t* events_ptr = new size_t(number_of_events);
58
+ context->on_reset_callback.BlockingCall(events_ptr, callback);
59
+ }
60
+ }
61
+
26
62
  Napi::Value CreateTimer(const Napi::CallbackInfo& info) {
27
63
  Napi::Env env = info.Env();
28
64
 
@@ -61,6 +97,30 @@ Napi::Value CreateTimer(const Napi::CallbackInfo& info) {
61
97
  auto js_obj =
62
98
  RclHandle::NewInstance(env, timer, clock_handle, [env](void* ptr) {
63
99
  rcl_timer_t* timer = reinterpret_cast<rcl_timer_t*>(ptr);
100
+
101
+ #if ROS_VERSION > 2205
102
+ // Clear the callback first to prevent any new callbacks from being
103
+ // triggered
104
+ rcl_ret_t callback_ret =
105
+ rcl_timer_set_on_reset_callback(timer, nullptr, nullptr);
106
+ THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, callback_ret,
107
+ rcl_get_error_string().str);
108
+ #endif
109
+
110
+ std::shared_ptr<TimerContext> context;
111
+ {
112
+ std::lock_guard<std::mutex> lock(g_timer_contexts_mutex);
113
+ auto it = g_timer_contexts.find(timer);
114
+ if (it != g_timer_contexts.end()) {
115
+ context = it->second;
116
+ g_timer_contexts.erase(it);
117
+ }
118
+ }
119
+
120
+ if (context) {
121
+ context->on_reset_callback.Release();
122
+ }
123
+
64
124
  rcl_ret_t ret = rcl_timer_fini(timer);
65
125
  free(ptr);
66
126
  THROW_ERROR_IF_NOT_EQUAL_NO_RETURN(RCL_RET_OK, ret,
@@ -197,6 +257,26 @@ Napi::Value GetTimerPeriod(const Napi::CallbackInfo& info) {
197
257
  return Napi::BigInt::New(env, period_nsec);
198
258
  }
199
259
 
260
+ #if ROS_VERSION > 2205 // 2205 == Humble
261
+ Napi::Value GetTimerNextCallTime(const Napi::CallbackInfo& info) {
262
+ Napi::Env env = info.Env();
263
+ RclHandle* timer_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
264
+ rcl_timer_t* timer = reinterpret_cast<rcl_timer_t*>(timer_handle->ptr());
265
+ int64_t next_call_time = 0;
266
+
267
+ rcl_ret_t ret = rcl_timer_get_next_call_time(timer, &next_call_time);
268
+
269
+ if (ret == RCL_RET_OK) {
270
+ return Napi::BigInt::New(env, next_call_time);
271
+ } else if (ret == RCL_RET_TIMER_CANCELED) {
272
+ return env.Null();
273
+ } else {
274
+ THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK, ret, rcl_get_error_string().str);
275
+ return env.Undefined(); // Safeguard return, should not reach here
276
+ }
277
+ }
278
+ #endif
279
+
200
280
  #if ROS_VERSION > 2205 // 2205 == Humble
201
281
  Napi::Value CallTimerWithInfo(const Napi::CallbackInfo& info) {
202
282
  Napi::Env env = info.Env();
@@ -215,6 +295,60 @@ Napi::Value CallTimerWithInfo(const Napi::CallbackInfo& info) {
215
295
  Napi::BigInt::New(env, call_info.actual_call_time));
216
296
  return timer_info;
217
297
  }
298
+
299
+ Napi::Value SetTimerOnResetCallback(const Napi::CallbackInfo& info) {
300
+ Napi::Env env = info.Env();
301
+ RclHandle* timer_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
302
+ rcl_timer_t* timer = reinterpret_cast<rcl_timer_t*>(timer_handle->ptr());
303
+
304
+ if (!info[1].IsFunction()) {
305
+ Napi::TypeError::New(env, "Callback must be a function")
306
+ .ThrowAsJavaScriptException();
307
+ return env.Undefined();
308
+ }
309
+
310
+ Napi::Function callback = info[1].As<Napi::Function>();
311
+
312
+ std::lock_guard<std::mutex> lock(g_timer_contexts_mutex);
313
+ std::shared_ptr<TimerContext> context;
314
+ auto it = g_timer_contexts.find(timer);
315
+ if (it == g_timer_contexts.end()) {
316
+ context = std::make_shared<TimerContext>();
317
+ g_timer_contexts[timer] = context;
318
+ } else {
319
+ context = it->second;
320
+ context->on_reset_callback.Release();
321
+ }
322
+
323
+ context->on_reset_callback = Napi::ThreadSafeFunction::New(
324
+ env, callback, "TimerOnResetCallback", 0, 1);
325
+
326
+ THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK,
327
+ rcl_timer_set_on_reset_callback(
328
+ timer, TimerOnResetCallbackTrampoline, timer),
329
+ rcl_get_error_string().str);
330
+
331
+ return env.Undefined();
332
+ }
333
+
334
+ Napi::Value ClearTimerOnResetCallback(const Napi::CallbackInfo& info) {
335
+ Napi::Env env = info.Env();
336
+ RclHandle* timer_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
337
+ rcl_timer_t* timer = reinterpret_cast<rcl_timer_t*>(timer_handle->ptr());
338
+
339
+ std::lock_guard<std::mutex> lock(g_timer_contexts_mutex);
340
+ auto it = g_timer_contexts.find(timer);
341
+ if (it != g_timer_contexts.end()) {
342
+ it->second->on_reset_callback.Release();
343
+ g_timer_contexts.erase(it);
344
+ }
345
+
346
+ THROW_ERROR_IF_NOT_EQUAL(
347
+ RCL_RET_OK, rcl_timer_set_on_reset_callback(timer, nullptr, nullptr),
348
+ rcl_get_error_string().str);
349
+
350
+ return env.Undefined();
351
+ }
218
352
  #endif
219
353
 
220
354
  Napi::Object InitTimerBindings(Napi::Env env, Napi::Object exports) {
@@ -231,6 +365,12 @@ Napi::Object InitTimerBindings(Napi::Env env, Napi::Object exports) {
231
365
  exports.Set("changeTimerPeriod", Napi::Function::New(env, ChangeTimerPeriod));
232
366
  exports.Set("getTimerPeriod", Napi::Function::New(env, GetTimerPeriod));
233
367
  #if ROS_VERSION > 2205 // 2205 == Humble
368
+ exports.Set("getTimerNextCallTime",
369
+ Napi::Function::New(env, GetTimerNextCallTime));
370
+ exports.Set("setTimerOnResetCallback",
371
+ Napi::Function::New(env, SetTimerOnResetCallback));
372
+ exports.Set("clearTimerOnResetCallback",
373
+ Napi::Function::New(env, ClearTimerOnResetCallback));
234
374
  exports.Set("callTimerWithInfo", Napi::Function::New(env, CallTimerWithInfo));
235
375
  #endif
236
376
  return exports;
@@ -21,6 +21,8 @@
21
21
 
22
22
  #include <cstdio>
23
23
  #include <memory>
24
+ #include <rcpputils/scope_exit.hpp>
25
+ // NOLINTNEXTLINE
24
26
  #include <string>
25
27
 
26
28
  namespace {
@@ -55,6 +57,7 @@ std::unique_ptr<rmw_qos_profile_t> GetQosProfileFromObject(
55
57
  auto depth = object.Get("depth");
56
58
  auto reliability = object.Get("reliability");
57
59
  auto durability = object.Get("durability");
60
+ auto liveliness = object.Get("liveliness");
58
61
  auto avoid_ros_namespace_conventions =
59
62
  object.Get("avoidRosNameSpaceConventions");
60
63
 
@@ -65,6 +68,8 @@ std::unique_ptr<rmw_qos_profile_t> GetQosProfileFromObject(
65
68
  reliability.As<Napi::Number>().Uint32Value());
66
69
  qos_profile->durability = static_cast<rmw_qos_durability_policy_t>(
67
70
  durability.As<Napi::Number>().Uint32Value());
71
+ qos_profile->liveliness = static_cast<rmw_qos_liveliness_policy_t>(
72
+ liveliness.As<Napi::Number>().Uint32Value());
68
73
  qos_profile->avoid_ros_namespace_conventions =
69
74
  avoid_ros_namespace_conventions.As<Napi::Boolean>();
70
75
 
@@ -119,6 +124,48 @@ Napi::Value ConvertToJSTopicEndpoint(
119
124
  return endpoint;
120
125
  }
121
126
 
127
+ #if ROS_VERSION > 2505
128
+ Napi::Value ConvertToJSServiceEndpointInfo(
129
+ Napi::Env env, const rmw_service_endpoint_info_t* service_endpoint_info) {
130
+ Napi::Object endpoint = Napi::Object::New(env);
131
+ endpoint.Set("node_name",
132
+ Napi::String::New(env, service_endpoint_info->node_name));
133
+ endpoint.Set("node_namespace",
134
+ Napi::String::New(env, service_endpoint_info->node_namespace));
135
+ endpoint.Set("service_type",
136
+ Napi::String::New(env, service_endpoint_info->service_type));
137
+ endpoint.Set(
138
+ "service_type_hash",
139
+ ConvertToHashObject(env, &service_endpoint_info->service_type_hash));
140
+ endpoint.Set(
141
+ "endpoint_type",
142
+ Napi::Number::New(
143
+ env, static_cast<int>(service_endpoint_info->endpoint_type)));
144
+ endpoint.Set("endpoint_count",
145
+ Napi::Number::New(env, service_endpoint_info->endpoint_count));
146
+
147
+ Napi::Array endpoint_gids =
148
+ Napi::Array::New(env, service_endpoint_info->endpoint_count);
149
+ Napi::Array qos_profiles =
150
+ Napi::Array::New(env, service_endpoint_info->endpoint_count);
151
+
152
+ for (size_t i = 0; i < service_endpoint_info->endpoint_count; i++) {
153
+ Napi::Array gid = Napi::Array::New(env, RMW_GID_STORAGE_SIZE);
154
+ for (size_t j = 0; j < RMW_GID_STORAGE_SIZE; j++) {
155
+ gid.Set(j, Napi::Number::New(env,
156
+ service_endpoint_info->endpoint_gids[i][j]));
157
+ }
158
+ endpoint_gids.Set(i, gid);
159
+ qos_profiles.Set(i, rclnodejs::ConvertToQoS(
160
+ env, &service_endpoint_info->qos_profiles[i]));
161
+ }
162
+ endpoint.Set("endpoint_gids", endpoint_gids);
163
+ endpoint.Set("qos_profiles", qos_profiles);
164
+
165
+ return endpoint;
166
+ }
167
+ #endif // ROS_VERSION > 2505
168
+
122
169
  uv_lib_t g_lib;
123
170
  Napi::Env g_env = nullptr;
124
171
 
@@ -261,6 +308,19 @@ Napi::Array ConvertToJSTopicEndpointInfoList(
261
308
  return list;
262
309
  }
263
310
 
311
+ #if ROS_VERSION > 2505
312
+ Napi::Array ConvertToJSServiceEndpointInfoList(
313
+ Napi::Env env, const rmw_service_endpoint_info_array_t* info_array) {
314
+ Napi::Array list = Napi::Array::New(env, info_array->size);
315
+ for (size_t i = 0; i < info_array->size; ++i) {
316
+ rmw_service_endpoint_info_t service_endpoint_info =
317
+ info_array->info_array[i];
318
+ list.Set(i, ConvertToJSServiceEndpointInfo(env, &service_endpoint_info));
319
+ }
320
+ return list;
321
+ }
322
+ #endif // ROS_VERSION > 2505
323
+
264
324
  char** AbstractArgsFromNapiArray(const Napi::Array& jsArgv) {
265
325
  size_t argc = jsArgv.Length();
266
326
  char** argv = nullptr;
@@ -286,9 +346,50 @@ void FreeArgs(char** argv, size_t argc) {
286
346
  }
287
347
  }
288
348
 
289
- bool HasUnparsedROSArgs(const rcl_arguments_t& rcl_args) {
349
+ void ThrowIfUnparsedROSArgs(Napi::Env env, const Napi::Array& jsArgv,
350
+ const rcl_arguments_t& rcl_args) {
290
351
  int unparsed_ros_args_count = rcl_arguments_get_count_unparsed_ros(&rcl_args);
291
- return unparsed_ros_args_count != 0;
352
+
353
+ if (unparsed_ros_args_count < 0) {
354
+ Napi::Error::New(env, "Failed to count unparsed arguments")
355
+ .ThrowAsJavaScriptException();
356
+ return;
357
+ }
358
+ if (0 == unparsed_ros_args_count) {
359
+ return;
360
+ }
361
+
362
+ rcl_allocator_t allocator = rcl_get_default_allocator();
363
+ int* unparsed_indices_c = nullptr;
364
+ rcl_ret_t ret =
365
+ rcl_arguments_get_unparsed_ros(&rcl_args, allocator, &unparsed_indices_c);
366
+ if (RCL_RET_OK != ret) {
367
+ Napi::Error::New(env, "Failed to get unparsed arguments")
368
+ .ThrowAsJavaScriptException();
369
+ return;
370
+ }
371
+
372
+ RCPPUTILS_SCOPE_EXIT(
373
+ { allocator.deallocate(unparsed_indices_c, allocator.state); });
374
+
375
+ std::string unparsed_args_str = "[";
376
+ for (int i = 0; i < unparsed_ros_args_count; ++i) {
377
+ int index = unparsed_indices_c[i];
378
+ if (index < 0 || static_cast<size_t>(index) >= jsArgv.Length()) {
379
+ Napi::Error::New(env, "Got invalid unparsed ROS arg index")
380
+ .ThrowAsJavaScriptException();
381
+ return;
382
+ }
383
+ std::string arg = jsArgv.Get(index).As<Napi::String>().Utf8Value();
384
+ unparsed_args_str += "'" + arg + "'";
385
+ if (i < unparsed_ros_args_count - 1) {
386
+ unparsed_args_str += ", ";
387
+ }
388
+ }
389
+ unparsed_args_str += "]";
390
+
391
+ Napi::Error::New(env, "Unknown ROS arguments: " + unparsed_args_str)
392
+ .ThrowAsJavaScriptException();
292
393
  }
293
394
 
294
395
  } // namespace rclnodejs
@@ -51,6 +51,11 @@ void ExtractNamesAndTypes(rcl_names_and_types_t names_and_types,
51
51
  Napi::Array ConvertToJSTopicEndpointInfoList(
52
52
  Napi::Env env, const rmw_topic_endpoint_info_array_t* info_array);
53
53
 
54
+ #if ROS_VERSION > 2505
55
+ Napi::Array ConvertToJSServiceEndpointInfoList(
56
+ Napi::Env env, const rmw_service_endpoint_info_array_t* info_array);
57
+ #endif // ROS_VERSION > 2505
58
+
54
59
  Napi::Value ConvertToQoS(Napi::Env env, const rmw_qos_profile_t* qos_profile);
55
60
 
56
61
  // `AbstractArgsFromNapiArray` and `FreeArgs` must be called in pairs.
@@ -58,7 +63,8 @@ char** AbstractArgsFromNapiArray(const Napi::Array& jsArgv);
58
63
  // `AbstractArgsFromNapiArray` and `FreeArgs` must be called in pairs.
59
64
  void FreeArgs(char** argv, size_t argc);
60
65
 
61
- bool HasUnparsedROSArgs(const rcl_arguments_t& rcl_args);
66
+ void ThrowIfUnparsedROSArgs(Napi::Env env, const Napi::Array& jsArgv,
67
+ const rcl_arguments_t& rcl_args);
62
68
 
63
69
  } // namespace rclnodejs
64
70
 
@@ -115,6 +115,14 @@ declare module 'rclnodejs' {
115
115
  statusSubQosProfile?: QoS | QoS.ProfileRef;
116
116
  }
117
117
 
118
+ /**
119
+ * Options for sending a goal
120
+ */
121
+ interface SendGoalOptions {
122
+ /** Override validateGoals setting for this call */
123
+ validate?: boolean;
124
+ }
125
+
118
126
  /**
119
127
  * ROS Action client.
120
128
  */
@@ -131,9 +139,23 @@ declare module 'rclnodejs' {
131
139
  node: Node,
132
140
  typeClass: T,
133
141
  actionName: string,
134
- options?: Options<ActionQoS>
142
+ options?: Options<ActionQoS> & {
143
+ validateGoals?: boolean;
144
+ validationOptions?: MessageValidationOptions;
145
+ }
135
146
  );
136
147
 
148
+ /**
149
+ * Whether goals will be validated before sending.
150
+ */
151
+ willValidateGoal: boolean;
152
+
153
+ /**
154
+ * Set validation options for this action client.
155
+ * @param options - Validation options
156
+ */
157
+ setValidation(options: MessageValidationOptions): void;
158
+
137
159
  /**
138
160
  * Send a goal and wait for the goal ACK asynchronously.
139
161
  *
@@ -143,12 +165,15 @@ declare module 'rclnodejs' {
143
165
  * @param goal - The goal request.
144
166
  * @param feedbackCallback - Callback function for feedback associated with the goal.
145
167
  * @param goalUuid - Universally unique identifier for the goal. If None, then a random UUID is generated.
168
+ * @param options - Send options (e.g., { validate: true })
146
169
  * @returns A Promise to a goal handle that resolves when the goal request has been accepted or rejected.
170
+ * @throws MessageValidationError if validation is enabled and goal is invalid
147
171
  */
148
172
  sendGoal(
149
173
  goal: ActionGoal<T>,
150
174
  feedbackCallback?: (feedbackMessage: ActionFeedback<T>) => void,
151
- goalUuid?: unique_identifier_msgs.msg.UUID
175
+ goalUuid?: unique_identifier_msgs.msg.UUID,
176
+ options?: SendGoalOptions
152
177
  ): Promise<ClientGoalHandle<T>>;
153
178
 
154
179
  /**
package/types/base.d.ts CHANGED
@@ -8,20 +8,26 @@
8
8
  /// <reference path="./distro.d.ts" />
9
9
  /// <reference path="./duration.d.ts" />
10
10
  /// <reference path="./entity.d.ts" />
11
+ /// <reference path="./errors.d.ts" />
11
12
  /// <reference path="./guard_condition.d.ts" />
12
13
  /// <reference path="./interfaces.d.ts" />
13
14
  /// <reference path="./lifecycle.d.ts" />
14
15
  /// <reference path="./lifecycle_publisher.d.ts" />
15
16
  /// <reference path="./logging.d.ts" />
17
+ /// <reference path="./message_introspector.d.ts" />
16
18
  /// <reference path="./node.d.ts" />
17
19
  /// <reference path="./node_options.d.ts" />
18
20
  /// <reference path="./parameter.d.ts" />
21
+ /// <reference path="./parameter_client.d.ts" />
22
+ /// <reference path="./parameter_watcher.d.ts" />
19
23
  /// <reference path="./publisher.d.ts" />
20
24
  /// <reference path="./qos.d.ts" />
21
25
  /// <reference path="./rate.d.ts" />
22
26
  /// <reference path="./service.d.ts" />
23
27
  /// <reference path="./service_introspection.d.ts" />
24
28
  /// <reference path="./subscription.d.ts" />
29
+ /// <reference path="./observable_subscription.d.ts" />
25
30
  /// <reference path="./time_source.d.ts" />
26
31
  /// <reference path="./time.d.ts" />
27
32
  /// <reference path="./timer.d.ts" />
33
+ /// <reference path="./validator.d.ts" />
package/types/client.d.ts CHANGED
@@ -1,19 +1,57 @@
1
1
  declare module 'rclnodejs' {
2
+ /**
3
+ * Options for sending a request
4
+ */
5
+ interface SendRequestOptions {
6
+ /** Override validateRequests setting for this call */
7
+ validate?: boolean;
8
+ }
9
+
2
10
  /**
3
11
  * A ROS service client.
4
12
  */
5
13
  interface Client<T extends TypeClass<ServiceTypeClassName>> extends Entity {
14
+ /**
15
+ * Whether requests will be validated before sending.
16
+ */
17
+ willValidateRequest: boolean;
18
+
19
+ /**
20
+ * Set validation options for this client.
21
+ * @param options - Validation options
22
+ */
23
+ setValidation(options: MessageValidationOptions): void;
24
+
6
25
  /**
7
26
  * Make a service request and wait for to be notified asynchronously through a callback.
8
27
  *
9
28
  * @param request - Request to be submitted.
10
29
  * @param callback - Callback for receiving the server response.
30
+ * @param options - Send options (e.g., { validate: true })
31
+ * @throws MessageValidationError if validation is enabled and request is invalid
11
32
  */
12
33
  sendRequest(
13
34
  request: ServiceRequestMessage<T>,
14
- callback: Client.ResponseCallback<T>
35
+ callback: Client.ResponseCallback<T>,
36
+ options?: SendRequestOptions
15
37
  ): void;
16
38
 
39
+ /**
40
+ * Make a service request and return a Promise that resolves with the response.
41
+ *
42
+ * @param request - Request to be submitted.
43
+ * @param options - Optional parameters for the request.
44
+ * @returns Promise that resolves with the service response.
45
+ * @throws TimeoutError if the request times out (when options.timeout is exceeded).
46
+ * @throws AbortError if the request is manually aborted (via options.signal).
47
+ * @throws MessageValidationError if validation is enabled and request is invalid.
48
+ * @throws Error if the request fails for other reasons.
49
+ */
50
+ sendRequestAsync(
51
+ request: ServiceRequestMessage<T>,
52
+ options?: Client.AsyncRequestOptions
53
+ ): Promise<ServiceResponseMessage<T>>;
54
+
17
55
  /**
18
56
  * Checks if the service is ready.
19
57
  *
@@ -74,5 +112,31 @@ declare module 'rclnodejs' {
74
112
  export type ResponseCallback<T extends TypeClass<ServiceTypeClassName>> = (
75
113
  response: ServiceResponseMessage<T>
76
114
  ) => void;
115
+
116
+ /**
117
+ * Options for async service requests
118
+ */
119
+ export interface AsyncRequestOptions {
120
+ /**
121
+ * Timeout in milliseconds for the request.
122
+ * Internally uses AbortSignal.timeout() for standards compliance.
123
+ */
124
+ timeout?: number;
125
+
126
+ /**
127
+ * AbortSignal to cancel the request.
128
+ * When the signal is aborted, the request will be cancelled
129
+ * and the promise will reject with an AbortError.
130
+ *
131
+ * Can be combined with timeout parameter - whichever happens first
132
+ * will abort the request.
133
+ */
134
+ signal?: AbortSignal;
135
+
136
+ /**
137
+ * Override validateRequests setting for this call
138
+ */
139
+ validate?: boolean;
140
+ }
77
141
  }
78
142
  }
package/types/clock.d.ts CHANGED
@@ -1,4 +1,35 @@
1
1
  declare module 'rclnodejs' {
2
+ /**
3
+ * Jump information provided to clock jump callbacks.
4
+ */
5
+ interface ClockJumpInfo {
6
+ /**
7
+ * Type of clock change that occurred.
8
+ */
9
+ clock_change: ClockChange;
10
+
11
+ /**
12
+ * Time delta in nanoseconds.
13
+ */
14
+ delta: bigint;
15
+ }
16
+
17
+ /**
18
+ * Callback object for clock jump events.
19
+ */
20
+ interface ClockCallbackObject {
21
+ /**
22
+ * Optional callback invoked before a time jump.
23
+ */
24
+ _pre_callback?: () => void;
25
+
26
+ /**
27
+ * Optional callback invoked after a time jump.
28
+ * @param jumpInfo - Information about the time jump.
29
+ */
30
+ _post_callback?: (jumpInfo: ClockJumpInfo) => void;
31
+ }
32
+
2
33
  /**
3
34
  * A ROS Clock.
4
35
  */
@@ -17,12 +48,67 @@ declare module 'rclnodejs' {
17
48
  */
18
49
  readonly clockType: ClockType;
19
50
 
51
+ /**
52
+ * Add a clock callback.
53
+ * @param callbackObject - The object containing callback methods.
54
+ * @param onClockChange - Whether to call the callback on clock change.
55
+ * @param minForward - Minimum forward jump in nanoseconds to trigger the callback.
56
+ * @param minBackward - Minimum backward jump in nanoseconds to trigger the callback.
57
+ */
58
+ addClockCallback(
59
+ callbackObject: ClockCallbackObject,
60
+ onClockChange: boolean,
61
+ minForward: bigint,
62
+ minBackward: bigint
63
+ ): void;
64
+
65
+ /**
66
+ * Remove a clock callback.
67
+ * @param callbackObject - The callback object that was previously registered with addClockCallback().
68
+ */
69
+ removeClockCallback(callbackObject: ClockCallbackObject): void;
70
+
20
71
  /**
21
72
  * Return the current time.
22
73
  *
23
74
  * @returns The current time.
24
75
  */
25
76
  now(): Time;
77
+
78
+ /**
79
+ * Sleep until a specific time is reached on this Clock.
80
+ *
81
+ * When using a ROSClock, this may sleep forever if the TimeSource is misconfigured
82
+ * and the context is never shut down. ROS time being activated or deactivated causes
83
+ * this function to cease sleeping and return false.
84
+ *
85
+ * @param until - Time at which this function should stop sleeping.
86
+ * @param context - Context which when shut down will cause this sleep to wake early.
87
+ * If context is null or undefined, then the default context is used.
88
+ * @returns Promise that resolves to true if 'until' was reached,
89
+ * or false if it woke for another reason (time jump, context shutdown).
90
+ * @throws {TypeError} if until is specified for a different type of clock than this one.
91
+ * @throws {Error} if context has not been initialized or is shutdown.
92
+ */
93
+ sleepUntil(until: Time, context?: Context | null): Promise<boolean>;
94
+
95
+ /**
96
+ * Sleep for a specified duration.
97
+ *
98
+ * Equivalent to: clock.sleepUntil(clock.now() + duration, context)
99
+ *
100
+ * When using a ROSClock, this may sleep forever if the TimeSource is misconfigured
101
+ * and the context is never shut down. ROS time being activated or deactivated causes
102
+ * this function to cease sleeping and return false.
103
+ *
104
+ * @param duration - Duration of time to sleep for.
105
+ * @param context - Context which when shut down will cause this sleep to wake early.
106
+ * If context is null or undefined, then the default context is used.
107
+ * @returns Promise that resolves to true if the full duration was slept,
108
+ * or false if it woke for another reason.
109
+ * @throws {Error} if context has not been initialized or is shutdown.
110
+ */
111
+ sleepFor(duration: Duration, context?: Context | null): Promise<boolean>;
26
112
  }
27
113
 
28
114
  /**
@@ -0,0 +1,27 @@
1
+ declare module 'rclnodejs' {
2
+ /**
3
+ * Clock change type identifiers
4
+ * Represents the type of clock change that occurred during a time jump.
5
+ */
6
+ enum ClockChange {
7
+ /**
8
+ * The source before and after the jump is ROS_TIME.
9
+ */
10
+ ROS_TIME_NO_CHANGE = 1,
11
+
12
+ /**
13
+ * The source switched to ROS_TIME from SYSTEM_TIME.
14
+ */
15
+ ROS_TIME_ACTIVATED = 2,
16
+
17
+ /**
18
+ * The source switched to SYSTEM_TIME from ROS_TIME.
19
+ */
20
+ ROS_TIME_DEACTIVATED = 3,
21
+
22
+ /**
23
+ * The source before and after the jump is SYSTEM_TIME.
24
+ */
25
+ SYSTEM_TIME_NO_CHANGE = 4,
26
+ }
27
+ }