@@ -573,6 +573,8 @@ where
573
573
if num_expired > 0 {
574
574
// Clean up the expired goal.
575
575
let uuid = GoalUuid ( expired_goal. goal_id . uuid ) ;
576
+ self . goal_results . lock ( ) . unwrap ( ) . remove ( & uuid) ;
577
+ self . result_requests . lock ( ) . unwrap ( ) . remove ( & uuid) ;
576
578
self . goal_handles . lock ( ) . unwrap ( ) . remove ( & uuid) ;
577
579
} else {
578
580
break ;
@@ -582,6 +584,29 @@ where
582
584
Ok ( ( ) )
583
585
}
584
586
587
+ // TODO(nwn): Replace `status` with a "properly typed" action_msgs::msg::GoalStatus enum.
588
+ pub ( crate ) fn terminate_goal ( & self , goal_id : & GoalUuid , status : i8 , result : <T :: Result as Message >:: RmwMsg ) -> Result < ( ) , RclrsError > {
589
+ let response_rmw = <T as ActionImpl >:: create_result_response ( status, result) ;
590
+
591
+ // Publish the result to anyone listening.
592
+ self . publish_result ( goal_id, response_rmw) ;
593
+
594
+ // Publish the state change.
595
+ self . publish_status ( ) ;
596
+
597
+ // Notify rcl that a goal has terminated and to therefore recalculate the expired goal timer.
598
+ unsafe {
599
+ // SAFETY: The action server is locked and valid. No other preconditions.
600
+ rcl_action_notify_goal_done ( & * self . handle . lock ( ) )
601
+ }
602
+ . ok ( ) ?;
603
+
604
+ // Release ownership of the goal handle. It will persist until the user also drops it.
605
+ self . goal_handles . lock ( ) . unwrap ( ) . remove ( & goal_id) ;
606
+
607
+ Ok ( ( ) )
608
+ }
609
+
585
610
pub ( crate ) fn publish_status ( & self ) -> Result < ( ) , RclrsError > {
586
611
let mut goal_statuses = DropGuard :: new (
587
612
unsafe {
@@ -628,6 +653,37 @@ where
628
653
}
629
654
. ok ( )
630
655
}
656
+
657
+ fn publish_result ( & self , goal_id : & GoalUuid , mut result : <<<T as ActionImpl >:: GetResultService as Service >:: Response as Message >:: RmwMsg ) -> Result < ( ) , RclrsError > {
658
+ let goal_exists = unsafe {
659
+ // SAFETY: No preconditions
660
+ let mut goal_info = rcl_action_get_zero_initialized_goal_info ( ) ;
661
+ goal_info. goal_id . uuid = goal_id. 0 ;
662
+
663
+ // SAFETY: The action server is locked through the handle. The `goal_info`
664
+ // argument points to a rcl_action_goal_info_t with the desired UUID.
665
+ rcl_action_server_goal_exists ( & * self . handle . lock ( ) , & goal_info)
666
+ } ;
667
+ if !goal_exists {
668
+ panic ! ( "Cannot publish result for unknown goal" )
669
+ }
670
+
671
+ // TODO(nwn): Fix synchronization problem between goal_results and result_requests.
672
+ // Currently, there is a gap between the request queue being drained and the result being
673
+ // stored for future requests. Any requests received during that gap would never receive a
674
+ // response. Fixing this means we'll need combined locking over these two hash maps.
675
+
676
+ // Respond to all queued requests.
677
+ if let Some ( result_requests) = self . result_requests . lock ( ) . unwrap ( ) . remove ( & goal_id) {
678
+ for mut result_request in result_requests {
679
+ self . send_result_response ( result_request, & mut result) ?;
680
+ }
681
+ }
682
+
683
+ self . goal_results . lock ( ) . unwrap ( ) . insert ( * goal_id, result) ;
684
+
685
+ Ok ( ( ) )
686
+ }
631
687
}
632
688
633
689
impl < T > ActionServerBase for ActionServer < T >
0 commit comments