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};
16use ace_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
38pub struct CanSimRunner<const N: usize, const Q: usize> {
39    bus: CanSimBus<N, Q>,
40}
41
42impl<const MAX_FRAME: usize, const MAX_OUTBOX: usize> CanSimRunner<MAX_FRAME, MAX_OUTBOX> {
43    pub fn new(bus: CanSimBus<MAX_FRAME, MAX_OUTBOX>) -> Self {
44        Self { bus }
45    }
46
47    pub fn bus(&mut self) -> &mut CanSimBus<MAX_FRAME, MAX_OUTBOX> {
48        &mut self.bus
49    }
50
51    pub fn now(&self) -> Instant {
52        self.bus.now()
53    }
54
55    /// Ticks the simulation by `duration`.
56    ///
57    /// Order of operations per tick:
58    ///     1. Advance bus clock, apply CAN fault injection, deliver due frames (no frames
59    ///        delivered if bus is in bus-off state)
60    ///     2. Deliver CAN events to nodes implementing `CanEventHandler`
61    ///     3. Tick all nodes
62    ///     4. Drain node outboxes onto the bus
63    ///
64    /// Returns the number of frames delivered.
65    pub fn tick(
66        &mut self,
67        nodes: &mut [&mut dyn SimNodeErased<MAX_FRAME, MAX_OUTBOX>],
68        can_event_nodes: &mut [&mut dyn CanEventHandler],
69        duration: Duration,
70    ) -> usize {
71        let delivered = self.bus.tick(duration);
72        let now = self.bus.now();
73        let mut count = 0;
74
75        for envelope in &delivered {
76            for node in nodes.iter_mut() {
77                if *node.address() == envelope.dst {
78                    node.handle(&envelope.src, &envelope.data, now);
79                    count += 1;
80                }
81            }
82        }
83
84        let can_events: Vec<CanEvent, 16> = self.bus.drain_events().collect();
85        for event in &can_events {
86            for handler in can_event_nodes.iter_mut() {
87                handler.on_can_event(event, now);
88            }
89        }
90
91        for node in nodes.iter_mut() {
92            node.tick(now);
93        }
94
95        let mut outbox: Vec<(NodeAddress, Vec<u8, MAX_FRAME>), MAX_OUTBOX> = 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