@@ -175,9 +175,144 @@ Napi::Value ClockGetNow(const Napi::CallbackInfo& info) {
175175 return Napi::BigInt::New (env, time_point.nanoseconds );
176176}
177177
178+ struct JumpCallbackData {
179+ Napi::ThreadSafeFunction tsfn_pre;
180+ Napi::ThreadSafeFunction tsfn_post;
181+ };
182+
183+ struct JumpCallbackContext {
184+ rcl_time_jump_t time_jump;
185+ bool before_jump;
186+ };
187+
188+ void _rclnodejs_on_time_jump (const rcl_time_jump_t * time_jump, bool before_jump,
189+ void * user_data) {
190+ JumpCallbackData* data = static_cast <JumpCallbackData*>(user_data);
191+
192+ auto context = new JumpCallbackContext{*time_jump, before_jump};
193+
194+ if (before_jump) {
195+ auto callback = [](Napi::Env env, Napi::Function js_callback,
196+ JumpCallbackContext* context) {
197+ js_callback.Call ({});
198+ delete context;
199+ };
200+ data->tsfn_pre .NonBlockingCall (context, callback);
201+ } else {
202+ auto callback = [](Napi::Env env, Napi::Function js_callback,
203+ JumpCallbackContext* context) {
204+ Napi::Object jump_info = Napi::Object::New (env);
205+ jump_info.Set (" clock_change" ,
206+ static_cast <int32_t >(context->time_jump .clock_change ));
207+ jump_info.Set (" delta" , Napi::BigInt::New (
208+ env, context->time_jump .delta .nanoseconds ));
209+ js_callback.Call ({jump_info});
210+ delete context;
211+ };
212+ data->tsfn_post .NonBlockingCall (context, callback);
213+ }
214+ }
215+
216+ Napi::Value ClockAddJumpCallback (const Napi::CallbackInfo& info) {
217+ Napi::Env env = info.Env ();
218+
219+ RclHandle* clock_handle = RclHandle::Unwrap (info[0 ].As <Napi::Object>());
220+ rcl_clock_t * clock = reinterpret_cast <rcl_clock_t *>(clock_handle->ptr ());
221+
222+ Napi::Object callback_obj = info[1 ].As <Napi::Object>();
223+ Napi::Function pre_callback =
224+ callback_obj.Get (" _pre_callback" ).As <Napi::Function>();
225+ Napi::Function post_callback =
226+ callback_obj.Get (" _post_callback" ).As <Napi::Function>();
227+
228+ bool on_clock_change = info[2 ].As <Napi::Boolean>();
229+
230+ bool lossless;
231+ int64_t min_forward = info[3 ].As <Napi::BigInt>().Int64Value (&lossless);
232+ if (!lossless) {
233+ Napi::TypeError::New (
234+ env, " min_forward BigInt value cannot be represented as int64_t" )
235+ .ThrowAsJavaScriptException ();
236+ return env.Undefined ();
237+ }
238+ int64_t min_backward = info[4 ].As <Napi::BigInt>().Int64Value (&lossless);
239+ if (!lossless) {
240+ Napi::TypeError::New (
241+ env, " min_backward BigInt value cannot be represented as int64_t" )
242+ .ThrowAsJavaScriptException ();
243+ return env.Undefined ();
244+ }
245+
246+ rcl_jump_threshold_t threshold;
247+ threshold.on_clock_change = on_clock_change;
248+ threshold.min_forward .nanoseconds = min_forward;
249+ threshold.min_backward .nanoseconds = min_backward;
250+
251+ JumpCallbackData* data = new JumpCallbackData ();
252+ data->tsfn_pre = Napi::ThreadSafeFunction::New (
253+ env, pre_callback, " ClockJumpPreCallback" , 10 , 1 , [](Napi::Env) {});
254+ data->tsfn_post =
255+ Napi::ThreadSafeFunction::New (env, post_callback, " ClockJumpPostCallback" ,
256+ 10 , 1 , [data](Napi::Env) { delete data; });
257+
258+ Napi::Object handle_obj = Napi::Object::New (env);
259+ handle_obj.Set (" _cpp_handle" ,
260+ Napi::External<JumpCallbackData>::New (env, data));
261+
262+ rcl_ret_t ret = rcl_clock_add_jump_callback (clock, threshold,
263+ _rclnodejs_on_time_jump, data);
264+
265+ if (ret != RCL_RET_OK) {
266+ data->tsfn_pre .Release ();
267+ data->tsfn_post .Release ();
268+ THROW_ERROR_IF_NOT_EQUAL (RCL_RET_OK, ret, rcl_get_error_string ().str );
269+ }
270+
271+ callback_obj.Set (" _cpp_handle" , handle_obj.Get (" _cpp_handle" ));
272+
273+ return env.Undefined ();
274+ }
275+
276+ Napi::Value ClockRemoveJumpCallback (const Napi::CallbackInfo& info) {
277+ Napi::Env env = info.Env ();
278+
279+ RclHandle* clock_handle = RclHandle::Unwrap (info[0 ].As <Napi::Object>());
280+ rcl_clock_t * clock = reinterpret_cast <rcl_clock_t *>(clock_handle->ptr ());
281+
282+ Napi::Object handle_obj = info[1 ].As <Napi::Object>();
283+ Napi::Value cpp_handle = handle_obj.Get (" _cpp_handle" );
284+
285+ if (cpp_handle.IsUndefined () || !cpp_handle.IsExternal ()) {
286+ Napi::Error::New (env,
287+ " Callback object was not registered or already removed" )
288+ .ThrowAsJavaScriptException ();
289+ return env.Undefined ();
290+ }
291+
292+ JumpCallbackData* data =
293+ cpp_handle.As <Napi::External<JumpCallbackData>>().Data ();
294+
295+ rcl_ret_t ret =
296+ rcl_clock_remove_jump_callback (clock, _rclnodejs_on_time_jump, data);
297+
298+ if (ret == RCL_RET_OK) {
299+ data->tsfn_pre .Release ();
300+ data->tsfn_post .Release ();
301+ handle_obj.Set (" _cpp_handle" , env.Undefined ());
302+ } else {
303+ THROW_ERROR_IF_NOT_EQUAL (RCL_RET_OK, ret, rcl_get_error_string ().str );
304+ }
305+
306+ return env.Undefined ();
307+ }
308+
178309Napi::Object InitTimePointBindings (Napi::Env env, Napi::Object exports) {
179310 exports.Set (" createClock" , Napi::Function::New (env, CreateClock));
180311 exports.Set (" clockGetNow" , Napi::Function::New (env, ClockGetNow));
312+ exports.Set (" clockAddJumpCallback" ,
313+ Napi::Function::New (env, ClockAddJumpCallback));
314+ exports.Set (" clockRemoveJumpCallback" ,
315+ Napi::Function::New (env, ClockRemoveJumpCallback));
181316 exports.Set (" createTimePoint" , Napi::Function::New (env, CreateTimePoint));
182317 exports.Set (" getNanoseconds" , Napi::Function::New (env, GetNanoseconds));
183318 exports.Set (" createDuration" , Napi::Function::New (env, CreateDuration));
0 commit comments