forked from ROMEO/nexosim
Merge pull request #61 from asynchronics/feature/unified_step_until
Merge step_by and step_until into a unique method
This commit is contained in:
commit
1cefe4b2f6
@ -230,7 +230,7 @@ fn main() -> Result<(), SimulationError> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// All data processed.
|
// All data processed.
|
||||||
simu.step_by(Duration::from_millis(101))?;
|
simu.step_until(Duration::from_millis(101))?;
|
||||||
expect(
|
expect(
|
||||||
&mut mode,
|
&mut mode,
|
||||||
Some(ModeId::Idle),
|
Some(ModeId::Idle),
|
||||||
@ -266,7 +266,7 @@ fn main() -> Result<(), SimulationError> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Wait for short processing to finish, check results.
|
// Wait for short processing to finish, check results.
|
||||||
simu.step_by(Duration::from_millis(11))?;
|
simu.step_until(Duration::from_millis(11))?;
|
||||||
expect(
|
expect(
|
||||||
&mut mode,
|
&mut mode,
|
||||||
Some(ModeId::Idle),
|
Some(ModeId::Idle),
|
||||||
@ -279,7 +279,7 @@ fn main() -> Result<(), SimulationError> {
|
|||||||
|
|
||||||
// Wait long enough, no state change as the long processing has been
|
// Wait long enough, no state change as the long processing has been
|
||||||
// cancelled.
|
// cancelled.
|
||||||
simu.step_by(Duration::from_millis(100))?;
|
simu.step_until(Duration::from_millis(100))?;
|
||||||
assert_eq!(mode.next(), None);
|
assert_eq!(mode.next(), None);
|
||||||
assert_eq!(value.next(), None);
|
assert_eq!(value.next(), None);
|
||||||
assert_eq!(hk.next(), None);
|
assert_eq!(hk.next(), None);
|
||||||
|
@ -175,7 +175,7 @@ fn main() -> Result<(), SimulationError> {
|
|||||||
|
|
||||||
// Advance simulation time by 0.9s, which with a 10Hz PPS should correspond to
|
// Advance simulation time by 0.9s, which with a 10Hz PPS should correspond to
|
||||||
// 9 position increments.
|
// 9 position increments.
|
||||||
simu.step_by(Duration::new(0, 900_000_000))?;
|
simu.step_until(Duration::new(0, 900_000_000))?;
|
||||||
t += Duration::new(0, 900_000_000);
|
t += Duration::new(0, 900_000_000);
|
||||||
assert_eq!(simu.time(), t);
|
assert_eq!(simu.time(), t);
|
||||||
for _ in 0..9 {
|
for _ in 0..9 {
|
||||||
|
@ -236,7 +236,7 @@ fn main() -> Result<(), SimulationError> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Advance simulation, external messages will be collected.
|
// Advance simulation, external messages will be collected.
|
||||||
simu.step_by(Duration::from_secs(2))?;
|
simu.step_until(Duration::from_secs(2))?;
|
||||||
|
|
||||||
// Shut down the server.
|
// Shut down the server.
|
||||||
let socket = sender_handle.join().unwrap();
|
let socket = sender_handle.join().unwrap();
|
||||||
|
@ -251,7 +251,7 @@ fn main() -> Result<(), asynchronix::simulation::SimulationError> {
|
|||||||
|
|
||||||
// Advance simulation time by 0.9s, which with a 10Hz PPS should correspond to
|
// Advance simulation time by 0.9s, which with a 10Hz PPS should correspond to
|
||||||
// 9 position increments.
|
// 9 position increments.
|
||||||
simu.step_by(Duration::new(0, 900_000_000))?;
|
simu.step_until(Duration::new(0, 900_000_000))?;
|
||||||
t += Duration::new(0, 900_000_000);
|
t += Duration::new(0, 900_000_000);
|
||||||
assert_eq!(simu.time(), t);
|
assert_eq!(simu.time(), t);
|
||||||
for _ in 0..9 {
|
for _ in 0..9 {
|
||||||
@ -290,7 +290,7 @@ fn main() -> Result<(), asynchronix::simulation::SimulationError> {
|
|||||||
|
|
||||||
// Advance simulation time by 0.7s, which with a 10Hz PPS should correspond to
|
// Advance simulation time by 0.7s, which with a 10Hz PPS should correspond to
|
||||||
// 7 position increments.
|
// 7 position increments.
|
||||||
simu.step_by(Duration::new(0, 700_000_000))?;
|
simu.step_until(Duration::new(0, 700_000_000))?;
|
||||||
t += Duration::new(0, 700_000_000);
|
t += Duration::new(0, 700_000_000);
|
||||||
assert_eq!(simu.time(), t);
|
assert_eq!(simu.time(), t);
|
||||||
for _ in 0..7 {
|
for _ in 0..7 {
|
||||||
@ -310,7 +310,7 @@ fn main() -> Result<(), asynchronix::simulation::SimulationError> {
|
|||||||
|
|
||||||
// Advance simulation time by 1.9s, which with a -10Hz PPS should correspond
|
// Advance simulation time by 1.9s, which with a -10Hz PPS should correspond
|
||||||
// to 19 position decrements.
|
// to 19 position decrements.
|
||||||
simu.step_by(Duration::new(1, 900_000_000))?;
|
simu.step_until(Duration::new(1, 900_000_000))?;
|
||||||
t += Duration::new(1, 900_000_000);
|
t += Duration::new(1, 900_000_000);
|
||||||
assert_eq!(simu.time(), t);
|
assert_eq!(simu.time(), t);
|
||||||
pos = (pos + Motor::STEPS_PER_REV - 19) % Motor::STEPS_PER_REV;
|
pos = (pos + Motor::STEPS_PER_REV - 19) % Motor::STEPS_PER_REV;
|
||||||
|
@ -118,7 +118,9 @@ impl ControllerService {
|
|||||||
"the specified deadline lies in the past",
|
"the specified deadline lies in the past",
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
simulation.step_by(duration).map_err(map_execution_error)?;
|
simulation
|
||||||
|
.step_until(duration)
|
||||||
|
.map_err(map_execution_error)?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -246,8 +246,8 @@
|
|||||||
//!
|
//!
|
||||||
//! 1. by advancing time, either until the next scheduled event with
|
//! 1. by advancing time, either until the next scheduled event with
|
||||||
//! [`Simulation::step()`](simulation::Simulation::step), or until a specific
|
//! [`Simulation::step()`](simulation::Simulation::step), or until a specific
|
||||||
//! deadline using for instance
|
//! deadline with
|
||||||
//! [`Simulation::step_by()`](simulation::Simulation::step_by).
|
//! [`Simulation::step_until()`](simulation::Simulation::step_until).
|
||||||
//! 2. by sending events or queries without advancing simulation time, using
|
//! 2. by sending events or queries without advancing simulation time, using
|
||||||
//! [`Simulation::process_event()`](simulation::Simulation::process_event) or
|
//! [`Simulation::process_event()`](simulation::Simulation::process_event) or
|
||||||
//! [`Simulation::send_query()`](simulation::Simulation::process_query),
|
//! [`Simulation::send_query()`](simulation::Simulation::process_query),
|
||||||
|
@ -122,9 +122,7 @@ mod scheduler;
|
|||||||
mod sim_init;
|
mod sim_init;
|
||||||
|
|
||||||
pub use mailbox::{Address, Mailbox};
|
pub use mailbox::{Address, Mailbox};
|
||||||
pub use scheduler::{
|
pub use scheduler::{Action, ActionKey, AutoActionKey, LocalScheduler, Scheduler, SchedulingError};
|
||||||
Action, ActionKey, AutoActionKey, Deadline, LocalScheduler, Scheduler, SchedulingError,
|
|
||||||
};
|
|
||||||
pub(crate) use scheduler::{
|
pub(crate) use scheduler::{
|
||||||
KeyedOnceAction, KeyedPeriodicAction, OnceAction, PeriodicAction, SchedulerQueue,
|
KeyedOnceAction, KeyedPeriodicAction, OnceAction, PeriodicAction, SchedulerQueue,
|
||||||
};
|
};
|
||||||
@ -148,7 +146,7 @@ use crate::channel::ChannelObserver;
|
|||||||
use crate::executor::{Executor, ExecutorError, Signal};
|
use crate::executor::{Executor, ExecutorError, Signal};
|
||||||
use crate::model::{BuildContext, Context, Model, ProtoModel};
|
use crate::model::{BuildContext, Context, Model, ProtoModel};
|
||||||
use crate::ports::{InputFn, ReplierFn};
|
use crate::ports::{InputFn, ReplierFn};
|
||||||
use crate::time::{AtomicTime, Clock, MonotonicTime, SyncStatus};
|
use crate::time::{AtomicTime, Clock, Deadline, MonotonicTime, SyncStatus};
|
||||||
use crate::util::seq_futures::SeqFuture;
|
use crate::util::seq_futures::SeqFuture;
|
||||||
use crate::util::slot;
|
use crate::util::slot;
|
||||||
|
|
||||||
@ -189,9 +187,8 @@ thread_local! { pub(crate) static CURRENT_MODEL_ID: Cell<ModelId> = const { Cell
|
|||||||
/// desired wall clock time, and finally
|
/// desired wall clock time, and finally
|
||||||
/// 3. run all computations scheduled for the new simulation time.
|
/// 3. run all computations scheduled for the new simulation time.
|
||||||
///
|
///
|
||||||
/// The [`step_by()`](Simulation::step_by) and
|
/// The [`step_until()`](Simulation::step_until) method operates similarly but
|
||||||
/// [`step_until()`](Simulation::step_until) methods operate similarly but
|
/// iterates until the target simulation time has been reached.
|
||||||
/// iterate until the target simulation time has been reached.
|
|
||||||
pub struct Simulation {
|
pub struct Simulation {
|
||||||
executor: Executor,
|
executor: Executor,
|
||||||
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
|
scheduler_queue: Arc<Mutex<SchedulerQueue>>,
|
||||||
@ -260,19 +257,6 @@ impl Simulation {
|
|||||||
self.step_to_next_bounded(MonotonicTime::MAX).map(|_| ())
|
self.step_to_next_bounded(MonotonicTime::MAX).map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iteratively advances the simulation time by the specified duration, as
|
|
||||||
/// if by calling [`Simulation::step()`] repeatedly.
|
|
||||||
///
|
|
||||||
/// This method blocks until all events scheduled up to the specified target
|
|
||||||
/// time have completed. The simulation time upon completion is equal to the
|
|
||||||
/// initial simulation time incremented by the specified duration, whether
|
|
||||||
/// or not an event was scheduled for that time.
|
|
||||||
pub fn step_by(&mut self, duration: Duration) -> Result<(), ExecutionError> {
|
|
||||||
let target_time = self.time.read() + duration;
|
|
||||||
|
|
||||||
self.step_until_unchecked(target_time)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iteratively advances the simulation time until the specified deadline,
|
/// Iteratively advances the simulation time until the specified deadline,
|
||||||
/// as if by calling [`Simulation::step()`] repeatedly.
|
/// as if by calling [`Simulation::step()`] repeatedly.
|
||||||
///
|
///
|
||||||
@ -280,8 +264,10 @@ impl Simulation {
|
|||||||
/// time have completed. The simulation time upon completion is equal to the
|
/// time have completed. The simulation time upon completion is equal to the
|
||||||
/// specified target time, whether or not an event was scheduled for that
|
/// specified target time, whether or not an event was scheduled for that
|
||||||
/// time.
|
/// time.
|
||||||
pub fn step_until(&mut self, target_time: MonotonicTime) -> Result<(), ExecutionError> {
|
pub fn step_until(&mut self, deadline: impl Deadline) -> Result<(), ExecutionError> {
|
||||||
if self.time.read() >= target_time {
|
let now = self.time.read();
|
||||||
|
let target_time = deadline.into_time(now);
|
||||||
|
if target_time < now {
|
||||||
return Err(ExecutionError::InvalidDeadline(target_time));
|
return Err(ExecutionError::InvalidDeadline(target_time));
|
||||||
}
|
}
|
||||||
self.step_until_unchecked(target_time)
|
self.step_until_unchecked(target_time)
|
||||||
|
@ -18,7 +18,7 @@ use crate::executor::Executor;
|
|||||||
use crate::model::Model;
|
use crate::model::Model;
|
||||||
use crate::ports::InputFn;
|
use crate::ports::InputFn;
|
||||||
use crate::simulation::Address;
|
use crate::simulation::Address;
|
||||||
use crate::time::{AtomicTimeReader, MonotonicTime};
|
use crate::time::{AtomicTimeReader, Deadline, MonotonicTime};
|
||||||
use crate::util::priority_queue::PriorityQueue;
|
use crate::util::priority_queue::PriorityQueue;
|
||||||
|
|
||||||
/// Scheduler.
|
/// Scheduler.
|
||||||
@ -557,30 +557,6 @@ impl<M: Model> fmt::Debug for LocalScheduler<M> {
|
|||||||
// control their relative order of execution.
|
// control their relative order of execution.
|
||||||
pub(crate) type SchedulerQueue = PriorityQueue<(MonotonicTime, usize), Action>;
|
pub(crate) type SchedulerQueue = PriorityQueue<(MonotonicTime, usize), Action>;
|
||||||
|
|
||||||
/// Trait abstracting over time-absolute and time-relative deadlines.
|
|
||||||
///
|
|
||||||
/// This trait is implemented by [`std::time::Duration`] and
|
|
||||||
/// [`MonotonicTime`].
|
|
||||||
pub trait Deadline {
|
|
||||||
/// Make this deadline into an absolute timestamp, using the provided
|
|
||||||
/// current time as a reference.
|
|
||||||
fn into_time(self, now: MonotonicTime) -> MonotonicTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deadline for Duration {
|
|
||||||
#[inline(always)]
|
|
||||||
fn into_time(self, now: MonotonicTime) -> MonotonicTime {
|
|
||||||
now + self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deadline for MonotonicTime {
|
|
||||||
#[inline(always)]
|
|
||||||
fn into_time(self, _: MonotonicTime) -> MonotonicTime {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Managed handle to a scheduled action.
|
/// Managed handle to a scheduled action.
|
||||||
///
|
///
|
||||||
/// An `AutoActionKey` is a managed handle to a scheduled action that cancels
|
/// An `AutoActionKey` is a managed handle to a scheduled action that cancels
|
||||||
|
@ -55,3 +55,27 @@ pub(crate) use monotonic_time::TearableAtomicTime;
|
|||||||
|
|
||||||
pub(crate) type AtomicTime = crate::util::sync_cell::SyncCell<TearableAtomicTime>;
|
pub(crate) type AtomicTime = crate::util::sync_cell::SyncCell<TearableAtomicTime>;
|
||||||
pub(crate) type AtomicTimeReader = crate::util::sync_cell::SyncCellReader<TearableAtomicTime>;
|
pub(crate) type AtomicTimeReader = crate::util::sync_cell::SyncCellReader<TearableAtomicTime>;
|
||||||
|
|
||||||
|
/// Trait abstracting over time-absolute and time-relative deadlines.
|
||||||
|
///
|
||||||
|
/// This trait is implemented by [`std::time::Duration`] and
|
||||||
|
/// [`MonotonicTime`].
|
||||||
|
pub trait Deadline {
|
||||||
|
/// Make this deadline into an absolute timestamp, using the provided
|
||||||
|
/// current time as a reference.
|
||||||
|
fn into_time(self, now: MonotonicTime) -> MonotonicTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deadline for std::time::Duration {
|
||||||
|
#[inline(always)]
|
||||||
|
fn into_time(self, now: MonotonicTime) -> MonotonicTime {
|
||||||
|
now + self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deadline for MonotonicTime {
|
||||||
|
#[inline(always)]
|
||||||
|
fn into_time(self, _: MonotonicTime) -> MonotonicTime {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,7 +23,7 @@ impl Model for TestModel {}
|
|||||||
// synchronization tolerance.
|
// synchronization tolerance.
|
||||||
//
|
//
|
||||||
// Returns the last simulation tick at completion or when the error occurred, and
|
// Returns the last simulation tick at completion or when the error occurred, and
|
||||||
// the result of `Simulation::step_by`.
|
// the result of `Simulation::step_until`.
|
||||||
fn clock_sync(
|
fn clock_sync(
|
||||||
num_threads: usize,
|
num_threads: usize,
|
||||||
block_time_ms: u64,
|
block_time_ms: u64,
|
||||||
@ -58,7 +58,7 @@ fn clock_sync(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = simu.step_by(delta);
|
let res = simu.step_until(delta);
|
||||||
let last_tick = simu.time().duration_since(t0);
|
let last_tick = simu.time().duration_since(t0);
|
||||||
|
|
||||||
(last_tick, res)
|
(last_tick, res)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user