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}