xdevs/simulator/
std.rs

1extern crate std;
2use std::time::{Duration, SystemTime};
3
4/// Closure for RT simulation on targets with `std`.
5/// It sleeps until the next state transition.
6pub fn sleep<T: crate::traits::Bag>(
7    t_start: f64,
8    time_scale: f64,
9    max_jitter: Option<std::time::Duration>,
10) -> impl FnMut(f64, &mut T) -> f64 {
11    wait_event(t_start, time_scale, max_jitter, |waiting_period, _| {
12        std::thread::sleep(waiting_period)
13    })
14}
15
16/// It computes the next wall-clock time corresponding to the next state transition of the model.
17///
18/// An input handler function waits for external events without exceeding the time for the next internal event.
19/// Finally, it checks that the wall-clock drift does not exceed the maximum jitter allowed (if any) and panics if it does.
20///
21///  # Arguments
22///
23///  * `t_start` - The virtual time at the beginning of the simulation.
24///  * `time_scale` - The time scale factor between virtual and wall-clock time.
25///  * `max_jitter` - The maximum allowed jitter duration. If `None`, no jitter check is performed.
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 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/// ```
41
42pub fn wait_event<T: crate::traits::Bag>(
43    t_start: f64,
44    time_scale: f64,
45    max_jitter: Option<Duration>,
46    mut input_handler: impl FnMut(Duration, &mut T),
47) -> impl FnMut(f64, &mut T) -> f64 {
48    let mut last_vt = t_start;
49    let mut last_rt = SystemTime::now();
50    let start_rt = last_rt;
51
52    move |t_next, binput: &mut T| -> f64 {
53        assert!(t_next >= last_vt);
54
55        let next_rt = last_rt + Duration::from_secs_f64((t_next - last_vt) * time_scale);
56
57        if let Ok(duration) = next_rt.duration_since(SystemTime::now()) {
58            input_handler(duration, binput);
59        }
60
61        let t = SystemTime::now();
62
63        last_vt = match t.duration_since(next_rt) {
64            Ok(duration) => {
65                // t >= next_rt, check for the jitter
66                if let Some(max_jitter) = max_jitter {
67                    if duration > max_jitter {
68                        panic!("[WE]>> Jitter too high: {:?}", duration);
69                    }
70                }
71                last_rt = next_rt;
72                t_next
73            }
74            Err(_) => {
75                // t < next_rt
76                last_rt = t;
77                let duration = last_rt.duration_since(start_rt).unwrap();
78                duration.as_secs_f64() / time_scale
79            }
80        };
81
82        last_vt
83    }
84}