use crate::dialect::{
self, CommandAck, CommandLong, Heartbeat, Message, MissionCount, MissionItemInt,
MissionRequest, MissionRequestInt, MissionRequestList,
};
use crate::error::{MavlinkError, Result};
use crate::frame::{Frame, Header};
use crate::protocol::mission::{MissionReceiver, MissionSender, ReceiverAction};
use crate::signing::{Signer, Verifier};
fn crc_extra_for(msgid: u32) -> Option<u8> {
dialect::crc_extra(msgid)
}
pub trait ByteLink {
fn read(&mut self, buf: &mut [u8]) -> impl core::future::Future<Output = Result<usize>>;
fn write_all(&mut self, data: &[u8]) -> impl core::future::Future<Output = Result<()>>;
}
const READ_CHUNK: usize = 512;
pub struct Connection<L> {
link: L,
parser: crate::parser::Parser,
header: Header,
signer: Option<Signer>,
verifier: Option<Verifier>,
require_signed: bool,
staging: [u8; READ_CHUNK],
staged_len: usize,
staged_pos: usize,
}
impl<L: ByteLink> Connection<L> {
pub fn new(link: L, system_id: u8, component_id: u8) -> Self {
Connection {
link,
parser: crate::parser::Parser::new(),
header: Header::new(system_id, component_id, 0),
signer: None,
verifier: None,
require_signed: false,
staging: [0u8; READ_CHUNK],
staged_len: 0,
staged_pos: 0,
}
}
pub fn with_signer(mut self, signer: Signer) -> Self {
self.signer = Some(signer);
self
}
pub fn with_verifier(mut self, verifier: Verifier) -> Self {
self.verifier = Some(verifier);
self.require_signed = true;
self
}
pub async fn send<M: Message>(&mut self, message: &M) -> Result<()> {
let mut payload = [0u8; crate::frame::MAX_PAYLOAD];
let len = message.encode(&mut payload);
let frame = match self.signer.as_mut() {
Some(signer) => signer.sign(self.header, M::ID, &payload[..len], M::CRC_EXTRA)?,
None => Frame::encode_v2(self.header, M::ID, &payload[..len], M::CRC_EXTRA)?,
};
self.link.write_all(frame.as_bytes()).await?;
self.header.sequence = self.header.sequence.wrapping_add(1);
Ok(())
}
pub async fn recv(&mut self) -> Result<Frame> {
loop {
while self.staged_pos < self.staged_len {
let byte = self.staging[self.staged_pos];
self.staged_pos += 1;
if let Some(frame) = self.parser.push_byte(byte, &crc_extra_for) {
if let Some(verifier) = self.verifier.as_mut() {
if frame.is_signed() {
verifier.verify(&frame)?;
} else if self.require_signed {
return Err(MavlinkError::Unsigned);
}
}
return Ok(frame);
}
}
let n = self.link.read(&mut self.staging).await?;
if n == 0 {
return Err(MavlinkError::Closed);
}
self.staged_len = n;
self.staged_pos = 0;
}
}
pub fn link(&self) -> &L {
&self.link
}
}
pub struct MemoryLink {
stream: tokio::io::DuplexStream,
}
impl MemoryLink {
pub fn pair() -> (MemoryLink, MemoryLink) {
let (a, b) = tokio::io::duplex(64 * 1024);
(MemoryLink { stream: a }, MemoryLink { stream: b })
}
}
impl ByteLink for MemoryLink {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
use tokio::io::AsyncReadExt;
self.stream
.read(buf)
.await
.map_err(|_| MavlinkError::Closed)
}
async fn write_all(&mut self, data: &[u8]) -> Result<()> {
use tokio::io::AsyncWriteExt;
self.stream
.write_all(data)
.await
.map_err(|_| MavlinkError::Closed)
}
}
pub struct SitlAutopilot {
connection: Connection<MemoryLink>,
mission: Vec<MissionItemInt>,
receiving: Option<(MissionReceiver, Vec<MissionItemInt>)>,
}
impl SitlAutopilot {
pub fn new(link: MemoryLink, system_id: u8, component_id: u8) -> Self {
SitlAutopilot {
connection: Connection::new(link, system_id, component_id),
mission: Vec::new(),
receiving: None,
}
}
pub fn load_mission(&mut self, items: &[MissionItemInt]) {
self.mission = items.to_vec();
}
pub fn secured(mut self, signer: Signer, verifier: Verifier) -> Self {
self.connection = self.connection.with_signer(signer).with_verifier(verifier);
self
}
pub async fn emit_heartbeat(&mut self) -> Result<()> {
let heartbeat = Heartbeat {
custom_mode: 0,
type_: dialect::mav_type::QUADROTOR,
autopilot: dialect::mav_autopilot::ARDUPILOTMEGA,
base_mode: dialect::mav_mode_flag::CUSTOM_MODE_ENABLED,
system_status: dialect::mav_state::ACTIVE,
mavlink_version: 3,
};
self.connection.send(&heartbeat).await
}
pub async fn serve_once(&mut self) -> Result<Frame> {
let frame = self.connection.recv().await?;
let (sys, comp) = (frame.system_id(), frame.component_id());
match frame.message_id() {
CommandLong::ID => {
let command = CommandLong::decode(frame.payload())?;
let ack = CommandAck {
command: command.command,
result: dialect::mav_result::ACCEPTED,
progress: 0,
result_param2: 0,
target_system: sys,
target_component: comp,
};
self.connection.send(&ack).await?;
}
MissionCount::ID => {
let count = MissionCount::decode(frame.payload())?.count;
let mut receiver =
MissionReceiver::new(sys, comp, dialect::mav_mission_type::MISSION);
let buffer = Vec::with_capacity(count as usize);
self.step_receive(receiver.on_count(count), receiver, buffer)
.await?;
}
MissionItemInt::ID => {
if let Some((mut receiver, mut buffer)) = self.receiving.take() {
let item = MissionItemInt::decode(frame.payload())?;
let (accepted, action) = receiver.on_item(&item);
if let Some(item) = accepted {
buffer.push(item);
}
self.step_receive(action, receiver, buffer).await?;
}
}
MissionRequestList::ID => {
let count = MissionSender::new(
&self.mission,
sys,
comp,
dialect::mav_mission_type::MISSION,
)
.count();
self.connection.send(&count).await?;
}
MissionRequestInt::ID => {
let seq = MissionRequestInt::decode(frame.payload())?.seq;
self.serve_item(sys, comp, seq).await?;
}
MissionRequest::ID => {
let seq = MissionRequest::decode(frame.payload())?.seq;
self.serve_item(sys, comp, seq).await?;
}
_ => {}
}
Ok(frame)
}
async fn step_receive(
&mut self,
action: ReceiverAction,
receiver: MissionReceiver,
buffer: Vec<MissionItemInt>,
) -> Result<()> {
match action {
ReceiverAction::Request(request) => {
self.connection.send(&request).await?;
self.receiving = Some((receiver, buffer));
}
ReceiverAction::Ack(ack) => {
self.mission = buffer;
self.connection.send(&ack).await?;
}
}
Ok(())
}
async fn serve_item(&mut self, sys: u8, comp: u8, seq: u16) -> Result<()> {
let item = MissionSender::new(&self.mission, sys, comp, dialect::mav_mission_type::MISSION)
.item(seq);
if let Some(item) = item {
self.connection.send(&item).await?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::signing::KEY_LEN;
const KEY: [u8; KEY_LEN] = [0x24; KEY_LEN];
fn arm_command() -> CommandLong {
CommandLong {
param1: 1.0,
param2: 0.0,
param3: 0.0,
param4: 0.0,
param5: 0.0,
param6: 0.0,
param7: 0.0,
command: dialect::mav_cmd::COMPONENT_ARM_DISARM,
target_system: 1,
target_component: 1,
confirmation: 0,
}
}
#[tokio::test]
async fn a_heartbeat_crosses_the_link() {
let (gcs_end, vehicle_end) = MemoryLink::pair();
let mut vehicle = SitlAutopilot::new(vehicle_end, 1, 1);
let mut gcs = Connection::new(gcs_end, 255, 190);
vehicle.emit_heartbeat().await.unwrap();
let frame = gcs.recv().await.unwrap();
assert_eq!(frame.message_id(), Heartbeat::ID);
let heartbeat = Heartbeat::decode(frame.payload()).unwrap();
assert_eq!(heartbeat.system_status, dialect::mav_state::ACTIVE);
}
#[tokio::test]
async fn a_command_is_answered_with_an_ack() {
let (gcs_end, vehicle_end) = MemoryLink::pair();
let mut vehicle = SitlAutopilot::new(vehicle_end, 1, 1);
let mut gcs = Connection::new(gcs_end, 255, 190);
gcs.send(&arm_command()).await.unwrap();
let served = vehicle.serve_once().await.unwrap();
assert_eq!(served.message_id(), CommandLong::ID);
let frame = gcs.recv().await.unwrap();
assert_eq!(frame.message_id(), CommandAck::ID);
let ack = CommandAck::decode(frame.payload()).unwrap();
assert_eq!(ack.command, dialect::mav_cmd::COMPONENT_ARM_DISARM);
assert_eq!(ack.result, dialect::mav_result::ACCEPTED);
}
#[tokio::test]
async fn a_signed_command_round_trips_over_the_link() {
let (gcs_end, vehicle_end) = MemoryLink::pair();
let mut vehicle = SitlAutopilot::new(vehicle_end, 1, 1)
.secured(Signer::new(KEY, 1, 10_000), Verifier::new(KEY));
let mut gcs = Connection::new(gcs_end, 255, 190)
.with_signer(Signer::new(KEY, 2, 20_000))
.with_verifier(Verifier::new(KEY));
gcs.send(&arm_command()).await.unwrap();
vehicle.serve_once().await.unwrap();
let frame = gcs.recv().await.unwrap();
assert!(frame.is_signed());
assert_eq!(frame.message_id(), CommandAck::ID);
}
#[tokio::test]
async fn an_unsigned_frame_is_refused_when_signing_is_required() {
let (gcs_end, vehicle_end) = MemoryLink::pair();
let mut vehicle = SitlAutopilot::new(vehicle_end, 1, 1)
.secured(Signer::new(KEY, 1, 10_000), Verifier::new(KEY));
let mut gcs = Connection::new(gcs_end, 255, 190);
gcs.send(&arm_command()).await.unwrap();
assert_eq!(vehicle.serve_once().await, Err(MavlinkError::Unsigned));
}
}