xdevs/simulator/std.rs
1extern crate std;
2
3use crate::{
4 simulator::Config,
5 traits::{AsyncInput, Bag},
6};
7use std::{
8 thread,
9 time::{Duration, Instant, SystemTime},
10};
11
12/// Closure for RT simulation on targets with `std`.
13/// It sleeps until the next state transition.
14pub fn sleep<T: Bag>(config: &Config) -> impl FnMut(f64, f64, &mut T) -> f64 {
15 wait_event(config, |waiting_period, _| thread::sleep(waiting_period))
16}
17
18/// It computes the next wall-clock time corresponding to the next state transition of the model.
19///
20/// An input handler function waits for external events without exceeding the time for the next internal event.
21/// Finally, it checks that the wall-clock drift does not exceed the maximum jitter allowed (if any) and panics if it does.
22///
23/// # Arguments
24///
25/// * `config` - The desired simulator configuration.
26/// * `input_handler` - The function to handle incoming external events. This function expects two arguments:
27/// - `duration: [Duration]` - Maximum duration of the time interval to wait for external events.
28/// The input handler function may return earlier if an input event is received.
29/// Note, however, that it must **NOT** return after, as it would result in an incorrect real-time implementation.
30/// - `input_ports: &mut T` - Mutable reference to the input ports of the top-most model under simulation.
31///
32/// # Returns
33///
34/// A closure that takes the current and next virtual time and a mutable reference to the bag and returns the next virtual time.
35///
36/// # Example
37///
38/// ```ignore
39/// xdevs::simulator::std::wait_event(0., 1., Some(Duration::from_millis(50)), some_input_handler);
40/// ```
41pub fn wait_event<T: Bag>(
42 config: &Config,
43 mut input_handler: impl FnMut(Duration, &mut T),
44) -> impl FnMut(f64, f64, &mut T) -> f64 {
45 let (time_scale, max_jitter) = (config.time_scale, config.max_jitter);
46 let mut last_rt = SystemTime::now();
47 let start_rt = last_rt;
48
49 move |t_from, t_until, binput: &mut T| -> f64 {
50 let next_rt = last_rt + Duration::from_secs_f64((t_until - t_from) * time_scale);
51
52 if let Ok(duration) = next_rt.duration_since(SystemTime::now()) {
53 input_handler(duration, binput);
54 }
55
56 let t = SystemTime::now();
57
58 match t.duration_since(next_rt) {
59 Ok(duration) => {
60 // t >= next_rt, check for the jitter
61 if let Some(max_jitter) = max_jitter {
62 if duration > max_jitter {
63 panic!("[WE]>> Jitter too high: {:?}", duration);
64 }
65 }
66 last_rt = next_rt;
67 t_until
68 }
69 Err(_) => {
70 // t < next_rt
71 last_rt = t;
72 let duration = last_rt.duration_since(start_rt).unwrap();
73 duration.as_secs_f64() / time_scale
74 }
75 }
76 }
77}
78
79/// A simple asynchronous input handler that sleeps until the next state transition of the model.
80#[derive(Default)]
81pub struct SleepAsync<T: Bag> {
82 /// The last recorded real time instant.
83 last_rt: Option<Instant>,
84 /// Phantom data to associate with the input bag type.
85 input: core::marker::PhantomData<T>,
86}
87
88impl<T: Bag> SleepAsync<T> {
89 /// Creates a new `SleepAsync` instance.
90 pub fn new() -> Self {
91 Self {
92 last_rt: None,
93 input: core::marker::PhantomData,
94 }
95 }
96}
97
98impl<T: Bag> AsyncInput for SleepAsync<T> {
99 type Input = T;
100
101 async fn handle(
102 &mut self,
103 config: &Config,
104 t_from: f64,
105 t_until: f64,
106 _input: &mut Self::Input,
107 ) -> f64 {
108 let last_rt = self.last_rt.unwrap_or_else(Instant::now);
109 let next_rt = last_rt + Duration::from_secs_f64((t_until - t_from) * config.time_scale);
110 tokio::time::sleep_until(next_rt.into()).await;
111 self.last_rt = Some(next_rt);
112 t_until
113 }
114}