apsis-rs/crates/sim/apsis-sim/src/session.rs

192 lines
6.4 KiB
Rust

//! Running a prepared simulation.
//!
//! 运行已准备的仿真。
use crate::activity::Activity;
use crate::simulation::Simulation;
use apsis_output::{RecordKind, Recorder, Trace};
use apsis_sim_engine::{Engine, EngineOutcome};
/// What a run produced.
///
/// The report pairs the engine's own outcome with the output trace, so a
/// caller has everything a run produced without reaching back into the
/// session.
///
/// 一次运行所产生的结果。
///
/// 该报告把引擎自身的结果与输出轨迹配对,使调用方无需回访会话即可得到
/// 一次运行所产生的全部内容。
#[derive(Debug, Clone, PartialEq)]
pub struct RunReport {
/// What the engine did.
///
/// 引擎所做的。
pub outcome: EngineOutcome,
/// The records the run made.
///
/// 该运行所作出的记录。
pub trace: Trace,
/// The number of records the recorder refused because they were not
/// usable.
///
/// 记录器因不可用而拒绝的记录数。
pub refused: u64,
}
impl RunReport {
/// Returns the number of activities the run executed.
///
/// 返回该运行所执行的活动数。
pub const fn executed(&self) -> u64 {
self.outcome.executed
}
/// Returns `true` when the run finished because the timeline was
/// exhausted rather than because a limit stopped it.
///
/// 当运行因时间线耗尽而结束、而非因某个限制而停止时返回 `true`。
pub fn is_complete(&self) -> bool {
self.outcome.is_natural()
}
/// Returns the number of warnings recorded.
///
/// 返回所记录的警告数。
pub fn warning_count(&self) -> usize {
self.trace.warning_count()
}
}
/// One run of a prepared simulation.
///
/// A session owns the engine and the recorder for a single run, so it can be
/// driven from one place without the caller holding either. It is created by
/// [`Simulation::start`].
///
/// 一次已准备仿真的运行。
///
/// 会话为单次运行拥有引擎与记录器,故它可在一处被驱动而无需调用方持有
/// 二者中的任何一个。它由 [`Simulation::start`] 创建。
#[derive(Debug, Clone)]
pub struct Session {
engine: Engine<Activity>,
recorder: Recorder,
}
impl Session {
/// Builds a session from a prepared simulation.
///
/// 由已准备的仿真构建会话。
pub(crate) fn new(simulation: &Simulation) -> Self {
let scenario = simulation.scenario();
let mut engine = Engine::new()
.with_time_limit(scenario.time_limit().unwrap_or(f64::INFINITY))
.with_event_limit(scenario.event_limit().unwrap_or(0));
engine.set_now(scenario.epoch());
// The timeline is relative to the scenario origin, so the epoch is
// added to every event time. Without the offset the engine would
// move the clock backwards when it delivered the first event.
// 时间线相对于场景原点,故历元被加到每个事件时间上。若无该偏移,
// 引擎在投递第一个事件时会把时钟往回移。
let epoch = scenario.epoch();
for (time, activity) in simulation.timeline() {
engine.schedule(epoch + time, *activity);
}
Self {
engine,
recorder: Recorder::new(),
}
}
/// Returns the engine, for a caller that needs its state between calls.
///
/// 返回引擎,供需要在两次调用之间获取其状态的调用方使用。
pub const fn engine(&self) -> &Engine<Activity> {
&self.engine
}
/// Returns the engine for modification.
///
/// 返回引擎以供修改。
pub const fn engine_mut(&mut self) -> &mut Engine<Activity> {
&mut self.engine
}
/// Returns the recorder, for a caller that reads the trace as it grows.
///
/// 返回记录器,供在轨迹增长时读取它的调用方使用。
pub const fn recorder(&self) -> &Recorder {
&self.recorder
}
/// Returns the recorder for modification.
///
/// 返回记录器以供修改。
pub const fn recorder_mut(&mut self) -> &mut Recorder {
&mut self.recorder
}
/// Returns the current simulation time, in seconds.
///
/// 返回当前仿真时刻,单位为秒。
pub const fn now(&self) -> f64 {
self.engine.now()
}
/// Runs the engine, calling the handler at each activity.
///
/// The handler receives the engine, the recorder, and the activity. It
/// normally records something for the activity and may schedule further
/// events; the engine delivers them in time order alongside the timeline.
///
/// Returns a report holding the engine outcome and the trace made during
/// this call. The recorder is left empty for the next call.
///
/// 运行引擎,在每个活动处调用处理器。
///
/// 处理器接收引擎、记录器与活动。它通常为该活动记录些什么,并可
/// 再调度更多事件;引擎把它们与时间线一起按时间次序投递。
///
/// 返回一个报告,其中含有引擎结果与本次调用期间所作出的轨迹。记录器
/// 被留空以备下次调用。
pub fn run<F>(&mut self, mut handler: F) -> RunReport
where
F: FnMut(&mut Engine<Activity>, &mut Recorder, apsis_event::Event<Activity>),
{
let outcome = self.engine.run(|engine, event| {
handler(engine, &mut self.recorder, event);
});
RunReport {
outcome,
trace: self.recorder.take_trace(),
refused: self.recorder.refused(),
}
}
/// Records an activity as a state change without running anything.
///
/// This is for a caller that drives the engine itself and only wants the
/// recorder.
///
/// 把某个活动记录为状态变更而不运行任何东西。
///
/// 这适用于自行驱动引擎而只想要记录器的调用方。
pub fn note(&mut self, time: f64, label: impl Into<String>, value: f64) -> bool {
self.recorder.note(time, label, value)
}
/// Records a value of a given kind without running anything.
///
/// 记录给定类别的一个值而不运行任何东西。
pub fn record(
&mut self,
time: f64,
kind: RecordKind,
label: impl Into<String>,
value: f64,
) -> bool {
self.recorder.record(time, kind, label, value)
}
}