use crate::error::{Error, Result};
const SYNC_START: u8 = 0xAA;
const SYNC_END: u8 = 0x55;
const MAX_PAYLOAD: usize = 50;
pub const FRAME_OVERHEAD: usize = 6;
const MAX_FRAME_SIZE: usize = MAX_PAYLOAD + FRAME_OVERHEAD;
#[derive(Debug)]
pub struct FrameEncoder {
}
impl FrameEncoder {
pub const fn new() -> Self {
Self {}
}
pub fn encode(&self, src_node: u8, data: &[u8], buf: &mut [u8]) -> Result<usize> {
if data.len() > MAX_PAYLOAD {
return Err(Error::BufferTooSmall);
}
let frame_len = data.len() + FRAME_OVERHEAD;
if buf.len() < frame_len {
return Err(Error::BufferTooSmall);
}
buf[0] = SYNC_START;
buf[1] = data.len() as u8;
buf[2] = src_node;
buf[3..3 + data.len()].copy_from_slice(data);
let crc = crc16_ccitt(&buf[1..3 + data.len()]);
buf[3 + data.len()] = (crc >> 8) as u8;
buf[4 + data.len()] = (crc & 0xFF) as u8;
buf[5 + data.len()] = SYNC_END;
Ok(frame_len)
}
}
impl Default for FrameEncoder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DecoderState {
WaitStart,
WaitLength,
WaitSource,
Payload,
CrcHigh,
CrcLow,
WaitEnd,
}
#[derive(Debug)]
pub struct FrameDecoder {
state: DecoderState,
buf: [u8; MAX_FRAME_SIZE],
payload_len: usize,
src_node: u8,
pos: usize,
crc: u16,
}
impl FrameDecoder {
pub const fn new() -> Self {
Self {
state: DecoderState::WaitStart,
buf: [0u8; MAX_FRAME_SIZE],
payload_len: 0,
src_node: 0,
pos: 0,
crc: 0,
}
}
pub fn reset(&mut self) {
self.state = DecoderState::WaitStart;
self.pos = 0;
self.payload_len = 0;
self.src_node = 0;
self.crc = 0;
}
pub fn feed(&mut self, byte: u8) -> Result<Option<(u8, &[u8])>> {
match self.state {
DecoderState::WaitStart => {
if byte == SYNC_START {
self.state = DecoderState::WaitLength;
self.pos = 0;
}
Ok(None)
}
DecoderState::WaitLength => {
if byte > MAX_PAYLOAD as u8 {
self.reset();
return Ok(None);
}
self.payload_len = byte as usize;
self.buf[0] = byte; self.state = DecoderState::WaitSource;
Ok(None)
}
DecoderState::WaitSource => {
self.src_node = byte;
self.buf[1] = byte; self.pos = 0;
if self.payload_len == 0 {
self.state = DecoderState::CrcHigh;
} else {
self.state = DecoderState::Payload;
}
Ok(None)
}
DecoderState::Payload => {
self.buf[2 + self.pos] = byte;
self.pos += 1;
if self.pos >= self.payload_len {
self.state = DecoderState::CrcHigh;
}
Ok(None)
}
DecoderState::CrcHigh => {
self.crc = (byte as u16) << 8;
self.state = DecoderState::CrcLow;
Ok(None)
}
DecoderState::CrcLow => {
self.crc |= byte as u16;
self.state = DecoderState::WaitEnd;
Ok(None)
}
DecoderState::WaitEnd => {
if byte == SYNC_END {
let expected_crc = crc16_ccitt(&self.buf[..2 + self.payload_len]);
if self.crc == expected_crc {
self.state = DecoderState::WaitStart;
let payload_end = 2 + self.payload_len;
self.payload_len = 0; self.crc = 0;
self.pos = 0;
return Ok(Some((self.src_node, &self.buf[2..payload_end])));
}
}
self.reset();
Ok(None)
}
}
}
pub const fn is_receiving(&self) -> bool {
!matches!(self.state, DecoderState::WaitStart)
}
}
impl Default for FrameDecoder {
fn default() -> Self {
Self::new()
}
}
fn crc16_ccitt(data: &[u8]) -> u16 {
let mut crc: u16 = 0xFFFF;
for &byte in data {
crc ^= (byte as u16) << 8;
for _ in 0..8 {
if crc & 0x8000 != 0 {
crc = (crc << 1) ^ 0x1021;
} else {
crc <<= 1;
}
}
}
crc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encoder_basic() {
let encoder = FrameEncoder::new();
let mut buf = [0u8; 64];
let len = encoder.encode(42, b"Hello", &mut buf).unwrap();
assert_eq!(len, 5 + FRAME_OVERHEAD); assert_eq!(buf[0], SYNC_START);
assert_eq!(buf[1], 5); assert_eq!(buf[2], 42); assert_eq!(&buf[3..8], b"Hello");
assert_eq!(buf[len - 1], SYNC_END);
}
#[test]
fn test_encoder_empty_payload() {
let encoder = FrameEncoder::new();
let mut buf = [0u8; 64];
let len = encoder.encode(1, &[], &mut buf).unwrap();
assert_eq!(len, FRAME_OVERHEAD);
assert_eq!(buf[0], SYNC_START);
assert_eq!(buf[1], 0); assert_eq!(buf[len - 1], SYNC_END);
}
#[test]
fn test_encoder_buffer_too_small() {
let encoder = FrameEncoder::new();
let mut buf = [0u8; 5];
let result = encoder.encode(1, b"Hello", &mut buf);
assert_eq!(result, Err(Error::BufferTooSmall));
}
#[test]
fn test_encoder_payload_too_large() {
let encoder = FrameEncoder::new();
let mut buf = [0u8; 256];
let data = [0u8; 60];
let result = encoder.encode(1, &data, &mut buf);
assert_eq!(result, Err(Error::BufferTooSmall));
}
#[test]
fn test_decoder_basic() {
let encoder = FrameEncoder::new();
let mut decoder = FrameDecoder::new();
let mut buf = [0u8; 64];
let len = encoder.encode(42, b"Test", &mut buf).unwrap();
for &byte in &buf[..len - 1] {
let result = decoder.feed(byte).unwrap();
assert!(result.is_none());
}
let result = decoder.feed(buf[len - 1]).unwrap();
assert!(result.is_some());
let (src, payload) = result.unwrap();
assert_eq!(src, 42);
assert_eq!(payload, b"Test");
}
#[test]
fn test_decoder_empty_payload() {
let encoder = FrameEncoder::new();
let mut decoder = FrameDecoder::new();
let mut buf = [0u8; 64];
let len = encoder.encode(1, &[], &mut buf).unwrap();
for &byte in &buf[..len - 1] {
assert!(decoder.feed(byte).unwrap().is_none());
}
let result = decoder.feed(buf[len - 1]).unwrap();
assert!(result.is_some());
let (src, payload) = result.unwrap();
assert_eq!(src, 1);
assert!(payload.is_empty());
}
#[test]
fn test_decoder_bad_crc() {
let encoder = FrameEncoder::new();
let mut decoder = FrameDecoder::new();
let mut buf = [0u8; 64];
let len = encoder.encode(42, b"Test", &mut buf).unwrap();
buf[5] ^= 0xFF;
for &byte in &buf[..len] {
let _ = decoder.feed(byte);
}
assert!(!decoder.is_receiving());
}
#[test]
fn test_decoder_bad_sync() {
let mut decoder = FrameDecoder::new();
for b in &[0x12, 0x34, 0x56, 0x78] {
let result = decoder.feed(*b).unwrap();
assert!(result.is_none());
}
assert!(!decoder.is_receiving());
}
#[test]
fn test_decoder_multiple_frames() {
let encoder = FrameEncoder::new();
let mut decoder = FrameDecoder::new();
let mut buf1 = [0u8; 64];
let mut buf2 = [0u8; 64];
let len1 = encoder.encode(1, b"First", &mut buf1).unwrap();
let len2 = encoder.encode(2, b"Second", &mut buf2).unwrap();
for &byte in &buf1[..len1 - 1] {
assert!(decoder.feed(byte).unwrap().is_none());
}
let result = decoder.feed(buf1[len1 - 1]).unwrap();
let (src, payload) = result.unwrap();
assert_eq!(src, 1);
assert_eq!(payload, b"First");
for &byte in &buf2[..len2 - 1] {
assert!(decoder.feed(byte).unwrap().is_none());
}
let result = decoder.feed(buf2[len2 - 1]).unwrap();
let (src, payload) = result.unwrap();
assert_eq!(src, 2);
assert_eq!(payload, b"Second");
}
#[test]
fn test_crc16_known_values() {
assert_eq!(crc16_ccitt(b""), 0xFFFF);
assert_eq!(crc16_ccitt(b"123456789"), 0x29B1);
}
#[test]
fn test_roundtrip_various_sizes() {
let encoder = FrameEncoder::new();
let mut decoder = FrameDecoder::new();
let mut frame_buf = [0u8; 64];
let mut data_buf = [0u8; 50];
for size in [0usize, 1, 10, 25, 50] {
for (i, slot) in data_buf[..size].iter_mut().enumerate() {
*slot = i as u8;
}
let data = &data_buf[..size];
let len = encoder.encode(42, data, &mut frame_buf).unwrap();
for &byte in &frame_buf[..len - 1] {
assert!(decoder.feed(byte).unwrap().is_none());
}
let result = decoder.feed(frame_buf[len - 1]).unwrap();
assert!(result.is_some());
let (src, payload) = result.unwrap();
assert_eq!(src, 42);
assert_eq!(payload, data);
}
}
}