1use crate::frame::CanFdFrame;
37
38pub const MAX_LINE_LENGTH: usize = 160;
47
48const CRC8_TABLE: [u8; 16] = [
50 0x00, 0x97, 0xb9, 0x2e, 0xe5, 0x72, 0x5c, 0xcb, 0x5d, 0xca, 0xe4, 0x73, 0xb8, 0x2f, 0x01, 0x96,
52];
53
54pub const fn compute_crc8(data: &[u8]) -> u8 {
59 let mut crc = 0u8;
60 let mut i = 0;
61 while i < data.len() {
62 let b = data[i];
63 crc = CRC8_TABLE[((crc >> 4) ^ (b >> 4)) as usize & 0x0f] ^ (crc << 4);
64 crc = CRC8_TABLE[((crc >> 4) ^ (b & 0x0f)) as usize & 0x0f] ^ (crc << 4);
65 i += 1;
66 }
67 crc
68}
69
70#[derive(Debug, Clone, Copy, Default)]
72pub struct EncodeOptions {
73 pub disable_brs: bool,
75 pub checksum: bool,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum EncodeError {
82 BufferTooSmall,
85}
86
87#[derive(Debug, Clone, PartialEq)]
90pub enum Line<'a> {
91 Ok,
93 Rcv(CanFdFrame),
95 Err(&'a [u8]),
97 Other(&'a [u8]),
99}
100
101struct Writer<'a> {
102 buf: &'a mut [u8],
103 pos: usize,
104}
105
106impl Writer<'_> {
107 fn write(&mut self, data: &[u8]) -> Result<(), EncodeError> {
108 if self.pos + data.len() > self.buf.len() {
109 return Err(EncodeError::BufferTooSmall);
110 }
111 self.buf[self.pos..self.pos + data.len()].copy_from_slice(data);
112 self.pos += data.len();
113 Ok(())
114 }
115
116 fn write_hex_byte(&mut self, value: u8) -> Result<(), EncodeError> {
118 const HEX: &[u8; 16] = b"0123456789ABCDEF";
119 self.write(&[HEX[(value >> 4) as usize], HEX[(value & 0x0f) as usize]])
120 }
121
122 fn write_hex_id(&mut self, value: u32) -> Result<(), EncodeError> {
126 const HEX: &[u8; 16] = b"0123456789abcdef";
127 let digits = {
128 let mut n = 4;
129 while n < 8 && (value >> (4 * n)) != 0 {
130 n += 1;
131 }
132 n
133 };
134 for i in (0..digits).rev() {
135 self.write(&[HEX[((value >> (4 * i)) & 0x0f) as usize]])?;
136 }
137 Ok(())
138 }
139}
140
141pub fn encode_can_send(
148 frame: &CanFdFrame,
149 options: &EncodeOptions,
150 buf: &mut [u8],
151) -> Result<usize, EncodeError> {
152 let mut w = Writer { buf, pos: 0 };
153
154 w.write(b"can send ")?;
155 w.write_hex_id(frame.arbitration_id)?;
156 w.write(b" ")?;
157
158 for b in frame.payload() {
159 w.write_hex_byte(*b)?;
160 }
161 let on_wire_size = CanFdFrame::round_up_dlc(frame.size as usize);
163 for _ in frame.size as usize..on_wire_size {
164 w.write(b"50")?;
165 }
166
167 w.write(b" ")?;
168 if frame.brs_enabled() && !options.disable_brs {
169 w.write(b"B")?;
170 } else {
171 w.write(b"b")?;
172 }
173 if frame.fdcan_enabled() {
174 w.write(b"F")?;
175 }
176
177 if options.checksum {
178 w.write(b" ")?;
181 let crc = compute_crc8(&w.buf[..w.pos]);
182 w.write(b"*")?;
183 w.write_hex_byte(crc)?;
184 }
185
186 w.write(b"\n")?;
187
188 Ok(w.pos)
189}
190
191pub fn strip_checksum(line: &[u8]) -> (&[u8], bool, bool) {
203 if line.len() < 3 || line[line.len() - 3] != b'*' {
206 return (line, false, false);
207 }
208
209 let star_pos = line.len() - 3;
210 let claimed = {
211 let hi = parse_hex_digit(line[star_pos + 1]);
212 let lo = parse_hex_digit(line[star_pos + 2]);
213 match (hi, lo) {
214 (Some(hi), Some(lo)) => (hi << 4) | lo,
215 _ => return (line, false, false),
216 }
217 };
218
219 let computed = compute_crc8(&line[..star_pos]);
220
221 let mut content = &line[..star_pos];
222 while let [head @ .., b' '] = content {
223 content = head;
224 }
225
226 (content, true, claimed == computed)
227}
228
229pub fn parse_line(line: &[u8]) -> Line<'_> {
234 let line = trim(line);
235
236 if line.starts_with(b"OK") {
237 return Line::Ok;
238 }
239 if line.starts_with(b"rcv") {
240 if let Some(frame) = parse_rcv(line) {
241 return Line::Rcv(frame);
242 }
243 return Line::Other(line);
244 }
245 if line.starts_with(b"ERR") {
246 return Line::Err(line);
247 }
248 Line::Other(line)
249}
250
251pub fn parse_rcv(line: &[u8]) -> Option<CanFdFrame> {
259 let line = trim(line);
260 let mut fields = line.split(|&b| b == b' ');
261
262 if fields.next() != Some(b"rcv".as_slice()) {
263 return None;
264 }
265
266 let arbitration_id = parse_hex_u32(fields.next()?)?;
267
268 let mut frame = CanFdFrame::new();
269 frame.arbitration_id = arbitration_id;
270
271 let data = fields.next()?;
272 if data.len() % 2 != 0 || data.len() > 2 * frame.data.len() {
273 return None;
274 }
275 for (i, pair) in data.chunks_exact(2).enumerate() {
276 frame.data[i] = ((parse_hex_digit(pair[0])? as u8) << 4) | parse_hex_digit(pair[1])? as u8;
277 }
278 frame.size = (data.len() / 2) as u8;
279
280 for flag in fields {
281 match flag {
282 b"E" => frame.arbitration_id |= 0x80000000, b"B" => frame.set_brs(true),
284 b"F" => frame.set_fdcan(true),
285 _ => {}
286 }
287 }
288
289 Some(frame)
290}
291
292pub fn is_checksum_error(line: &[u8]) -> bool {
295 const NEEDLE: &[u8] = b"checksum";
296 line.windows(NEEDLE.len())
297 .any(|w| w.eq_ignore_ascii_case(NEEDLE))
298}
299
300fn trim(mut line: &[u8]) -> &[u8] {
301 while let [b, rest @ ..] = line {
302 if b.is_ascii_whitespace() {
303 line = rest;
304 } else {
305 break;
306 }
307 }
308 while let [rest @ .., b] = line {
309 if b.is_ascii_whitespace() {
310 line = rest;
311 } else {
312 break;
313 }
314 }
315 line
316}
317
318fn parse_hex_digit(c: u8) -> Option<u8> {
319 match c {
320 b'0'..=b'9' => Some(c - b'0'),
321 b'a'..=b'f' => Some(c - b'a' + 10),
322 b'A'..=b'F' => Some(c - b'A' + 10),
323 _ => None,
324 }
325}
326
327fn parse_hex_u32(digits: &[u8]) -> Option<u32> {
328 if digits.is_empty() || digits.len() > 8 {
329 return None;
330 }
331 let mut value = 0u32;
332 for &c in digits {
333 value = (value << 4) | parse_hex_digit(c)? as u32;
334 }
335 Some(value)
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341
342 #[test]
346 fn test_compute_crc8() {
347 assert_eq!(compute_crc8(b""), 0x00);
348 assert_eq!(compute_crc8(b"OK "), 0xBD);
349 assert_eq!(compute_crc8(b"can send 0001 20 "), 0xAE);
350 assert_eq!(compute_crc8(b"rcv 0502 1234 "), 0x3C);
351 assert_eq!(compute_crc8(b"rcv 00000100 230b0a00 "), 0xD2);
352
353 assert_eq!(compute_crc8(b"rcv 00000100 00 "), 0x3E);
355 assert_eq!(compute_crc8(b"rcv 00000100 01 "), 0xED);
356 assert_eq!(compute_crc8(b"rcv 00000100 ff "), 0x57);
357 }
358
359 fn make_frame(id: u32, data: &[u8]) -> CanFdFrame {
360 let mut frame = CanFdFrame::new();
361 frame.arbitration_id = id;
362 frame.data[..data.len()].copy_from_slice(data);
363 frame.size = data.len() as u8;
364 frame
365 }
366
367 fn encode<'a>(frame: &CanFdFrame, options: &EncodeOptions, buf: &'a mut [u8]) -> &'a [u8] {
368 let len = encode_can_send(frame, options, buf).unwrap();
369 &buf[..len]
370 }
371
372 #[test]
373 fn test_encode_basic() {
374 let frame = make_frame(0x8001, &[0x01, 0x00, 0x0A]);
375 let mut buf = [0u8; MAX_LINE_LENGTH];
376 assert_eq!(
377 encode(&frame, &EncodeOptions::default(), &mut buf),
378 b"can send 8001 01000A BF\n"
379 );
380 }
381
382 #[test]
383 fn test_encode_dlc_padding() {
384 let frame = make_frame(0x0001, &[0x11; 9]);
387 let mut buf = [0u8; MAX_LINE_LENGTH];
388 assert_eq!(
389 encode(&frame, &EncodeOptions::default(), &mut buf),
390 b"can send 0001 111111111111111111505050 BF\n"
391 );
392 }
393
394 #[test]
395 fn test_encode_disable_brs() {
396 let frame = make_frame(0x8001, &[0x01]);
397 let mut buf = [0u8; MAX_LINE_LENGTH];
398 assert_eq!(
399 encode(
400 &frame,
401 &EncodeOptions {
402 disable_brs: true,
403 ..Default::default()
404 },
405 &mut buf
406 ),
407 b"can send 8001 01 bF\n"
408 );
409 }
410
411 #[test]
412 fn test_encode_checksum() {
413 let frame = make_frame(0x0001, &[0x20]);
414 let mut buf = [0u8; MAX_LINE_LENGTH];
415 let line = encode(
416 &frame,
417 &EncodeOptions {
418 disable_brs: true,
419 checksum: true,
420 },
421 &mut buf,
422 );
423 assert!(line.starts_with(b"can send 0001 20 bF *"));
426 assert!(line.ends_with(b"\n"));
427
428 let (content, had, valid) = strip_checksum(&line[..line.len() - 1]);
430 assert_eq!(content, b"can send 0001 20 bF");
431 assert!(had);
432 assert!(valid);
433 }
434
435 #[test]
436 fn test_encode_extended_id() {
437 let frame = make_frame(0x12345, &[0xFF]);
440 let mut buf = [0u8; MAX_LINE_LENGTH];
441 assert_eq!(
442 encode(&frame, &EncodeOptions::default(), &mut buf),
443 b"can send 12345 FF BF\n"
444 );
445
446 let frame = make_frame(0x1234_5678, &[]);
447 let mut buf = [0u8; MAX_LINE_LENGTH];
448 assert_eq!(
449 encode(&frame, &EncodeOptions::default(), &mut buf),
450 b"can send 12345678 BF\n"
451 );
452 }
453
454 #[test]
455 fn test_encode_buffer_too_small() {
456 let frame = make_frame(0x8001, &[0x01]);
457 let mut buf = [0u8; 8];
458 assert_eq!(
459 encode_can_send(&frame, &EncodeOptions::default(), &mut buf),
460 Err(EncodeError::BufferTooSmall)
461 );
462 }
463
464 #[test]
465 fn test_max_line_length_is_sufficient() {
466 let frame = make_frame(0x1FFF_FFFF, &[0xAB; 64]);
467 let mut buf = [0u8; MAX_LINE_LENGTH];
468 let len = encode_can_send(
469 &frame,
470 &EncodeOptions {
471 disable_brs: false,
472 checksum: true,
473 },
474 &mut buf,
475 )
476 .unwrap();
477 assert!(len <= MAX_LINE_LENGTH);
478 }
479
480 #[test]
481 fn test_strip_checksum_valid() {
482 let (content, had, valid) = strip_checksum(b"OK *BD");
483 assert_eq!(content, b"OK");
484 assert!(had);
485 assert!(valid);
486 }
487
488 #[test]
489 fn test_strip_checksum_invalid() {
490 let (content, had, valid) = strip_checksum(b"OK *FF");
491 assert_eq!(content, b"OK");
492 assert!(had);
493 assert!(!valid);
494 }
495
496 #[test]
497 fn test_strip_checksum_absent() {
498 let (content, had, valid) = strip_checksum(b"OK");
499 assert_eq!(content, b"OK");
500 assert!(!had);
501 assert!(!valid);
502
503 let (content, had, _) = strip_checksum(b"rcv 0001 2a *1");
505 assert_eq!(content, b"rcv 0001 2a *1");
506 assert!(!had);
507
508 let (content, had, _) = strip_checksum(b"some *zz");
510 assert_eq!(content, b"some *zz");
511 assert!(!had);
512 }
513
514 #[test]
515 fn test_parse_line() {
516 assert_eq!(parse_line(b"OK"), Line::Ok);
517 assert_eq!(parse_line(b"OK\r\n"), Line::Ok);
518 assert!(matches!(parse_line(b"rcv 0502 1234"), Line::Rcv(_)));
519 assert!(matches!(parse_line(b"ERR unknown command"), Line::Err(_)));
520 assert!(matches!(parse_line(b""), Line::Other(_)));
521 assert!(matches!(parse_line(b"rcv zz"), Line::Other(_)));
523 }
524
525 #[test]
526 fn test_parse_rcv() {
527 let frame = parse_rcv(b"rcv 8001 01000A B F").unwrap();
528 assert_eq!(frame.arbitration_id, 0x8001);
529 assert_eq!(frame.payload(), &[0x01, 0x00, 0x0A]);
530 assert!(frame.brs_enabled());
531 assert!(frame.fdcan_enabled());
532 }
533
534 #[test]
535 fn test_parse_rcv_extended_id() {
536 let frame = parse_rcv(b"rcv 00010001 20").unwrap();
537 assert_eq!(frame.arbitration_id, 0x00010001);
538
539 let frame = parse_rcv(b"rcv 0100 20 E").unwrap();
540 assert_eq!(frame.arbitration_id, 0x80000100);
541 }
542
543 #[test]
544 fn test_parse_rcv_empty_data() {
545 let frame = parse_rcv(b"rcv 0100 e B F").unwrap();
547 assert_eq!(frame.arbitration_id, 0x0100);
548 assert_eq!(frame.size, 0);
549 }
550
551 #[test]
552 fn test_parse_rcv_malformed() {
553 assert!(parse_rcv(b"rcv").is_none());
554 assert!(parse_rcv(b"rcv 0100").is_none());
555 assert!(parse_rcv(b"rcv zz 00").is_none());
556 assert!(parse_rcv(b"rcv 0100 0").is_none()); assert!(parse_rcv(b"OK").is_none());
558 }
559
560 #[test]
561 fn test_is_checksum_error() {
562 assert!(is_checksum_error(b"ERR checksum required"));
563 assert!(is_checksum_error(b"ERR invalid Checksum"));
564 assert!(!is_checksum_error(b"ERR unknown command"));
565 }
566
567 #[test]
568 fn test_encode_parse_round_trip() {
569 let frame = make_frame(0x0542, &[0x12, 0x34]);
570 let mut buf = [0u8; MAX_LINE_LENGTH];
571 let len = encode_can_send(&frame, &EncodeOptions::default(), &mut buf).unwrap();
572
573 let content = &buf[b"can send ".len()..len - 1];
577 let mut line = [0u8; MAX_LINE_LENGTH];
578 line[..4].copy_from_slice(b"rcv ");
579 line[4..4 + content.len()].copy_from_slice(content);
580
581 let parsed = parse_rcv(&line[..4 + content.len()]).unwrap();
582 assert_eq!(parsed.arbitration_id, 0x0542);
583 assert_eq!(parsed.payload(), &[0x12, 0x34]);
584 assert!(parsed.brs_enabled());
585 assert!(parsed.fdcan_enabled());
586 }
587}