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