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}