m_bus_parser/frames/
mod.rs

1//! is part of the MBUS data link layer
2//! It is used to encapsulate the application layer data
3#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
4#[derive(Debug, PartialEq)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6pub enum Frame<'a> {
7    SingleCharacter {
8        character: u8,
9    },
10    ShortFrame {
11        function: Function,
12        address: Address,
13    },
14    LongFrame {
15        function: Function,
16        address: Address,
17        #[cfg_attr(feature = "serde", serde(skip_serializing))]
18        data: &'a [u8],
19    },
20    ControlFrame {
21        function: Function,
22        address: Address,
23        #[cfg_attr(feature = "serde", serde(skip_serializing))]
24        data: &'a [u8],
25    },
26}
27
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29#[derive(Debug, Clone, PartialEq)]
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31pub enum Function {
32    SndNk,
33    SndUd { fcb: bool },
34    ReqUd2 { fcb: bool },
35    ReqUd1 { fcb: bool },
36    RspUd { acd: bool, dfc: bool },
37}
38
39#[cfg(feature = "std")]
40impl std::fmt::Display for Function {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match self {
43            Function::SndNk => write!(f, "SndNk"),
44            Function::SndUd { fcb } => write!(f, "SndUd (FCB: {})", fcb),
45            Function::ReqUd2 { fcb } => write!(f, "ReqUd2 (FCB: {})", fcb),
46            Function::ReqUd1 { fcb } => write!(f, "ReqUd1 (FCB: {})", fcb),
47            Function::RspUd { acd, dfc } => write!(f, "RspUd (ACD: {}, DFC: {})", acd, dfc),
48        }
49    }
50}
51
52impl TryFrom<u8> for Function {
53    type Error = FrameError;
54
55    fn try_from(byte: u8) -> Result<Self, Self::Error> {
56        match byte {
57            0x40 => Ok(Self::SndNk),
58            0x53 => Ok(Self::SndUd { fcb: false }),
59            0x73 => Ok(Self::SndUd { fcb: true }),
60            0x5B => Ok(Self::ReqUd2 { fcb: false }),
61            0x7B => Ok(Self::ReqUd2 { fcb: true }),
62            0x5A => Ok(Self::ReqUd1 { fcb: false }),
63            0x7A => Ok(Self::ReqUd1 { fcb: true }),
64            0x08 => Ok(Self::RspUd {
65                acd: false,
66                dfc: false,
67            }),
68            0x18 => Ok(Self::RspUd {
69                acd: false,
70                dfc: true,
71            }),
72            0x28 => Ok(Self::RspUd {
73                acd: true,
74                dfc: false,
75            }),
76            0x38 => Ok(Self::RspUd {
77                acd: true,
78                dfc: true,
79            }),
80            _ => Err(FrameError::InvalidFunction { byte }),
81        }
82    }
83}
84#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
85#[derive(Debug, Clone, PartialEq)]
86#[cfg_attr(feature = "defmt", derive(defmt::Format))]
87pub enum Address {
88    Uninitalized,
89    Primary(u8),
90    Secondary,
91    Broadcast { reply_required: bool },
92}
93
94#[cfg(feature = "std")]
95impl std::fmt::Display for Address {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        match self {
98            Address::Uninitalized => write!(f, "Uninitalized"),
99            Address::Primary(byte) => write!(f, "Primary ({})", byte),
100            Address::Secondary => write!(f, "Secondary"),
101            Address::Broadcast { reply_required } => {
102                write!(f, "Broadcast (Reply Required: {})", reply_required)
103            }
104        }
105    }
106}
107
108impl Address {
109    const fn from(byte: u8) -> Self {
110        match byte {
111            0 => Self::Uninitalized,
112            253 => Self::Secondary,
113            254 => Self::Broadcast {
114                reply_required: true,
115            },
116            255 => Self::Broadcast {
117                reply_required: false,
118            },
119            _ => Self::Primary(byte),
120        }
121    }
122}
123
124#[derive(Debug, PartialEq, Eq)]
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126#[cfg_attr(feature = "defmt", derive(defmt::Format))]
127pub enum FrameError {
128    EmptyData,
129    InvalidStartByte,
130    InvalidStopByte,
131    WrongLengthIndication,
132    LengthShort,
133    LengthShorterThanSix { length: usize },
134    WrongChecksum { expected: u8, actual: u8 },
135    InvalidControlInformation { byte: u8 },
136    InvalidFunction { byte: u8 },
137}
138
139impl<'a> TryFrom<&'a [u8]> for Frame<'a> {
140    type Error = FrameError;
141
142    fn try_from(data: &'a [u8]) -> Result<Self, FrameError> {
143        let first_byte = *data.first().ok_or(FrameError::EmptyData)?;
144
145        if first_byte == 0xE5 {
146            return Ok(Frame::SingleCharacter { character: 0xE5 });
147        }
148
149        let second_byte = *data.get(1).ok_or(FrameError::LengthShort)?;
150        let third_byte = *data.get(2).ok_or(FrameError::LengthShort)?;
151
152        match first_byte {
153            0x68 => {
154                validate_checksum(data.get(4..).ok_or(FrameError::LengthShort)?)?;
155
156                let length = *data.get(1).ok_or(FrameError::LengthShort)? as usize;
157
158                if second_byte != third_byte || data.len() != length + 6 {
159                    return Err(FrameError::WrongLengthIndication);
160                }
161
162                if *data.last().ok_or(FrameError::LengthShort)? != 0x16 {
163                    return Err(FrameError::InvalidStopByte);
164                }
165                let control_field = *data.get(4).ok_or(FrameError::LengthShort)?;
166                let address_field = *data.get(5).ok_or(FrameError::LengthShort)?;
167                match control_field {
168                    0x53 => Ok(Frame::ControlFrame {
169                        function: Function::try_from(control_field)?,
170                        address: Address::from(address_field),
171                        data: data.get(6..data.len() - 2).ok_or(FrameError::LengthShort)?,
172                    }),
173                    _ => Ok(Frame::LongFrame {
174                        function: Function::try_from(control_field)?,
175                        address: Address::from(address_field),
176                        data: data.get(6..data.len() - 2).ok_or(FrameError::LengthShort)?,
177                    }),
178                }
179            }
180            0x10 => {
181                validate_checksum(data.get(1..).ok_or(FrameError::LengthShort)?)?;
182                if data.len() == 5 && *data.last().ok_or(FrameError::InvalidStopByte)? == 0x16 {
183                    Ok(Frame::ShortFrame {
184                        function: Function::try_from(second_byte)?,
185                        address: Address::from(third_byte),
186                    })
187                } else {
188                    Err(FrameError::LengthShort)
189                }
190            }
191            _ => Err(FrameError::InvalidStartByte),
192        }
193    }
194}
195
196fn validate_checksum(data: &[u8]) -> Result<(), FrameError> {
197    // Assuming the checksum is the second to last byte in the data array.
198    let checksum_byte_index = data.len() - 2;
199    let checksum_byte = *data
200        .get(checksum_byte_index)
201        .ok_or(FrameError::LengthShort)?;
202
203    let calculated_checksum = data
204        .get(..checksum_byte_index)
205        .ok_or(FrameError::LengthShort)?
206        .iter()
207        .fold(0, |acc: u8, &x| acc.wrapping_add(x));
208
209    if checksum_byte == calculated_checksum {
210        Ok(())
211    } else {
212        Err(FrameError::WrongChecksum {
213            expected: checksum_byte,
214            actual: calculated_checksum,
215        })
216    }
217}
218
219#[cfg(feature = "std")]
220impl std::error::Error for FrameError {}
221
222#[cfg(feature = "std")]
223impl std::fmt::Display for FrameError {
224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225        match self {
226            FrameError::EmptyData => write!(f, "Data is empty"),
227            FrameError::InvalidStartByte => write!(f, "Invalid start byte"),
228            FrameError::InvalidStopByte => write!(f, "Invalid stop byte"),
229            FrameError::LengthShort => write!(f, "Length mismatch"),
230            FrameError::LengthShorterThanSix { length } => {
231                write!(f, "Length is shorter than six: {}", length)
232            }
233            FrameError::WrongChecksum { expected, actual } => write!(
234                f,
235                "Wrong checksum, expected: {}, actual: {}",
236                expected, actual
237            ),
238            FrameError::InvalidControlInformation { byte } => {
239                write!(f, "Invalid control information: {}", byte)
240            }
241            FrameError::InvalidFunction { byte } => write!(f, "Invalid function: {}", byte),
242            FrameError::WrongLengthIndication => write!(f, "Wrong length indication"),
243        }
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250
251    #[test]
252    fn test_detect_frame_type() {
253        let single_character_frame: &[u8] = &[0xE5];
254        let short_frame: &[u8] = &[0x10, 0x7B, 0x8b, 0x06, 0x16];
255        let control_frame: &[u8] = &[0x68, 0x03, 0x03, 0x68, 0x53, 0x01, 0x51, 0xA5, 0x16];
256
257        let example: &[u8] = &[
258            0x68, 0x4D, 0x4D, 0x68, 0x08, 0x01, 0x72, 0x01, 0x00, 0x00, 0x00, 0x96, 0x15, 0x01,
259            0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x78, 0x56, 0x00, 0x00, 0x00, 0x01, 0xFD, 0x1B,
260            0x00, 0x02, 0xFC, 0x03, 0x48, 0x52, 0x25, 0x74, 0x44, 0x0D, 0x22, 0xFC, 0x03, 0x48,
261            0x52, 0x25, 0x74, 0xF1, 0x0C, 0x12, 0xFC, 0x03, 0x48, 0x52, 0x25, 0x74, 0x63, 0x11,
262            0x02, 0x65, 0xB4, 0x09, 0x22, 0x65, 0x86, 0x09, 0x12, 0x65, 0xB7, 0x09, 0x01, 0x72,
263            0x00, 0x72, 0x65, 0x00, 0x00, 0xB2, 0x01, 0x65, 0x00, 0x00, 0x1F, 0xB3, 0x16,
264        ];
265
266        assert_eq!(
267            Frame::try_from(single_character_frame),
268            Ok(Frame::SingleCharacter { character: 0xE5 })
269        );
270        assert_eq!(
271            Frame::try_from(short_frame),
272            Ok(Frame::ShortFrame {
273                function: Function::try_from(0x7B).unwrap(),
274                address: Address::from(0x8B)
275            })
276        );
277        assert_eq!(
278            Frame::try_from(control_frame),
279            Ok(Frame::ControlFrame {
280                function: Function::try_from(0x53).unwrap(),
281                address: Address::from(0x01),
282                data: &[0x51]
283            })
284        );
285
286        assert_eq!(
287            Frame::try_from(example),
288            Ok(Frame::LongFrame {
289                function: Function::try_from(8).unwrap(),
290                address: Address::from(1),
291                data: &[
292                    114, 1, 0, 0, 0, 150, 21, 1, 0, 24, 0, 0, 0, 12, 120, 86, 0, 0, 0, 1, 253, 27,
293                    0, 2, 252, 3, 72, 82, 37, 116, 68, 13, 34, 252, 3, 72, 82, 37, 116, 241, 12,
294                    18, 252, 3, 72, 82, 37, 116, 99, 17, 2, 101, 180, 9, 34, 101, 134, 9, 18, 101,
295                    183, 9, 1, 114, 0, 114, 101, 0, 0, 178, 1, 101, 0, 0, 31
296                ]
297            })
298        );
299    }
300}