use crate::error::ProtocolError;
pub(crate) const ASYNC_MARKER: &[u8; 10] = b"Moduletech";
pub(crate) const ASYNC_TERMINATOR: u8 = 0xBB;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AsyncPayload<'a> {
pub subcommand: u16,
pub subcommand_data: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AsyncPayloadOwned {
pub subcommand: u16,
pub subcommand_data: Vec<u8>,
}
pub fn parse_async_payload(data: &[u8]) -> Result<AsyncPayload<'_>, ProtocolError> {
if data.len() < 13 {
return Err(ProtocolError::InvalidResponse(
"async payload too short for marker+code+subcrc+terminator",
));
}
if &data[..10] != ASYNC_MARKER {
return Err(ProtocolError::InvalidResponse("invalid async marker"));
}
let subcommand = u16::from_be_bytes([data[10], data[11]]);
let terminator = data[data.len() - 1];
if terminator != ASYNC_TERMINATOR {
return Err(ProtocolError::InvalidResponse("invalid async terminator"));
}
let sub_crc_actual = data[data.len() - 2];
let subcommand_data = &data[12..data.len() - 2];
let sub_crc_expected = subcommand_crc(subcommand, subcommand_data);
if sub_crc_actual != sub_crc_expected {
return Err(ProtocolError::InvalidResponse("invalid async sub-CRC"));
}
Ok(AsyncPayload {
subcommand,
subcommand_data,
})
}
pub fn parse_async_payload_owned(data: &[u8]) -> Result<AsyncPayloadOwned, ProtocolError> {
let parsed = parse_async_payload(data)?;
Ok(AsyncPayloadOwned {
subcommand: parsed.subcommand,
subcommand_data: parsed.subcommand_data.to_vec(),
})
}
pub fn subcommand_crc(subcommand: u16, subcommand_data: &[u8]) -> u8 {
let [hi, lo] = subcommand.to_be_bytes();
let mut sum = hi.wrapping_add(lo);
for b in subcommand_data {
sum = sum.wrapping_add(*b);
}
sum
}