Skip to content

Commit 16363a9

Browse files
committed
Begin implementing ActionServerGoalHandle functions
So far, none of the implemented functionality actually depends on the action type. It could be separated out into a concrete type like is done in rclcpp, in case that reduces the cost of monomorphization.
1 parent ed9a2ee commit 16363a9

File tree

1 file changed

+95
-6
lines changed

1 file changed

+95
-6
lines changed

rclrs/src/action/server_goal_handle.rs

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{rcl_bindings::*, RclrsError};
1+
use crate::{rcl_bindings::*, RclrsError, ToResult};
22
use std::{
33
marker::PhantomData,
44
sync::{Arc, Mutex},
@@ -10,13 +10,25 @@ unsafe impl Send for rcl_action_goal_handle_t {}
1010

1111
unsafe impl Sync for rcl_action_goal_handle_t {}
1212

13+
// Values defined by `action_msgs/msg/GoalStatus`
14+
#[repr(i8)]
15+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
16+
enum GoalStatus {
17+
Unknown = 0,
18+
Accepted = 1,
19+
Executing = 2,
20+
Canceling = 3,
21+
Succeeded = 4,
22+
Canceled = 5,
23+
Aborted = 6,
24+
}
25+
1326
pub struct ServerGoalHandle<T>
1427
where
1528
T: rosidl_runtime_rs::Action,
1629
{
1730
rcl_handle: Arc<Mutex<rcl_action_goal_handle_t>>,
1831
goal_request: Arc<T>,
19-
_marker: PhantomData<T>,
2032
}
2133

2234
impl<T> ServerGoalHandle<T>
@@ -27,27 +39,104 @@ where
2739
Self {
2840
rcl_handle,
2941
goal_request: Arc::clone(&goal_request),
30-
_marker: Default::default(),
3142
}
3243
}
3344

45+
/// Returns the goal state.
46+
fn get_state(&self) -> Result<GoalStatus, RclrsError> {
47+
let mut state = GoalStatus::Unknown as rcl_action_goal_state_t;
48+
{
49+
let rcl_handle = self.rcl_handle.lock().unwrap();
50+
// SAFETY: The provided goal handle is properly initialized by construction.
51+
unsafe { rcl_action_goal_handle_get_status(&*rcl_handle, &mut state).ok()? }
52+
}
53+
// SAFETY: state is initialized to a valid GoalStatus value and will only ever by set by
54+
// rcl_action_goal_handle_get_status to a valid GoalStatus value.
55+
Ok(unsafe { std::mem::transmute(state) })
56+
}
57+
58+
/// Returns whether the client has requested that this goal be cancelled.
3459
pub fn is_canceling(&self) -> bool {
35-
false
60+
self.get_state().unwrap() == GoalStatus::Canceling
3661
}
3762

63+
/// Returns true if the goal is either pending or executing, or false if it has reached a
64+
/// terminal state.
3865
pub fn is_active(&self) -> bool {
39-
false
66+
let rcl_handle = self.rcl_handle.lock().unwrap();
67+
// SAFETY: The provided goal handle is properly initialized by construction.
68+
unsafe { rcl_action_goal_handle_is_active(&*rcl_handle) }
4069
}
4170

71+
/// Returns whether the goal is executing.
4272
pub fn is_executing(&self) -> bool {
43-
false
73+
self.get_state().unwrap() == GoalStatus::Executing
74+
}
75+
76+
/// Attempt to perform the given goal state transition.
77+
fn update_state(&self, event: rcl_action_goal_event_t) -> Result<(), RclrsError> {
78+
let mut rcl_handle = self.rcl_handle.lock().unwrap();
79+
// SAFETY: The provided goal handle is properly initialized by construction.
80+
unsafe { rcl_action_update_goal_state(&mut *rcl_handle, event).ok() }
81+
}
82+
83+
pub fn abort(&self, result: &T::Result) -> Result<(), RclrsError> {
84+
self.update_state(rcl_action_goal_event_t::GOAL_EVENT_ABORT)?;
85+
86+
// TODO: Invoke on_terminal_state callback
87+
Ok(())
4488
}
4589

4690
pub fn succeed(&self, result: &T::Result) -> Result<(), RclrsError> {
91+
self.update_state(rcl_action_goal_event_t::GOAL_EVENT_SUCCEED)?;
92+
93+
// TODO: Invoke on_terminal_state callback
4794
Ok(())
4895
}
4996

5097
pub fn canceled(&self, result: &T::Result) -> Result<(), RclrsError> {
98+
self.update_state(rcl_action_goal_event_t::GOAL_EVENT_CANCELED)?;
99+
100+
// TODO: Invoke on_terminal_state callback
101+
Ok(())
102+
}
103+
104+
pub fn execute(&self, result: &T::Result) -> Result<(), RclrsError> {
105+
self.update_state(rcl_action_goal_event_t::GOAL_EVENT_EXECUTE)?;
106+
107+
// TODO: Invoke on_executing callback
51108
Ok(())
52109
}
110+
111+
/// Try canceling the goal if possible.
112+
fn try_canceling(&mut self) -> Result<bool, RclrsError> {
113+
let rcl_handle = self.rcl_handle.lock().unwrap();
114+
115+
// If the goal is in a cancelable state, transition to canceling.
116+
// SAFETY: The provided goal handle is properly initialized by construction.
117+
let is_cancelable = unsafe { rcl_action_goal_handle_is_cancelable(&*rcl_handle) };
118+
if is_cancelable {
119+
self.update_state(rcl_action_goal_event_t::GOAL_EVENT_CANCEL_GOAL)?;
120+
}
121+
122+
// If the goal is canceling, transition to canceled.
123+
if self.get_state()? == GoalStatus::Canceling {
124+
self.update_state(rcl_action_goal_event_t::GOAL_EVENT_CANCELED)?;
125+
Ok(true)
126+
} else {
127+
Ok(false)
128+
}
129+
}
130+
}
131+
132+
impl<T> Drop for ServerGoalHandle<T>
133+
where
134+
T: rosidl_runtime_rs::Action,
135+
{
136+
/// Cancel the goal if its handle is dropped without reaching a terminal state.
137+
fn drop(&mut self) {
138+
if self.try_canceling() == Ok(true) {
139+
// TODO: Invoke on_terminal_state callback
140+
}
141+
}
53142
}

0 commit comments

Comments
 (0)