@@ -275,7 +275,10 @@ where
275275
276276 self . send_goal_response ( request_id, true ) ?;
277277
278- self . goal_handles . lock ( ) . unwrap ( ) . insert ( uuid, Arc :: clone ( & goal_handle) ) ;
278+ self . goal_handles
279+ . lock ( )
280+ . unwrap ( )
281+ . insert ( uuid, Arc :: clone ( & goal_handle) ) ;
279282
280283 if response == GoalResponse :: AcceptAndExecute {
281284 goal_handle. execute ( ) ?;
@@ -289,8 +292,157 @@ where
289292 Ok ( ( ) )
290293 }
291294
295+ fn take_cancel_request ( & self ) -> Result < ( action_msgs__srv__CancelGoal_Request , rmw_request_id_t ) , RclrsError > {
296+ let mut request_id = rmw_request_id_t {
297+ writer_guid : [ 0 ; 16 ] ,
298+ sequence_number : 0 ,
299+ } ;
300+ // SAFETY: No preconditions
301+ let mut request_rmw = unsafe { rcl_action_get_zero_initialized_cancel_request ( ) } ;
302+ let handle = & * self . handle . lock ( ) ;
303+ unsafe {
304+ // SAFETY: The action server is locked by the handle. The request_id is a
305+ // zero-initialized rmw_request_id_t, and the request_rmw is a zero-initialized
306+ // action_msgs__srv__CancelGoal_Request.
307+ rcl_action_take_cancel_request (
308+ handle,
309+ & mut request_id,
310+ & mut request_rmw as * mut _ as * mut _ ,
311+ )
312+ }
313+ . ok ( ) ?;
314+
315+ Ok ( ( request_rmw, request_id) )
316+ }
317+
318+ fn send_cancel_response (
319+ & self ,
320+ mut request_id : rmw_request_id_t ,
321+ response_rmw : & mut action_msgs__srv__CancelGoal_Response ,
322+ ) -> Result < ( ) , RclrsError > {
323+ let handle = & * self . handle . lock ( ) ;
324+ let result = unsafe {
325+ // SAFETY: The action server handle is locked and so synchronized with other functions.
326+ // The request_id and response are both uniquely owned or borrowed, and so neither will
327+ // mutate during this function call.
328+ rcl_action_send_cancel_response (
329+ handle,
330+ & mut request_id,
331+ response_rmw as * mut _ as * mut _ ,
332+ )
333+ }
334+ . ok ( ) ;
335+ match result {
336+ Ok ( ( ) ) => Ok ( ( ) ) ,
337+ Err ( RclrsError :: RclError {
338+ code : RclReturnCode :: Timeout ,
339+ ..
340+ } ) => {
341+ // TODO(nwn): Log an error and continue.
342+ // (See https://github.com/ros2/rclcpp/pull/2215 for reasoning.)
343+ Ok ( ( ) )
344+ }
345+ _ => result,
346+ }
347+ }
348+
292349 fn execute_cancel_request ( & self ) -> Result < ( ) , RclrsError > {
293- todo ! ( )
350+ let ( request, request_id) = match self . take_cancel_request ( ) {
351+ Ok ( res) => res,
352+ Err ( RclrsError :: RclError {
353+ code : RclReturnCode :: ServiceTakeFailed ,
354+ ..
355+ } ) => {
356+ // Spurious wakeup – this may happen even when a waitset indicated that this
357+ // action was ready, so it shouldn't be an error.
358+ return Ok ( ( ) ) ;
359+ }
360+ Err ( err) => return Err ( err) ,
361+ } ;
362+
363+ let mut response_rmw = {
364+ // SAFETY: No preconditions
365+ let mut response_rmw = unsafe { rcl_action_get_zero_initialized_cancel_response ( ) } ;
366+ unsafe {
367+ // SAFETY: The action server is locked by the handle. The request was initialized
368+ // by rcl_action, and the response is a zero-initialized
369+ // rcl_action_cancel_response_t.
370+ rcl_action_process_cancel_request (
371+ & * self . handle . lock ( ) ,
372+ & request,
373+ & mut response_rmw as * mut _ ,
374+ )
375+ }
376+ . ok ( ) ?;
377+
378+ DropGuard :: new ( response_rmw, |mut response_rmw| unsafe {
379+ // SAFETY: The response was initialized by rcl_action_process_cancel_request().
380+ // Later modifications only truncate the size of the array and shift elements,
381+ // without modifying the data pointer or capacity.
382+ rcl_action_cancel_response_fini ( & mut response_rmw) ;
383+ } )
384+ } ;
385+
386+ let num_candidates = response_rmw. msg . goals_canceling . size ;
387+ let mut num_accepted = 0 ;
388+ for idx in 0 ..response_rmw. msg . goals_canceling . size {
389+ let goal_info = unsafe {
390+ // SAFETY: The array pointed to by response_rmw.msg.goals_canceling.data is
391+ // guaranteed to contain at least response_rmw.msg.goals_canceling.size members.
392+ & * response_rmw. msg . goals_canceling . data . add ( idx)
393+ } ;
394+ let goal_uuid = GoalUuid ( goal_info. goal_id . uuid ) ;
395+
396+ let response = {
397+ if let Some ( goal_handle) = self . goal_handles . lock ( ) . unwrap ( ) . get ( & goal_uuid) {
398+ let response: CancelResponse = todo ! ( "Call self.cancel_callback(goal_handle)" ) ;
399+ if response == CancelResponse :: Accept {
400+ // Still reject the request if the goal is no longer cancellable.
401+ if goal_handle. cancel ( ) . is_ok ( ) {
402+ CancelResponse :: Accept
403+ } else {
404+ CancelResponse :: Reject
405+ }
406+ } else {
407+ CancelResponse :: Reject
408+ }
409+ } else {
410+ CancelResponse :: Reject
411+ }
412+ } ;
413+
414+ if response == CancelResponse :: Accept {
415+ // Shift the accepted entry back to the first rejected slot, if necessary.
416+ if num_accepted < idx {
417+ let goal_info_slot = unsafe {
418+ // SAFETY: The array pointed to by response_rmw.msg.goals_canceling.data is
419+ // guaranteed to contain at least response_rmw.msg.goals_canceling.size
420+ // members. Since `num_accepted` is strictly less than `idx`, it is a
421+ // distinct element of the array, so there is no mutable aliasing.
422+ & mut * response_rmw. msg . goals_canceling . data . add ( num_accepted)
423+ } ;
424+ }
425+ num_accepted += 1 ;
426+ }
427+ }
428+ response_rmw. msg . goals_canceling . size = num_accepted;
429+
430+ // If the user rejects all individual cancel requests, consider the entire request as
431+ // having been rejected.
432+ if num_accepted == 0 && num_candidates > 0 {
433+ // TODO(nwn): Include action_msgs__srv__CancelGoal_Response__ERROR_REJECTED in the rcl
434+ // bindings.
435+ response_rmw. msg . return_code = 1 ;
436+ }
437+
438+ // If any goal states changed, publish a status update.
439+ if num_accepted > 0 {
440+ self . publish_status ( ) ?;
441+ }
442+
443+ self . send_cancel_response ( request_id, & mut response_rmw. msg ) ?;
444+
445+ Ok ( ( ) )
294446 }
295447
296448 fn execute_result_request ( & self ) -> Result < ( ) , RclrsError > {
@@ -319,7 +471,7 @@ where
319471 let uuid = GoalUuid ( expired_goal. goal_id . uuid ) ;
320472 self . goal_handles . lock ( ) . unwrap ( ) . remove ( & uuid) ;
321473 } else {
322- break
474+ break ;
323475 }
324476 }
325477
0 commit comments