Skip to main content

Simulation

Struct Simulation 

Source
pub struct Simulation { /* private fields */ }
Expand description

The core simulation state, advanced by calling step().

Implementations§

Source§

impl Simulation

Source

pub fn new( config: &SimConfig, dispatch: impl DispatchStrategy + 'static, ) -> Result<Self, SimError>

Create a new simulation from config and a dispatch strategy.

Returns Err if the config is invalid (zero stops, duplicate IDs, negative speeds, etc.).

§Errors

Returns SimError::InvalidConfig if the configuration has zero stops, duplicate stop IDs, zero elevators, non-positive physics parameters, invalid starting stops, or non-positive tick rate.

Source

pub fn set_dispatch( &mut self, group: GroupId, strategy: Box<dyn DispatchStrategy>, id: BuiltinStrategy, )

Replace the dispatch strategy for a group.

The id parameter identifies the strategy for snapshot serialization. Use BuiltinStrategy::Custom("name") for custom strategies.

Source

pub fn set_reposition( &mut self, group: GroupId, strategy: Box<dyn RepositionStrategy>, id: BuiltinReposition, )

Set the reposition strategy for a group.

Enables the reposition phase for this group. Idle elevators will be repositioned according to the strategy after each dispatch phase.

Source

pub fn remove_reposition(&mut self, group: GroupId)

Remove the reposition strategy for a group, disabling repositioning.

Source

pub fn reposition_id(&self, group: GroupId) -> Option<&BuiltinReposition>

Get the reposition strategy identifier for a group.

Source

pub fn add_before_hook( &mut self, phase: Phase, hook: impl Fn(&mut World) + Send + Sync + 'static, )

Register a hook to run before a simulation phase.

Hooks are called in registration order. The hook receives mutable access to the world, allowing entity inspection or modification.

Source

pub fn add_after_hook( &mut self, phase: Phase, hook: impl Fn(&mut World) + Send + Sync + 'static, )

Register a hook to run after a simulation phase.

Hooks are called in registration order. The hook receives mutable access to the world, allowing entity inspection or modification.

Source

pub fn add_before_group_hook( &mut self, phase: Phase, group: GroupId, hook: impl Fn(&mut World) + Send + Sync + 'static, )

Register a hook to run before a phase for a specific group.

Source

pub fn add_after_group_hook( &mut self, phase: Phase, group: GroupId, hook: impl Fn(&mut World) + Send + Sync + 'static, )

Register a hook to run after a phase for a specific group.

Source§

impl Simulation

Source

pub fn load_extensions(&mut self)

Deserialize extension components from a snapshot.

Call this after restoring from a snapshot and registering all extension types via world.register_ext::<T>(name).

let mut sim = snapshot.restore(None);
sim.world_mut().register_ext::<VipTag>("vip_tag");
sim.load_extensions();
Source

pub fn reroute( &mut self, rider: EntityId, new_destination: EntityId, ) -> Result<(), SimError>

Change a rider’s destination mid-route.

Replaces remaining route legs with a single direct leg to new_destination, keeping the rider’s current stop as origin.

Returns Err if the rider does not exist or is not in Waiting phase (riding/boarding riders cannot be rerouted until they exit).

§Errors

Returns SimError::EntityNotFound if rider does not exist. Returns SimError::InvalidState if the rider is not in RiderPhase::Waiting or has no current stop.

Source

pub fn set_rider_route( &mut self, rider: EntityId, route: Route, ) -> Result<(), SimError>

Replace a rider’s entire remaining route.

§Errors

Returns SimError::EntityNotFound if rider does not exist.

Source

pub fn settle_rider(&mut self, id: EntityId) -> Result<(), SimError>

Transition an Arrived or Abandoned rider to Resident at their current stop.

Resident riders are parked — invisible to dispatch and loading, but queryable via residents_at(). They can later be given a new route via reroute_rider().

§Errors

Returns SimError::EntityNotFound if id does not exist. Returns SimError::InvalidState if the rider is not in Arrived or Abandoned phase, or has no current stop.

Source

pub fn reroute_rider( &mut self, id: EntityId, route: Route, ) -> Result<(), SimError>

Give a Resident rider a new route, transitioning them to Waiting.

The rider begins waiting at their current stop for an elevator matching the route’s transport mode. If the rider has a Patience component, its waited_ticks is reset to zero.

§Errors

Returns SimError::EntityNotFound if id does not exist. Returns SimError::InvalidState if the rider is not in Resident phase, the route has no legs, or the route’s first leg origin does not match the rider’s current stop.

Source

pub fn despawn_rider(&mut self, id: EntityId) -> Result<(), SimError>

Remove a rider from the simulation entirely.

Cleans up the population index, metric tags, and elevator cross-references (if the rider is currently aboard). Emits Event::RiderDespawned.

All rider removal should go through this method rather than calling world.despawn() directly, to keep the population index consistent.

§Errors

Returns SimError::EntityNotFound if id does not exist or is not a rider.

Source

pub fn set_rider_access( &mut self, rider: EntityId, allowed_stops: HashSet<EntityId>, ) -> Result<(), SimError>

Set the allowed stops for a rider.

When set, the rider will only be allowed to board elevators that can take them to a stop in the allowed set. See AccessControl for details.

§Errors

Returns SimError::EntityNotFound if the rider does not exist.

Source

pub fn set_elevator_restricted_stops( &mut self, elevator: EntityId, restricted_stops: HashSet<EntityId>, ) -> Result<(), SimError>

Set the restricted stops for an elevator.

Riders whose current destination is in this set will be rejected with RejectionReason::AccessDenied during the loading phase.

§Errors

Returns SimError::EntityNotFound if the elevator does not exist.

Source

pub fn residents_at( &self, stop: EntityId, ) -> impl Iterator<Item = EntityId> + '_

Iterate over resident rider IDs at a stop (O(1) lookup).

Source

pub fn resident_count_at(&self, stop: EntityId) -> usize

Count of residents at a stop (O(1)).

Source

pub fn waiting_at(&self, stop: EntityId) -> impl Iterator<Item = EntityId> + '_

Iterate over waiting rider IDs at a stop (O(1) lookup).

Source

pub fn waiting_count_at(&self, stop: EntityId) -> usize

Count of waiting riders at a stop (O(1)).

Source

pub fn abandoned_at( &self, stop: EntityId, ) -> impl Iterator<Item = EntityId> + '_

Iterate over abandoned rider IDs at a stop (O(1) lookup).

Source

pub fn abandoned_count_at(&self, stop: EntityId) -> usize

Count of abandoned riders at a stop (O(1)).

Source

pub fn riders_on(&self, elevator: EntityId) -> &[EntityId]

Get the rider entities currently aboard an elevator.

Returns an empty slice if the elevator does not exist.

Source

pub fn occupancy(&self, elevator: EntityId) -> usize

Get the number of riders aboard an elevator.

Returns 0 if the elevator does not exist.

Source

pub fn disable(&mut self, id: EntityId) -> Result<(), SimError>

Disable an entity. Disabled entities are skipped by all systems.

If the entity is an elevator in motion, it is reset to Idle with zero velocity to prevent stale target references on re-enable.

Note on residents: disabling a stop does not automatically handle Resident riders parked there. Callers should listen for Event::EntityDisabled and manually reroute or despawn any residents at the affected stop.

Emits EntityDisabled. Returns Err if the entity does not exist.

§Errors

Returns SimError::EntityNotFound if id does not refer to a living entity.

Source

pub fn enable(&mut self, id: EntityId) -> Result<(), SimError>

Re-enable a disabled entity.

Emits EntityEnabled. Returns Err if the entity does not exist.

§Errors

Returns SimError::EntityNotFound if id does not refer to a living entity.

Source

pub fn is_disabled(&self, id: EntityId) -> bool

Check if an entity is disabled.

Source

pub fn is_elevator(&self, id: EntityId) -> bool

Check if an entity is an elevator.

use elevator_core::prelude::*;

let sim = SimulationBuilder::demo().build().unwrap();
let stop = sim.stop_entity(StopId(0)).unwrap();
assert!(!sim.is_elevator(stop));
assert!(sim.is_stop(stop));
Source

pub fn is_rider(&self, id: EntityId) -> bool

Check if an entity is a rider.

Source

pub fn is_stop(&self, id: EntityId) -> bool

Check if an entity is a stop.

Source

pub fn idle_elevator_count(&self) -> usize

Count of elevators currently in the Idle phase.

Excludes disabled elevators (whose phase is reset to Idle on disable).

use elevator_core::prelude::*;

let sim = SimulationBuilder::demo().build().unwrap();
assert_eq!(sim.idle_elevator_count(), 1);
Source

pub fn elevator_load(&self, id: EntityId) -> Option<f64>

Current total weight aboard an elevator, or None if the entity is not an elevator.

use elevator_core::prelude::*;

let sim = SimulationBuilder::demo().build().unwrap();
let stop = sim.stop_entity(StopId(0)).unwrap();
assert_eq!(sim.elevator_load(stop), None); // not an elevator
Source

pub fn elevator_going_up(&self, id: EntityId) -> Option<bool>

Whether the elevator’s up-direction indicator lamp is lit.

Returns None if the entity is not an elevator. See Elevator::going_up for semantics.

Source

pub fn elevator_going_down(&self, id: EntityId) -> Option<bool>

Whether the elevator’s down-direction indicator lamp is lit.

Returns None if the entity is not an elevator. See Elevator::going_down for semantics.

Source

pub fn elevator_direction(&self, id: EntityId) -> Option<Direction>

Direction the elevator is currently signalling, derived from the indicator-lamp pair. Returns None if the entity is not an elevator.

Source

pub fn elevator_move_count(&self, id: EntityId) -> Option<u64>

Count of rounded-floor transitions for an elevator (passing-floor crossings plus arrivals). Returns None if the entity is not an elevator.

Source

pub fn braking_distance(&self, id: EntityId) -> Option<f64>

Distance the elevator would travel while braking to a stop from its current velocity, at its configured deceleration rate.

Uses the standard v² / (2·a) kinematic formula. A stationary elevator returns Some(0.0). Returns None if the entity is not an elevator or lacks a velocity component.

Useful for writing opportunistic dispatch strategies (e.g. “stop at this floor if we can brake in time”) without duplicating the physics computation.

Source

pub fn future_stop_position(&self, id: EntityId) -> Option<f64>

The position where the elevator would come to rest if it began braking this instant. Current position plus a signed braking distance in the direction of travel.

Returns None if the entity is not an elevator or lacks the required components.

Source

pub fn elevators_in_phase(&self, phase: ElevatorPhase) -> usize

Count of elevators currently in the given phase.

Excludes disabled elevators (whose phase is reset to Idle on disable).

use elevator_core::prelude::*;

let sim = SimulationBuilder::demo().build().unwrap();
assert_eq!(sim.elevators_in_phase(ElevatorPhase::Idle), 1);
assert_eq!(sim.elevators_in_phase(ElevatorPhase::Loading), 0);
Source

pub fn set_service_mode( &mut self, elevator: EntityId, mode: ServiceMode, ) -> Result<(), SimError>

Set the service mode for an elevator.

Emits Event::ServiceModeChanged if the mode actually changes.

§Errors

Returns SimError::EntityNotFound if the elevator does not exist.

Source

pub fn service_mode(&self, elevator: EntityId) -> ServiceMode

Get the current service mode for an elevator.

Source§

impl Simulation

Source

pub fn add_stop( &mut self, name: String, position: f64, line: EntityId, ) -> Result<EntityId, SimError>

Add a new stop to a group at runtime. Returns its EntityId.

Runtime-added stops have no StopId — they are identified purely by EntityId. The stop_lookup (config StopIdEntityId) is not updated.

§Errors

Returns SimError::LineNotFound if the line entity does not exist.

Source

pub fn add_elevator( &mut self, params: &ElevatorParams, line: EntityId, starting_position: f64, ) -> Result<EntityId, SimError>

Add a new elevator to a line at runtime. Returns its EntityId.

§Errors

Returns SimError::LineNotFound if the line entity does not exist.

Source

pub fn add_line(&mut self, params: &LineParams) -> Result<EntityId, SimError>

Add a new line to a group. Returns the line entity.

§Errors

Returns SimError::GroupNotFound if the specified group does not exist.

Source

pub fn remove_line(&mut self, line: EntityId) -> Result<(), SimError>

Remove a line and all its elevators from the simulation.

Elevators on the line are disabled (not despawned) so riders are properly ejected to the nearest stop.

§Errors

Returns SimError::LineNotFound if the line entity is not found in any group.

Source

pub fn remove_elevator(&mut self, elevator: EntityId) -> Result<(), SimError>

Remove an elevator from the simulation.

The elevator is disabled first (ejecting any riders), then removed from its line and despawned from the world.

§Errors

Returns SimError::EntityNotFound if the elevator does not exist.

Source

pub fn remove_stop(&mut self, stop: EntityId) -> Result<(), SimError>

Remove a stop from the simulation.

The stop is disabled first (invalidating routes that reference it), then removed from all lines and despawned from the world.

§Errors

Returns SimError::EntityNotFound if the stop does not exist.

Source

pub fn add_group( &mut self, name: impl Into<String>, dispatch: impl DispatchStrategy + 'static, ) -> GroupId

Create a new dispatch group. Returns the group ID.

Source

pub fn assign_line_to_group( &mut self, line: EntityId, new_group: GroupId, ) -> Result<GroupId, SimError>

Reassign a line to a different group. Returns the old GroupId.

§Errors

Returns SimError::LineNotFound if the line is not found in any group. Returns SimError::GroupNotFound if new_group does not exist.

Source

pub fn reassign_elevator_to_line( &mut self, elevator: EntityId, new_line: EntityId, ) -> Result<(), SimError>

Reassign an elevator to a different line (swing-car pattern).

The elevator is moved from its current line to the target line. Both lines must be in the same group, or you must reassign the line first via assign_line_to_group.

§Errors

Returns SimError::EntityNotFound if the elevator does not exist. Returns SimError::LineNotFound if the target line is not found in any group.

Source

pub fn add_stop_to_line( &mut self, stop: EntityId, line: EntityId, ) -> Result<(), SimError>

Add a stop to a line’s served stops.

§Errors

Returns SimError::EntityNotFound if the stop does not exist. Returns SimError::LineNotFound if the line is not found in any group.

Source

pub fn remove_stop_from_line( &mut self, stop: EntityId, line: EntityId, ) -> Result<(), SimError>

Remove a stop from a line’s served stops.

§Errors

Returns SimError::LineNotFound if the line is not found in any group.

Source

pub fn all_lines(&self) -> Vec<EntityId>

Get all line entities across all groups.

Source

pub fn line_count(&self) -> usize

Number of lines in the simulation.

Source

pub fn lines_in_group(&self, group: GroupId) -> Vec<EntityId>

Get all line entities in a group.

Source

pub fn elevators_on_line(&self, line: EntityId) -> Vec<EntityId>

Get elevator entities on a specific line.

Source

pub fn stops_served_by_line(&self, line: EntityId) -> Vec<EntityId>

Get stop entities served by a specific line.

Source

pub fn line_for_elevator(&self, elevator: EntityId) -> Option<EntityId>

Get the line entity for an elevator.

Source

pub fn iter_repositioning_elevators( &self, ) -> impl Iterator<Item = EntityId> + '_

Iterate over elevators currently repositioning.

Source

pub fn lines_serving_stop(&self, stop: EntityId) -> Vec<EntityId>

Get all line entities that serve a given stop.

Source

pub fn groups_serving_stop(&self, stop: EntityId) -> Vec<GroupId>

Get all group IDs that serve a given stop.

Source

pub fn reachable_stops_from(&self, stop: EntityId) -> Vec<EntityId>

All stops reachable from a given stop through the line/group topology.

Source

pub fn transfer_points(&self) -> Vec<EntityId>

Stops that serve as transfer points between groups.

Source

pub fn shortest_route(&self, from: EntityId, to: EntityId) -> Option<Route>

Find the shortest route between two stops, possibly spanning multiple groups.

Source§

impl Simulation

Source

pub const fn world(&self) -> &World

Get a shared reference to the world.

Source

pub const fn world_mut(&mut self) -> &mut World

Get a mutable reference to the world.

Exposed for advanced use cases (manual rider management, custom component attachment). Prefer spawn_rider / spawn_rider_by_stop_id for standard operations.

Source

pub const fn current_tick(&self) -> u64

Current simulation tick.

Source

pub const fn dt(&self) -> f64

Time delta per tick (seconds).

Source

pub fn position_at(&self, id: EntityId, alpha: f64) -> Option<f64>

Interpolated position between the previous and current tick.

alpha is clamped to [0.0, 1.0], where 0.0 returns the entity’s position at the start of the last completed tick and 1.0 returns the current position. Intended for smooth rendering when a render frame falls between simulation ticks.

Returns None if the entity has no position component. Returns the current position unchanged if no previous snapshot exists (i.e. before the first step).

Source

pub fn velocity(&self, id: EntityId) -> Option<f64>

Current velocity of an entity along the shaft axis (signed: +up, -down).

Convenience wrapper over World::velocity that returns the raw f64 value. Returns None if the entity has no velocity component.

Source

pub const fn metrics(&self) -> &Metrics

Get current simulation metrics.

Source

pub const fn time(&self) -> &TimeAdapter

The time adapter for tick↔wall-clock conversion.

Source

pub fn groups(&self) -> &[ElevatorGroup]

Get the elevator groups.

Source

pub fn stop_entity(&self, id: StopId) -> Option<EntityId>

Resolve a config StopId to its runtime EntityId.

Source

pub fn strategy_id(&self, group: GroupId) -> Option<&BuiltinStrategy>

Get the strategy identifier for a group.

Source

pub fn stop_lookup_iter(&self) -> impl Iterator<Item = (&StopId, &EntityId)>

Iterate over the stop ID → entity ID mapping.

Source

pub fn pending_events(&self) -> &[Event]

Peek at events pending for consumer retrieval.

Source

pub fn destination_queue(&self, elev: EntityId) -> Option<&[EntityId]>

Read-only view of an elevator’s destination queue (FIFO of target stop EntityIds).

Returns None if elev is not an elevator entity. Returns Some(&[]) for elevators with an empty queue.

Source

pub fn push_destination( &mut self, elev: EntityId, stop: EntityId, ) -> Result<(), SimError>

Push a stop onto the back of an elevator’s destination queue.

Adjacent duplicates are suppressed: if the last entry already equals stop, the queue is unchanged and no event is emitted. Otherwise emits Event::DestinationQueued.

§Errors
Source

pub fn push_destination_front( &mut self, elev: EntityId, stop: EntityId, ) -> Result<(), SimError>

Insert a stop at the front of an elevator’s destination queue — “go here next, before anything else in the queue”.

On the next AdvanceQueue phase (between Dispatch and Movement), the elevator redirects to this new front if it differs from the current target.

Adjacent duplicates are suppressed: if the first entry already equals stop, the queue is unchanged and no event is emitted.

§Errors
Source

pub fn clear_destinations(&mut self, elev: EntityId) -> Result<(), SimError>

Clear an elevator’s destination queue.

TODO: clearing does not currently abort an in-flight movement — the elevator will finish its current leg and then go idle (since the queue is empty). A future change can add a phase transition to cancel mid-flight.

§Errors

Returns SimError::InvalidState if elev is not an elevator.

Source

pub fn eta(&self, elev: EntityId, stop: EntityId) -> Option<Duration>

Estimated time until elev arrives at stop, summing closed-form trapezoidal travel time for every leg up to (and including) the leg that ends at stop, plus the door dwell at every intermediate stop.

“Arrival” is the moment the door cycle begins at stop — door time at stop itself is not added; door time at earlier stops along the route is.

Returns None if:

  • elev is not an elevator or stop is not a stop,
  • the elevator’s ServiceMode is dispatch-excluded (Manual / Independent), or
  • stop is neither the elevator’s current movement target nor anywhere in its destination_queue.

The estimate is best-effort. It assumes the queue is served in order with no mid-trip insertions; dispatch decisions, manual door commands, and rider boarding/exiting beyond the configured dwell will perturb the actual arrival.

Source

pub fn best_eta( &self, stop: EntityId, direction: Direction, ) -> Option<(EntityId, Duration)>

Best ETA to stop across all dispatch-eligible elevators, optionally filtered by indicator-lamp Direction.

Pass Direction::Either to consider every car. Otherwise, only cars whose committed direction is Either or matches the requested direction are considered — useful for hall-call assignment (“which up-going car arrives first?”).

Returns the entity ID of the winning elevator and its ETA, or None if no eligible car has stop queued.

Source

pub fn set_max_speed( &mut self, elevator: EntityId, speed: f64, ) -> Result<(), SimError>

Set the maximum travel speed for an elevator at runtime.

The new value applies on the next movement integration step; the car’s current velocity is preserved (see the runtime upgrades section of the crate docs). If the new cap is below the current velocity, the movement system clamps velocity down on the next tick.

§Errors
§Example
use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let elev = sim.world().iter_elevators().next().unwrap().0;
sim.set_max_speed(elev, 4.0).unwrap();
assert_eq!(sim.world().elevator(elev).unwrap().max_speed(), 4.0);
Source

pub fn set_acceleration( &mut self, elevator: EntityId, accel: f64, ) -> Result<(), SimError>

Set the acceleration rate for an elevator at runtime.

See set_max_speed for the general velocity-preservation rules that apply to kinematic setters.

§Errors
§Example
use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let elev = sim.world().iter_elevators().next().unwrap().0;
sim.set_acceleration(elev, 3.0).unwrap();
assert_eq!(sim.world().elevator(elev).unwrap().acceleration(), 3.0);
Source

pub fn set_deceleration( &mut self, elevator: EntityId, decel: f64, ) -> Result<(), SimError>

Set the deceleration rate for an elevator at runtime.

See set_max_speed for the general velocity-preservation rules that apply to kinematic setters.

§Errors
§Example
use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let elev = sim.world().iter_elevators().next().unwrap().0;
sim.set_deceleration(elev, 3.5).unwrap();
assert_eq!(sim.world().elevator(elev).unwrap().deceleration(), 3.5);
Source

pub fn set_weight_capacity( &mut self, elevator: EntityId, capacity: f64, ) -> Result<(), SimError>

Set the weight capacity for an elevator at runtime.

Applied immediately. If the new capacity is below the car’s current load the car is temporarily overweight; no riders are ejected, but subsequent boarding attempts that would push load further over the cap will be rejected as RejectionReason::OverCapacity.

§Errors
§Example
use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let elev = sim.world().iter_elevators().next().unwrap().0;
sim.set_weight_capacity(elev, 1200.0).unwrap();
assert_eq!(sim.world().elevator(elev).unwrap().weight_capacity(), 1200.0);
Source

pub fn set_door_transition_ticks( &mut self, elevator: EntityId, ticks: u32, ) -> Result<(), SimError>

Set the door open/close transition duration for an elevator.

Applied on the next door cycle — an in-progress transition keeps its original timing to avoid visual glitches.

§Errors
§Example
use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let elev = sim.world().iter_elevators().next().unwrap().0;
sim.set_door_transition_ticks(elev, 3).unwrap();
assert_eq!(sim.world().elevator(elev).unwrap().door_transition_ticks(), 3);
Source

pub fn set_door_open_ticks( &mut self, elevator: EntityId, ticks: u32, ) -> Result<(), SimError>

Set how long doors hold fully open for an elevator.

Applied on the next door cycle — a door that is currently holding open will complete its original dwell before the new value takes effect.

§Errors
§Example
use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let elev = sim.world().iter_elevators().next().unwrap().0;
sim.set_door_open_ticks(elev, 20).unwrap();
assert_eq!(sim.world().elevator(elev).unwrap().door_open_ticks(), 20);
Source

pub fn request_door_open(&mut self, elevator: EntityId) -> Result<(), SimError>

Request the doors to open.

Applied immediately if the car is stopped at a stop with closed or closing doors; otherwise queued until the car next arrives. A no-op if the doors are already open or opening.

§Errors
§Example
use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let elev = sim.world().iter_elevators().next().unwrap().0;
sim.request_door_open(elev).unwrap();
Source

pub fn request_door_close(&mut self, elevator: EntityId) -> Result<(), SimError>

Request the doors to close now.

Applied immediately if the doors are open or loading — forcing an early close — unless a rider is mid-boarding/exiting this car, in which case the close waits for the rider to finish. If doors are currently opening, the close queues and fires once fully open.

§Errors
§Example
use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let elev = sim.world().iter_elevators().next().unwrap().0;
sim.request_door_close(elev).unwrap();
Source

pub fn hold_door_open( &mut self, elevator: EntityId, ticks: u32, ) -> Result<(), SimError>

Extend the doors’ open dwell by ticks.

Cumulative — two calls of 30 ticks each extend the dwell by 60 ticks in total. If the doors aren’t open yet, the hold is queued and applied when they next reach the fully-open state.

§Errors
§Example
use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let elev = sim.world().iter_elevators().next().unwrap().0;
sim.hold_door_open(elev, 30).unwrap();
Source

pub fn cancel_door_hold(&mut self, elevator: EntityId) -> Result<(), SimError>

Cancel any pending hold extension.

If the base open timer has already elapsed the doors close on the next doors-phase tick.

§Errors
§Example
use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let elev = sim.world().iter_elevators().next().unwrap().0;
sim.hold_door_open(elev, 100).unwrap();
sim.cancel_door_hold(elev).unwrap();
Source

pub fn set_target_velocity( &mut self, elevator: EntityId, velocity: f64, ) -> Result<(), SimError>

Set the target velocity for a manual-mode elevator.

The velocity is clamped to the elevator’s [-max_speed, max_speed] range after validation. The car ramps toward the target each tick using acceleration (speeding up, or starting from rest) or deceleration (slowing down, or reversing direction). Positive values command upward travel, negative values command downward travel.

§Errors
  • Entity is not an elevator, or is disabled.
  • Elevator is not in ServiceMode::Manual.
  • velocity is not finite (NaN or infinite).
Source

pub fn emergency_stop(&mut self, elevator: EntityId) -> Result<(), SimError>

Command an immediate stop on a manual-mode elevator.

Sets the target velocity to zero; the car decelerates at its configured deceleration rate. Equivalent to set_target_velocity(elevator, 0.0) but emits a distinct Event::ManualVelocityCommanded with None payload so games can distinguish an emergency stop from a deliberate hold.

§Errors

Same as set_target_velocity, minus the finite-velocity check.

Source

pub fn tag_entity(&mut self, id: EntityId, tag: impl Into<String>)

Attach a metric tag to an entity (rider, stop, elevator, etc.).

Tags enable per-tag metric breakdowns. An entity can have multiple tags. Riders automatically inherit tags from their origin stop when spawned.

Source

pub fn untag_entity(&mut self, id: EntityId, tag: &str)

Remove a metric tag from an entity.

Source

pub fn metrics_for_tag(&self, tag: &str) -> Option<&TaggedMetric>

Query the metric accumulator for a specific tag.

Source

pub fn all_tags(&self) -> Vec<&str>

List all registered metric tags.

Source

pub const fn build_rider( &mut self, origin: EntityId, destination: EntityId, ) -> RiderBuilder<'_>

Create a rider builder for fluent rider spawning.

use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let s0 = sim.stop_entity(StopId(0)).unwrap();
let s1 = sim.stop_entity(StopId(1)).unwrap();
let rider = sim.build_rider(s0, s1)
    .weight(80.0)
    .spawn()
    .unwrap();
Source

pub fn build_rider_by_stop_id( &mut self, origin: StopId, destination: StopId, ) -> Result<RiderBuilder<'_>, SimError>

Create a rider builder using config StopIds.

§Errors

Returns SimError::StopNotFound if either stop ID is unknown.

use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
let rider = sim.build_rider_by_stop_id(StopId(0), StopId(1))
    .unwrap()
    .weight(80.0)
    .spawn()
    .unwrap();
Source

pub fn spawn_rider( &mut self, origin: EntityId, destination: EntityId, weight: f64, ) -> Result<EntityId, SimError>

Spawn a rider at the given origin stop entity, headed to destination stop entity.

Auto-detects the elevator group by finding groups that serve both origin and destination stops.

§Errors

Returns SimError::NoRoute if no group serves both stops. Returns SimError::AmbiguousRoute if multiple groups serve both stops.

Source

pub fn spawn_rider_with_route( &mut self, origin: EntityId, destination: EntityId, weight: f64, route: Route, ) -> Result<EntityId, SimError>

Spawn a rider with an explicit route.

Same as spawn_rider but uses the provided route instead of auto-detecting the group.

§Errors

Returns SimError::EntityNotFound if origin does not exist. Returns SimError::InvalidState if origin doesn’t match the route’s first leg from.

Source

pub fn spawn_rider_by_stop_id( &mut self, origin: StopId, destination: StopId, weight: f64, ) -> Result<EntityId, SimError>

Convenience: spawn a rider by config StopId.

Returns Err if either stop ID is not found.

§Errors

Returns SimError::StopNotFound if the origin or destination stop ID is not in the building configuration.

use elevator_core::prelude::*;

// Default builder has StopId(0) and StopId(1).
let mut sim = SimulationBuilder::demo().build().unwrap();

let rider = sim.spawn_rider_by_stop_id(StopId(0), StopId(1), 80.0).unwrap();
sim.step(); // metrics are updated during the tick
assert_eq!(sim.metrics().total_spawned(), 1);
Source

pub fn spawn_rider_in_group( &mut self, origin: EntityId, destination: EntityId, weight: f64, group: GroupId, ) -> Result<EntityId, SimError>

Spawn a rider using a specific group for routing.

Like spawn_rider but skips auto-detection — uses the given group directly. Useful when the caller already knows the group, or to resolve an AmbiguousRoute.

§Errors

Returns SimError::GroupNotFound if the group does not exist.

Source

pub fn spawn_rider_in_group_by_stop_id( &mut self, origin: StopId, destination: StopId, weight: f64, group: GroupId, ) -> Result<EntityId, SimError>

Convenience: spawn a rider by config StopId in a specific group.

§Errors

Returns SimError::StopNotFound if a stop ID is unknown, or SimError::GroupNotFound if the group does not exist.

Source

pub fn drain_events(&mut self) -> Vec<Event>

Drain all pending events from completed ticks.

Events emitted during step() (or per-phase methods) are buffered and made available here after advance_tick() is called. Events emitted outside the tick loop (e.g., spawn_rider, disable) are also included.

use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();

sim.spawn_rider_by_stop_id(StopId(0), StopId(1), 70.0).unwrap();
sim.step();

let events = sim.drain_events();
assert!(!events.is_empty());
Source

pub fn drain_events_where( &mut self, predicate: impl Fn(&Event) -> bool, ) -> Vec<Event>

Drain only events matching a predicate.

Events that don’t match the predicate remain in the buffer and will be returned by future drain_events or drain_events_where calls.

use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
sim.spawn_rider_by_stop_id(StopId(0), StopId(1), 70.0).unwrap();
sim.step();

let spawns: Vec<Event> = sim.drain_events_where(|e| {
    matches!(e, Event::RiderSpawned { .. })
});
Source

pub fn dispatchers(&self) -> &BTreeMap<GroupId, Box<dyn DispatchStrategy>>

Get the dispatch strategies map (for advanced sub-stepping).

Source

pub fn dispatchers_mut( &mut self, ) -> &mut BTreeMap<GroupId, Box<dyn DispatchStrategy>>

Get the dispatch strategies map mutably (for advanced sub-stepping).

Source

pub const fn events_mut(&mut self) -> &mut EventBus

Get a mutable reference to the event bus.

Source

pub const fn metrics_mut(&mut self) -> &mut Metrics

Get a mutable reference to the metrics.

Source

pub const fn phase_context(&self) -> PhaseContext

Build the PhaseContext for the current tick.

Source

pub fn run_advance_transient(&mut self)

Run only the advance_transient phase (with hooks).

Source

pub fn run_dispatch(&mut self)

Run only the dispatch phase (with hooks).

Source

pub fn run_movement(&mut self)

Run only the movement phase (with hooks).

Source

pub fn run_doors(&mut self)

Run only the doors phase (with hooks).

Source

pub fn run_loading(&mut self)

Run only the loading phase (with hooks).

Source

pub fn run_advance_queue(&mut self)

Run only the advance-queue phase (with hooks).

Reconciles each elevator’s phase/target with the front of its DestinationQueue. Runs between Reposition and Movement.

Source

pub fn run_reposition(&mut self)

Run only the reposition phase (with hooks).

Only runs if at least one group has a RepositionStrategy configured. Idle elevators with no pending dispatch assignment are repositioned according to their group’s strategy.

Source

pub fn run_metrics(&mut self)

Run only the metrics phase (with hooks).

Source

pub fn advance_tick(&mut self)

Increment the tick counter and flush events to the output buffer.

Call after running all desired phases. Events emitted during this tick are moved to the output buffer and available via drain_events().

Source

pub fn step(&mut self)

Advance the simulation by one tick.

Events from this tick are buffered internally and available via drain_events(). The metrics system only processes events from the current tick, regardless of whether the consumer drains them.

use elevator_core::prelude::*;

let mut sim = SimulationBuilder::demo().build().unwrap();
sim.step();
assert_eq!(sim.current_tick(), 1);
Source§

impl Simulation

Source

pub fn snapshot(&self) -> WorldSnapshot

Create a serializable snapshot of the current simulation state.

The snapshot captures all entities, components, groups, metrics, and the tick counter. Extension components and custom resources are NOT included — games must serialize those separately.

Source

pub fn snapshot_bytes(&self) -> Result<Vec<u8>, SimError>

Serialize the current state to a self-describing byte blob.

The blob is postcard-encoded and carries a magic prefix plus the elevator-core crate version. Use [Simulation::restore_bytes] on the receiving end. Determinism is bit-exact across builds of the same crate version; cross-version restores return SimError::SnapshotVersion.

Extension component data is serialized (identical to [Simulation::snapshot]); after restore you must still call world.register_ext::<T>(name) for each extension type and then [Simulation::load_extensions] to materialize them. Custom dispatch strategies and arbitrary World resources are not included.

§Errors

Returns SimError::SnapshotFormat if postcard encoding fails. This is unreachable for well-formed WorldSnapshot values (all fields derive Serialize), so callers that don’t care can unwrap.

Source

pub fn restore_bytes( bytes: &[u8], custom_strategy_factory: Option<&'_ dyn Fn(&str) -> Option<Box<dyn DispatchStrategy>>>, ) -> Result<Self, SimError>

Restore a simulation from bytes produced by [Simulation::snapshot_bytes].

Built-in dispatch strategies are auto-restored. For groups using BuiltinStrategy::Custom, provide a factory; pass None otherwise.

§Errors

Trait Implementations§

Source§

impl Debug for Simulation

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.