Skip to content

Commit cbc1a10

Browse files
committed
Hook up goal termination methods for goal handles
These now call into the action server to send a response to prior (queued) and future goal result requests. There are still some synchronization tweaks needed for this to be correct.
1 parent 079bbd4 commit cbc1a10

File tree

2 files changed

+80
-4
lines changed

2 files changed

+80
-4
lines changed

rclrs/src/action/server.rs

+56
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,8 @@ where
573573
if num_expired > 0 {
574574
// Clean up the expired goal.
575575
let uuid = GoalUuid(expired_goal.goal_id.uuid);
576+
self.goal_results.lock().unwrap().remove(&uuid);
577+
self.result_requests.lock().unwrap().remove(&uuid);
576578
self.goal_handles.lock().unwrap().remove(&uuid);
577579
} else {
578580
break;
@@ -582,6 +584,29 @@ where
582584
Ok(())
583585
}
584586

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+
585610
pub(crate) fn publish_status(&self) -> Result<(), RclrsError> {
586611
let mut goal_statuses = DropGuard::new(
587612
unsafe {
@@ -628,6 +653,37 @@ where
628653
}
629654
.ok()
630655
}
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+
}
631687
}
632688

633689
impl<T> ActionServerBase for ActionServer<T>

rclrs/src/action/server_goal_handle.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,12 @@ where
112112
pub fn abort(&self, result: &ActionT::Result) -> Result<(), RclrsError> {
113113
self.update_state(rcl_action_goal_event_t::GOAL_EVENT_ABORT)?;
114114

115-
// TODO: Invoke on_terminal_state callback
115+
if let Some(action_server) = self.action_server.upgrade() {
116+
let result_rmw = <ActionT::Result as rosidl_runtime_rs::Message>::into_rmw_message(std::borrow::Cow::Borrowed(result)).into_owned();
117+
// TODO(nwn): Include action_msgs__msg__GoalStatus__STATUS_ABORTED in the rcl
118+
// bindings.
119+
action_server.terminate_goal(&self.uuid, 6, result_rmw)?;
120+
}
116121
Ok(())
117122
}
118123

@@ -125,7 +130,12 @@ where
125130
pub fn succeed(&self, result: &ActionT::Result) -> Result<(), RclrsError> {
126131
self.update_state(rcl_action_goal_event_t::GOAL_EVENT_SUCCEED)?;
127132

128-
// TODO: Invoke on_terminal_state callback
133+
if let Some(action_server) = self.action_server.upgrade() {
134+
let result_rmw = <ActionT::Result as rosidl_runtime_rs::Message>::into_rmw_message(std::borrow::Cow::Borrowed(result)).into_owned();
135+
// TODO(nwn): Include action_msgs__msg__GoalStatus__STATUS_SUCCEEDED in the rcl
136+
// bindings.
137+
action_server.terminate_goal(&self.uuid, 4, result_rmw)?;
138+
}
129139
Ok(())
130140
}
131141

@@ -138,7 +148,12 @@ where
138148
pub fn canceled(&self, result: &ActionT::Result) -> Result<(), RclrsError> {
139149
self.update_state(rcl_action_goal_event_t::GOAL_EVENT_CANCELED)?;
140150

141-
// TODO: Invoke on_terminal_state callback
151+
if let Some(action_server) = self.action_server.upgrade() {
152+
let result_rmw = <ActionT::Result as rosidl_runtime_rs::Message>::into_rmw_message(std::borrow::Cow::Borrowed(result)).into_owned();
153+
// TODO(nwn): Include action_msgs__msg__GoalStatus__STATUS_CANCELED in the rcl
154+
// bindings.
155+
action_server.terminate_goal(&self.uuid, 5, result_rmw)?;
156+
}
142157
Ok(())
143158
}
144159

@@ -209,7 +224,12 @@ where
209224
/// Cancel the goal if its handle is dropped without reaching a terminal state.
210225
fn drop(&mut self) {
211226
if self.try_canceling() == Ok(true) {
212-
// TODO: Invoke on_terminal_state callback
227+
if let Some(action_server) = self.action_server.upgrade() {
228+
let response_rmw = <ActionT::Result as rosidl_runtime_rs::Message>::RmwMsg::default();
229+
// TODO(nwn): Include action_msgs__msg__GoalStatus__STATUS_CANCELED in the rcl
230+
// bindings.
231+
action_server.terminate_goal(&self.uuid, 5, response_rmw);
232+
}
213233
}
214234
{
215235
let rcl_handle = self.rcl_handle.lock().unwrap();

0 commit comments

Comments
 (0)