1use std::collections::VecDeque;
39use std::fmt;
40use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
41use std::time::Duration;
42
43use crate::frame::Frame;
44use crate::id::CanId;
45use crate::j1939::J1939Id;
46
47#[cfg(all(feature = "linux", target_os = "linux"))]
48mod socketcan;
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub enum CanBusKind {
53 Device,
56 Simulated,
58}
59
60#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85pub struct Filter {
86 id: CanId,
87 mask: u32,
88}
89
90fn width(id: CanId) -> u32 {
91 if id.is_extended() {
92 CanId::EXTENDED_MASK
93 } else {
94 CanId::STANDARD_MASK
95 }
96}
97
98impl Filter {
99 pub fn new(id: CanId, mask: u32) -> Filter {
111 Filter {
112 id,
113 mask: mask & width(id),
114 }
115 }
116
117 pub fn exact(id: CanId) -> Filter {
127 Filter::new(id, width(id))
128 }
129
130 pub fn pgn(pgn: u32) -> Filter {
141 let id = J1939Id::from_parts(0, pgn, 0, 0);
142 let mask = if id.is_broadcast() {
143 0x03FF_FF00
144 } else {
145 0x03FF_0000
146 };
147 Filter::new(id.to_id(), mask)
148 }
149
150 pub fn id(&self) -> CanId {
152 self.id
153 }
154
155 pub fn mask(&self) -> u32 {
157 self.mask
158 }
159
160 pub fn matches(&self, id: CanId) -> bool {
170 id.is_extended() == self.id.is_extended()
171 && id.raw() & self.mask == self.id.raw() & self.mask
172 }
173}
174
175#[derive(Clone, Debug, PartialEq, Eq)]
177pub enum OpenError {
178 Unsupported,
180 Device {
182 interface: String,
184 reason: String,
186 },
187}
188
189impl fmt::Display for OpenError {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 match self {
192 OpenError::Unsupported => f.write_str(
193 "a CAN interface is opened through SocketCAN, which only Linux has here",
194 ),
195 OpenError::Device { interface, reason } => write!(f, "{interface}: {reason}"),
196 }
197 }
198}
199
200impl std::error::Error for OpenError {}
201
202#[derive(Clone, Debug, PartialEq, Eq)]
204pub enum BusError {
205 Device {
208 reason: String,
210 },
211}
212
213impl fmt::Display for BusError {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 match self {
216 BusError::Device { reason } => f.write_str(reason),
217 }
218 }
219}
220
221impl std::error::Error for BusError {}
222
223#[derive(Clone)]
255pub struct CanBus {
256 node: Arc<Mutex<Node>>,
257}
258
259struct Node {
260 backend: Backend,
261 sent: usize,
262 received: usize,
263 waited_ns: u64,
264}
265
266enum Backend {
267 #[cfg(all(feature = "linux", target_os = "linux"))]
268 Device {
269 interface: String,
270 socket: Arc<socketcan::Socket>,
271 },
272 Simulated {
273 network: Arc<Mutex<Network>>,
274 seat: usize,
275 },
276}
277
278#[derive(Default)]
279struct Network {
280 seats: Vec<Seat>,
281}
282
283struct Seat {
284 inbox: VecDeque<Frame>,
285 filters: Option<Vec<Filter>>,
286 joined: bool,
287}
288
289impl Seat {
290 fn new() -> Seat {
291 Seat {
292 inbox: VecDeque::new(),
293 filters: None,
294 joined: true,
295 }
296 }
297
298 fn keeps(&self, frame: &Frame) -> bool {
299 self.joined
300 && self
301 .filters
302 .as_ref()
303 .is_none_or(|filters| filters.iter().any(|filter| filter.matches(frame.id())))
304 }
305}
306
307impl Drop for Node {
308 #[allow(irrefutable_let_patterns)]
310 fn drop(&mut self) {
311 if let Backend::Simulated { network, seat } = &self.backend {
312 let mut network = lock(network);
313 let seat = &mut network.seats[*seat];
314 seat.joined = false;
315 seat.inbox.clear();
316 }
317 }
318}
319
320fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
321 mutex.lock().unwrap_or_else(PoisonError::into_inner)
322}
323
324fn nanos(duration: Duration) -> u64 {
325 u64::try_from(duration.as_nanos()).unwrap_or(u64::MAX)
326}
327
328impl CanBus {
329 fn with(backend: Backend) -> CanBus {
330 CanBus {
331 node: Arc::new(Mutex::new(Node {
332 backend,
333 sent: 0,
334 received: 0,
335 waited_ns: 0,
336 })),
337 }
338 }
339
340 pub fn open(interface: &str) -> Result<CanBus, OpenError> {
369 #[cfg(all(feature = "linux", target_os = "linux"))]
370 {
371 let socket = socketcan::Socket::open(interface).map_err(|error| OpenError::Device {
372 interface: interface.to_string(),
373 reason: error.to_string(),
374 })?;
375 Ok(CanBus::with(Backend::Device {
376 interface: interface.to_string(),
377 socket: Arc::new(socket),
378 }))
379 }
380 #[cfg(not(all(feature = "linux", target_os = "linux")))]
381 {
382 let _ = interface;
383 Err(OpenError::Unsupported)
384 }
385 }
386
387 pub fn simulated() -> CanBus {
393 let mut network = Network::default();
394 network.seats.push(Seat::new());
395 CanBus::with(Backend::Simulated {
396 network: Arc::new(Mutex::new(network)),
397 seat: 0,
398 })
399 }
400
401 pub fn join(&self) -> Result<CanBus, OpenError> {
412 let node = lock(&self.node);
413 match &node.backend {
414 #[cfg(all(feature = "linux", target_os = "linux"))]
415 Backend::Device { interface, .. } => {
416 let interface = interface.clone();
417 drop(node);
418 CanBus::open(&interface)
419 }
420 Backend::Simulated { network, .. } => {
421 let mut shared = lock(network);
422 shared.seats.push(Seat::new());
423 let seat = shared.seats.len() - 1;
424 Ok(CanBus::with(Backend::Simulated {
425 network: Arc::clone(network),
426 seat,
427 }))
428 }
429 }
430 }
431
432 pub fn kind(&self) -> CanBusKind {
434 match lock(&self.node).backend {
435 #[cfg(all(feature = "linux", target_os = "linux"))]
436 Backend::Device { .. } => CanBusKind::Device,
437 Backend::Simulated { .. } => CanBusKind::Simulated,
438 }
439 }
440
441 pub fn interface(&self) -> Option<String> {
443 match &lock(&self.node).backend {
444 #[cfg(all(feature = "linux", target_os = "linux"))]
445 Backend::Device { interface, .. } => Some(interface.clone()),
446 Backend::Simulated { .. } => None,
447 }
448 }
449
450 pub fn send(&self, frame: &Frame) -> Result<(), BusError> {
460 let mut node = lock(&self.node);
461 match &node.backend {
462 #[cfg(all(feature = "linux", target_os = "linux"))]
463 Backend::Device { interface, socket } => {
464 let (interface, socket) = (interface.clone(), Arc::clone(socket));
465 drop(node);
466 socket.send(frame).map_err(|error| BusError::Device {
467 reason: format!("{interface}: {error}"),
468 })?;
469 node = lock(&self.node);
470 }
471 Backend::Simulated { network, seat } => {
472 let mut network = lock(network);
473 for (index, other) in network.seats.iter_mut().enumerate() {
474 if index != *seat && other.keeps(frame) {
475 other.inbox.push_back(*frame);
476 }
477 }
478 }
479 }
480 node.sent += 1;
481 Ok(())
482 }
483
484 pub fn receive(&self, timeout: Duration) -> Result<Option<Frame>, BusError> {
502 let mut node = lock(&self.node);
503 let frame = match &node.backend {
504 #[cfg(all(feature = "linux", target_os = "linux"))]
505 Backend::Device { interface, socket } => {
506 let (interface, socket) = (interface.clone(), Arc::clone(socket));
507 drop(node);
508 let frame = socket.receive(timeout).map_err(|error| BusError::Device {
509 reason: format!("{interface}: {error}"),
510 })?;
511 node = lock(&self.node);
512 frame
513 }
514 Backend::Simulated { network, seat } => lock(network).seats[*seat].inbox.pop_front(),
515 };
516 match frame {
517 Some(_) => node.received += 1,
518 None => node.waited_ns = node.waited_ns.saturating_add(nanos(timeout)),
519 }
520 Ok(frame)
521 }
522
523 pub fn set_filters(&self, filters: &[Filter]) -> Result<(), BusError> {
536 self.filter(Some(filters.to_vec()))
537 }
538
539 pub fn clear_filters(&self) -> Result<(), BusError> {
545 self.filter(None)
546 }
547
548 fn filter(&self, filters: Option<Vec<Filter>>) -> Result<(), BusError> {
549 let node = lock(&self.node);
550 match &node.backend {
551 #[cfg(all(feature = "linux", target_os = "linux"))]
552 Backend::Device { interface, socket } => socket
553 .set_filters(filters.as_deref())
554 .map_err(|error| BusError::Device {
555 reason: format!("{interface}: {error}"),
556 }),
557 Backend::Simulated { network, seat } => {
558 lock(network).seats[*seat].filters = filters;
559 Ok(())
560 }
561 }
562 }
563
564 pub fn sent(&self) -> usize {
566 lock(&self.node).sent
567 }
568
569 pub fn received(&self) -> usize {
571 lock(&self.node).received
572 }
573
574 pub fn waited_micros(&self) -> u64 {
577 lock(&self.node).waited_ns / 1_000
578 }
579}
580
581impl fmt::Debug for CanBus {
582 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583 let node = lock(&self.node);
584 f.debug_struct("CanBus")
585 .field(
586 "kind",
587 &match node.backend {
588 #[cfg(all(feature = "linux", target_os = "linux"))]
589 Backend::Device { .. } => CanBusKind::Device,
590 Backend::Simulated { .. } => CanBusKind::Simulated,
591 },
592 )
593 .field("sent", &node.sent)
594 .field("received", &node.received)
595 .finish_non_exhaustive()
596 }
597}
598
599#[cfg(test)]
600mod tests {
601 use super::*;
602
603 fn speed() -> CanId {
604 J1939Id::broadcast(3, 61_444, 0x00).to_id()
605 }
606
607 #[test]
608 fn every_other_node_hears_a_frame_and_the_sender_does_not() {
609 let engine = CanBus::simulated();
610 let gateway = engine.join().unwrap();
611 let logger = gateway.join().unwrap();
612 let frame = Frame::new(speed(), &[0xFF; 8]).unwrap();
613 engine.send(&frame).unwrap();
614
615 assert_eq!(gateway.receive(Duration::ZERO).unwrap(), Some(frame));
616 assert_eq!(logger.receive(Duration::ZERO).unwrap(), Some(frame));
617 assert_eq!(engine.receive(Duration::from_millis(5)).unwrap(), None);
618 assert_eq!(engine.waited_micros(), 5_000);
619 assert_eq!((engine.sent(), gateway.received()), (1, 1));
620 assert_eq!(engine.kind(), CanBusKind::Simulated);
621 assert_eq!(engine.interface(), None);
622 }
623
624 #[test]
625 fn frames_arrive_in_the_order_they_were_sent() {
626 let one = CanBus::simulated();
627 let other = one.join().unwrap();
628 for value in 0u8..5 {
629 one.send(&Frame::new(CanId::standard(0x100), &[value]).unwrap())
630 .unwrap();
631 }
632 let heard: Vec<u8> = (0..5)
633 .map(|_| other.receive(Duration::ZERO).unwrap().unwrap().data()[0])
634 .collect();
635 assert_eq!(heard, [0, 1, 2, 3, 4]);
636 }
637
638 #[test]
639 fn a_filter_keeps_what_it_passes_and_an_empty_list_keeps_nothing() {
640 let engine = CanBus::simulated();
641 let gateway = engine.join().unwrap();
642 gateway.set_filters(&[Filter::pgn(61_444)]).unwrap();
643 let other = J1939Id::broadcast(6, 65_262, 0x00).to_id();
644 engine.send(&Frame::new(other, &[0; 8]).unwrap()).unwrap();
645 engine.send(&Frame::new(speed(), &[0; 8]).unwrap()).unwrap();
646 assert_eq!(
647 gateway.receive(Duration::ZERO).unwrap().unwrap().id(),
648 speed()
649 );
650 assert_eq!(gateway.receive(Duration::ZERO).unwrap(), None);
651
652 gateway.set_filters(&[]).unwrap();
653 engine.send(&Frame::new(speed(), &[0; 8]).unwrap()).unwrap();
654 assert_eq!(gateway.receive(Duration::ZERO).unwrap(), None);
655
656 gateway.clear_filters().unwrap();
657 engine.send(&Frame::new(other, &[0; 8]).unwrap()).unwrap();
658 assert!(gateway.receive(Duration::ZERO).unwrap().is_some());
659 }
660
661 #[test]
662 fn a_pgn_filter_ignores_priority_source_and_an_addressed_groups_destination() {
663 let broadcast = Filter::pgn(61_444);
665 assert!(broadcast.matches(J1939Id::broadcast(7, 61_444, 0x21).to_id()));
666 assert!(!broadcast.matches(J1939Id::broadcast(3, 61_445, 0x00).to_id()));
667 let request = Filter::pgn(59_904);
669 assert!(request.matches(J1939Id::from_parts(6, 59_904, 0x01, 0x00).to_id()));
670 assert!(request.matches(J1939Id::from_parts(6, 59_904, 0xF9, 0x21).to_id()));
671 assert!(!request.matches(J1939Id::from_parts(6, 60_160, 0x01, 0x00).to_id()));
672 assert!(!request.matches(CanId::standard(0x000)));
673 }
674
675 #[test]
676 fn a_node_that_leaves_stops_collecting_frames() {
677 let engine = CanBus::simulated();
678 let gateway = engine.join().unwrap();
679 drop(gateway);
680 engine.send(&Frame::new(speed(), &[0; 8]).unwrap()).unwrap();
681 let network = match &lock(&engine.node).backend {
682 Backend::Simulated { network, .. } => Arc::clone(network),
683 #[cfg(all(feature = "linux", target_os = "linux"))]
684 Backend::Device { .. } => unreachable!(),
685 };
686 assert!(lock(&network).seats[1].inbox.is_empty());
687 }
688
689 #[test]
690 fn opening_an_interface_off_linux_is_unsupported() {
691 if !cfg!(all(feature = "linux", target_os = "linux")) {
692 assert_eq!(CanBus::open("can0").err(), Some(OpenError::Unsupported));
693 }
694 }
695
696 #[cfg(all(feature = "linux", target_os = "linux"))]
699 #[test]
700 fn a_virtual_interface_carries_every_kind_of_frame_between_two_sockets() {
701 let Ok(interface) = std::env::var("PAMOJA_VCAN") else {
702 return;
703 };
704 let engine = CanBus::open(&interface).unwrap();
705 let gateway = engine.join().unwrap();
706 assert_eq!(engine.kind(), CanBusKind::Device);
707 assert_eq!(gateway.interface().as_deref(), Some(interface.as_str()));
708
709 let frames = [
710 Frame::new(CanId::standard(0x20A), &[0x01, 0xF4]).unwrap(),
711 Frame::new(speed(), &[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]).unwrap(),
712 Frame::remote(CanId::standard(0x301), 4),
713 Frame::fd(CanId::extended(0x1ABC_DEF0), &[0xA5; 32]).unwrap(),
714 ];
715 for frame in &frames {
716 engine.send(frame).unwrap();
717 }
718 for frame in &frames {
719 let heard = gateway.receive(Duration::from_millis(500)).unwrap();
720 assert_eq!(heard, Some(*frame));
721 }
722 assert_eq!(engine.receive(Duration::from_millis(20)).unwrap(), None);
723
724 gateway.set_filters(&[Filter::pgn(61_444)]).unwrap();
725 engine
726 .send(&Frame::new(CanId::standard(0x20A), &[0]).unwrap())
727 .unwrap();
728 engine.send(&frames[1]).unwrap();
729 let kept = gateway.receive(Duration::from_millis(500)).unwrap();
730 assert_eq!(kept.map(|frame| frame.id()), Some(speed()));
731 assert_eq!(gateway.receive(Duration::from_millis(20)).unwrap(), None);
732 gateway.clear_filters().unwrap();
733 assert_eq!((engine.sent(), gateway.received()), (6, 5));
734 }
735}