use crate::dialect::{
encode_message, mav_mission_result, CommandAck, Message, MissionAck, MissionCount,
MissionItemInt, MissionRequest, MissionRequestInt, MissionRequestList,
};
use crate::error::Result;
use crate::frame::{Frame, Header};
use super::{AckOutcome, CommandProtocol, MissionReceiver, MissionSender, ReceiverAction};
#[derive(Clone, Copy, Debug)]
pub struct ReceiverStep {
pub accepted: Option<MissionItemInt>,
pub action: ReceiverAction,
pub reply: Frame,
}
impl MissionReceiver {
pub fn request_list_frame(&self, header: Header) -> Result<Frame> {
encode_message(header, &self.request_list())
}
pub fn on_frame(&mut self, frame: &Frame, header: Header) -> Result<Option<ReceiverStep>> {
let id = frame.message_id();
if id == MissionCount::ID {
let count = MissionCount::decode(frame.payload())?;
let action = self.on_count(count.count);
Ok(Some(ReceiverStep {
accepted: None,
reply: reply_of(&action, header)?,
action,
}))
} else if id == MissionItemInt::ID {
let item = MissionItemInt::decode(frame.payload())?;
let (accepted, action) = self.on_item(&item);
Ok(Some(ReceiverStep {
accepted,
reply: reply_of(&action, header)?,
action,
}))
} else {
Ok(None)
}
}
}
fn reply_of(action: &ReceiverAction, header: Header) -> Result<Frame> {
match action {
ReceiverAction::Request(request) => encode_message(header, request),
ReceiverAction::Ack(ack) => encode_message(header, ack),
}
}
#[derive(Clone, Copy, Debug)]
#[allow(clippy::large_enum_variant)]
pub enum SenderStep {
Reply(Frame),
Finished(u8),
}
impl MissionSender<'_> {
pub fn count_frame(&self, header: Header) -> Result<Frame> {
encode_message(header, &self.count())
}
pub fn on_frame(&self, frame: &Frame, header: Header) -> Result<Option<SenderStep>> {
let id = frame.message_id();
if id == MissionRequestList::ID {
MissionRequestList::decode(frame.payload())?;
encode_message(header, &self.count()).map(|frame| Some(SenderStep::Reply(frame)))
} else if id == MissionRequestInt::ID || id == MissionRequest::ID {
let seq = if id == MissionRequestInt::ID {
MissionRequestInt::decode(frame.payload())?.seq
} else {
MissionRequest::decode(frame.payload())?.seq
};
let reply = match self.item(seq) {
Some(item) => encode_message(header, &item)?,
None => encode_message(header, &self.refuse(mav_mission_result::INVALID_SEQUENCE))?,
};
Ok(Some(SenderStep::Reply(reply)))
} else if id == MissionAck::ID {
let ack = MissionAck::decode(frame.payload())?;
Ok(Some(SenderStep::Finished(ack.type_)))
} else {
Ok(None)
}
}
fn refuse(&self, result: u8) -> MissionAck {
let count = self.count();
MissionAck {
target_system: count.target_system,
target_component: count.target_component,
type_: result,
mission_type: count.mission_type,
..MissionAck::zeroed()
}
}
}
impl CommandProtocol {
pub fn on_frame(&self, frame: &Frame) -> Result<Option<AckOutcome>> {
if frame.message_id() == CommandAck::ID {
Ok(Some(self.on_ack(&CommandAck::decode(frame.payload())?)))
} else {
Ok(None)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dialect::{mav_cmd, mav_result};
const VEHICLE: Header = Header::new(1, 1, 0);
const STATION: Header = Header::new(255, 190, 0);
fn plan() -> [MissionItemInt; 2] {
[
MissionItemInt {
command: mav_cmd::NAV_TAKEOFF,
z: 20.0,
..MissionItemInt::zeroed()
},
MissionItemInt {
command: mav_cmd::NAV_WAYPOINT,
x: -338_567_800,
y: 1_512_153_000,
z: 50.0,
..MissionItemInt::zeroed()
},
]
}
#[test]
fn a_whole_upload_runs_frame_to_frame() -> Result<()> {
let items = plan();
let upload = MissionSender::new(&items, 1, 1, 0);
let mut download = MissionReceiver::new(255, 190, 0);
let opened = download.request_list_frame(STATION)?;
let Some(SenderStep::Reply(mut from_vehicle)) = upload.on_frame(&opened, VEHICLE)? else {
panic!("a request list is answered");
};
assert_eq!(from_vehicle.message_id(), MissionCount::ID);
let mut accepted = 0;
loop {
let step = download
.on_frame(&from_vehicle, STATION)?
.expect("the vehicle only sends what the receiver handles");
if step.accepted.is_some() {
accepted += 1;
}
match upload.on_frame(&step.reply, VEHICLE)? {
Some(SenderStep::Reply(next)) => from_vehicle = next,
Some(SenderStep::Finished(result)) => {
assert_eq!(result, mav_mission_result::ACCEPTED);
break;
}
None => panic!("the receiver only sends what the sender handles"),
}
}
assert_eq!(accepted, 2);
assert!(download.is_complete());
Ok(())
}
#[test]
fn a_request_past_the_plan_is_refused_with_the_published_result() -> Result<()> {
let items = plan();
let upload = MissionSender::new(&items, 1, 1, 0);
let request = MissionRequestInt {
seq: 7,
target_system: 255,
target_component: 190,
mission_type: 0,
};
let Some(SenderStep::Reply(reply)) =
upload.on_frame(&encode_message(VEHICLE, &request)?, STATION)?
else {
panic!("a request is answered");
};
assert_eq!(reply.message_id(), MissionAck::ID);
assert_eq!(
MissionAck::decode(reply.payload())?.type_,
mav_mission_result::INVALID_SEQUENCE
);
Ok(())
}
#[test]
fn the_older_request_message_is_answered_too() -> Result<()> {
let items = plan();
let upload = MissionSender::new(&items, 1, 1, 0);
let request = MissionRequest {
seq: 1,
target_system: 255,
target_component: 190,
mission_type: 0,
};
let Some(SenderStep::Reply(reply)) =
upload.on_frame(&encode_message(VEHICLE, &request)?, STATION)?
else {
panic!("a request is answered");
};
assert_eq!(MissionItemInt::decode(reply.payload())?.seq, 1);
Ok(())
}
#[test]
fn a_frame_for_another_machine_is_passed_over() -> Result<()> {
let items = plan();
let upload = MissionSender::new(&items, 1, 1, 0);
let mut download = MissionReceiver::new(255, 190, 0);
let arm = CommandProtocol::new(mav_cmd::COMPONENT_ARM_DISARM, 3);
let ack = CommandAck {
command: mav_cmd::COMPONENT_ARM_DISARM,
result: mav_result::IN_PROGRESS,
progress: 40,
..CommandAck::zeroed()
};
let frame = encode_message(VEHICLE, &ack)?;
assert!(upload.on_frame(&frame, STATION)?.is_none());
assert!(download.on_frame(&frame, STATION)?.is_none());
assert_eq!(arm.on_frame(&frame)?, Some(AckOutcome::InProgress(40)));
Ok(())
}
}