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