use crate::PackError;
use crate::{DjiValidator, UnPackError, Validator};
use crate::{ImplMarshal, ImplUnMarshal, MarshalerError};
use core::marker::PhantomData;
const SOF: u8 = 0xA5;
const HEAD_SIZE: usize = 5;
const CMDID_SIZE: usize = size_of::<u16>();
const TAIL_SIZE: usize = size_of::<u16>();
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[doc(alias("Msg", "Msger", "MessageParser", "FrameParser"))]
pub struct Messager<V: Validator = DjiValidator> {
sequence: u8,
_marker: PhantomData<V>,
}
impl<V: Validator> Messager<V> {
pub const fn new(seq: u8) -> Self {
Self {
sequence: seq,
_marker: PhantomData,
}
}
pub fn pack<M: ImplMarshal>(&mut self, msg: &M, dst: &mut [u8]) -> Result<usize, PackError> {
let mut cursor: usize = 0;
let payload_offset = HEAD_SIZE + CMDID_SIZE;
let payload_size = M::PAYLOAD_SIZE as usize;
let total = payload_offset + payload_size + TAIL_SIZE;
if dst.len() < total {
return Err(PackError::BufferTooSmall { need: total });
}
let size = msg.marshal(&mut dst[payload_offset..payload_offset + payload_size])?;
if size != payload_size {
return Err(PackError::InvalidPayloadSize {
expected: M::PAYLOAD_SIZE as usize,
found: size,
});
}
let cmd_id = M::CMD_ID;
let sequence = self.sequence;
let header: [u8; HEAD_SIZE] = {
let mut temp = [0; _];
let size_bytes = (payload_size as u16).to_le_bytes();
temp[0] = SOF;
temp[1] = size_bytes[0];
temp[2] = size_bytes[1];
temp[3] = sequence;
temp[4] = V::calculate_crc8(&temp[..4]);
temp
};
dst[cursor..cursor + HEAD_SIZE].copy_from_slice(&header);
cursor += HEAD_SIZE;
dst[cursor..cursor + CMDID_SIZE].copy_from_slice(&cmd_id.to_le_bytes());
cursor += CMDID_SIZE;
cursor += payload_size;
let crc: u16 = V::calculate_crc16(&dst[..cursor]);
dst[cursor..cursor + TAIL_SIZE].copy_from_slice(&crc.to_le_bytes());
cursor += TAIL_SIZE;
self.sequence = self.sequence.wrapping_add(1);
Ok(cursor)
}
pub fn unpack<'t>(&self, src: &'t [u8]) -> Result<(RawFrame<'t>, usize), UnPackError> {
let mut cursor = 0;
if !src.starts_with(&[SOF]) {
if let Some(start) = src.iter().position(|&x| SOF == x) {
return Err(UnPackError::ReSync { skip: start });
} else {
return Err(UnPackError::MissingHeader { skip: src.len() });
}
}
let Some(header) = src.get(cursor..cursor + HEAD_SIZE) else {
return Err(UnPackError::UnexpectedEnd { read: src.len() });
};
cursor += HEAD_SIZE;
let (length, sequence) = {
let (header_bytes, crc) = (&header[..4], header[4]);
if V::calculate_crc8(header_bytes) != crc {
return Err(UnPackError::InvalidChecksum { at: cursor });
}
let length = u16::from_le_bytes([header_bytes[1], header_bytes[2]]);
let sequence = header_bytes[3];
(length as usize, sequence)
};
let Some(cmd) = src.get(cursor..cursor + CMDID_SIZE) else {
return Err(UnPackError::UnexpectedEnd { read: src.len() });
};
cursor += CMDID_SIZE;
let Some(payload) = src.get(cursor..cursor + length) else {
return Err(UnPackError::UnexpectedEnd { read: src.len() });
};
cursor += length;
let frame_bytes = &src[..cursor];
let Some(tail) = src.get(cursor..cursor + TAIL_SIZE) else {
return Err(UnPackError::UnexpectedEnd { read: src.len() });
};
cursor += TAIL_SIZE;
let crc = u16::from_le_bytes([tail[0], tail[1]]);
if V::calculate_crc16(frame_bytes) != crc {
return Err(UnPackError::InvalidChecksum { at: cursor });
}
let cmd_id = u16::from_le_bytes([cmd[0], cmd[1]]);
Ok((
RawFrame {
cmd_id,
sequence,
payload,
},
cursor,
))
}
pub fn unmarshal<M: ImplUnMarshal>(&self, src: &[u8]) -> Result<(M, usize), UnPackError> {
let (frame, cursor) = self.unpack(src)?;
Ok((frame.unmarshal::<M>()?, cursor))
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[doc(alias("Frame", "RawMessage", "RawMsg"))]
pub struct RawFrame<'t> {
pub(crate) cmd_id: u16,
pub(crate) sequence: u8,
pub(crate) payload: &'t [u8],
}
impl<'t> RawFrame<'t> {
pub fn cmd_id(&self) -> u16 {
self.cmd_id
}
pub fn sequence(&self) -> u8 {
self.sequence
}
pub fn unmarshal<M: ImplUnMarshal>(&self) -> Result<M, MarshalerError> {
if M::CMD_ID != self.cmd_id {
return Err(MarshalerError::InvalidCmdID {
expected: M::CMD_ID,
found: self.cmd_id,
});
}
if self.payload.len() != M::PAYLOAD_SIZE as usize {
return Err(MarshalerError::InvalidDataLength {
expected: M::PAYLOAD_SIZE as usize,
found: self.payload.len(),
});
}
M::unmarshal(self.payload)
}
pub fn payload(&self) -> &'t [u8] {
self.payload
}
}