use crate::{SimTime, Simulation};
use tracing::{debug, info, instrument, trace, warn};
pub trait Execute {
fn execute(self, sim: &mut Simulation);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum EndCondition {
Time(SimTime),
NoEvents,
Steps(usize),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Executor {
end_condition: EndCondition,
}
impl Executor {
#[must_use]
pub fn unbound() -> Self {
Self {
end_condition: EndCondition::NoEvents,
}
}
#[must_use]
pub fn timed(time: SimTime) -> Self {
Self {
end_condition: EndCondition::Time(time),
}
}
#[must_use]
pub fn steps(steps: usize) -> Self {
Self {
end_condition: EndCondition::Steps(steps),
}
}
#[must_use]
pub fn side_effect<F>(self, func: F) -> ExecutorWithSideEffect<F>
where
F: Fn(&Simulation),
{
ExecutorWithSideEffect {
end_condition: self.end_condition,
side_effect: func,
}
}
}
impl Execute for Executor {
#[instrument(skip(sim), fields(end_condition = ?self.end_condition))]
fn execute(self, sim: &mut Simulation) {
info!("Starting simulation execution");
run_with(sim, self.end_condition, |_| {});
info!(
final_time = ?sim.time(),
"Simulation execution completed"
);
}
}
pub struct ExecutorWithSideEffect<F>
where
F: Fn(&Simulation),
{
end_condition: EndCondition,
side_effect: F,
}
impl<F> Execute for ExecutorWithSideEffect<F>
where
F: Fn(&Simulation),
{
#[instrument(skip(sim, self), fields(end_condition = ?self.end_condition))]
fn execute(self, sim: &mut Simulation) {
info!("Starting simulation execution with side effects");
run_with(sim, self.end_condition, self.side_effect);
info!(
final_time = ?sim.time(),
"Simulation execution with side effects completed"
);
}
}
fn run_with<F>(sim: &mut Simulation, end_condition: EndCondition, side_effect: F)
where
F: Fn(&Simulation),
{
debug!(?end_condition, "Starting simulation run");
let start_time = sim.time();
let step_fn = |sim: &mut Simulation| {
let result = sim.step();
if result {
side_effect(sim);
}
result
};
match end_condition {
EndCondition::Time(time) => {
debug!(?time, "Executing until time limit");
execute_until(sim, time, step_fn)
}
EndCondition::NoEvents => {
debug!("Executing until no events remain");
execute_until_empty(sim, step_fn)
}
EndCondition::Steps(steps) => {
debug!(steps, "Executing for fixed number of steps");
execute_steps(sim, steps, step_fn)
}
}
let final_time = sim.time();
let time_elapsed = final_time - start_time;
debug!(
start_time = ?start_time,
final_time = ?final_time,
time_elapsed = ?time_elapsed,
"Simulation run completed"
);
}
fn execute_until_empty<F>(sim: &mut Simulation, step: F)
where
F: Fn(&mut Simulation) -> bool,
{
let mut steps = 0;
while step(sim) {
steps += 1;
if steps % 10000 == 0 {
trace!(
steps,
current_time = ?sim.time(),
"Execution progress"
);
}
}
debug!(steps, "Executed until no events remained");
}
fn execute_until<F>(sim: &mut Simulation, time: SimTime, step: F)
where
F: Fn(&mut Simulation) -> bool,
{
let mut steps = 0;
while sim.peek_next_event_time().is_some_and(|t| t <= time) {
step(sim);
steps += 1;
if steps % 10000 == 0 {
trace!(
steps,
current_time = ?sim.time(),
time_limit = ?time,
"Execution progress"
);
}
}
debug!(steps, final_time = ?sim.time(), "Executed until time limit");
}
fn execute_steps<F>(sim: &mut Simulation, steps: usize, step: F)
where
F: Fn(&mut Simulation) -> bool,
{
let mut executed = 0;
for i in 0..steps {
if !step(sim) {
debug!(
requested_steps = steps,
executed_steps = executed,
"Execution stopped early - no more events"
);
break;
}
executed += 1;
if i % 10000 == 0 && i > 0 {
trace!(
executed_steps = executed,
total_steps = steps,
current_time = ?sim.time(),
"Execution progress"
);
}
}
debug!(
requested_steps = steps,
executed_steps = executed,
final_time = ?sim.time(),
"Step-limited execution completed"
);
}
#[cfg(test)]
mod test {
use super::*;
use crate::Component;
use std::time::Duration;
struct TestComponent {
counter: usize,
}
#[derive(Debug)]
struct TestEvent;
impl Component for TestComponent {
type Event = TestEvent;
fn process_event(
&mut self,
self_id: crate::Key<Self::Event>,
_event: &Self::Event,
scheduler: &mut crate::Scheduler,
) {
self.counter += 1;
if self.counter < 10 {
scheduler.schedule(
SimTime::from_duration(Duration::from_secs(2)),
self_id,
TestEvent,
);
}
}
}
#[test]
fn test_create_executor() {
assert_eq!(
Executor::unbound(),
Executor {
end_condition: EndCondition::NoEvents
}
);
assert_eq!(
Executor::timed(SimTime::from_duration(Duration::default())),
Executor {
end_condition: EndCondition::Time(SimTime::from_duration(Duration::default()))
}
);
assert_eq!(
Executor::steps(7),
Executor {
end_condition: EndCondition::Steps(7)
}
);
assert_eq!(&format!("{TestEvent:?}"), "TestEvent");
}
#[test]
fn test_steps() {
let mut sim = Simulation::default();
let component = sim.add_component(TestComponent { counter: 0 });
sim.schedule(
SimTime::from_duration(Duration::default()),
component,
TestEvent,
);
Executor::steps(10).execute(&mut sim);
let c: TestComponent = sim.remove_component(component).unwrap();
assert_eq!(c.counter, 10);
}
#[test]
fn test_steps_stops_before() {
let mut sim = Simulation::default();
let component = sim.add_component(TestComponent { counter: 0 });
sim.schedule(
SimTime::from_duration(Duration::default()),
component,
TestEvent,
);
Executor::steps(100).execute(&mut sim);
let c: TestComponent = sim.remove_component(component).unwrap();
assert_eq!(c.counter, 10);
}
#[test]
fn test_timed() {
let mut sim = Simulation::default();
let component = sim.add_component(TestComponent { counter: 0 });
sim.schedule(
SimTime::from_duration(Duration::default()),
component,
TestEvent,
);
Executor::timed(SimTime::from_duration(Duration::from_secs(6))).execute(&mut sim);
let c: TestComponent = sim.remove_component(component).unwrap();
assert_eq!(c.counter, 4);
assert_eq!(
sim.clock().time(),
SimTime::from_duration(Duration::from_secs(6))
);
}
#[test]
fn test_timed_clock_stops_early() {
let mut sim = Simulation::default();
let component = sim.add_component(TestComponent { counter: 0 });
sim.schedule(
SimTime::from_duration(Duration::default()),
component,
TestEvent,
);
Executor::timed(SimTime::from_duration(Duration::from_secs(5))).execute(&mut sim);
let c: TestComponent = sim.remove_component(component).unwrap();
assert_eq!(c.counter, 3);
assert_eq!(
sim.clock().time(),
SimTime::from_duration(Duration::from_secs(4))
);
}
}