apsis-rs/crates/sim/apsis-output/src/recorder.rs

160 lines
4.8 KiB
Rust

//! Building a trace as a simulation runs.
//!
//! 在仿真运行过程中构建轨迹。
use crate::record::{Record, RecordKind};
use crate::trace::Trace;
/// Collects records as a simulation runs.
///
/// The recorder is the write side of a trace: a handler calls one of its
/// methods for each value it wants to keep, and asks for the trace when the
/// run finishes. The two are separate so that a handler can hold a recorder
/// while the trace is read elsewhere.
///
/// ```
/// use apsis_output::Recorder;
///
/// let mut recorder = Recorder::new();
/// recorder.observe(0.0, "range", 1_000.0);
/// recorder.observe(10.0, "range", 950.0);
///
/// let summary = recorder.trace().summarize("range").expect("the series exists");
/// assert_eq!(summary.count, 2);
/// assert_eq!(summary.change, -50.0);
/// ```
///
/// 在仿真运行过程中收集记录。
///
/// 记录器是轨迹的写入侧:处理器为其想保留的每个值调用它的某个方法,
/// 并在运行结束时索取轨迹。二者分开,使处理器可以持有记录器而轨迹
/// 在别处被读取。
///
/// ```
/// use apsis_output::Recorder;
///
/// let mut recorder = Recorder::new();
/// recorder.observe(0.0, "range", 1_000.0);
/// recorder.observe(10.0, "range", 950.0);
///
/// let summary = recorder.trace().summarize("range").expect("the series exists");
/// assert_eq!(summary.count, 2);
/// assert_eq!(summary.change, -50.0);
/// ```
#[derive(Debug, Clone, Default)]
pub struct Recorder {
trace: Trace,
refused: u64,
}
impl Recorder {
/// Creates an empty recorder.
///
/// 创建一个空记录器。
pub const fn new() -> Self {
Self {
trace: Trace::new(),
refused: 0,
}
}
/// Records a value of a given kind.
///
/// Returns `false` when the record is not usable, which a caller can
/// ignore when the value is known to be finite.
///
/// 记录给定类别的一个值。
///
/// 该记录不可用时返回 `false`;当值已知为有限时调用方可忽略。
pub fn record(
&mut self,
time: f64,
kind: RecordKind,
label: impl Into<String>,
value: f64,
) -> bool {
let accepted = self.trace.push(Record::new(time, kind, label, value));
if !accepted {
self.refused = self.refused.wrapping_add(1);
}
accepted
}
/// Records an observation.
///
/// 记录一次观测。
pub fn observe(&mut self, time: f64, label: impl Into<String>, value: f64) -> bool {
self.record(time, RecordKind::Observation, label, value)
}
/// Records a state change.
///
/// 记录一次状态变更。
pub fn note(&mut self, time: f64, label: impl Into<String>, value: f64) -> bool {
self.record(time, RecordKind::State, label, value)
}
/// Records a warning.
///
/// 记录一次警告。
pub fn warn(&mut self, time: f64, label: impl Into<String>, value: f64) -> bool {
self.record(time, RecordKind::Warning, label, value)
}
/// Returns the number of records made.
///
/// 返回已作出的记录数。
pub fn len(&self) -> usize {
self.trace.len()
}
/// Returns `true` when no records have been made.
///
/// 当尚无记录时返回 `true`。
pub fn is_empty(&self) -> bool {
self.trace.is_empty()
}
/// Returns the number of records refused because they were not usable.
///
/// A caller that records values from a model checks this to see whether
/// any value was dropped, rather than comparing counts by hand.
///
/// 返回因不可用而被拒绝的记录数。
///
/// 从模型记录值的调用方检查它即可看出是否有值被丢弃,而无需手工
/// 比较计数。
pub const fn refused(&self) -> u64 {
self.refused
}
/// Returns the trace recorded so far.
///
/// 返回迄今所记录的轨迹。
pub const fn trace(&self) -> &Trace {
&self.trace
}
/// Returns the trace, leaving an empty one behind.
///
/// This is what a caller does between runs: the finished trace goes to
/// whoever reads it, and the recorder starts fresh. The refused count is
/// not reset, because it records the whole life of the recorder.
///
/// 返回轨迹并留下一个空轨迹。
///
/// 这是调用方在两次运行之间所做的:已完成的轨迹交给读取它的一方,
/// 记录器重新开始。被拒绝的计数不重置,因为它记录记录器的整个
/// 生命期。
pub fn take_trace(&mut self) -> Trace {
core::mem::take(&mut self.trace)
}
/// Removes every record without changing the refused count.
///
/// 移除所有记录而不改变被拒绝的计数。
pub fn clear(&mut self) -> usize {
self.trace.clear()
}
}