use heapless::Vec as HVec;
use crate::protocol::dds::api::error::DdsApiError;
use crate::protocol::dds::api::participant::Participant;
use crate::protocol::dds::api::publisher::Publisher;
use crate::protocol::dds::api::service::wrappers::{ReplyWrapper, RequestWrapper};
use crate::protocol::dds::api::subscription::Subscription;
use crate::protocol::dds::ros2::msgs::action_msgs::{
goal_status, CancelGoal, CancelGoalResponse, GoalInfo, GoalStatusArray,
};
use crate::protocol::dds::ros2::msgs::builtin_interfaces::Time;
use super::types::{
Action, ActionHandler, ExecuteResult, GetResultResponse, GetResultService, GoalOutcome,
GoalState, SendGoalResponse, SendGoalService,
};
pub(crate) struct ActionServerParts<A: Action> {
pub send_goal_req_sub: Subscription<RequestWrapper<SendGoalService<A>>>,
pub send_goal_rep_pub: Publisher<ReplyWrapper<SendGoalService<A>>>,
pub cancel_goal_req_sub: Subscription<RequestWrapper<CancelGoal>>,
pub cancel_goal_rep_pub: Publisher<ReplyWrapper<CancelGoal>>,
pub get_result_req_sub: Subscription<RequestWrapper<GetResultService<A>>>,
pub get_result_rep_pub: Publisher<ReplyWrapper<GetResultService<A>>>,
pub feedback_pub: Publisher<A::Feedback>,
pub status_pub: Publisher<GoalStatusArray>,
}
pub struct ActionServer<A: Action> {
send_goal_req_sub: Subscription<RequestWrapper<SendGoalService<A>>>,
send_goal_rep_pub: Publisher<ReplyWrapper<SendGoalService<A>>>,
cancel_goal_req_sub: Subscription<RequestWrapper<CancelGoal>>,
cancel_goal_rep_pub: Publisher<ReplyWrapper<CancelGoal>>,
get_result_req_sub: Subscription<RequestWrapper<GetResultService<A>>>,
get_result_rep_pub: Publisher<ReplyWrapper<GetResultService<A>>>,
feedback_pub: Publisher<A::Feedback>,
status_pub: Publisher<GoalStatusArray>,
goals: HVec<GoalState<A>, 16>,
}
impl<A: Action> ActionServer<A> {
pub(crate) fn new(parts: ActionServerParts<A>) -> Self {
Self {
send_goal_req_sub: parts.send_goal_req_sub,
send_goal_rep_pub: parts.send_goal_rep_pub,
cancel_goal_req_sub: parts.cancel_goal_req_sub,
cancel_goal_rep_pub: parts.cancel_goal_rep_pub,
get_result_req_sub: parts.get_result_req_sub,
get_result_rep_pub: parts.get_result_rep_pub,
feedback_pub: parts.feedback_pub,
status_pub: parts.status_pub,
goals: HVec::new(),
}
}
pub fn process<H: ActionHandler<A>>(
&mut self,
participant: &mut Participant,
handler: &mut H,
) -> Result<(), DdsApiError> {
let send_goal_samples = participant.take(&self.send_goal_req_sub);
for sample in send_goal_samples {
let RequestWrapper { header, body: req } = sample.data;
let outcome = handler.accept_goal(&req.goal_id, &req.goal);
let accepted = matches!(outcome, GoalOutcome::Accept);
if accepted && !self.goals.is_full() {
let gs = GoalState::<A>::new(req.goal_id, req.goal);
let _ = self.goals.push(gs);
}
let response = SendGoalResponse {
accepted,
stamp: Time::default(),
};
let reply = ReplyWrapper::<SendGoalService<A>> {
header,
body: response,
};
participant.publish(&self.send_goal_rep_pub, &reply)?;
}
let cancel_samples = participant.take(&self.cancel_goal_req_sub);
for sample in cancel_samples {
let RequestWrapper { header, body: req } = sample.data;
let target_id = req.goal_info.goal_id;
let mut goals_canceling: HVec<GoalInfo, 16> = HVec::new();
for gs in self.goals.iter_mut() {
if gs.goal_info.goal_id == target_id
&& (gs.status == goal_status::ACCEPTED || gs.status == goal_status::EXECUTING)
&& handler.cancel_goal(&gs.goal_info.goal_id)
{
gs.status = goal_status::CANCELING;
let _ = goals_canceling.push(gs.goal_info.clone());
}
}
let response = CancelGoalResponse {
return_code: 0,
goals_canceling,
};
let reply = ReplyWrapper::<CancelGoal> {
header,
body: response,
};
participant.publish(&self.cancel_goal_rep_pub, &reply)?;
}
for gs in self.goals.iter_mut() {
if gs.status == goal_status::ACCEPTED {
gs.status = goal_status::EXECUTING;
}
if gs.status == goal_status::EXECUTING || gs.status == goal_status::CANCELING {
let goal_id = gs.goal_info.goal_id;
let goal = gs.goal.clone();
let mut collected_feedback: HVec<A::Feedback, 8> = HVec::new();
let exec_result = {
let mut fb_cb = |fb: A::Feedback| {
let _ = collected_feedback.push(fb);
};
handler.execute_goal(&goal_id, &goal, &mut fb_cb)
};
for fb in collected_feedback {
let _ = gs.pending_feedback.push(fb);
}
match exec_result {
ExecuteResult::Succeeded(r) => {
gs.status = goal_status::SUCCEEDED;
gs.result = Some(r);
}
ExecuteResult::Canceled(r) => {
gs.status = goal_status::CANCELED;
gs.result = Some(r);
}
ExecuteResult::Aborted(r) => {
gs.status = goal_status::ABORTED;
gs.result = Some(r);
}
}
}
}
for gs in self.goals.iter_mut() {
while let Some(fb) = gs.pending_feedback.pop() {
participant.publish(&self.feedback_pub, &fb)?;
}
}
let get_result_samples = participant.take(&self.get_result_req_sub);
for sample in get_result_samples {
let RequestWrapper { header, body: req } = sample.data;
let result_opt = self.goals.iter().find_map(|gs| {
if gs.goal_info.goal_id == req.goal_id
&& (gs.status == goal_status::SUCCEEDED
|| gs.status == goal_status::CANCELED
|| gs.status == goal_status::ABORTED)
{
gs.result.as_ref().map(|r| (gs.status, r.clone()))
} else {
None
}
});
if let Some((status, result)) = result_opt {
let response = GetResultResponse::<A> { status, result };
let reply = ReplyWrapper::<GetResultService<A>> {
header,
body: response,
};
participant.publish(&self.get_result_rep_pub, &reply)?;
}
}
let mut status_array = GoalStatusArray::default();
for gs in &self.goals {
let _ = status_array.status_list.push(gs.to_goal_status());
}
participant.publish(&self.status_pub, &status_array)?;
Ok(())
}
}