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

90 lines
3.7 KiB
Rust

//! Simulation facade that integrates the simulation layer.
//!
//! This crate is the single entry point to the simulation layer. It ties the
//! four pieces together: a [`Scenario`] describes the initial conditions, the
//! ops layer plans it, the engine runs the resulting timeline, and a
//! [`Recorder`] collects what the run produced.
//!
//! The sequence a caller follows is short:
//!
//! 1. [`Simulation::prepare`] checks a scenario and plans it, producing a
//! schedule.
//! 2. [`Simulation::start`] turns that schedule into a timeline of start and
//! end events and hands back a [`Session`].
//! 3. [`Session::run`] drives the engine, calling a handler at each event,
//! and returns a [`RunReport`] holding the engine outcome and the trace.
//!
//! The plan is a greedy heuristic, described in `apsis-scheduler`, so a run
//! may leave tasks unplaced. The facade reports that rather than hiding it:
//! [`Simulation::plan`] exposes the full planning result, and
//! [`Simulation::has_full_plan`] answers the common question directly.
//!
//! The timeline is the schedule, not a physical model. A start event fires
//! when a task begins and an end event when it ends, which is what a caller
//! needs to record state over time; the handler supplies whatever physics or
//! bookkeeping the run requires.
//!
//! ```
//! use apsis_sim::{Activity, Scenario, Simulation};
//!
//! let text = "\
//! scenario demo
//! resource 0 capacity 1
//! window 0 0 600
//! task 0 duration 60
//! require 0 0 1
//! ";
//! let scenario = Scenario::parse(text).expect("the text is well formed");
//! let simulation = Simulation::prepare(scenario).expect("the scenario is usable");
//!
//! let mut session = simulation.start();
//! let mut starts = 0_u32;
//! let report = session.run(|_engine, _recorder, event| {
//! if matches!(event.payload, Activity::Start { .. }) {
//! starts += 1;
//! }
//! });
//!
//! assert!(simulation.has_full_plan());
//! assert_eq!(starts, 1);
//! assert_eq!(report.outcome.executed, 2);
//! ```
//!
//! 整合仿真层的仿真门面。
//!
//! 本 crate 是仿真层的单一入口。它把四部分联系起来:[`Scenario`] 描述初始条件,
//! ops 层为其作计划,引擎运行所得时间线,[`Recorder`] 收集该运行所产生的。
//!
//! 调用方所遵循的序列很短:
//!
//! 1. [`Simulation::prepare`] 检查场景并为其作计划,产生一个调度。
//! 2. [`Simulation::start`] 把该调度转为由开始与结束事件构成的时间线,并交回一个
//! [`Session`]。
//! 3. [`Session::run`] 驱动引擎,在每个事件处调用一个处理器,并返回一个
//! [`RunReport`],其中含有引擎结果与轨迹。
//!
//! 该计划是贪心启发式,在 `apsis-scheduler` 中有述,故一次运行可能留下未放置的任务。
//! 门面报告这一点而非隐藏它:[`Simulation::plan`] 公开完整规划结果,而
//! [`Simulation::has_full_plan`] 直接回答那个常见的问题。
//!
//! 时间线是调度而非物理模型。任务开始时触发一个开始事件,结束时触发一个结束事件,
//! 这正是调用方随时间记录状态所需的;处理器提供该运行所需的任何物理或簿记。
#![forbid(unsafe_code)]
#![deny(missing_docs)]
mod activity;
mod error;
mod session;
mod simulation;
pub use activity::Activity;
pub use error::{SimulationError, SimulationErrorKind};
pub use session::{RunReport, Session};
pub use simulation::Simulation;
pub use apsis_event::{Event, EventId, EventQueue};
pub use apsis_output::{Record, RecordKind, Recorder, SeriesSummary, Trace};
pub use apsis_scenario::{Scenario, ScenarioError, ScenarioErrorKind, ScenarioSummary};
pub use apsis_sim_engine::{Engine, EngineOutcome, EngineState, StopReason};