Skip to main content

acex_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};
16use acex_core::Vec;
17
18// endregion: Imports
19
20// region: TcpEventHandler
21
22/// Optional trait for nodes that need to observe TCP connection events.
23///
24/// Nodes on TCP bus may implement this to react to connection establishment, reset, and closure.
25/// The `TcpSimRunner` calls this after delivering messages on each tick.
26///
27/// Not all nodes need TCP event awareness - only the `DoipTester` and gateway face nodes need it.
28/// Nodes that do not implement this trait simply ignore connection events.
29pub trait TcpEventHandler {
30    fn on_tcp_event(&mut self, event: &TcpEvent, now: Instant);
31}
32
33// endregion: TcpEventHandler
34
35// region: TcpSimRunner
36
37/// Drives `SimNodeErased` slices over a `TcpSimBus`.
38///
39/// `N` - max message payload bytes
40/// `Q` - max messages in-flight on the bus
41#[cfg_attr(all(feature = "defmt", not(feature = "alloc")), derive(defmt::Format))]
42pub struct TcpSimRunner<
43    const MAX_DATA: usize,
44    const MAX_QUEUED: usize,
45    const TCP_MAX_EVENTS: usize,
46    const MAX_OUTBOX: usize,
47> {
48    bus: TcpSimBus<MAX_DATA, MAX_QUEUED, TCP_MAX_EVENTS>,
49}
50
51impl<
52        const MAX_DATA: usize,
53        const MAX_QUEUED: usize,
54        const TCP_MAX_EVENTS: usize,
55        const MAX_OUTBOX: usize,
56    > TcpSimRunner<MAX_DATA, MAX_QUEUED, TCP_MAX_EVENTS, MAX_OUTBOX>
57{
58    pub fn new(bus: TcpSimBus<MAX_DATA, MAX_QUEUED, TCP_MAX_EVENTS>) -> Self {
59        Self { bus }
60    }
61
62    pub fn bus(&mut self) -> &mut TcpSimBus<MAX_DATA, MAX_QUEUED, TCP_MAX_EVENTS> {
63        &mut self.bus
64    }
65
66    pub fn now(&self) -> Instant {
67        self.bus.now()
68    }
69
70    /// Ticks the simulation by `duration`.
71    ///
72    /// Order of operations per tick:
73    ///     1. Advance bus clock, apply TCP fault injection, deliver due messages
74    ///     2. Deliver TCP connection events to nodes implementing `TcpEventHandler`
75    ///     3. Tick all nodes for time-based transitions
76    ///     4. Drain node outboxes and enqueue onto the bus
77    ///
78    /// Returns the number of messages delivered.
79    pub fn tick(
80        &mut self,
81        nodes: &mut [&mut dyn SimNodeErased<MAX_DATA, MAX_OUTBOX>],
82        tcp_event_nodes: &mut [&mut dyn TcpEventHandler],
83        duration: Duration,
84    ) -> usize {
85        let delivered = self.bus.tick(duration);
86        let now = self.bus.now();
87        let mut count = 0;
88
89        for envelope in &delivered {
90            for node in nodes.iter_mut() {
91                if *node.address() == envelope.dst {
92                    node.handle(&envelope.src, &envelope.data, now);
93                    count += 1;
94                }
95            }
96        }
97
98        let tcp_events: Vec<TcpEvent, TCP_MAX_EVENTS> = self.bus.drain_events().collect();
99        for event in &tcp_events {
100            for handler in tcp_event_nodes.iter_mut() {
101                handler.on_tcp_event(event, now);
102            }
103        }
104
105        for node in nodes.iter_mut() {
106            node.tick(now);
107        }
108
109        let mut outbox: Vec<(NodeAddress, Vec<u8, MAX_DATA>), MAX_OUTBOX> = Vec::new();
110        for node in nodes.iter_mut() {
111            outbox.clear();
112            node.drain_outbox(&mut outbox);
113
114            let src = node.address().clone();
115
116            for (dst, data) in outbox.iter() {
117                self.bus.send(src.clone(), dst.clone(), data);
118            }
119        }
120
121        count
122    }
123}
124
125// endregion: TcpSimRunner