1use core::convert::TryFrom;
2
3use nom::{
4 IResult,
5 branch::alt,
6 number::streaming::u8,
7 bytes::streaming::{tag, take},
8 combinator::cut,
9 sequence::tuple,
10};
11
12use super::{Error, Control, Address};
13
14#[derive(Debug, Clone, PartialEq)]
16pub enum Telegram<'ud> {
17 SingleCharacter,
18 ShortFrame {
19 control: Control,
20 address: Address,
21 },
22 ControlFrame {
23 control: Control,
24 address: Address,
25 control_information: u8,
26 },
27 LongFrame {
28 control: Control,
29 address: Address,
30 control_information: u8,
31 user_data: &'ud [u8],
32 },
33}
34
35impl<'ud> Telegram<'ud> {
36 const SINGLE_CHAR: u8 = 0xe5;
37 const SHORT_START_CHAR: u8 = 0x10;
38 const LONG_START_CHAR: u8 = 0x68;
39 const STOP_CHAR: u8 = 0x16;
40
41 fn map_error(nom_error: nom::Err<nom::error::Error<&[u8]>>, error: Error) -> nom::Err<Error> {
42 nom_error.map(|_| error)
43 }
44
45 fn parse_single(input: &'ud [u8]) -> IResult<&'ud [u8], Self, Error> {
46 let (input, _) = tag([Telegram::SINGLE_CHAR])(input)
47 .map_err(|err| Self::map_error(err, Error::InvalidStartCharacter))?;
48 Ok((input, Self::SingleCharacter))
49 }
50
51 fn parse_short(input: &'ud [u8]) -> IResult<&'ud [u8], Self, Error> {
52 let (input, _) = tag([Self::SHORT_START_CHAR])(input)
53 .map_err(|err| Self::map_error(err, Error::InvalidStartCharacter))?;
54 Self::parse_payload(input, 2)
55 }
56
57 fn parse_long(input: &'ud [u8]) -> IResult<&'ud [u8], Self, Error> {
58 let start_char = tag([Self::LONG_START_CHAR]);
59 let (input, (_, payload_len, payload_len_check, _)) =
60 tuple((&start_char, u8, u8, &start_char))(input)
61 .map_err(|err| Self::map_error(err, Error::InvalidStartCharacter))?;
62
63 if payload_len != payload_len_check {
64 return Err(nom::Err::Error(Error::InvalidStartCharacter))
65 }
66
67 Self::parse_payload(input, payload_len.into())
68 }
69
70 fn parse_payload(input: &'ud [u8], mut payload_len: usize) -> IResult<&'ud [u8], Self, Error> {
71 let mut calculated_checksum = 0u8;
72
73 let mut checksummed_u8 = |input| {
74 let (input, value) = cut(u8)(input)?;
75 calculated_checksum = calculated_checksum.wrapping_add(value);
76 Ok((input, value))
77 };
78
79 let (input, control) = checksummed_u8(input)?;
80 let control = Control::try_from(control)
81 .map_err(|_| nom::Err::Failure(Error::InvalidFormat))?;
82 payload_len -= 1;
83
84 let (input, address) = checksummed_u8(input)?;
85 let address = Address::from(address);
86 payload_len -= 1;
87
88 let (input, control_information) = if payload_len > 0 {
89 let (input, control_information) = checksummed_u8(input)?;
90 payload_len -= 1;
91 (input, Some(control_information))
92 } else {
93 (input, None)
94 };
95
96 let (input, payload) = take(payload_len)(input)?;
97 for &value in payload {
98 calculated_checksum = calculated_checksum.wrapping_add(value);
99 }
100
101 let (input, checksum) = cut(u8)(input)?;
102
103 let (input, _stop_char) = cut(tag([Self::STOP_CHAR]))(input)?;
104
105 if calculated_checksum != checksum {
106 return Err(nom::Err::Failure(Error::ChecksumMismatch))
107 }
108
109 if let Some(control_information) = control_information {
110 let payload_len = payload.len();
111 if payload_len > 0 {
112 Ok((input, Self::LongFrame { control, address, control_information, user_data: payload }))
113 } else {
114 Ok((input, Self::ControlFrame { control, address, control_information }))
115 }
116 } else {
117 Ok((input, Self::ShortFrame { control, address }))
118 }
119 }
120
121 pub fn parse(input: &'ud [u8]) -> Result<(&'ud [u8], Telegram<'ud>), Error> {
122 use nom::Finish;
123
124 alt((Self::parse_single, Self::parse_short, Self::parse_long))(input)
125 .map_err(|err| match err {
126 nom::Err::Incomplete(needed) => nom::Err::Failure(Error::Incomplete(match needed {
127 nom::Needed::Unknown => None,
128 nom::Needed::Size(size) => Some(size),
129 })),
130 err => err,
131 })
132 .finish()
133 }
134}
135
136#[cfg(test)]
137mod test {
138 use super::*;
139
140 const TELEGRAMS: [u8; 376] = [
141 0x68, 0xfa, 0xfa, 0x68, 0x53, 0xff, 0x00, 0x01, 0x67, 0xdb, 0x08, 0x4b, 0x46, 0x4d, 0x10, 0x20, 0x01, 0x12, 0xa9, 0x82, 0x01, 0x55, 0x21,
149 0x00, 0x02, 0xbc, 0x66, 0x2f, 0xba, 0x85, 0x66, 0x9e, 0x76, 0xef, 0x03, 0x47, 0x06, 0xcc, 0xfc,
150 0x93, 0x70, 0xf9, 0xb7, 0xab, 0x49, 0xd0, 0x35, 0xdd, 0x0e, 0xe7, 0x5a, 0x95, 0x36, 0xfc, 0x7a,
151 0x19, 0x48, 0xf7, 0x9c, 0x69, 0x8f, 0xac, 0xc2, 0xfc, 0x5f, 0x7b, 0xe5, 0xf1, 0x47, 0xf3, 0xee,
152 0x87, 0x40, 0xbe, 0xe9, 0x11, 0x8c, 0x8f, 0x7b, 0xc5, 0xb2, 0xc5, 0x12, 0x53, 0x29, 0xce, 0xb4,
153 0xbe, 0xad, 0xe1, 0x16, 0xd1, 0x61, 0x2c, 0x7f, 0x82, 0xa0, 0x4f, 0xaa, 0x70, 0xc3, 0x5d, 0x06,
154 0x67, 0xd9, 0xee, 0xec, 0xf6, 0x86, 0xaf, 0xb6, 0x43, 0x19, 0xdc, 0x13, 0xec, 0x3c, 0x9a, 0x39,
155 0x10, 0x5b, 0x46, 0x37, 0x2a, 0xca, 0x24, 0xf2, 0xa1, 0x73, 0x39, 0x32, 0x5f, 0x27, 0x0a, 0xb9,
156 0x8c, 0x40, 0xe7, 0xaa, 0x8a, 0x61, 0xec, 0xf5, 0x88, 0x4c, 0x4e, 0x3b, 0x7e, 0xb4, 0xef, 0x5b,
157 0xb2, 0x92, 0x68, 0x4a, 0x6b, 0xba, 0x91, 0xb3, 0x49, 0xbb, 0x70, 0x41, 0x62, 0xa2, 0x10, 0xc3,
158 0x6d, 0xc1, 0xf2, 0x52, 0x88, 0x42, 0x71, 0x7b, 0xfe, 0x64, 0xf8, 0x05, 0xce, 0xab, 0x98, 0x0e,
159 0x14, 0xc1, 0xe2, 0x9e, 0x10, 0x19, 0x5e, 0x1b, 0xa2, 0xef, 0x24, 0xe8, 0xf9, 0xcb, 0x0f, 0xe6,
160 0x09, 0xd3, 0x2b, 0xb8, 0xc3, 0x6e, 0x23, 0xb8, 0x47, 0x7b, 0x14, 0xda, 0xc2, 0x37, 0x63, 0xa2,
161 0x5b, 0xee, 0x27, 0xa8, 0x1f, 0x20, 0xa7, 0x6c, 0x2f, 0x8e, 0x28, 0xc9, 0x2b, 0x3e, 0xbe, 0x04,
162 0x48, 0x6d, 0xc2, 0xdc, 0x07, 0x41, 0x63, 0xbe, 0x49, 0xdf, 0x25, 0x96, 0x30, 0x9c, 0x86, 0x39,
163 0x53, 0x31, 0x65, 0x35, 0xd1, 0xf0, 0xdf,
164 0x8a, 0x16, 0x68, 0x72, 0x72, 0x68, 0x53, 0xff, 0x11, 0x01, 0x67, 0x5b, 0x5f, 0x0f, 0xb3, 0x7e, 0xde, 0xda, 0xb5, 0xaf, 0xed, 0x57, 0xbd, 0xa7, 0x5a,
175 0x2e, 0x17, 0xcf, 0x11, 0x79, 0xc8, 0x1d, 0xbe, 0xb4, 0xac, 0xc8, 0x80, 0x2c, 0xb1, 0xdb, 0xf8,
176 0x74, 0xe6, 0x76, 0xa3, 0x42, 0xf6, 0xe5, 0xde, 0x97, 0x29, 0x86, 0x1f, 0x07, 0x67, 0xac, 0xf9,
177 0x04, 0xf8, 0x0a, 0x44, 0xa0, 0xdd, 0x16, 0x46, 0xf2, 0x08, 0x83, 0x44, 0x5e, 0x11, 0x91, 0xe3,
178 0x52, 0x49, 0x58, 0x0e, 0xaa, 0x4b, 0xec, 0x58, 0xaa, 0xee, 0x1a, 0xdf, 0xda, 0x60, 0x14, 0x5f,
179 0x51, 0xb8, 0xbe, 0xd4, 0x36, 0x10, 0xdf, 0xee, 0x5b, 0x2c, 0xe3, 0x38, 0x0d, 0xe7, 0xf3, 0x4d,
180 0x9f, 0xca, 0x2a, 0x15, 0x6f, 0x68, 0x79, 0xf4, 0x1e, 0xec, 0x8d, 0x20, 0xef, 0xa2, 0xdf,
181 0x38, 0x16, ];
184
185 #[test]
186 fn test_parse() {
187 let input = &TELEGRAMS[..];
188
189 let (input, telegram) = Telegram::parse(input).unwrap();
190 assert_eq!(telegram, Telegram::LongFrame {
191 control: Control::SndUd { fcb: false },
192 address: Address::Broadcast,
193 control_information: 0x00,
194 user_data: &TELEGRAMS[7..254],
195 });
196
197 let (input, telegram) = Telegram::parse(input).unwrap();
198 assert_eq!(telegram, Telegram::LongFrame {
199 control: Control::SndUd { fcb: false },
200 address: Address::Broadcast,
201 control_information: 0x11,
202 user_data: &TELEGRAMS[263..374],
203 });
204
205 assert!(input.is_empty());
206 }
207}