Skip to main content

elevator_core/
door.rs

1//! Door open/close finite-state machine.
2
3use serde::{Deserialize, Serialize};
4
5/// State machine for elevator doors.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[non_exhaustive]
8pub enum DoorState {
9    /// Doors are fully closed.
10    Closed,
11    /// Doors are in the process of opening.
12    Opening {
13        /// Ticks left in the opening transition.
14        ticks_remaining: u32,
15        /// How many ticks the door stays open once fully opened.
16        open_duration: u32,
17        /// How many ticks the closing transition takes.
18        close_duration: u32,
19    },
20    /// Doors are fully open and holding.
21    Open {
22        /// Ticks left before the doors begin closing.
23        ticks_remaining: u32,
24        /// How many ticks the closing transition takes.
25        close_duration: u32,
26    },
27    /// Doors are in the process of closing.
28    Closing {
29        /// Ticks left in the closing transition.
30        ticks_remaining: u32,
31    },
32}
33
34/// Transition emitted when the door state changes phase.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[non_exhaustive]
37pub enum DoorTransition {
38    /// No phase change occurred this tick.
39    None,
40    /// Doors just finished opening and are now fully open.
41    FinishedOpening,
42    /// Doors just finished holding open and are about to close.
43    FinishedOpen,
44    /// Doors just finished closing and are now fully closed.
45    FinishedClosing,
46}
47
48impl std::fmt::Display for DoorState {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        match self {
51            Self::Closed => write!(f, "Closed"),
52            Self::Opening {
53                ticks_remaining, ..
54            } => write!(f, "Opening({ticks_remaining})"),
55            Self::Open {
56                ticks_remaining, ..
57            } => write!(f, "Open({ticks_remaining})"),
58            Self::Closing { ticks_remaining } => write!(f, "Closing({ticks_remaining})"),
59        }
60    }
61}
62
63impl DoorState {
64    /// Returns `true` if the doors are fully open.
65    #[must_use]
66    pub const fn is_open(&self) -> bool {
67        matches!(self, Self::Open { .. })
68    }
69
70    /// Returns `true` if the doors are fully closed.
71    #[must_use]
72    pub const fn is_closed(&self) -> bool {
73        matches!(self, Self::Closed)
74    }
75
76    /// Begin opening the door.
77    #[must_use]
78    pub const fn request_open(transition_ticks: u32, open_ticks: u32) -> Self {
79        Self::Opening {
80            ticks_remaining: transition_ticks,
81            open_duration: open_ticks,
82            close_duration: transition_ticks,
83        }
84    }
85
86    /// Advance the door state by one tick. Returns the transition that occurred.
87    pub const fn tick(&mut self) -> DoorTransition {
88        match self {
89            Self::Closed => DoorTransition::None,
90            Self::Opening {
91                ticks_remaining,
92                open_duration,
93                close_duration,
94            } => {
95                if *ticks_remaining <= 1 {
96                    let od = *open_duration;
97                    let cd = *close_duration;
98                    *self = Self::Open {
99                        ticks_remaining: od,
100                        close_duration: cd,
101                    };
102                    DoorTransition::FinishedOpening
103                } else {
104                    *ticks_remaining -= 1;
105                    DoorTransition::None
106                }
107            }
108            Self::Open {
109                ticks_remaining,
110                close_duration,
111            } => {
112                if *ticks_remaining <= 1 {
113                    let cd = *close_duration;
114                    *self = Self::Closing {
115                        ticks_remaining: cd,
116                    };
117                    DoorTransition::FinishedOpen
118                } else {
119                    *ticks_remaining -= 1;
120                    DoorTransition::None
121                }
122            }
123            Self::Closing { ticks_remaining } => {
124                if *ticks_remaining <= 1 {
125                    *self = Self::Closed;
126                    DoorTransition::FinishedClosing
127                } else {
128                    *ticks_remaining -= 1;
129                    DoorTransition::None
130                }
131            }
132        }
133    }
134}