1use std::time::Duration;
6use thiserror::Error;
7
8#[cfg(target_os = "linux")]
9pub mod socketcan;
10
11#[cfg(target_os = "linux")]
12pub use socketcan::SocketCanAdapter;
13
14#[cfg(target_os = "linux")]
15pub use socketcan::split::{SocketCanRxAdapter, SocketCanTxAdapter};
16
17pub mod gs_usb;
18
19pub use gs_usb::GsUsbCanAdapter;
21
22pub mod gs_usb_udp;
24
25pub use gs_usb::split::{GsUsbRxAdapter, GsUsbTxAdapter};
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub struct PiperFrame {
37 pub id: u32,
39
40 pub data: [u8; 8],
42
43 pub len: u8,
45
46 pub is_extended: bool,
48
49 pub timestamp_us: u64,
61}
62
63impl PiperFrame {
64 pub fn new_standard(id: u16, data: &[u8]) -> Self {
66 Self::new(id as u32, data, false)
67 }
68
69 pub fn new_extended(id: u32, data: &[u8]) -> Self {
71 Self::new(id, data, true)
72 }
73
74 fn new(id: u32, data: &[u8], is_extended: bool) -> Self {
76 let mut fixed_data = [0u8; 8];
77 let len = data.len().min(8);
78 fixed_data[..len].copy_from_slice(&data[..len]);
79
80 Self {
81 id,
82 data: fixed_data,
83 len: len as u8,
84 is_extended,
85 timestamp_us: 0, }
87 }
88
89 pub fn data_slice(&self) -> &[u8] {
91 &self.data[..self.len as usize]
92 }
93}
94
95#[derive(Error, Debug)]
97pub enum CanError {
98 #[error("IO Error: {0}")]
100 Io(#[from] std::io::Error),
101
102 #[error("Device Error: {0}")]
104 Device(#[from] CanDeviceError),
105
106 #[error("Read timeout")]
108 Timeout,
109
110 #[error("Buffer overflow")]
112 BufferOverflow,
113
114 #[error("Bus off")]
116 BusOff,
117
118 #[error("Device not started")]
120 NotStarted,
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum CanDeviceErrorKind {
126 Unknown,
127 NotFound,
129 NoDevice,
131 AccessDenied,
133 Busy,
135 UnsupportedConfig,
137 InvalidResponse,
139 InvalidFrame,
141 Backend,
143}
144
145#[derive(Error, Debug, Clone)]
147#[error("{kind:?}: {message}")]
148pub struct CanDeviceError {
149 pub kind: CanDeviceErrorKind,
150 pub message: String,
151}
152
153impl CanDeviceError {
154 pub fn new(kind: CanDeviceErrorKind, message: impl Into<String>) -> Self {
155 Self {
156 kind,
157 message: message.into(),
158 }
159 }
160
161 pub fn is_fatal(&self) -> bool {
170 matches!(
171 self.kind,
172 CanDeviceErrorKind::NoDevice
173 | CanDeviceErrorKind::AccessDenied
174 | CanDeviceErrorKind::NotFound
175 )
176 }
177}
178
179impl From<String> for CanDeviceError {
180 fn from(message: String) -> Self {
181 Self::new(CanDeviceErrorKind::Unknown, message)
182 }
183}
184
185impl From<&str> for CanDeviceError {
186 fn from(message: &str) -> Self {
187 Self::new(CanDeviceErrorKind::Unknown, message)
188 }
189}
190
191pub trait CanAdapter {
199 fn send(&mut self, frame: PiperFrame) -> Result<(), CanError>;
210
211 fn receive(&mut self) -> Result<PiperFrame, CanError>;
224
225 fn set_receive_timeout(&mut self, _timeout: Duration) {
236 }
239
240 fn receive_timeout(&mut self, timeout: Duration) -> Result<PiperFrame, CanError> {
256 self.set_receive_timeout(timeout);
259 self.receive()
263 }
264
265 fn try_receive(&mut self) -> Result<Option<PiperFrame>, CanError> {
278 match self.receive_timeout(Duration::ZERO) {
280 Ok(frame) => Ok(Some(frame)),
281 Err(CanError::Timeout) => Ok(None),
282 Err(e) => Err(e),
283 }
284 }
285
286 fn send_timeout(&mut self, frame: PiperFrame, _timeout: Duration) -> Result<(), CanError> {
303 self.send(frame)
306 }
307}
308
309pub trait RxAdapter {
314 fn receive(&mut self) -> Result<PiperFrame, CanError>;
326}
327
328pub trait TxAdapter {
333 fn send(&mut self, frame: PiperFrame) -> Result<(), CanError>;
343}
344
345pub trait SplittableAdapter: CanAdapter {
358 type RxAdapter: RxAdapter;
360
361 type TxAdapter: TxAdapter;
363
364 fn split(self) -> Result<(Self::RxAdapter, Self::TxAdapter), CanError>;
376}
377
378#[cfg(test)]
379mod tests {
380 use super::*;
381
382 #[test]
383 fn test_can_device_error_is_fatal() {
384 let fatal_errors = vec![
386 CanDeviceError::new(CanDeviceErrorKind::NoDevice, "Device not found"),
387 CanDeviceError::new(CanDeviceErrorKind::AccessDenied, "Access denied"),
388 CanDeviceError::new(CanDeviceErrorKind::NotFound, "Device not found"),
389 ];
390
391 for error in fatal_errors {
392 assert!(error.is_fatal(), "Error should be fatal: {:?}", error);
393 }
394
395 let non_fatal_errors = vec![
397 CanDeviceError::new(CanDeviceErrorKind::Backend, "Backend error"),
398 CanDeviceError::new(CanDeviceErrorKind::Busy, "Device busy"),
399 CanDeviceError::new(CanDeviceErrorKind::InvalidFrame, "Invalid frame"),
400 CanDeviceError::new(CanDeviceErrorKind::InvalidResponse, "Invalid response"),
401 CanDeviceError::new(CanDeviceErrorKind::UnsupportedConfig, "Unsupported config"),
402 CanDeviceError::new(CanDeviceErrorKind::Unknown, "Unknown error"),
403 ];
404
405 for error in non_fatal_errors {
406 assert!(!error.is_fatal(), "Error should not be fatal: {:?}", error);
407 }
408 }
409
410 #[test]
411 fn test_piper_frame_new_standard() {
412 let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
413 let frame = PiperFrame::new_standard(0x123, &data[..4]);
414
415 assert_eq!(frame.id, 0x123);
416 assert_eq!(frame.len, 4);
417 assert_eq!(frame.data[..4], data[..4]);
418 assert!(!frame.is_extended);
419 }
420
421 #[test]
422 fn test_piper_frame_new_extended() {
423 let data = [0xFF; 8];
424 let frame = PiperFrame::new_extended(0x12345678, &data);
425
426 assert_eq!(frame.id, 0x12345678);
427 assert_eq!(frame.len, 8);
428 assert!(frame.is_extended);
429 }
430
431 #[test]
432 fn test_piper_frame_data_truncation() {
433 let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A];
435 let frame = PiperFrame::new_standard(0x123, &data);
436
437 assert_eq!(frame.len, 8); assert_eq!(frame.data[7], 0x08);
439 }
440
441 #[test]
442 fn test_piper_frame_data_slice() {
443 let data = [0x01, 0x02, 0x03];
444 let frame = PiperFrame::new_standard(0x123, &data);
445
446 let slice = frame.data_slice();
447 assert_eq!(slice.len(), 3);
448 assert_eq!(slice, &[0x01, 0x02, 0x03]);
449 }
450
451 #[test]
452 fn test_piper_frame_copy_trait() {
453 let frame1 = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
455 let frame2 = frame1; assert_eq!(frame1.id, frame2.id); }
459
460 #[test]
461 fn test_can_error_display() {
462 let err = CanError::Timeout;
463 assert!(err.to_string().contains("timeout"));
464 }
465
466 #[test]
467 fn test_can_error_from_io_error() {
468 let io_err = std::io::Error::new(std::io::ErrorKind::TimedOut, "test");
469 let can_err: CanError = io_err.into();
470
471 match can_err {
472 CanError::Io(_) => {},
473 _ => panic!("Expected Io variant"),
474 }
475 }
476
477 struct MockCanAdapter {
479 sent_frames: Vec<PiperFrame>,
480 received_frames: Vec<PiperFrame>,
481 receive_index: usize,
482 }
483
484 impl CanAdapter for MockCanAdapter {
485 fn send(&mut self, frame: PiperFrame) -> Result<(), CanError> {
486 self.sent_frames.push(frame);
487 Ok(())
488 }
489
490 fn receive(&mut self) -> Result<PiperFrame, CanError> {
491 if self.receive_index < self.received_frames.len() {
492 let frame = self.received_frames[self.receive_index];
493 self.receive_index += 1;
494 Ok(frame)
495 } else {
496 Err(CanError::Timeout)
497 }
498 }
499 }
500
501 #[test]
502 fn test_can_adapter_send() {
503 let mut adapter = MockCanAdapter {
504 sent_frames: Vec::new(),
505 received_frames: Vec::new(),
506 receive_index: 0,
507 };
508
509 let frame = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
510 adapter.send(frame).unwrap();
511
512 assert_eq!(adapter.sent_frames.len(), 1);
513 assert_eq!(adapter.sent_frames[0].id, 0x123);
514 }
515
516 #[test]
517 fn test_piper_frame_timestamp_default() {
518 let frame = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
519 assert_eq!(frame.timestamp_us, 0); }
521
522 #[test]
523 fn test_piper_frame_timestamp_preservation() {
524 let frame = PiperFrame {
526 id: 0x123,
527 data: [0x01, 0x02, 0, 0, 0, 0, 0, 0],
528 len: 2,
529 is_extended: false,
530 timestamp_us: 12345,
531 };
532 assert_eq!(frame.timestamp_us, 12345);
533 }
534
535 #[test]
536 fn test_piper_frame_eq_trait() {
537 let frame1 = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
538 let frame2 = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
539 assert_eq!(frame1, frame2); let frame3 = PiperFrame {
543 timestamp_us: 1000,
544 ..frame1
545 };
546 assert_ne!(frame1, frame3);
547 }
548
549 #[test]
550 fn test_piper_frame_with_timestamp() {
551 let frame = PiperFrame {
552 id: 0x123,
553 data: [0x01, 0x02, 0x03, 0x04, 0, 0, 0, 0],
554 len: 4,
555 is_extended: false,
556 timestamp_us: 12345678, };
558
559 assert_eq!(frame.id, 0x123);
560 assert_eq!(frame.len, 4);
561 assert_eq!(frame.timestamp_us, 12345678);
562 }
563
564 #[test]
565 fn test_piper_frame_empty_data() {
566 let frame = PiperFrame::new_standard(0x123, &[]);
567 assert_eq!(frame.len, 0);
568 assert_eq!(frame.data, [0u8; 8]);
569 assert_eq!(frame.timestamp_us, 0);
570 }
571
572 #[test]
573 fn test_piper_frame_data_slice_empty() {
574 let frame = PiperFrame::new_standard(0x123, &[]);
575 let slice = frame.data_slice();
576 assert_eq!(slice.len(), 0);
577 }
578
579 #[test]
580 fn test_piper_frame_data_slice_full() {
581 let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
582 let frame = PiperFrame::new_standard(0x123, &data);
583 let slice = frame.data_slice();
584 assert_eq!(slice.len(), 8);
585 assert_eq!(slice, &data);
586 }
587
588 #[test]
589 fn test_can_adapter_receive() {
590 let frame1 = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
591 let frame2 = PiperFrame::new_extended(0x456, &[0x03, 0x04]);
592
593 let mut adapter = MockCanAdapter {
594 sent_frames: Vec::new(),
595 received_frames: vec![frame1, frame2],
596 receive_index: 0,
597 };
598
599 let rx_frame1 = adapter.receive().unwrap();
600 assert_eq!(rx_frame1.id, 0x123);
601
602 let rx_frame2 = adapter.receive().unwrap();
603 assert_eq!(rx_frame2.id, 0x456);
604
605 assert!(adapter.receive().is_err());
607 }
608
609 #[test]
610 fn test_can_error_variants() {
611 let timeout = CanError::Timeout;
613 assert!(timeout.to_string().to_lowercase().contains("timeout"));
614
615 let buffer_overflow = CanError::BufferOverflow;
616 assert!(buffer_overflow.to_string().to_lowercase().contains("overflow"));
617
618 let bus_off = CanError::BusOff;
619 assert!(bus_off.to_string().to_lowercase().contains("bus"));
620
621 let not_started = CanError::NotStarted;
622 assert!(not_started.to_string().to_lowercase().contains("start"));
623
624 let device = CanError::Device("test error".into());
625 assert!(device.to_string().contains("test error"));
626 }
627}