use crate::dialect::{mav_result, CommandAck};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AckOutcome {
Unrelated,
InProgress(u8),
Final(u8),
}
#[derive(Clone, Copy, Debug)]
pub struct CommandProtocol {
command: u16,
confirmation: u8,
retries_left: u8,
}
impl CommandProtocol {
pub fn new(command: u16, max_retries: u8) -> Self {
CommandProtocol {
command,
confirmation: 0,
retries_left: max_retries,
}
}
pub fn command(&self) -> u16 {
self.command
}
pub fn confirmation(&self) -> u8 {
self.confirmation
}
pub fn on_ack(&self, ack: &CommandAck) -> AckOutcome {
if ack.command != self.command {
return AckOutcome::Unrelated;
}
if ack.result == mav_result::IN_PROGRESS {
AckOutcome::InProgress(ack.progress)
} else {
AckOutcome::Final(ack.result)
}
}
pub fn on_timeout(&mut self) -> Option<u8> {
if self.retries_left == 0 {
return None;
}
self.retries_left -= 1;
self.confirmation = self.confirmation.wrapping_add(1);
Some(self.confirmation)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dialect::mav_cmd;
fn ack(command: u16, result: u8, progress: u8) -> CommandAck {
CommandAck {
command,
result,
progress,
result_param2: 0,
target_system: 1,
target_component: 1,
}
}
#[test]
fn an_ack_for_another_command_is_unrelated() {
let protocol = CommandProtocol::new(mav_cmd::COMPONENT_ARM_DISARM, 5);
let other = ack(mav_cmd::NAV_TAKEOFF, mav_result::ACCEPTED, 0);
assert_eq!(protocol.on_ack(&other), AckOutcome::Unrelated);
}
#[test]
fn an_accepted_ack_is_final() {
let protocol = CommandProtocol::new(mav_cmd::COMPONENT_ARM_DISARM, 5);
let accepted = ack(mav_cmd::COMPONENT_ARM_DISARM, mav_result::ACCEPTED, 0);
assert_eq!(
protocol.on_ack(&accepted),
AckOutcome::Final(mav_result::ACCEPTED)
);
}
#[test]
fn an_in_progress_ack_keeps_waiting_with_the_progress() {
let protocol = CommandProtocol::new(mav_cmd::NAV_TAKEOFF, 5);
let running = ack(mav_cmd::NAV_TAKEOFF, mav_result::IN_PROGRESS, 42);
assert_eq!(protocol.on_ack(&running), AckOutcome::InProgress(42));
}
#[test]
fn a_timeout_resends_with_an_incremented_confirmation_until_the_budget_runs_out() {
let mut protocol = CommandProtocol::new(mav_cmd::COMPONENT_ARM_DISARM, 2);
assert_eq!(protocol.confirmation(), 0);
assert_eq!(protocol.on_timeout(), Some(1));
assert_eq!(protocol.confirmation(), 1);
assert_eq!(protocol.on_timeout(), Some(2));
assert_eq!(protocol.confirmation(), 2);
assert_eq!(protocol.on_timeout(), None);
}
}