mod accessors;
mod calls;
mod construction;
mod destinations;
mod eta;
mod lifecycle;
mod manual;
mod rider;
mod runtime;
mod substep;
mod tagging;
mod topology;
use crate::components::{
Accel, AccessControl, Orientation, Patience, Preferences, Route, SpatialPosition, Speed, Weight,
};
use crate::dispatch::{BuiltinReposition, DispatchStrategy, ElevatorGroup, RepositionStrategy};
use crate::entity::{EntityId, RiderId};
use crate::error::SimError;
use crate::events::{Event, EventBus};
use crate::hooks::PhaseHooks;
use crate::ids::GroupId;
use crate::metrics::Metrics;
use crate::rider_index::RiderIndex;
use crate::stop::StopId;
use crate::time::TimeAdapter;
use crate::topology::TopologyGraph;
use crate::world::World;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::sync::Mutex;
#[derive(Debug, Clone)]
pub struct ElevatorParams {
pub max_speed: Speed,
pub acceleration: Accel,
pub deceleration: Accel,
pub weight_capacity: Weight,
pub door_transition_ticks: u32,
pub door_open_ticks: u32,
pub restricted_stops: HashSet<EntityId>,
pub inspection_speed_factor: f64,
}
impl Default for ElevatorParams {
fn default() -> Self {
Self {
max_speed: Speed::from(2.0),
acceleration: Accel::from(1.5),
deceleration: Accel::from(2.0),
weight_capacity: Weight::from(800.0),
door_transition_ticks: 5,
door_open_ticks: 10,
restricted_stops: HashSet::new(),
inspection_speed_factor: 0.25,
}
}
}
#[derive(Debug, Clone)]
pub struct LineParams {
pub name: String,
pub group: GroupId,
pub orientation: Orientation,
pub min_position: f64,
pub max_position: f64,
pub position: Option<SpatialPosition>,
pub max_cars: Option<usize>,
}
impl LineParams {
pub fn new(name: impl Into<String>, group: GroupId) -> Self {
Self {
name: name.into(),
group,
orientation: Orientation::default(),
min_position: 0.0,
max_position: 0.0,
position: None,
max_cars: None,
}
}
}
pub struct RiderBuilder<'a> {
sim: &'a mut Simulation,
origin: EntityId,
destination: EntityId,
weight: Weight,
group: Option<GroupId>,
route: Option<Route>,
patience: Option<u64>,
preferences: Option<Preferences>,
access_control: Option<AccessControl>,
}
impl RiderBuilder<'_> {
#[must_use]
pub fn weight(mut self, weight: impl Into<Weight>) -> Self {
self.weight = weight.into();
self
}
#[must_use]
pub const fn group(mut self, group: GroupId) -> Self {
self.group = Some(group);
self
}
#[must_use]
pub fn route(mut self, route: Route) -> Self {
self.route = Some(route);
self
}
#[must_use]
pub const fn patience(mut self, max_wait_ticks: u64) -> Self {
self.patience = Some(max_wait_ticks);
self
}
#[must_use]
pub const fn preferences(mut self, prefs: Preferences) -> Self {
self.preferences = Some(prefs);
self
}
#[must_use]
pub fn access_control(mut self, ac: AccessControl) -> Self {
self.access_control = Some(ac);
self
}
pub fn spawn(self) -> Result<RiderId, SimError> {
let route = if let Some(route) = self.route {
if let Some(leg) = route.current()
&& leg.from != self.origin
{
return Err(SimError::RouteOriginMismatch {
expected_origin: self.origin,
route_origin: leg.from,
});
}
route
} else {
if self.origin == self.destination {
return Err(SimError::InvalidConfig {
field: "destination",
reason: "origin and destination must differ; same-stop \
spawns deadlock with no hall call to summon a car"
.into(),
});
}
if let Some(group) = self.group {
if !self.sim.groups.iter().any(|g| g.id() == group) {
return Err(SimError::GroupNotFound(group));
}
Route::direct(self.origin, self.destination, group)
} else {
let group = self.sim.auto_detect_group(self.origin, self.destination)?;
Route::direct(self.origin, self.destination, group)
}
};
let eid = self
.sim
.spawn_rider_inner(self.origin, self.destination, self.weight, route);
if let Some(max_wait) = self.patience {
self.sim.world.set_patience(
eid,
Patience {
max_wait_ticks: max_wait,
waited_ticks: 0,
},
);
}
if let Some(prefs) = self.preferences {
self.sim.world.set_preferences(eid, prefs);
}
if let Some(ac) = self.access_control {
self.sim.world.set_access_control(eid, ac);
}
Ok(RiderId::from(eid))
}
}
pub struct Simulation {
world: World,
events: EventBus,
pending_output: Vec<Event>,
tick: u64,
dt: f64,
groups: Vec<ElevatorGroup>,
stop_lookup: HashMap<StopId, EntityId>,
dispatchers: BTreeMap<GroupId, Box<dyn DispatchStrategy>>,
strategy_ids: BTreeMap<GroupId, crate::dispatch::BuiltinStrategy>,
repositioners: BTreeMap<GroupId, Box<dyn RepositionStrategy>>,
reposition_ids: BTreeMap<GroupId, BuiltinReposition>,
metrics: Metrics,
time: TimeAdapter,
hooks: PhaseHooks,
elevator_ids_buf: Vec<EntityId>,
reposition_buf: Vec<(EntityId, EntityId)>,
topo_graph: Mutex<TopologyGraph>,
rider_index: RiderIndex,
pub(crate) tick_in_progress: bool,
}
impl fmt::Debug for Simulation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Simulation")
.field("tick", &self.tick)
.field("dt", &self.dt)
.field("groups", &self.groups.len())
.field("entities", &self.world.entity_count())
.finish_non_exhaustive()
}
}