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