1717#include < rcl/error_handling.h>
1818#include < rcl/rcl.h>
1919
20+ #include < mutex>
21+ #include < unordered_map>
22+
2023#include " macros.h"
2124#include " rcl_handle.h"
2225#include " rcl_utilities.h"
2326
2427namespace rclnodejs {
2528
29+ struct TimerContext {
30+ Napi::ThreadSafeFunction on_reset_callback;
31+ };
32+
33+ static std::unordered_map<rcl_timer_t *, TimerContext*> g_timer_contexts;
34+ static std::mutex g_timer_contexts_mutex;
35+
36+ void TimerOnResetCallbackTrampoline (const void * user_data,
37+ size_t number_of_events) {
38+ const TimerContext* context = static_cast <const TimerContext*>(user_data);
39+ if (context) {
40+ auto callback = [](Napi::Env env, Napi::Function js_callback,
41+ size_t * events) {
42+ js_callback.Call ({Napi::Number::New (env, *events)});
43+ delete events;
44+ };
45+ size_t * events_ptr = new size_t (number_of_events);
46+ context->on_reset_callback .BlockingCall (events_ptr, callback);
47+ }
48+ }
49+
2650Napi::Value CreateTimer (const Napi::CallbackInfo& info) {
2751 Napi::Env env = info.Env ();
2852
@@ -61,6 +85,17 @@ Napi::Value CreateTimer(const Napi::CallbackInfo& info) {
6185 auto js_obj =
6286 RclHandle::NewInstance (env, timer, clock_handle, [env](void * ptr) {
6387 rcl_timer_t * timer = reinterpret_cast <rcl_timer_t *>(ptr);
88+
89+ {
90+ std::lock_guard<std::mutex> lock (g_timer_contexts_mutex);
91+ auto it = g_timer_contexts.find (timer);
92+ if (it != g_timer_contexts.end ()) {
93+ it->second ->on_reset_callback .Release ();
94+ delete it->second ;
95+ g_timer_contexts.erase (it);
96+ }
97+ }
98+
6499 rcl_ret_t ret = rcl_timer_fini (timer);
65100 free (ptr);
66101 THROW_ERROR_IF_NOT_EQUAL_NO_RETURN (RCL_RET_OK , ret,
@@ -217,6 +252,61 @@ Napi::Value CallTimerWithInfo(const Napi::CallbackInfo& info) {
217252}
218253#endif
219254
255+ Napi::Value SetTimerOnResetCallback (const Napi::CallbackInfo& info) {
256+ Napi::Env env = info.Env ();
257+ RclHandle* timer_handle = RclHandle::Unwrap (info[0 ].As <Napi::Object>());
258+ rcl_timer_t * timer = reinterpret_cast <rcl_timer_t *>(timer_handle->ptr ());
259+
260+ if (!info[1 ].IsFunction ()) {
261+ Napi::TypeError::New (env, " Callback must be a function" )
262+ .ThrowAsJavaScriptException ();
263+ return env.Undefined ();
264+ }
265+
266+ Napi::Function callback = info[1 ].As <Napi::Function>();
267+
268+ std::lock_guard<std::mutex> lock (g_timer_contexts_mutex);
269+ TimerContext* context = nullptr ;
270+ auto it = g_timer_contexts.find (timer);
271+ if (it == g_timer_contexts.end ()) {
272+ context = new TimerContext ();
273+ g_timer_contexts[timer] = context;
274+ } else {
275+ context = it->second ;
276+ context->on_reset_callback .Release ();
277+ }
278+
279+ context->on_reset_callback = Napi::ThreadSafeFunction::New (
280+ env, callback, " TimerOnResetCallback" , 0 , 1 );
281+
282+ THROW_ERROR_IF_NOT_EQUAL (RCL_RET_OK ,
283+ rcl_timer_set_on_reset_callback (
284+ timer, TimerOnResetCallbackTrampoline, context),
285+ rcl_get_error_string ().str );
286+
287+ return env.Undefined ();
288+ }
289+
290+ Napi::Value ClearTimerOnResetCallback (const Napi::CallbackInfo& info) {
291+ Napi::Env env = info.Env ();
292+ RclHandle* timer_handle = RclHandle::Unwrap (info[0 ].As <Napi::Object>());
293+ rcl_timer_t * timer = reinterpret_cast <rcl_timer_t *>(timer_handle->ptr ());
294+
295+ std::lock_guard<std::mutex> lock (g_timer_contexts_mutex);
296+ auto it = g_timer_contexts.find (timer);
297+ if (it != g_timer_contexts.end ()) {
298+ it->second ->on_reset_callback .Release ();
299+ delete it->second ;
300+ g_timer_contexts.erase (it);
301+ }
302+
303+ THROW_ERROR_IF_NOT_EQUAL (
304+ RCL_RET_OK , rcl_timer_set_on_reset_callback (timer, nullptr , nullptr ),
305+ rcl_get_error_string ().str );
306+
307+ return env.Undefined ();
308+ }
309+
220310Napi::Object InitTimerBindings (Napi::Env env, Napi::Object exports) {
221311 exports.Set (" createTimer" , Napi::Function::New (env, CreateTimer));
222312 exports.Set (" isTimerReady" , Napi::Function::New (env, IsTimerReady));
@@ -230,6 +320,10 @@ Napi::Object InitTimerBindings(Napi::Env env, Napi::Object exports) {
230320 Napi::Function::New (env, TimerGetTimeUntilNextCall));
231321 exports.Set (" changeTimerPeriod" , Napi::Function::New (env, ChangeTimerPeriod));
232322 exports.Set (" getTimerPeriod" , Napi::Function::New (env, GetTimerPeriod));
323+ exports.Set (" setTimerOnResetCallback" ,
324+ Napi::Function::New (env, SetTimerOnResetCallback));
325+ exports.Set (" clearTimerOnResetCallback" ,
326+ Napi::Function::New (env, ClearTimerOnResetCallback));
233327#if ROS_VERSION > 2205 // 2205 == Humble
234328 exports.Set (" callTimerWithInfo" , Napi::Function::New (env, CallTimerWithInfo));
235329#endif
0 commit comments