use super::GoalStatusCode;
use crate::{
log_error, rcl_bindings::*, ActionServerHandle, GoalUuid, RclErrorMsg, RclReturnCode,
RclrsError, ToResult,
};
use rosidl_runtime_rs::{Action, RmwResultResponse};
use std::sync::{Mutex, MutexGuard};
pub(super) struct ActionServerGoalHandle<A: Action> {
rcl_handle: Mutex<rcl_action_goal_handle_t>,
result_response: Mutex<ResponseState<A>>,
uuid: GoalUuid,
}
impl<A: Action> ActionServerGoalHandle<A> {
pub(super) fn new(rcl_handle: rcl_action_goal_handle_s, uuid: GoalUuid) -> Self {
Self {
rcl_handle: Mutex::new(rcl_handle),
result_response: Mutex::new(ResponseState::new()),
uuid,
}
}
pub(super) fn lock(&self) -> MutexGuard<'_, rcl_action_goal_handle_t> {
self.rcl_handle.lock().unwrap()
}
pub(super) fn get_status(&self) -> GoalStatusCode {
let mut state = GoalStatusCode::Unknown as rcl_action_goal_state_t;
{
let rcl_handle = self.lock();
let r = unsafe { rcl_action_goal_handle_get_status(&*rcl_handle, &mut state).ok() };
if let Err(err) = r {
log_error!(
"ActionServerGoalHandle.get_status",
"Unexpected error while getting status: {err}",
);
}
}
unsafe { std::mem::transmute(state) }
}
pub(super) fn is_cancelled(&self) -> bool {
self.get_status() == GoalStatusCode::Cancelled
}
pub(super) fn goal_id(&self) -> &GoalUuid {
&self.uuid
}
pub(super) fn provide_result(
&self,
action_server_handle: &ActionServerHandle<A>,
result: RmwResultResponse<A>,
) -> Result<(), RclrsError> {
self.result_response
.lock()?
.provide_result(action_server_handle, &self.uuid, result)
}
pub(super) fn add_result_request(
&self,
action_server_handle: &ActionServerHandle<A>,
result_request: rmw_request_id_t,
) -> Result<(), RclrsError> {
self.result_response
.lock()?
.add_result_request(action_server_handle, result_request)
}
}
impl<A: Action> Drop for ActionServerGoalHandle<A> {
fn drop(&mut self) {
let mut rcl_handle = self.rcl_handle.lock().unwrap();
unsafe {
rcl_action_goal_handle_fini(&mut *rcl_handle);
}
}
}
unsafe impl Send for rcl_action_goal_handle_t {}
enum ResponseState<A: Action> {
Waiting(Vec<rmw_request_id_t>),
Available(RmwResultResponse<A>),
}
impl<A: Action> ResponseState<A> {
fn new() -> Self {
Self::Waiting(Vec::new())
}
fn provide_result(
&mut self,
action_server_handle: &ActionServerHandle<A>,
goal_id: &GoalUuid,
mut result: RmwResultResponse<A>,
) -> Result<(), RclrsError> {
let result_requests = match self {
Self::Waiting(waiting) => waiting,
Self::Available(previous) => {
log_error!(
"action_server_goal_handle.provide_result",
"Action goal {goal_id} was provided with multiple results, \
which is not allowed by the action server state machine and \
indicates a bug in rclrs. The new result will be discarded.\
\nPrevious result: {previous:?}\
\nNew result: {result:?}"
);
return Err(RclrsError::RclError {
code: RclReturnCode::ActionGoalEventInvalid,
msg: Some(RclErrorMsg(
"action goal response is already set".to_string(),
)),
});
}
};
if !result_requests.is_empty() {
let action_server = action_server_handle.lock();
for mut result_request in result_requests {
Self::send_result(&*action_server, &mut result_request, &mut result)?;
}
}
*self = Self::Available(result);
Ok(())
}
fn add_result_request(
&mut self,
action_server_handle: &ActionServerHandle<A>,
mut result_request: rmw_request_id_t,
) -> Result<(), RclrsError> {
match self {
Self::Waiting(waiting) => {
waiting.push(result_request);
}
Self::Available(result) => {
let action_server = action_server_handle.lock();
Self::send_result(&*action_server, &mut result_request, result)?;
}
}
Ok(())
}
fn send_result(
action_server: &rcl_action_server_t,
result_request: &mut rmw_request_id_t,
result_response: &mut RmwResultResponse<A>,
) -> Result<(), RclrsError> {
unsafe {
rcl_action_send_result_response(
action_server,
result_request,
result_response as *mut _ as *mut _,
)
.ok()
}
}
}