1use crate::lrc::LrcMode;
13
14pub const STX: u8 = 0x02;
16pub const ETX: u8 = 0x03;
18pub const SOH: u8 = 0x01;
20pub const EOT: u8 = 0x04;
22pub const ACK: u8 = 0x06;
24pub const NAK: u8 = 0x15;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum PacketType {
30 Application,
32 Progress,
34 Ack,
36 Nak,
38 Unknown,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct DecodedPacket {
46 pub packet_type: PacketType,
48 pub payload: Vec<u8>,
50 pub valid_lrc: bool,
53}
54
55impl DecodedPacket {
56 fn unknown() -> Self {
57 Self {
58 packet_type: PacketType::Unknown,
59 payload: Vec::new(),
60 valid_lrc: false,
61 }
62 }
63
64 fn control(packet_type: PacketType) -> Self {
65 Self {
66 packet_type,
67 payload: Vec::new(),
68 valid_lrc: true,
69 }
70 }
71}
72
73#[derive(Debug, Clone, Copy)]
75pub struct PacketCodec {
76 lrc_mode: LrcMode,
77}
78
79impl PacketCodec {
80 #[must_use]
82 pub fn new(mode: LrcMode) -> Self {
83 Self { lrc_mode: mode }
84 }
85
86 #[must_use]
88 pub fn lrc_mode(&self) -> LrcMode {
89 self.lrc_mode
90 }
91
92 #[must_use]
94 pub fn encode_application(&self, payload: &[u8]) -> Vec<u8> {
95 let mut frame = Vec::with_capacity(payload.len() + 3);
96 frame.push(STX);
97 frame.extend_from_slice(payload);
98 frame.push(ETX);
99 frame.push(self.lrc_mode.compute(payload));
100 frame
101 }
102
103 #[must_use]
107 pub fn encode_control(&self, ctrl: u8) -> Vec<u8> {
108 vec![ctrl, ETX, self.lrc_mode.compute(&[ctrl])]
109 }
110
111 #[must_use]
124 pub fn decode(&self, data: &[u8]) -> DecodedPacket {
125 let Some(&first) = data.first() else {
126 return DecodedPacket::unknown();
127 };
128
129 match first {
130 ACK => return DecodedPacket::control(PacketType::Ack),
131 NAK => return DecodedPacket::control(PacketType::Nak),
132 _ => {}
133 }
134
135 if first == SOH {
136 if data.len() < 2 || *data.last().unwrap() != EOT {
139 return DecodedPacket::unknown();
140 }
141 return DecodedPacket {
142 packet_type: PacketType::Progress,
143 payload: data[1..data.len() - 1].to_vec(),
144 valid_lrc: true,
145 };
146 }
147
148 if first == STX {
149 let Some(etx_index) = data.iter().position(|&b| b == ETX) else {
150 return DecodedPacket::unknown();
151 };
152 if etx_index + 2 != data.len() {
157 return DecodedPacket::unknown();
158 }
159 let payload = &data[1..etx_index];
160 let rx_lrc = data[etx_index + 1];
161 let calc_lrc = self.lrc_mode.compute(payload);
162 return DecodedPacket {
163 packet_type: PacketType::Application,
164 payload: payload.to_vec(),
165 valid_lrc: rx_lrc == calc_lrc,
166 };
167 }
168
169 DecodedPacket::unknown()
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn encode_application_frames_stx_payload_etx_lrc() {
179 let codec = PacketCodec::new(LrcMode::Std);
180 let frame = codec.encode_application(b"AB");
181 assert_eq!(frame, vec![STX, b'A', b'B', ETX, 0x7C]); }
183
184 #[test]
185 fn application_round_trip_all_modes() {
186 for mode in [
187 LrcMode::Stx,
188 LrcMode::Std,
189 LrcMode::Noext,
190 LrcMode::StxNoext,
191 ] {
192 let codec = PacketCodec::new(mode);
193 let payload = b"123456780P0000065000";
194 let decoded = codec.decode(&codec.encode_application(payload));
195 assert_eq!(decoded.packet_type, PacketType::Application);
196 assert_eq!(decoded.payload, payload);
197 assert!(decoded.valid_lrc);
198 }
199 }
200
201 #[test]
202 fn application_detects_corrupted_lrc() {
203 let codec = PacketCodec::new(LrcMode::Std);
204 let mut frame = codec.encode_application(b"HELLO");
205 *frame.last_mut().unwrap() ^= 0xFF; let decoded = codec.decode(&frame);
207 assert_eq!(decoded.packet_type, PacketType::Application);
208 assert_eq!(decoded.payload, b"HELLO");
209 assert!(!decoded.valid_lrc);
210 }
211
212 #[test]
213 fn encode_control_frames_ctrl_etx_lrc() {
214 let codec = PacketCodec::new(LrcMode::Std);
215 let frame = codec.encode_control(ACK);
216 assert_eq!(frame.len(), 3);
217 assert_eq!(frame[0], ACK);
218 assert_eq!(frame[1], ETX);
219 }
220
221 #[test]
222 fn decode_ack() {
223 let decoded = PacketCodec::new(LrcMode::Std).decode(&[ACK]);
224 assert_eq!(decoded.packet_type, PacketType::Ack);
225 assert!(decoded.valid_lrc);
226 }
227
228 #[test]
229 fn decode_nak() {
230 let decoded = PacketCodec::new(LrcMode::Std).decode(&[NAK]);
231 assert_eq!(decoded.packet_type, PacketType::Nak);
232 assert!(decoded.valid_lrc);
233 }
234
235 #[test]
240 fn decode_full_control_frame_from_encode_control() {
241 let codec = PacketCodec::new(LrcMode::Std);
242 assert_eq!(
243 codec.decode(&codec.encode_control(ACK)).packet_type,
244 PacketType::Ack
245 );
246 assert_eq!(
247 codec.decode(&codec.encode_control(NAK)).packet_type,
248 PacketType::Nak
249 );
250 }
251
252 #[test]
256 fn decode_control_is_lead_byte_only_ignoring_trailing_and_lrc() {
257 let codec = PacketCodec::new(LrcMode::Std);
258 assert_eq!(codec.decode(&[ACK, 0x00]).packet_type, PacketType::Ack);
260 assert_eq!(codec.decode(&[ACK, ETX, 0xFF]).packet_type, PacketType::Ack);
262 assert_eq!(
264 codec.decode(&[NAK, 0x99, 0x42]).packet_type,
265 PacketType::Nak
266 );
267 }
268
269 #[test]
270 fn decode_empty_is_unknown() {
271 let decoded = PacketCodec::new(LrcMode::Std).decode(&[]);
272 assert_eq!(decoded.packet_type, PacketType::Unknown);
273 assert!(!decoded.valid_lrc);
274 }
275
276 #[test]
278 fn decode_lone_soh_is_unknown_not_panic() {
279 let decoded = PacketCodec::new(LrcMode::Std).decode(&[SOH]);
280 assert_eq!(decoded.packet_type, PacketType::Unknown);
281 assert!(!decoded.valid_lrc);
282 }
283
284 #[test]
285 fn decode_progress_update() {
286 let codec = PacketCodec::new(LrcMode::Std);
287 let msg = b"ELABORAZIONE... "; let mut frame = vec![SOH];
289 frame.extend_from_slice(msg);
290 frame.push(EOT);
291 let decoded = codec.decode(&frame);
292 assert_eq!(decoded.packet_type, PacketType::Progress);
293 assert_eq!(decoded.payload, msg);
294 }
295
296 #[test]
297 fn decode_stx_without_etx_is_unknown() {
298 let decoded = PacketCodec::new(LrcMode::Std).decode(&[STX, b'A', b'B']);
299 assert_eq!(decoded.packet_type, PacketType::Unknown);
300 assert!(!decoded.valid_lrc);
301 }
302
303 #[test]
306 fn decode_stx_with_etx_but_no_lrc_is_unknown() {
307 let decoded = PacketCodec::new(LrcMode::Std).decode(&[STX, b'A', ETX]);
308 assert_eq!(decoded.packet_type, PacketType::Unknown);
309 assert!(!decoded.valid_lrc);
310 }
311
312 #[test]
313 fn decode_unknown_lead_byte() {
314 let decoded = PacketCodec::new(LrcMode::Std).decode(&[0x99, 0x00]);
315 assert_eq!(decoded.packet_type, PacketType::Unknown);
316 assert!(!decoded.valid_lrc);
317 }
318
319 #[test]
321 fn decode_stx_with_trailing_bytes_after_lrc_is_unknown() {
322 let codec = PacketCodec::new(LrcMode::Std);
323 let mut frame = codec.encode_application(b"AB"); frame.push(0x00); let decoded = codec.decode(&frame);
326 assert_eq!(decoded.packet_type, PacketType::Unknown);
327 assert!(!decoded.valid_lrc);
328 }
329
330 #[test]
333 fn decode_coalesced_frames_is_unknown() {
334 let codec = PacketCodec::new(LrcMode::Std);
335 let mut first = codec.encode_application(b"AB");
336 first.extend_from_slice(&codec.encode_application(b"CD"));
337 let decoded = codec.decode(&first);
338 assert_eq!(decoded.packet_type, PacketType::Unknown);
339 assert!(!decoded.valid_lrc);
340 }
341
342 #[test]
344 fn decode_soh_without_eot_is_unknown() {
345 let codec = PacketCodec::new(LrcMode::Std);
346 let mut frame = vec![SOH];
347 frame.extend_from_slice(b"ELABORAZIONE... ");
348 frame.push(0xFF); let decoded = codec.decode(&frame);
350 assert_eq!(decoded.packet_type, PacketType::Unknown);
351 assert!(!decoded.valid_lrc);
352 }
353}