1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
use crate::simulation::commands::SimulationCommand;
use crate::simulation::Simulation;
use std::time::{Duration, Instant};
/// A struct to execute a simulation with a specific frame rate and runtime.
#[derive(Debug)]
pub struct SimulationExecutor {
fps: u32,
run_time: Duration,
elapsed_time: Duration,
/// if true, the simulation executor will sleep the thread until fps is reached
pub sleep_enabled: bool,
}
impl SimulationExecutor {
/// Creates a new `SimulationExecutor` instance.
///
/// # Arguments
///
/// * `fps` - The desired frame rate for the simulation execution.
/// * `run_time_secs` - The total runtime of the simulation in seconds.
///
/// # Returns
///
/// A new `SimulationExecutor` instance.
pub fn new(fps: u32, run_time_secs: u64) -> Self {
Self {
fps,
run_time: Duration::from_secs(run_time_secs),
elapsed_time: Duration::from_secs(0),
sleep_enabled: false,
}
}
/// Executes the simulation for the specified runtime with the specified frame rate.
///
/// # Arguments
///
/// * `simulation` - A mutable reference to the `Simulation` instance to be executed.
pub fn execute(&mut self, simulation: &mut Simulation) {
let start_time = Instant::now();
let frame_duration = Duration::from_secs_f64(1.0 / self.fps as f64);
while self.elapsed_time < self.run_time {
self.update_simulation_frame(simulation, frame_duration);
self.elapsed_time = start_time.elapsed();
}
}
/// Updates the simulation for a single frame.
///
/// # Arguments
///
/// * `simulation` - A mutable reference to the `Simulation` instance to be updated.
/// * `frame_duration` - The duration of the frame to update.
fn update_simulation_frame(&mut self, simulation: &mut Simulation, frame_duration: Duration) {
let frame_start_time = Instant::now();
if self.sleep_enabled {
simulation.update(frame_duration);
let frame_elapsed = frame_start_time.elapsed();
if frame_duration > frame_elapsed {
std::thread::sleep(frame_duration - frame_elapsed);
}
} else {
simulation.update(frame_duration);
self.elapsed_time += frame_duration;
}
}
/// Processes a command for the given simulation.
///
/// # Arguments
///
/// * `simulation` - A mutable reference to the `Simulation` instance.
/// * `command` - A reference to a command that implements the `Command` trait.
///
/// # Returns
///
/// * An optional `String` containing a message or any other relevant information about the changes made.
pub fn process_command(
&self,
simulation: &mut Simulation,
command: &dyn SimulationCommand,
) -> Option<String> {
command.execute(simulation)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{simulation::Simulation, tests::test_graph_1};
use approx::assert_relative_eq;
#[test]
fn test_simulation_executor() {
let fps = 60;
let run_time_secs = 5;
let graph = test_graph_1();
let mut simulation: Simulation = Simulation::new(graph);
let mut simulation_executor = SimulationExecutor::new(fps, run_time_secs);
simulation_executor.execute(&mut simulation);
assert_relative_eq!(
simulation_executor.elapsed_time.as_secs_f64(),
Duration::from_secs(run_time_secs).as_secs_f64(),
epsilon = 1.0 / fps as f64
);
}
}