Skip to main content

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