Skip to main content

acex_sim/
can_runner.rs

1//! CanSimRunner - drives SimNodeErased slices over a CanSimBus.
2//!
3//! Extends the SimRunner concept for CAN-aware scenarios. Alongside message delivery it:
4//!     - Delivers CanEvents to nodes that implement CanEventHandler
5//!     - Suppresses message delivery when the bus is in bus-off state
6//!     - Drains node outboxes back onto the CAN bus
7
8// region: Imports
9
10use crate::{
11    can_bus::{CanEvent, CanSimBus},
12    clock::{Duration, Instant},
13    io::NodeAddress,
14    node::SimNodeErased,
15};
16use acex_core::Vec;
17
18// endregion: Imports
19
20// region: CanEventHandler
21
22/// Optional trait for nodes that need to observe CAN bus events.
23///
24/// Nodes may implement this to react to bus-off, recovery, and bit errors. The `CanSimRunner`
25/// calls this after message delivery on each tick.
26pub trait CanEventHandler {
27    fn on_can_event(&mut self, event: &CanEvent, now: Instant);
28}
29
30// endregion: CanEventHandler
31
32// region: CanSimRunner
33
34/// Drivers `SimNodeErased` slices over a `CanSimBus`.
35///
36/// `N` - max CAN frame payload bytes (8 classic, 64 FD)
37/// `Q` - max frames in-flight on the bus
38#[cfg_attr(all(feature = "defmt", not(feature = "alloc")), derive(defmt::Format))]
39pub struct CanSimRunner<const N: usize, const Q: usize> {
40    bus: CanSimBus<N, Q>,
41}
42
43impl<const MAX_FRAME: usize, const MAX_OUTBOX: usize> CanSimRunner<MAX_FRAME, MAX_OUTBOX> {
44    pub fn new(bus: CanSimBus<MAX_FRAME, MAX_OUTBOX>) -> Self {
45        Self { bus }
46    }
47
48    pub fn bus(&mut self) -> &mut CanSimBus<MAX_FRAME, MAX_OUTBOX> {
49        &mut self.bus
50    }
51
52    pub fn now(&self) -> Instant {
53        self.bus.now()
54    }
55
56    /// Ticks the simulation by `duration`.
57    ///
58    /// Order of operations per tick:
59    ///     1. Advance bus clock, apply CAN fault injection, deliver due frames (no frames
60    ///        delivered if bus is in bus-off state)
61    ///     2. Deliver CAN events to nodes implementing `CanEventHandler`
62    ///     3. Tick all nodes
63    ///     4. Drain node outboxes onto the bus
64    ///
65    /// Returns the number of frames delivered.
66    pub fn tick(
67        &mut self,
68        nodes: &mut [&mut dyn SimNodeErased<MAX_FRAME, MAX_OUTBOX>],
69        can_event_nodes: &mut [&mut dyn CanEventHandler],
70        duration: Duration,
71    ) -> usize {
72        let delivered = self.bus.tick(duration);
73        let now = self.bus.now();
74        let mut count = 0;
75
76        for envelope in &delivered {
77            for node in nodes.iter_mut() {
78                if *node.address() == envelope.dst {
79                    node.handle(&envelope.src, &envelope.data, now);
80                    count += 1;
81                }
82            }
83        }
84
85        let can_events: Vec<CanEvent, 16> = self.bus.drain_events().collect();
86        for event in &can_events {
87            for handler in can_event_nodes.iter_mut() {
88                handler.on_can_event(event, now);
89            }
90        }
91
92        for node in nodes.iter_mut() {
93            node.tick(now);
94        }
95
96        let mut outbox: Vec<(NodeAddress, Vec<u8, MAX_FRAME>), MAX_OUTBOX> = Vec::new();
97        for node in nodes.iter_mut() {
98            outbox.clear();
99            node.drain_outbox(&mut outbox);
100
101            let src = node.address().clone();
102            for (dst, data) in outbox.iter() {
103                self.bus.send(src.clone(), dst.clone(), data);
104            }
105        }
106
107        count
108    }
109}
110
111// endregion: CanSimRunner