Skip to main content

ace_sim/
tcp_runner.rs

1//! TcpSimRunner - drives SimNodeErased slices over a TcpSimBus.
2//!
3//! Extends the SimRunner concept for TCP-aware scenarios. Alongside message delivery it:
4//!     - Delivers TcpEvents to nodes that implement TcpEventHandler
5//!     - Handles connection state transitions visible to nodes
6//!     - Drains node outboxes back onto the TCP bus (rejected if disconnected)
7
8// region: Imports
9
10use crate::{
11    clock::{Duration, Instant},
12    io::NodeAddress,
13    node::SimNodeErased,
14    tcp_bus::{TcpEvent, TcpSimBus},
15};
16
17// endregion: Imports
18
19// region: TcpEventHandler
20
21/// Optional trait for nodes that need to observe TCP connection events.
22///
23/// Nodes on TCP bus may implement this to react to connection establishent, reset, and closure.
24/// The `TcpSimRunner` calls this after deliverying messages on each tick.
25///
26/// Not all nodes need TCP event awareness - only the `DoipTester` and gateway face nodes need it.
27/// Nodes that do not implement this trait simply ignore connection events.
28pub trait TcpEventHandler {
29    fn on_tcp_event(&mut self, event: &TcpEvent, now: Instant);
30}
31
32// endregion: TcpEventHandler
33
34// region: TcpSimRunner
35
36/// Drives `SimNodeErased` slices over a `TcpSimBus`.
37///
38/// `N` - max message payload bytes
39/// `Q` - max messages in-flight on the bus
40pub struct TcpSimRunner<const N: usize, const Q: usize> {
41    bus: TcpSimBus<N, Q>,
42}
43
44impl<const N: usize, const Q: usize> TcpSimRunner<N, Q> {
45    pub fn new(bus: TcpSimBus<N, Q>) -> Self {
46        Self { bus }
47    }
48
49    pub fn bus(&mut self) -> &mut TcpSimBus<N, Q> {
50        &mut self.bus
51    }
52
53    pub fn now(&self) -> Instant {
54        self.bus.now()
55    }
56
57    /// Ticks the simulation by `duration`.
58    ///
59    /// Order of operations per tick:
60    ///     1. Advance bus clock, apply TCP fault injection, deliver due messages
61    ///     2. Deliver TCP connection events to nodes implementing `TcpEventHandler`
62    ///     3. Tick all nodes for time-based transitions
63    ///     4. Drain node outboxes and enqueue onto the bus
64    ///
65    /// Returns the number of messages delivered.
66    pub fn tick(
67        &mut self,
68        nodes: &mut [&mut dyn SimNodeErased<N, Q>],
69        tcp_event_nodes: &mut [&mut dyn TcpEventHandler],
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 tcp_events: heapless::Vec<TcpEvent, 16> = self.bus.drain_events().collect();
86        for event in &tcp_events {
87            for handler in tcp_event_nodes.iter_mut() {
88                handler.on_tcp_event(event, now);
89            }
90        }
91
92        for node in nodes.iter_mut() {
93            node.tick(now);
94        }
95
96        let mut outbox: heapless::Vec<(NodeAddress, heapless::Vec<u8, N>), Q> =
97            heapless::Vec::new();
98        for node in nodes.iter_mut() {
99            outbox.clear();
100            node.drain_outbox(&mut outbox);
101
102            let src = node.address().clone();
103
104            for (dst, data) in outbox.iter() {
105                self.bus.send(src.clone(), dst.clone(), data);
106            }
107        }
108
109        count
110    }
111}
112
113// endregion: TcpSimRunner