1use m_bus_core::{FrameError, Function};
4
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6#[derive(Debug, PartialEq)]
7#[non_exhaustive]
8pub enum WiredFrame<'a> {
9 SingleCharacter {
10 character: u8,
11 },
12 ShortFrame {
13 function: Function,
14 address: Address,
15 },
16 LongFrame {
17 function: Function,
18 address: Address,
19 #[cfg_attr(feature = "serde", serde(skip_serializing))]
20 data: &'a [u8],
21 },
22 ControlFrame {
23 function: Function,
24 address: Address,
25 #[cfg_attr(feature = "serde", serde(skip_serializing))]
26 data: &'a [u8],
27 },
28}
29
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31#[derive(Debug, Clone, PartialEq)]
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33#[non_exhaustive]
34pub enum Address {
35 Uninitalized,
36 Primary(u8),
37 Secondary,
38 Broadcast { reply_required: bool },
39}
40
41#[cfg(feature = "std")]
42impl std::fmt::Display for Address {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 match self {
45 Address::Uninitalized => write!(f, "Uninitalized"),
46 Address::Primary(byte) => write!(f, "Primary ({byte})"),
47 Address::Secondary => write!(f, "Secondary"),
48 Address::Broadcast { reply_required } => {
49 write!(f, "Broadcast (Reply Required: {})", reply_required)
50 }
51 }
52 }
53}
54
55impl Address {
56 const fn from(byte: u8) -> Self {
57 match byte {
58 0 => Self::Uninitalized,
59 253 => Self::Secondary,
60 254 => Self::Broadcast {
61 reply_required: true,
62 },
63 255 => Self::Broadcast {
64 reply_required: false,
65 },
66 _ => Self::Primary(byte),
67 }
68 }
69}
70
71impl<'a> TryFrom<&'a [u8]> for WiredFrame<'a> {
72 type Error = FrameError;
73
74 fn try_from(data: &'a [u8]) -> Result<Self, FrameError> {
75 let first_byte = *data.first().ok_or(FrameError::EmptyData)?;
76
77 if first_byte == 0xE5 {
78 return Ok(WiredFrame::SingleCharacter { character: 0xE5 });
79 }
80
81 let second_byte = *data.get(1).ok_or(FrameError::LengthShort)?;
82 let third_byte = *data.get(2).ok_or(FrameError::LengthShort)?;
83
84 match first_byte {
85 0x68 => {
86 validate_checksum(data.get(4..).ok_or(FrameError::LengthShort)?)?;
87
88 let length = *data.get(1).ok_or(FrameError::LengthShort)? as usize;
89
90 if second_byte != third_byte || data.len() != length + 6 {
91 return Err(FrameError::WrongLengthIndication);
92 }
93
94 if *data.last().ok_or(FrameError::LengthShort)? != 0x16 {
95 return Err(FrameError::InvalidStopByte);
96 }
97 let control_field = *data.get(4).ok_or(FrameError::LengthShort)?;
98 let address_field = *data.get(5).ok_or(FrameError::LengthShort)?;
99 match control_field {
100 0x53 => Ok(WiredFrame::ControlFrame {
101 function: Function::try_from(control_field)?,
102 address: Address::from(address_field),
103 data: data.get(6..data.len() - 2).ok_or(FrameError::LengthShort)?,
104 }),
105 _ => Ok(WiredFrame::LongFrame {
106 function: Function::try_from(control_field)?,
107 address: Address::from(address_field),
108 data: data.get(6..data.len() - 2).ok_or(FrameError::LengthShort)?,
109 }),
110 }
111 }
112 0x10 => {
113 validate_checksum(data.get(1..).ok_or(FrameError::LengthShort)?)?;
114 if data.len() == 5 && *data.last().ok_or(FrameError::InvalidStopByte)? == 0x16 {
115 Ok(WiredFrame::ShortFrame {
116 function: Function::try_from(second_byte)?,
117 address: Address::from(third_byte),
118 })
119 } else {
120 Err(FrameError::LengthShort)
121 }
122 }
123 _ => Err(FrameError::InvalidStartByte),
124 }
125 }
126}
127
128fn validate_checksum(data: &[u8]) -> Result<(), FrameError> {
129 let checksum_byte_index = data.len() - 2;
131 let checksum_byte = *data
132 .get(checksum_byte_index)
133 .ok_or(FrameError::LengthShort)?;
134
135 let calculated_checksum = data
136 .get(..checksum_byte_index)
137 .ok_or(FrameError::LengthShort)?
138 .iter()
139 .fold(0, |acc: u8, &x| acc.wrapping_add(x));
140
141 if checksum_byte == calculated_checksum {
142 Ok(())
143 } else {
144 Err(FrameError::WrongChecksum {
145 expected: checksum_byte,
146 actual: calculated_checksum,
147 })
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_detect_frame_type() {
157 let single_character_frame: &[u8] = &[0xE5];
158 let short_frame: &[u8] = &[0x10, 0x7B, 0x8b, 0x06, 0x16];
159 let control_frame: &[u8] = &[0x68, 0x03, 0x03, 0x68, 0x53, 0x01, 0x51, 0xA5, 0x16];
160
161 let example: &[u8] = &[
162 0x68, 0x4D, 0x4D, 0x68, 0x08, 0x01, 0x72, 0x01, 0x00, 0x00, 0x00, 0x96, 0x15, 0x01,
163 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x78, 0x56, 0x00, 0x00, 0x00, 0x01, 0xFD, 0x1B,
164 0x00, 0x02, 0xFC, 0x03, 0x48, 0x52, 0x25, 0x74, 0x44, 0x0D, 0x22, 0xFC, 0x03, 0x48,
165 0x52, 0x25, 0x74, 0xF1, 0x0C, 0x12, 0xFC, 0x03, 0x48, 0x52, 0x25, 0x74, 0x63, 0x11,
166 0x02, 0x65, 0xB4, 0x09, 0x22, 0x65, 0x86, 0x09, 0x12, 0x65, 0xB7, 0x09, 0x01, 0x72,
167 0x00, 0x72, 0x65, 0x00, 0x00, 0xB2, 0x01, 0x65, 0x00, 0x00, 0x1F, 0xB3, 0x16,
168 ];
169
170 assert_eq!(
171 WiredFrame::try_from(single_character_frame),
172 Ok(WiredFrame::SingleCharacter { character: 0xE5 })
173 );
174 assert_eq!(
175 WiredFrame::try_from(short_frame),
176 Ok(WiredFrame::ShortFrame {
177 function: Function::try_from(0x7B).unwrap(),
178 address: Address::from(0x8B)
179 })
180 );
181 assert_eq!(
182 WiredFrame::try_from(control_frame),
183 Ok(WiredFrame::ControlFrame {
184 function: Function::try_from(0x53).unwrap(),
185 address: Address::from(0x01),
186 data: &[0x51]
187 })
188 );
189
190 assert_eq!(
191 WiredFrame::try_from(example),
192 Ok(WiredFrame::LongFrame {
193 function: Function::try_from(8).unwrap(),
194 address: Address::from(1),
195 data: &[
196 114, 1, 0, 0, 0, 150, 21, 1, 0, 24, 0, 0, 0, 12, 120, 86, 0, 0, 0, 1, 253, 27,
197 0, 2, 252, 3, 72, 82, 37, 116, 68, 13, 34, 252, 3, 72, 82, 37, 116, 241, 12,
198 18, 252, 3, 72, 82, 37, 116, 99, 17, 2, 101, 180, 9, 34, 101, 134, 9, 18, 101,
199 183, 9, 1, 114, 0, 114, 101, 0, 0, 178, 1, 101, 0, 0, 31
200 ]
201 })
202 );
203 }
204}