rustmod_core/frame/
rtu.rs1use crate::encoding::Writer;
2use crate::{DecodeError, EncodeError};
3
4const fn build_crc16_table() -> [u16; 256] {
5 let mut table = [0u16; 256];
6 let mut i = 0;
7 while i < 256 {
8 let mut crc = i as u16;
9 let mut bit = 0;
10 while bit < 8 {
11 if (crc & 0x0001) != 0 {
12 crc = (crc >> 1) ^ 0xA001;
13 } else {
14 crc >>= 1;
15 }
16 bit += 1;
17 }
18 table[i] = crc;
19 i += 1;
20 }
21 table
22}
23
24const CRC16_TABLE: [u16; 256] = build_crc16_table();
25
26pub fn crc16(data: &[u8]) -> u16 {
27 let mut crc = 0xFFFFu16;
28 for byte in data {
29 let idx = ((crc ^ (*byte as u16)) & 0x00FF) as usize;
30 crc = (crc >> 8) ^ CRC16_TABLE[idx];
31 }
32 crc
33}
34
35pub fn encode_frame(w: &mut Writer<'_>, address: u8, pdu: &[u8]) -> Result<(), EncodeError> {
36 if pdu.is_empty() {
37 return Err(EncodeError::InvalidLength);
38 }
39
40 w.write_u8(address)?;
41 w.write_all(pdu)?;
42
43 let mut tmp = [0u8; 254];
44 if pdu.len() > 253 {
45 return Err(EncodeError::ValueOutOfRange);
46 }
47 tmp[0] = address;
48 tmp[1..1 + pdu.len()].copy_from_slice(pdu);
49 let crc = crc16(&tmp[..1 + pdu.len()]);
50 w.write_all(&crc.to_le_bytes())?;
51 Ok(())
52}
53
54pub fn decode_frame(data: &[u8]) -> Result<(u8, &[u8]), DecodeError> {
55 if data.len() < 4 {
56 return Err(DecodeError::InvalidLength);
57 }
58
59 let payload = &data[..data.len() - 2];
60 let expected = crc16(payload);
61 let got = u16::from_le_bytes([data[data.len() - 2], data[data.len() - 1]]);
62 if expected != got {
63 return Err(DecodeError::InvalidCrc);
64 }
65
66 let address = payload[0];
67 let pdu = &payload[1..];
68 if pdu.is_empty() {
69 return Err(DecodeError::InvalidLength);
70 }
71 Ok((address, pdu))
72}
73
74#[cfg(test)]
75mod tests {
76 use super::{crc16, decode_frame, encode_frame};
77 use crate::encoding::Writer;
78 use crate::DecodeError;
79
80 #[test]
81 fn crc16_known_vector() {
82 let frame_wo_crc = [0x01u8, 0x03, 0x00, 0x00, 0x00, 0x0A];
83 assert_eq!(crc16(&frame_wo_crc), 0xCDC5);
84 }
85
86 #[test]
87 fn rtu_roundtrip() {
88 let mut buf = [0u8; 32];
89 let mut w = Writer::new(&mut buf);
90 encode_frame(&mut w, 0x11, &[0x03, 0x00, 0x6B, 0x00, 0x03]).unwrap();
91 let written = w.as_written();
92
93 let (address, pdu) = decode_frame(written).unwrap();
94 assert_eq!(address, 0x11);
95 assert_eq!(pdu, &[0x03, 0x00, 0x6B, 0x00, 0x03]);
96 }
97
98 #[test]
99 fn detects_bad_crc() {
100 let bad = [0x11u8, 0x03, 0x00, 0x6B, 0x00, 0x03, 0x00, 0x00];
101 assert_eq!(decode_frame(&bad).unwrap_err(), DecodeError::InvalidCrc);
102 }
103}