Skip to main content

elevator_core/components/
elevator.rs

1//! Elevator state and configuration component.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashSet;
5
6use crate::door::DoorState;
7use crate::entity::EntityId;
8
9/// Operational phase of an elevator.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11#[non_exhaustive]
12pub enum ElevatorPhase {
13    /// Parked with no pending requests.
14    Idle,
15    /// Travelling toward a specific stop.
16    MovingToStop(EntityId),
17    /// Doors are currently opening.
18    DoorOpening,
19    /// Doors open; riders may board or exit.
20    Loading,
21    /// Doors are currently closing.
22    DoorClosing,
23    /// Stopped at a floor (doors closed, awaiting dispatch).
24    Stopped,
25}
26
27impl std::fmt::Display for ElevatorPhase {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        match self {
30            Self::Idle => write!(f, "Idle"),
31            Self::MovingToStop(id) => write!(f, "MovingToStop({id:?})"),
32            Self::DoorOpening => write!(f, "DoorOpening"),
33            Self::Loading => write!(f, "Loading"),
34            Self::DoorClosing => write!(f, "DoorClosing"),
35            Self::Stopped => write!(f, "Stopped"),
36        }
37    }
38}
39
40/// Component for an elevator entity.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Elevator {
43    /// Current operational phase.
44    pub(crate) phase: ElevatorPhase,
45    /// Door finite-state machine.
46    pub(crate) door: DoorState,
47    /// Maximum travel speed (distance/tick).
48    pub(crate) max_speed: f64,
49    /// Acceleration rate (distance/tick^2).
50    pub(crate) acceleration: f64,
51    /// Deceleration rate (distance/tick^2).
52    pub(crate) deceleration: f64,
53    /// Maximum weight the car can carry.
54    pub(crate) weight_capacity: f64,
55    /// Total weight of riders currently aboard.
56    pub(crate) current_load: f64,
57    /// Entity IDs of riders currently aboard.
58    pub(crate) riders: Vec<EntityId>,
59    /// Stop entity the car is heading toward, if any.
60    pub(crate) target_stop: Option<EntityId>,
61    /// Ticks for a door open/close transition.
62    pub(crate) door_transition_ticks: u32,
63    /// Ticks the door stays fully open.
64    pub(crate) door_open_ticks: u32,
65    /// Line entity this car belongs to.
66    #[serde(alias = "group")]
67    pub(crate) line: EntityId,
68    /// Whether this elevator is currently repositioning (not serving a dispatch).
69    #[serde(default)]
70    pub(crate) repositioning: bool,
71    /// Stop entity IDs this elevator cannot serve (access restriction).
72    #[serde(default)]
73    pub(crate) restricted_stops: HashSet<EntityId>,
74    /// Speed multiplier for Inspection mode (0.0..1.0).
75    #[serde(default = "default_inspection_speed_factor")]
76    pub(crate) inspection_speed_factor: f64,
77    /// Up-direction indicator lamp: whether this car will serve upward trips.
78    ///
79    /// Auto-managed by the dispatch phase: set true when heading up (or idle),
80    /// false while actively committed to a downward trip. Affects boarding:
81    /// a rider whose next leg goes up will not board a car with `going_up=false`.
82    #[serde(default = "default_true")]
83    pub(crate) going_up: bool,
84    /// Down-direction indicator lamp: whether this car will serve downward trips.
85    ///
86    /// Auto-managed by the dispatch phase: set true when heading down (or idle),
87    /// false while actively committed to an upward trip. Affects boarding:
88    /// a rider whose next leg goes down will not board a car with `going_down=false`.
89    #[serde(default = "default_true")]
90    pub(crate) going_down: bool,
91    /// Count of rounded-floor transitions (passing-floors + arrivals). Analogous
92    /// to elevator-saga's `moveCount` scoring axis.
93    #[serde(default)]
94    pub(crate) move_count: u64,
95}
96
97/// Default inspection speed factor (25% of normal speed).
98const fn default_inspection_speed_factor() -> f64 {
99    0.25
100}
101
102/// Default value for direction indicator fields (both lamps on = idle/either direction).
103const fn default_true() -> bool {
104    true
105}
106
107impl Elevator {
108    /// Current operational phase.
109    #[must_use]
110    pub const fn phase(&self) -> ElevatorPhase {
111        self.phase
112    }
113
114    /// Door finite-state machine.
115    #[must_use]
116    pub const fn door(&self) -> &DoorState {
117        &self.door
118    }
119
120    /// Maximum travel speed (distance/tick).
121    #[must_use]
122    pub const fn max_speed(&self) -> f64 {
123        self.max_speed
124    }
125
126    /// Acceleration rate (distance/tick^2).
127    #[must_use]
128    pub const fn acceleration(&self) -> f64 {
129        self.acceleration
130    }
131
132    /// Deceleration rate (distance/tick^2).
133    #[must_use]
134    pub const fn deceleration(&self) -> f64 {
135        self.deceleration
136    }
137
138    /// Maximum weight the car can carry.
139    #[must_use]
140    pub const fn weight_capacity(&self) -> f64 {
141        self.weight_capacity
142    }
143
144    /// Total weight of riders currently aboard.
145    #[must_use]
146    pub const fn current_load(&self) -> f64 {
147        self.current_load
148    }
149
150    /// Entity IDs of riders currently aboard.
151    #[must_use]
152    pub fn riders(&self) -> &[EntityId] {
153        &self.riders
154    }
155
156    /// Stop entity the car is heading toward, if any.
157    #[must_use]
158    pub const fn target_stop(&self) -> Option<EntityId> {
159        self.target_stop
160    }
161
162    /// Ticks for a door open/close transition.
163    #[must_use]
164    pub const fn door_transition_ticks(&self) -> u32 {
165        self.door_transition_ticks
166    }
167
168    /// Ticks the door stays fully open.
169    #[must_use]
170    pub const fn door_open_ticks(&self) -> u32 {
171        self.door_open_ticks
172    }
173
174    /// Line entity this car belongs to.
175    #[must_use]
176    pub const fn line(&self) -> EntityId {
177        self.line
178    }
179
180    /// Whether this elevator is currently repositioning (not serving a dispatch).
181    #[must_use]
182    pub const fn repositioning(&self) -> bool {
183        self.repositioning
184    }
185
186    /// Stop entity IDs this elevator cannot serve (access restriction).
187    #[must_use]
188    pub const fn restricted_stops(&self) -> &HashSet<EntityId> {
189        &self.restricted_stops
190    }
191
192    /// Speed multiplier applied during Inspection mode.
193    #[must_use]
194    pub const fn inspection_speed_factor(&self) -> f64 {
195        self.inspection_speed_factor
196    }
197
198    /// Whether this car's up-direction indicator lamp is lit.
199    ///
200    /// A lit up-lamp signals the car will serve upward-travelling riders.
201    /// Both lamps lit means the car is idle and will accept either direction.
202    #[must_use]
203    pub const fn going_up(&self) -> bool {
204        self.going_up
205    }
206
207    /// Whether this car's down-direction indicator lamp is lit.
208    ///
209    /// A lit down-lamp signals the car will serve downward-travelling riders.
210    /// Both lamps lit means the car is idle and will accept either direction.
211    #[must_use]
212    pub const fn going_down(&self) -> bool {
213        self.going_down
214    }
215
216    /// Count of rounded-floor transitions this elevator has made
217    /// (both passing-floor crossings and arrivals).
218    ///
219    /// Analogous to elevator-saga's `moveCount` scoring axis.
220    #[must_use]
221    pub const fn move_count(&self) -> u64 {
222        self.move_count
223    }
224}