Skip to main content

std_rs/snl/
delay_do.rs

1//! Delay-and-Do state machine — native Rust port of `delayDo.st`.
2//!
3//! Implements a state machine that waits for a standby condition,
4//! monitors an active condition, and after the active condition
5//! clears (with a configurable delay), triggers an action.
6//!
7//! # State Machine
8//!
9//! ```text
10//!   init ──► idle ◄──────────────────────────┐
11//!            │  ▲                              │
12//!            │  └── maybeStandby ◄── disable  │
13//!            ▼                       ▲        │
14//!         standby ──► maybeWait ──► waiting ──► action
15//!            ▲            │           │
16//!            │            ▼           ▼
17//!            │          idle       active ──► waiting
18//!            └──────────────────────┘
19//! ```
20
21use std::time::{Duration, Instant};
22
23/// States of the delay-do state machine.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum DelayDoState {
26    Init,
27    Disable,
28    MaybeStandby,
29    Idle,
30    Standby,
31    MaybeWait,
32    Active,
33    Waiting,
34    Action,
35}
36
37impl std::fmt::Display for DelayDoState {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        match self {
40            DelayDoState::Init => write!(f, "init"),
41            DelayDoState::Disable => write!(f, "disable"),
42            DelayDoState::MaybeStandby => write!(f, "maybeStandby"),
43            DelayDoState::Idle => write!(f, "idle"),
44            DelayDoState::Standby => write!(f, "standby"),
45            DelayDoState::MaybeWait => write!(f, "maybeWait"),
46            DelayDoState::Active => write!(f, "active"),
47            DelayDoState::Waiting => write!(f, "waiting"),
48            DelayDoState::Action => write!(f, "action"),
49        }
50    }
51}
52
53/// Input signals for the delay-do state machine.
54#[derive(Debug, Clone, Copy)]
55pub struct DelayDoInputs {
56    /// Enable/disable control
57    pub enable: bool,
58    /// Whether the "enable" signal changed since last step
59    pub enable_changed: bool,
60    /// Standby condition
61    pub standby: bool,
62    /// Whether the "standby" signal changed since last step
63    pub standby_changed: bool,
64    /// Active condition
65    pub active: bool,
66    /// Whether the "active" signal changed since last step
67    pub active_changed: bool,
68}
69
70/// Output actions from the delay-do state machine.
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub enum DelayDoAction {
73    /// No action this step.
74    None,
75    /// Process the action sequence (doSeq).
76    ProcessAction,
77}
78
79/// The delay-do controller.
80pub struct DelayDoController {
81    pub state: DelayDoState,
82    /// Delay period before triggering the action.
83    pub delay_period: Duration,
84    /// Whether to resume waiting when re-entering from standby.
85    resume_waiting: bool,
86    /// Whether the active signal was seen during standby.
87    active_seen: bool,
88    /// When the waiting state was entered (for delay timing).
89    wait_start: Option<Instant>,
90}
91
92impl Default for DelayDoController {
93    fn default() -> Self {
94        Self {
95            state: DelayDoState::Init,
96            delay_period: Duration::from_secs(0),
97            resume_waiting: false,
98            active_seen: false,
99            wait_start: None,
100        }
101    }
102}
103
104impl DelayDoController {
105    pub fn new(delay_secs: f64) -> Self {
106        Self {
107            delay_period: Duration::from_secs_f64(delay_secs),
108            ..Default::default()
109        }
110    }
111
112    /// Advance the state machine given current inputs.
113    /// Returns the action to take (if any) and the new state.
114    pub fn step(&mut self, inputs: &DelayDoInputs) -> (DelayDoAction, DelayDoState) {
115        let action;
116
117        match self.state {
118            DelayDoState::Init => {
119                action = DelayDoAction::None;
120                self.resume_waiting = false;
121                self.state = DelayDoState::Idle;
122            }
123
124            DelayDoState::Disable => {
125                action = DelayDoAction::None;
126                if inputs.enable_changed && inputs.enable {
127                    self.active_seen = false;
128                    self.state = DelayDoState::MaybeStandby;
129                }
130            }
131
132            DelayDoState::MaybeStandby => {
133                action = DelayDoAction::None;
134                if inputs.standby {
135                    self.state = DelayDoState::Standby;
136                } else if inputs.active {
137                    self.state = DelayDoState::Active;
138                } else {
139                    self.state = DelayDoState::Idle;
140                }
141            }
142
143            DelayDoState::Idle => {
144                action = DelayDoAction::None;
145                if inputs.enable_changed && !inputs.enable {
146                    self.state = DelayDoState::Disable;
147                } else if inputs.standby_changed && inputs.standby {
148                    self.state = DelayDoState::Standby;
149                } else if inputs.active_changed && inputs.active {
150                    self.state = DelayDoState::Active;
151                }
152            }
153
154            DelayDoState::Standby => {
155                action = DelayDoAction::None;
156                if inputs.active_changed && inputs.active {
157                    self.active_seen = true;
158                }
159                if inputs.enable_changed && !inputs.enable {
160                    self.resume_waiting = false;
161                    self.state = DelayDoState::Disable;
162                } else if inputs.standby_changed && !inputs.standby {
163                    self.state = DelayDoState::MaybeWait;
164                }
165            }
166
167            DelayDoState::MaybeWait => {
168                action = DelayDoAction::None;
169                if inputs.active {
170                    self.state = DelayDoState::Active;
171                } else if self.active_seen || self.resume_waiting {
172                    self.active_seen = false;
173                    self.wait_start = Some(Instant::now());
174                    self.state = DelayDoState::Waiting;
175                } else {
176                    self.state = DelayDoState::Idle;
177                }
178            }
179
180            DelayDoState::Active => {
181                action = DelayDoAction::None;
182                if inputs.enable_changed && !inputs.enable {
183                    self.state = DelayDoState::Disable;
184                } else if inputs.standby_changed && inputs.standby {
185                    self.state = DelayDoState::Standby;
186                } else if inputs.active_changed && !inputs.active {
187                    self.wait_start = Some(Instant::now());
188                    self.state = DelayDoState::Waiting;
189                }
190            }
191
192            DelayDoState::Waiting => {
193                if inputs.enable_changed && !inputs.enable {
194                    action = DelayDoAction::None;
195                    self.state = DelayDoState::Disable;
196                } else if inputs.standby_changed && inputs.standby {
197                    action = DelayDoAction::None;
198                    self.resume_waiting = true;
199                    self.state = DelayDoState::Standby;
200                } else if inputs.active_changed && inputs.active {
201                    action = DelayDoAction::None;
202                    self.state = DelayDoState::Active;
203                } else if let Some(start) = self.wait_start {
204                    if start.elapsed() >= self.delay_period {
205                        self.resume_waiting = false;
206                        self.wait_start = None;
207                        self.state = DelayDoState::Action;
208                        action = DelayDoAction::None;
209                    } else {
210                        action = DelayDoAction::None;
211                    }
212                } else {
213                    action = DelayDoAction::None;
214                }
215            }
216
217            DelayDoState::Action => {
218                action = DelayDoAction::ProcessAction;
219                self.state = DelayDoState::Idle;
220            }
221        }
222
223        (action, self.state)
224    }
225}