diff --git a/macros/src/component.rs b/macros/src/component.rs index ae246cd..89102a9 100644 --- a/macros/src/component.rs +++ b/macros/src/component.rs @@ -181,7 +181,7 @@ impl Component { } } } - unsafe impl xdevs::aux::Component for #ident { + unsafe impl xdevs::traits::Component for #ident { type Input = #input_ident; type Output = #output_ident; #[inline] diff --git a/macros/src/component/atomic.rs b/macros/src/component/atomic.rs index 4b242ab..61d7f57 100644 --- a/macros/src/component/atomic.rs +++ b/macros/src/component/atomic.rs @@ -37,18 +37,18 @@ impl State { }; quote! { - unsafe impl xdevs::aux::PartialAtomic for #atomic_ident { + unsafe impl xdevs::traits::PartialAtomic for #atomic_ident { type State = #state_ty; } - unsafe impl xdevs::aux::AbstractSimulator for #atomic_ident { + unsafe impl xdevs::traits::AbstractSimulator for #atomic_ident { #[inline] fn start(&mut self, t_start: f64) -> f64 { // set t_last to t_start - xdevs::aux::Component::set_t_last(self, t_start); + xdevs::traits::Component::set_t_last(self, t_start); // start state and get t_next from ta ::start(&mut self.state); let t_next = t_start + ::ta(&self.state); - xdevs::aux::Component::set_t_next(self, t_next); + xdevs::traits::Component::set_t_next(self, t_next); t_next } @@ -57,30 +57,30 @@ impl State { // stop state ::stop(&mut self.state); // set t_last to t_stop and t_next to infinity - xdevs::aux::Component::set_t_last(self, t_stop); - xdevs::aux::Component::set_t_next(self, f64::INFINITY); + xdevs::traits::Component::set_t_last(self, t_stop); + xdevs::traits::Component::set_t_next(self, f64::INFINITY); } #[inline] fn lambda(&mut self, t: f64) { - if t >= xdevs::aux::Component::get_t_next(self) { + if t >= xdevs::traits::Component::get_t_next(self) { // execute atomic model's lambda if applies ::lambda(&self.state, &mut self.output); } } #[inline] fn delta(&mut self, t: f64) -> f64 { - let mut t_next = xdevs::aux::Component::get_t_next(self); - if !xdevs::aux::Bag::is_empty(&self.input) { + let mut t_next = xdevs::traits::Component::get_t_next(self); + if !xdevs::traits::Bag::is_empty(&self.input) { if t >= t_next { // confluent transition ::delta_conf(&mut self.state, &self.input); } else { // external transition - let e = t - xdevs::aux::Component::get_t_last(self); + let e = t - xdevs::traits::Component::get_t_last(self); ::delta_ext(&mut self.state, e, &self.input); } // clear input events - xdevs::aux::Component::clear_input(self); + xdevs::traits::Component::clear_input(self); } else if t >= t_next { // internal transition ::delta_int(&mut self.state); @@ -88,11 +88,11 @@ impl State { return t_next; // nothing to do } // clear output events - xdevs::aux::Component::clear_output(self); + xdevs::traits::Component::clear_output(self); // get t_next from ta and set new t_last and t_next t_next = t + ::ta(&self.state); - xdevs::aux::Component::set_t_last(self, t); - xdevs::aux::Component::set_t_next(self, t_next); + xdevs::traits::Component::set_t_last(self, t); + xdevs::traits::Component::set_t_next(self, t_next); t_next } diff --git a/macros/src/component/coupled.rs b/macros/src/component/coupled.rs index b6e88a1..f692cfd 100644 --- a/macros/src/component/coupled.rs +++ b/macros/src/component/coupled.rs @@ -24,16 +24,16 @@ impl Coupled { }; quote! { - unsafe impl xdevs::aux::AbstractSimulator for #coupled_ident { + unsafe impl xdevs::traits::AbstractSimulator for #coupled_ident { #[inline] fn start(&mut self, t_start: f64) -> f64 { // set t_last to t_start - xdevs::aux::Component::set_t_last(self, t_start); + xdevs::traits::Component::set_t_last(self, t_start); // get minimum t_next from all components let mut t_next = f64::INFINITY; - #(t_next = f64::min(t_next, xdevs::aux::AbstractSimulator::start(&mut self.#component, t_start));)* + #(t_next = f64::min(t_next, xdevs::traits::AbstractSimulator::start(&mut self.#component, t_start));)* // set t_next to minimum t_next - xdevs::aux::Component::set_t_next(self, t_next); + xdevs::traits::Component::set_t_next(self, t_next); t_next } @@ -41,17 +41,17 @@ impl Coupled { #[inline] fn stop(&mut self, t_stop: f64) { // stop all components - #(xdevs::aux::AbstractSimulator::stop(&mut self.#component, t_stop);)* + #(xdevs::traits::AbstractSimulator::stop(&mut self.#component, t_stop);)* // set t_last to t_stop and t_next to infinity - xdevs::aux::Component::set_t_last(self, t_stop); - xdevs::aux::Component::set_t_next(self, f64::INFINITY); + xdevs::traits::Component::set_t_last(self, t_stop); + xdevs::traits::Component::set_t_next(self, f64::INFINITY); } #[inline] fn lambda(&mut self, t: f64) { - if t >= xdevs::aux::Component::get_t_next(self) { + if t >= xdevs::traits::Component::get_t_next(self) { // propagate lambda to all components - #(xdevs::aux::AbstractSimulator::lambda(&mut self.#component, t);)* + #(xdevs::traits::AbstractSimulator::lambda(&mut self.#component, t);)* // propagate EOCs #(#eoc);* } @@ -63,13 +63,13 @@ impl Coupled { #(#xic);* // get minimum t_next from all components after executing their delta let mut t_next = f64::INFINITY; - #(t_next = f64::min(t_next, xdevs::aux::AbstractSimulator::delta(&mut self.#component, t));)* + #(t_next = f64::min(t_next, xdevs::traits::AbstractSimulator::delta(&mut self.#component, t));)* // clear input and output events - xdevs::aux::Component::clear_output(self); - xdevs::aux::Component::clear_input(self); + xdevs::traits::Component::clear_output(self); + xdevs::traits::Component::clear_input(self); // set t_last to t and t_next to minimum t_next - xdevs::aux::Component::set_t_last(self, t); - xdevs::aux::Component::set_t_next(self, t_next); + xdevs::traits::Component::set_t_last(self, t); + xdevs::traits::Component::set_t_next(self, t_next); t_next } diff --git a/macros/src/component/port.rs b/macros/src/component/port.rs index ebcda36..37387b2 100644 --- a/macros/src/component/port.rs +++ b/macros/src/component/port.rs @@ -76,7 +76,7 @@ impl Ports { Self { #(#ports_ident: xdevs::port::Port::new()),* } } } - unsafe impl xdevs::aux::Bag for #ident { + unsafe impl xdevs::traits::Bag for #ident { #[inline] fn is_empty(&self) -> bool { true #( && self.#ports_ident.is_empty() )* diff --git a/src/lib.rs b/src/lib.rs index 7fca0b9..a3fc95a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,12 @@ pub use xdevs_no_std_macros::*; -pub mod aux; +pub mod traits; pub mod port; pub mod simulator; /// Interface for DEVS atomic models. All DEVS atomic models must implement this trait. -pub trait Atomic: aux::PartialAtomic { +pub trait Atomic: traits::PartialAtomic { /// Method for performing any operation before simulating. By default, it does nothing. #[allow(unused_variables)] #[inline] diff --git a/src/port.rs b/src/port.rs index 6c2c633..a62d2a6 100644 --- a/src/port.rs +++ b/src/port.rs @@ -55,7 +55,7 @@ impl Port { } } -unsafe impl crate::aux::Bag for Port { +unsafe impl crate::traits::Bag for Port { fn is_empty(&self) -> bool { self.is_empty() } diff --git a/src/simulator.rs b/src/simulator.rs index 18c7abd..ae13adb 100644 --- a/src/simulator.rs +++ b/src/simulator.rs @@ -1,4 +1,4 @@ -use crate::aux::{AbstractSimulator, Bag}; +use crate::traits::{AbstractSimulator, Bag}; #[cfg(feature = "std")] pub mod std; diff --git a/src/simulator/std.rs b/src/simulator/std.rs index 83b2491..42a4424 100644 --- a/src/simulator/std.rs +++ b/src/simulator/std.rs @@ -3,29 +3,82 @@ use std::time::{Duration, SystemTime}; /// Closure for RT simulation on targets with `std`. /// It sleeps until the next state transition. -pub fn sleep( +pub fn sleep( t_start: f64, time_scale: f64, max_jitter: Option, +) -> impl FnMut(f64, &mut T) -> f64 { + wait_event(t_start, time_scale, max_jitter, |waiting_period, _| { + std::thread::sleep(waiting_period) + }) +} + +/// It computes the next wall-clock time corresponding to the next state transition of the model. +/// +/// An input handler function waits for external events without exceeding the time for the next internal event. +/// Finally, it checks that the wall-clock drift does not exceed the maximum jitter allowed (if any) and panics if it does. +/// +/// # Arguments +/// +/// * `t_start` - The virtual time at the beginning of the simulation. +/// * `time_scale` - The time scale factor between virtual and wall-clock time. +/// * `max_jitter` - The maximum allowed jitter duration. If `None`, no jitter check is performed. +/// * `input_handler` - The function to handle incoming external events. This function expects two arguments: +/// - `duration: [Duration]` - Maximum duration of the time interval to wait for external events. +/// The input handler function may return earlier if an input event is received. +/// Note, however, that it must **NOT** return after, as it would result in an incorrect real-time implementation. +/// - `input_ports: &mut T` - Mutable reference to the input ports of the top-most model under simulation. +/// +/// # Returns +/// +/// A closure that takes the next virtual time and a mutable reference to the bag and returns the next virtual time. +/// +/// # Example +/// +/// ```ignore +/// xdevs::simulator::std::wait_event(0., 1., Some(Duration::from_millis(50)), some_input_handler); +/// ``` + +pub fn wait_event( + t_start: f64, + time_scale: f64, + max_jitter: Option, + mut input_handler: impl FnMut(Duration, &mut T), ) -> impl FnMut(f64, &mut T) -> f64 { let mut last_vt = t_start; let mut last_rt = SystemTime::now(); + let start_rt = last_rt; + + move |t_next, binput: &mut T| -> f64 { + assert!(t_next >= last_vt); - move |t_next, _| -> f64 { let next_rt = last_rt + Duration::from_secs_f64((t_next - last_vt) * time_scale); - match next_rt.duration_since(SystemTime::now()) { - Ok(duration) => std::thread::sleep(duration), - Err(err) => { + + if let Ok(duration) = next_rt.duration_since(SystemTime::now()) { + input_handler(duration, binput); + } + + let t = SystemTime::now(); + + last_vt = match t.duration_since(next_rt) { + Ok(duration) => { + // t >= next_rt, check for the jitter if let Some(max_jitter) = max_jitter { - if err.duration() > max_jitter { - panic!("Jitter too high"); + if duration > max_jitter { + panic!("[WE]>> Jitter too high: {:?}", duration); } } + last_rt = next_rt; + t_next } - } - last_vt = t_next; - last_rt = next_rt; + Err(_) => { + // t < next_rt + last_rt = t; + let duration = last_rt.duration_since(start_rt).unwrap(); + duration.as_secs_f64() / time_scale + } + }; - t_next + last_vt } } diff --git a/src/aux.rs b/src/traits.rs similarity index 100% rename from src/aux.rs rename to src/traits.rs