rclnodejs 1.7.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.
- package/binding.gyp +2 -0
- package/index.js +93 -0
- package/lib/action/client.js +54 -1
- package/lib/client.js +66 -1
- package/lib/clock.js +178 -0
- package/lib/clock_change.js +49 -0
- package/lib/clock_event.js +88 -0
- package/lib/errors.js +50 -0
- package/lib/logging.js +78 -0
- package/lib/message_introspector.js +123 -0
- package/lib/message_validation.js +512 -0
- package/lib/node.js +133 -1
- package/lib/node_options.js +40 -1
- package/lib/observable_subscription.js +105 -0
- package/lib/publisher.js +56 -1
- package/lib/qos.js +57 -0
- package/lib/subscription.js +8 -0
- package/lib/timer.js +42 -0
- package/lib/validator.js +63 -7
- package/package.json +4 -2
- package/prebuilds/linux-arm64/humble-jammy-arm64-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/jazzy-noble-arm64-rclnodejs.node +0 -0
- package/prebuilds/linux-arm64/kilted-noble-arm64-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/humble-jammy-x64-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/jazzy-noble-x64-rclnodejs.node +0 -0
- package/prebuilds/linux-x64/kilted-noble-x64-rclnodejs.node +0 -0
- package/rosidl_gen/message_translator.js +0 -61
- package/scripts/config.js +1 -0
- package/src/addon.cpp +2 -0
- package/src/clock_event.cpp +268 -0
- package/src/clock_event.hpp +62 -0
- package/src/macros.h +2 -4
- package/src/rcl_action_server_bindings.cpp +21 -3
- package/src/rcl_bindings.cpp +59 -0
- package/src/rcl_context_bindings.cpp +5 -0
- package/src/rcl_graph_bindings.cpp +73 -0
- package/src/rcl_logging_bindings.cpp +158 -0
- package/src/rcl_node_bindings.cpp +14 -2
- package/src/rcl_publisher_bindings.cpp +12 -0
- package/src/rcl_service_bindings.cpp +7 -6
- package/src/rcl_subscription_bindings.cpp +51 -14
- package/src/rcl_time_point_bindings.cpp +135 -0
- package/src/rcl_timer_bindings.cpp +140 -0
- package/src/rcl_utilities.cpp +103 -2
- package/src/rcl_utilities.h +7 -1
- package/types/action_client.d.ts +27 -2
- package/types/base.d.ts +3 -0
- package/types/client.d.ts +29 -1
- package/types/clock.d.ts +86 -0
- package/types/clock_change.d.ts +27 -0
- package/types/clock_event.d.ts +51 -0
- package/types/errors.d.ts +49 -0
- package/types/index.d.ts +10 -0
- package/types/interfaces.d.ts +1 -1910
- package/types/logging.d.ts +32 -0
- package/types/message_introspector.d.ts +75 -0
- package/types/message_validation.d.ts +183 -0
- package/types/node.d.ts +67 -0
- package/types/node_options.d.ts +13 -0
- package/types/observable_subscription.d.ts +39 -0
- package/types/publisher.d.ts +28 -1
- package/types/qos.d.ts +18 -0
- package/types/subscription.d.ts +6 -0
- package/types/timer.d.ts +18 -0
- package/types/validator.d.ts +86 -0
|
@@ -0,0 +1,268 @@
|
|
|
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 "clock_event.hpp"
|
|
16
|
+
|
|
17
|
+
#include <rcl/error_handling.h>
|
|
18
|
+
#include <rcl/rcl.h>
|
|
19
|
+
#include <rcl/time.h>
|
|
20
|
+
#include <rcl/types.h>
|
|
21
|
+
|
|
22
|
+
#include <chrono>
|
|
23
|
+
#include <memory>
|
|
24
|
+
#include <mutex>
|
|
25
|
+
#include <stdexcept>
|
|
26
|
+
|
|
27
|
+
#include "macros.h"
|
|
28
|
+
#include "rcl_handle.h"
|
|
29
|
+
|
|
30
|
+
namespace rclnodejs {
|
|
31
|
+
|
|
32
|
+
template <typename ClockType>
|
|
33
|
+
void ClockEvent::wait_until(rcl_clock_t* clock, rcl_time_point_t until) {
|
|
34
|
+
// Synchronize because clock epochs might differ
|
|
35
|
+
rcl_time_point_value_t now_value;
|
|
36
|
+
rcl_ret_t ret = rcl_clock_get_now(clock, &now_value);
|
|
37
|
+
if (RCL_RET_OK != ret) {
|
|
38
|
+
throw std::runtime_error("failed to get current time");
|
|
39
|
+
}
|
|
40
|
+
rcl_time_point_t rcl_entry;
|
|
41
|
+
rcl_entry.nanoseconds = now_value;
|
|
42
|
+
rcl_entry.clock_type = clock->type;
|
|
43
|
+
|
|
44
|
+
const typename ClockType::time_point chrono_entry = ClockType::now();
|
|
45
|
+
|
|
46
|
+
rcl_duration_t delta_t;
|
|
47
|
+
ret = rcl_difference_times(&rcl_entry, &until, &delta_t);
|
|
48
|
+
|
|
49
|
+
if (RCL_RET_OK != ret) {
|
|
50
|
+
throw std::runtime_error("failed to subtract times");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Cast because system clock resolution is too big for nanoseconds on Windows
|
|
54
|
+
// & OSX
|
|
55
|
+
const typename ClockType::time_point chrono_until =
|
|
56
|
+
chrono_entry + std::chrono::duration_cast<typename ClockType::duration>(
|
|
57
|
+
std::chrono::nanoseconds(delta_t.nanoseconds));
|
|
58
|
+
|
|
59
|
+
std::unique_lock<std::mutex> lock(mutex_);
|
|
60
|
+
cv_.wait_until(lock, chrono_until, [this]() { return state_; });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void ClockEvent::wait_until_ros(rcl_clock_t* clock, rcl_time_point_t until) {
|
|
64
|
+
bool is_enabled;
|
|
65
|
+
rcl_ret_t ret = rcl_is_enabled_ros_time_override(clock, &is_enabled);
|
|
66
|
+
if (RCL_RET_OK != ret) {
|
|
67
|
+
throw std::runtime_error("failed to check if ros time override is enabled");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Check if ROS time is enabled in C++ to avoid TOCTTOU with TimeSource
|
|
71
|
+
if (is_enabled) {
|
|
72
|
+
std::unique_lock<std::mutex> lock(mutex_);
|
|
73
|
+
// Caller must have setup a time jump callback to wake this event
|
|
74
|
+
cv_.wait(lock, [this]() { return state_; });
|
|
75
|
+
} else {
|
|
76
|
+
// ROS time not enabled is system time
|
|
77
|
+
wait_until<std::chrono::system_clock>(clock, until);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
bool ClockEvent::is_set() {
|
|
82
|
+
std::unique_lock<std::mutex> lock(mutex_);
|
|
83
|
+
return state_;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
void ClockEvent::set() {
|
|
87
|
+
{
|
|
88
|
+
std::unique_lock<std::mutex> lock(mutex_);
|
|
89
|
+
state_ = true;
|
|
90
|
+
}
|
|
91
|
+
cv_.notify_all();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
void ClockEvent::clear() {
|
|
95
|
+
{
|
|
96
|
+
std::unique_lock<std::mutex> lock(mutex_);
|
|
97
|
+
state_ = false;
|
|
98
|
+
}
|
|
99
|
+
cv_.notify_all();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Explicit instantiation
|
|
103
|
+
template void ClockEvent::wait_until<std::chrono::steady_clock>(
|
|
104
|
+
rcl_clock_t* clock, rcl_time_point_t until);
|
|
105
|
+
template void ClockEvent::wait_until<std::chrono::system_clock>(
|
|
106
|
+
rcl_clock_t* clock, rcl_time_point_t until);
|
|
107
|
+
|
|
108
|
+
enum class WaitType { Steady, System, Ros };
|
|
109
|
+
|
|
110
|
+
class ClockEventWaitWorker : public Napi::AsyncWorker {
|
|
111
|
+
public:
|
|
112
|
+
ClockEventWaitWorker(Napi::Env env, ClockEvent* event, rcl_clock_t* clock,
|
|
113
|
+
int64_t until, WaitType type)
|
|
114
|
+
: Napi::AsyncWorker(env),
|
|
115
|
+
event_(event),
|
|
116
|
+
clock_(clock),
|
|
117
|
+
until_(until),
|
|
118
|
+
type_(type),
|
|
119
|
+
deferred_(Napi::Promise::Deferred::New(env)) {}
|
|
120
|
+
|
|
121
|
+
~ClockEventWaitWorker() {}
|
|
122
|
+
|
|
123
|
+
void Execute() override {
|
|
124
|
+
try {
|
|
125
|
+
rcl_time_point_t until_time_point;
|
|
126
|
+
until_time_point.nanoseconds = until_;
|
|
127
|
+
until_time_point.clock_type = clock_->type;
|
|
128
|
+
|
|
129
|
+
switch (type_) {
|
|
130
|
+
case WaitType::Ros:
|
|
131
|
+
event_->wait_until_ros(clock_, until_time_point);
|
|
132
|
+
break;
|
|
133
|
+
case WaitType::Steady:
|
|
134
|
+
event_->wait_until<std::chrono::steady_clock>(clock_,
|
|
135
|
+
until_time_point);
|
|
136
|
+
break;
|
|
137
|
+
case WaitType::System:
|
|
138
|
+
event_->wait_until<std::chrono::system_clock>(clock_,
|
|
139
|
+
until_time_point);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
} catch (const std::exception& e) {
|
|
143
|
+
SetError(e.what());
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
void OnOK() override { deferred_.Resolve(Env().Undefined()); }
|
|
148
|
+
|
|
149
|
+
void OnError(const Napi::Error& e) override { deferred_.Reject(e.Value()); }
|
|
150
|
+
|
|
151
|
+
Napi::Promise Promise() { return deferred_.Promise(); }
|
|
152
|
+
|
|
153
|
+
private:
|
|
154
|
+
ClockEvent* event_;
|
|
155
|
+
rcl_clock_t* clock_;
|
|
156
|
+
int64_t until_;
|
|
157
|
+
WaitType type_;
|
|
158
|
+
Napi::Promise::Deferred deferred_;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
Napi::Value CreateClockEvent(const Napi::CallbackInfo& info) {
|
|
162
|
+
Napi::Env env = info.Env();
|
|
163
|
+
ClockEvent* event = new ClockEvent();
|
|
164
|
+
return RclHandle::NewInstance(env, event, nullptr, [](void* ptr) {
|
|
165
|
+
delete static_cast<ClockEvent*>(ptr);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
Napi::Value ClockEventWaitUntilSteady(const Napi::CallbackInfo& info) {
|
|
170
|
+
Napi::Env env = info.Env();
|
|
171
|
+
RclHandle* event_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
|
|
172
|
+
ClockEvent* event = static_cast<ClockEvent*>(event_handle->ptr());
|
|
173
|
+
|
|
174
|
+
RclHandle* clock_handle = RclHandle::Unwrap(info[1].As<Napi::Object>());
|
|
175
|
+
rcl_clock_t* clock = static_cast<rcl_clock_t*>(clock_handle->ptr());
|
|
176
|
+
|
|
177
|
+
bool lossless;
|
|
178
|
+
int64_t until = info[2].As<Napi::BigInt>().Int64Value(&lossless);
|
|
179
|
+
if (!lossless) {
|
|
180
|
+
Napi::TypeError::New(env, "until value lost precision during conversion")
|
|
181
|
+
.ThrowAsJavaScriptException();
|
|
182
|
+
return env.Undefined();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
auto worker =
|
|
186
|
+
new ClockEventWaitWorker(env, event, clock, until, WaitType::Steady);
|
|
187
|
+
worker->Queue();
|
|
188
|
+
return worker->Promise();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
Napi::Value ClockEventWaitUntilSystem(const Napi::CallbackInfo& info) {
|
|
192
|
+
Napi::Env env = info.Env();
|
|
193
|
+
RclHandle* event_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
|
|
194
|
+
ClockEvent* event = static_cast<ClockEvent*>(event_handle->ptr());
|
|
195
|
+
|
|
196
|
+
RclHandle* clock_handle = RclHandle::Unwrap(info[1].As<Napi::Object>());
|
|
197
|
+
rcl_clock_t* clock = static_cast<rcl_clock_t*>(clock_handle->ptr());
|
|
198
|
+
|
|
199
|
+
bool lossless;
|
|
200
|
+
int64_t until = info[2].As<Napi::BigInt>().Int64Value(&lossless);
|
|
201
|
+
if (!lossless) {
|
|
202
|
+
Napi::TypeError::New(env, "until value lost precision during conversion")
|
|
203
|
+
.ThrowAsJavaScriptException();
|
|
204
|
+
return env.Undefined();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
auto worker =
|
|
208
|
+
new ClockEventWaitWorker(env, event, clock, until, WaitType::System);
|
|
209
|
+
worker->Queue();
|
|
210
|
+
return worker->Promise();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
Napi::Value ClockEventWaitUntilRos(const Napi::CallbackInfo& info) {
|
|
214
|
+
Napi::Env env = info.Env();
|
|
215
|
+
RclHandle* event_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
|
|
216
|
+
ClockEvent* event = static_cast<ClockEvent*>(event_handle->ptr());
|
|
217
|
+
|
|
218
|
+
RclHandle* clock_handle = RclHandle::Unwrap(info[1].As<Napi::Object>());
|
|
219
|
+
rcl_clock_t* clock = static_cast<rcl_clock_t*>(clock_handle->ptr());
|
|
220
|
+
|
|
221
|
+
bool lossless;
|
|
222
|
+
int64_t until = info[2].As<Napi::BigInt>().Int64Value(&lossless);
|
|
223
|
+
if (!lossless) {
|
|
224
|
+
Napi::TypeError::New(env, "until value lost precision during conversion")
|
|
225
|
+
.ThrowAsJavaScriptException();
|
|
226
|
+
return env.Undefined();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
auto worker =
|
|
230
|
+
new ClockEventWaitWorker(env, event, clock, until, WaitType::Ros);
|
|
231
|
+
worker->Queue();
|
|
232
|
+
return worker->Promise();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
Napi::Value ClockEventIsSet(const Napi::CallbackInfo& info) {
|
|
236
|
+
RclHandle* event_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
|
|
237
|
+
ClockEvent* event = static_cast<ClockEvent*>(event_handle->ptr());
|
|
238
|
+
return Napi::Boolean::New(info.Env(), event->is_set());
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
Napi::Value ClockEventSet(const Napi::CallbackInfo& info) {
|
|
242
|
+
RclHandle* event_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
|
|
243
|
+
ClockEvent* event = static_cast<ClockEvent*>(event_handle->ptr());
|
|
244
|
+
event->set();
|
|
245
|
+
return info.Env().Undefined();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
Napi::Value ClockEventClear(const Napi::CallbackInfo& info) {
|
|
249
|
+
RclHandle* event_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
|
|
250
|
+
ClockEvent* event = static_cast<ClockEvent*>(event_handle->ptr());
|
|
251
|
+
event->clear();
|
|
252
|
+
return info.Env().Undefined();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
void InitClockEventBindings(Napi::Env env, Napi::Object exports) {
|
|
256
|
+
exports.Set("createClockEvent", Napi::Function::New(env, CreateClockEvent));
|
|
257
|
+
exports.Set("clockEventWaitUntilSteady",
|
|
258
|
+
Napi::Function::New(env, ClockEventWaitUntilSteady));
|
|
259
|
+
exports.Set("clockEventWaitUntilSystem",
|
|
260
|
+
Napi::Function::New(env, ClockEventWaitUntilSystem));
|
|
261
|
+
exports.Set("clockEventWaitUntilRos",
|
|
262
|
+
Napi::Function::New(env, ClockEventWaitUntilRos));
|
|
263
|
+
exports.Set("clockEventIsSet", Napi::Function::New(env, ClockEventIsSet));
|
|
264
|
+
exports.Set("clockEventSet", Napi::Function::New(env, ClockEventSet));
|
|
265
|
+
exports.Set("clockEventClear", Napi::Function::New(env, ClockEventClear));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
} // namespace rclnodejs
|
|
@@ -0,0 +1,62 @@
|
|
|
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_CLOCK_EVENT_HPP_
|
|
16
|
+
#define SRC_CLOCK_EVENT_HPP_
|
|
17
|
+
|
|
18
|
+
#include <napi.h>
|
|
19
|
+
#include <rcl/time.h>
|
|
20
|
+
|
|
21
|
+
#include <condition_variable>
|
|
22
|
+
#include <memory>
|
|
23
|
+
#include <mutex>
|
|
24
|
+
|
|
25
|
+
namespace rclnodejs {
|
|
26
|
+
|
|
27
|
+
class ClockEvent {
|
|
28
|
+
public:
|
|
29
|
+
/// Wait until a time specified by a system or steady clock.
|
|
30
|
+
/// \param clock the clock to use for time synchronization with until
|
|
31
|
+
/// \param until this method will block until this time is reached.
|
|
32
|
+
template <typename ClockType>
|
|
33
|
+
void wait_until(rcl_clock_t* clock, rcl_time_point_t until);
|
|
34
|
+
|
|
35
|
+
/// Wait until a time specified by a ROS clock.
|
|
36
|
+
/// \warning The caller is responsible for creating a time jump callback
|
|
37
|
+
/// to set this event when the target ROS time is reached.
|
|
38
|
+
/// \param clock The clock to use for time synchronization.
|
|
39
|
+
/// \param until This method will block until this time is reached.
|
|
40
|
+
void wait_until_ros(rcl_clock_t* clock, rcl_time_point_t until);
|
|
41
|
+
|
|
42
|
+
/// Indicate if the ClockEvent is set.
|
|
43
|
+
/// \return True if the ClockEvent is set.
|
|
44
|
+
bool is_set();
|
|
45
|
+
|
|
46
|
+
/// Set the event.
|
|
47
|
+
void set();
|
|
48
|
+
|
|
49
|
+
/// Clear the event.
|
|
50
|
+
void clear();
|
|
51
|
+
|
|
52
|
+
private:
|
|
53
|
+
bool state_ = false;
|
|
54
|
+
std::mutex mutex_;
|
|
55
|
+
std::condition_variable cv_;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
void InitClockEventBindings(Napi::Env env, Napi::Object exports);
|
|
59
|
+
|
|
60
|
+
} // namespace rclnodejs
|
|
61
|
+
|
|
62
|
+
#endif // SRC_CLOCK_EVENT_HPP_
|
package/src/macros.h
CHANGED
|
@@ -23,8 +23,7 @@
|
|
|
23
23
|
{ \
|
|
24
24
|
if (lhs op rhs) { \
|
|
25
25
|
rcl_reset_error(); \
|
|
26
|
-
Napi::Error::New(env, message)
|
|
27
|
-
.ThrowAsJavaScriptException(); \
|
|
26
|
+
Napi::Error::New(env, message).ThrowAsJavaScriptException(); \
|
|
28
27
|
return env.Undefined(); \
|
|
29
28
|
} \
|
|
30
29
|
}
|
|
@@ -33,8 +32,7 @@
|
|
|
33
32
|
{ \
|
|
34
33
|
if (lhs op rhs) { \
|
|
35
34
|
rcl_reset_error(); \
|
|
36
|
-
Napi::Error::New(env, message)
|
|
37
|
-
.ThrowAsJavaScriptException(); \
|
|
35
|
+
Napi::Error::New(env, message).ThrowAsJavaScriptException(); \
|
|
38
36
|
} \
|
|
39
37
|
}
|
|
40
38
|
|
|
@@ -317,9 +317,27 @@ Napi::Value ActionPublishStatus(const Napi::CallbackInfo& info) {
|
|
|
317
317
|
rcl_action_get_goal_status_array(action_server, &status_message),
|
|
318
318
|
RCL_RET_OK, rcl_get_error_string().str);
|
|
319
319
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
320
|
+
rcl_ret_t ret = rcl_action_publish_status(action_server, &status_message);
|
|
321
|
+
|
|
322
|
+
std::string publish_error_msg;
|
|
323
|
+
if (ret != RCL_RET_OK) {
|
|
324
|
+
publish_error_msg = rcl_get_error_string().str;
|
|
325
|
+
rcl_reset_error();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
rcl_ret_t ret_fini = rcl_action_goal_status_array_fini(&status_message);
|
|
329
|
+
|
|
330
|
+
if (ret != RCL_RET_OK) {
|
|
331
|
+
Napi::Error::New(env, publish_error_msg).ThrowAsJavaScriptException();
|
|
332
|
+
return env.Undefined();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (ret_fini != RCL_RET_OK) {
|
|
336
|
+
Napi::Error::New(env, rcl_get_error_string().str)
|
|
337
|
+
.ThrowAsJavaScriptException();
|
|
338
|
+
rcl_reset_error();
|
|
339
|
+
return env.Undefined();
|
|
340
|
+
}
|
|
323
341
|
|
|
324
342
|
return env.Undefined();
|
|
325
343
|
}
|
package/src/rcl_bindings.cpp
CHANGED
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
#include "rcl_bindings.h"
|
|
16
16
|
|
|
17
17
|
#include <node.h>
|
|
18
|
+
#include <rcl/arguments.h>
|
|
19
|
+
#include <rcl/rcl.h>
|
|
20
|
+
|
|
21
|
+
#include <rcpputils/scope_exit.hpp>
|
|
22
|
+
|
|
18
23
|
#if ROS_VERSION >= 2006
|
|
19
24
|
#include <rosidl_runtime_c/string_functions.h>
|
|
20
25
|
#else
|
|
@@ -23,11 +28,64 @@
|
|
|
23
28
|
|
|
24
29
|
#include <memory>
|
|
25
30
|
#include <string>
|
|
31
|
+
#include <vector>
|
|
26
32
|
|
|
27
33
|
#include "rcl_handle.h"
|
|
34
|
+
#include "rcl_utilities.h"
|
|
28
35
|
|
|
29
36
|
namespace rclnodejs {
|
|
30
37
|
|
|
38
|
+
Napi::Value RemoveROSArgs(const Napi::CallbackInfo& info) {
|
|
39
|
+
Napi::Env env = info.Env();
|
|
40
|
+
Napi::Array jsArgv = info[0].As<Napi::Array>();
|
|
41
|
+
size_t argc = jsArgv.Length();
|
|
42
|
+
char** argv = AbstractArgsFromNapiArray(jsArgv);
|
|
43
|
+
|
|
44
|
+
rcl_allocator_t allocator = rcl_get_default_allocator();
|
|
45
|
+
rcl_arguments_t parsed_args = rcl_get_zero_initialized_arguments();
|
|
46
|
+
|
|
47
|
+
rcl_ret_t ret = rcl_parse_arguments(argc, const_cast<const char**>(argv),
|
|
48
|
+
allocator, &parsed_args);
|
|
49
|
+
|
|
50
|
+
if (RCL_RET_OK != ret) {
|
|
51
|
+
FreeArgs(argv, argc);
|
|
52
|
+
Napi::Error::New(env, "Failed to parse arguments")
|
|
53
|
+
.ThrowAsJavaScriptException();
|
|
54
|
+
return env.Undefined();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
RCPPUTILS_SCOPE_EXIT({
|
|
58
|
+
rcl_ret_t fini_ret = rcl_arguments_fini(&parsed_args);
|
|
59
|
+
if (RCL_RET_OK != fini_ret) {
|
|
60
|
+
// Log error but don't throw since we might be already throwing
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
int nonros_argc = 0;
|
|
65
|
+
const char** nonros_argv = nullptr;
|
|
66
|
+
|
|
67
|
+
ret = rcl_remove_ros_arguments(const_cast<const char**>(argv), &parsed_args,
|
|
68
|
+
allocator, &nonros_argc, &nonros_argv);
|
|
69
|
+
|
|
70
|
+
if (RCL_RET_OK != ret) {
|
|
71
|
+
FreeArgs(argv, argc);
|
|
72
|
+
Napi::Error::New(env, "Failed to remove ROS arguments")
|
|
73
|
+
.ThrowAsJavaScriptException();
|
|
74
|
+
return env.Undefined();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
RCPPUTILS_SCOPE_EXIT({ allocator.deallocate(nonros_argv, allocator.state); });
|
|
78
|
+
|
|
79
|
+
Napi::Array result = Napi::Array::New(env, nonros_argc);
|
|
80
|
+
for (int i = 0; i < nonros_argc; ++i) {
|
|
81
|
+
result.Set(i, Napi::String::New(env, nonros_argv[i]));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
FreeArgs(argv, argc);
|
|
85
|
+
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
|
|
31
89
|
Napi::Value InitString(const Napi::CallbackInfo& info) {
|
|
32
90
|
Napi::Env env = info.Env();
|
|
33
91
|
|
|
@@ -110,6 +168,7 @@ Napi::Object InitBindings(Napi::Env env, Napi::Object exports) {
|
|
|
110
168
|
Napi::Function::New(env, CreateArrayBufferFromAddress));
|
|
111
169
|
exports.Set("createArrayBufferCleaner",
|
|
112
170
|
Napi::Function::New(env, CreateArrayBufferCleaner));
|
|
171
|
+
exports.Set("removeROSArgs", Napi::Function::New(env, RemoveROSArgs));
|
|
113
172
|
return exports;
|
|
114
173
|
}
|
|
115
174
|
|
|
@@ -73,6 +73,11 @@ Napi::Value Init(const Napi::CallbackInfo& info) {
|
|
|
73
73
|
rcl_init(argc, argc > 0 ? argv : nullptr, &init_options, context),
|
|
74
74
|
rcl_get_error_string().str);
|
|
75
75
|
|
|
76
|
+
ThrowIfUnparsedROSArgs(env, jsArgv, context->global_arguments);
|
|
77
|
+
if (env.IsExceptionPending()) {
|
|
78
|
+
return env.Undefined();
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
THROW_ERROR_IF_NOT_EQUAL(
|
|
77
82
|
RCL_RET_OK, rcl_logging_configure(&context->global_arguments, &allocator),
|
|
78
83
|
rcl_get_error_string().str);
|
|
@@ -33,6 +33,13 @@ typedef rcl_ret_t (*rcl_get_info_by_topic_func_t)(
|
|
|
33
33
|
const char* topic_name, bool no_mangle,
|
|
34
34
|
rcl_topic_endpoint_info_array_t* info_array);
|
|
35
35
|
|
|
36
|
+
#if ROS_VERSION > 2505
|
|
37
|
+
typedef rcl_ret_t (*rcl_get_info_by_service_func_t)(
|
|
38
|
+
const rcl_node_t* node, rcutils_allocator_t* allocator,
|
|
39
|
+
const char* service_name, bool no_mangle,
|
|
40
|
+
rcl_service_endpoint_info_array_t* info_array);
|
|
41
|
+
#endif // ROS_VERSION > 2505
|
|
42
|
+
|
|
36
43
|
Napi::Value GetPublisherNamesAndTypesByNode(const Napi::CallbackInfo& info) {
|
|
37
44
|
Napi::Env env = info.Env();
|
|
38
45
|
|
|
@@ -257,6 +264,66 @@ Napi::Value GetSubscriptionsInfoByTopic(const Napi::CallbackInfo& info) {
|
|
|
257
264
|
"subscriptions", rcl_get_subscriptions_info_by_topic);
|
|
258
265
|
}
|
|
259
266
|
|
|
267
|
+
#if ROS_VERSION > 2505
|
|
268
|
+
Napi::Value GetInfoByService(
|
|
269
|
+
Napi::Env env, rcl_node_t* node, const char* service_name, bool no_mangle,
|
|
270
|
+
const char* type, rcl_get_info_by_service_func_t rcl_get_info_by_service) {
|
|
271
|
+
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
|
272
|
+
rcl_service_endpoint_info_array_t info_array =
|
|
273
|
+
rcl_get_zero_initialized_service_endpoint_info_array();
|
|
274
|
+
|
|
275
|
+
RCPPUTILS_SCOPE_EXIT({
|
|
276
|
+
rcl_ret_t fini_ret =
|
|
277
|
+
rcl_service_endpoint_info_array_fini(&info_array, &allocator);
|
|
278
|
+
if (RCL_RET_OK != fini_ret) {
|
|
279
|
+
Napi::Error::New(env, rcl_get_error_string().str)
|
|
280
|
+
.ThrowAsJavaScriptException();
|
|
281
|
+
rcl_reset_error();
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
rcl_ret_t ret = rcl_get_info_by_service(node, &allocator, service_name,
|
|
286
|
+
no_mangle, &info_array);
|
|
287
|
+
if (RCL_RET_OK != ret) {
|
|
288
|
+
if (RCL_RET_UNSUPPORTED == ret) {
|
|
289
|
+
Napi::Error::New(
|
|
290
|
+
env, std::string("Failed to get information by service for ") + type +
|
|
291
|
+
": function not supported by RMW_IMPLEMENTATION")
|
|
292
|
+
.ThrowAsJavaScriptException();
|
|
293
|
+
return env.Undefined();
|
|
294
|
+
}
|
|
295
|
+
Napi::Error::New(
|
|
296
|
+
env, std::string("Failed to get information by service for ") + type)
|
|
297
|
+
.ThrowAsJavaScriptException();
|
|
298
|
+
return env.Undefined();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return ConvertToJSServiceEndpointInfoList(env, &info_array);
|
|
302
|
+
}
|
|
303
|
+
#endif // ROS_VERSION > 2505
|
|
304
|
+
|
|
305
|
+
#if ROS_VERSION > 2505
|
|
306
|
+
Napi::Value GetClientsInfoByService(const Napi::CallbackInfo& info) {
|
|
307
|
+
RclHandle* node_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
|
|
308
|
+
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
|
|
309
|
+
std::string service_name = info[1].As<Napi::String>().Utf8Value();
|
|
310
|
+
bool no_mangle = info[2].As<Napi::Boolean>();
|
|
311
|
+
|
|
312
|
+
return GetInfoByService(info.Env(), node, service_name.c_str(), no_mangle,
|
|
313
|
+
"clients", rcl_get_clients_info_by_service);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
Napi::Value GetServersInfoByService(const Napi::CallbackInfo& info) {
|
|
317
|
+
RclHandle* node_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
|
|
318
|
+
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
|
|
319
|
+
std::string service_name = info[1].As<Napi::String>().Utf8Value();
|
|
320
|
+
bool no_mangle = info[2].As<Napi::Boolean>();
|
|
321
|
+
|
|
322
|
+
return GetInfoByService(info.Env(), node, service_name.c_str(), no_mangle,
|
|
323
|
+
"servers", rcl_get_servers_info_by_service);
|
|
324
|
+
}
|
|
325
|
+
#endif // ROS_VERSION > 2505
|
|
326
|
+
|
|
260
327
|
Napi::Object InitGraphBindings(Napi::Env env, Napi::Object exports) {
|
|
261
328
|
exports.Set("getPublisherNamesAndTypesByNode",
|
|
262
329
|
Napi::Function::New(env, GetPublisherNamesAndTypesByNode));
|
|
@@ -274,6 +341,12 @@ Napi::Object InitGraphBindings(Napi::Env env, Napi::Object exports) {
|
|
|
274
341
|
Napi::Function::New(env, GetPublishersInfoByTopic));
|
|
275
342
|
exports.Set("getSubscriptionsInfoByTopic",
|
|
276
343
|
Napi::Function::New(env, GetSubscriptionsInfoByTopic));
|
|
344
|
+
#if ROS_VERSION > 2505
|
|
345
|
+
exports.Set("getClientsInfoByService",
|
|
346
|
+
Napi::Function::New(env, GetClientsInfoByService));
|
|
347
|
+
exports.Set("getServersInfoByService",
|
|
348
|
+
Napi::Function::New(env, GetServersInfoByService));
|
|
349
|
+
#endif // ROS_VERSION > 2505
|
|
277
350
|
return exports;
|
|
278
351
|
}
|
|
279
352
|
|