pub const SYNC: u8 = 0x02;
pub const DEFAULT_ADDR: u8 = 0x03;
pub const ACK: u8 = 0x00;
pub const NAK: u8 = 0xFF;
use crate::error::{Error, Result};
#[derive(Debug, Clone)]
pub struct Frame {
pub addr: u8,
pub data: Vec<u8>,
}
impl Frame {
pub fn new(data: Vec<u8>) -> Self {
Self {
addr: DEFAULT_ADDR,
data,
}
}
pub fn encode(&self) -> Vec<u8> {
let lng = self.data.len() + 5;
assert!(lng <= 255, "frame too large");
let mut bytes = Vec::with_capacity(lng);
bytes.push(SYNC);
bytes.push(self.addr);
bytes.push(lng as u8);
bytes.extend_from_slice(&self.data);
let crc = crc16(&bytes);
bytes.push((crc & 0xFF) as u8); bytes.push((crc >> 8) as u8);
bytes
}
pub fn decode(raw: &[u8]) -> Result<Self> {
if raw.len() < 6 {
return Err(Error::InvalidFrame("frame shorter than minimum 6 bytes"));
}
if raw[0] != SYNC {
return Err(Error::InvalidFrame("missing SYNC byte (0x02)"));
}
let lng = raw[2] as usize;
if lng < 6 {
return Err(Error::InvalidFrame("LNG field is below minimum (6)"));
}
if raw.len() < lng {
return Err(Error::InvalidFrame(
"buffer shorter than LNG field indicates",
));
}
let payload = &raw[..lng];
let crc_calculated = crc16(&payload[..lng - 2]);
let crc_received = (payload[lng - 2] as u16) | ((payload[lng - 1] as u16) << 8);
if crc_calculated != crc_received {
return Err(Error::CrcMismatch {
expected: crc_calculated,
actual: crc_received,
});
}
Ok(Frame {
addr: raw[1],
data: payload[3..lng - 2].to_vec(),
})
}
}
pub fn crc16(data: &[u8]) -> u16 {
let mut crc: u16 = 0x0000;
for &byte in data {
crc ^= byte as u16;
for _ in 0..8 {
if crc & 1 != 0 {
crc = (crc >> 1) ^ 0x8408;
} else {
crc >>= 1;
}
}
}
crc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_decode_roundtrip() {
let original = Frame::new(vec![0x33]); let encoded = original.encode();
let decoded = Frame::decode(&encoded).expect("decode failed");
assert_eq!(decoded.addr, DEFAULT_ADDR);
assert_eq!(decoded.data, vec![0x33]);
}
#[test]
fn encode_sets_correct_lng() {
let frame = Frame::new(vec![0x33]);
let encoded = frame.encode();
assert_eq!(encoded[2], 6);
}
#[test]
fn decode_rejects_bad_crc() {
let mut encoded = Frame::new(vec![0x33]).encode();
let last = encoded.len() - 1;
encoded[last] ^= 0xFF;
assert!(matches!(
Frame::decode(&encoded),
Err(Error::CrcMismatch { .. })
));
}
#[test]
fn decode_rejects_bad_sync() {
let mut encoded = Frame::new(vec![0x33]).encode();
encoded[0] = 0x00;
assert!(matches!(
Frame::decode(&encoded),
Err(Error::InvalidFrame(_))
));
}
#[test]
fn crc16_known_value() {
assert_eq!(crc16(&[]), 0x0000);
assert_eq!(crc16(&[0x02, 0x03, 0x06, 0x37]), 0xC7FE);
}
}