Skip to main content

veloxity_core/
state_machine.rs

1#![allow(non_camel_case_types)]
2#[cfg(test)]
3mod tests;
4
5use crate::params::{ParamId, ParamValue, Params};
6use bitflags::bitflags;
7use core::mem::take;
8
9// Events that trigger state transitions
10#[derive(Debug, Clone, Copy)]
11pub enum Event {
12    INITIALIZED,
13    REQUEST_ARM,
14    REQUEST_DISARM,
15    CALIBRATION_COMPLETE,
16    CALIBRATION_FAILED,
17    HARDFAULT_REARM_REQUESTED,
18    ERROR_OCCURRED(ErrorFlag),
19    ERROR_CLEARED(ErrorFlag),
20}
21
22// Bitflags for tracking specific error conditions
23bitflags! {
24    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
25    pub struct ErrorFlag: u16 {
26        const INVALID_MIXER = 1;
27        const IMU_NOT_RESPONDING = 1 << 1;
28        const RC_LOST = 1 << 2;
29        const UNHEALTHY_ESTIMATOR = 1 << 3;
30        const TIME_GOING_BACKWARDS = 1 << 4;
31        const UNCALIBRATED_IMU = 1 << 5;
32        const BUFFER_OVERRUN = 1 << 6;
33        const INVALID_FAILSAFE = 1 << 7;
34    }
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
38pub(crate) struct State<S> {
39    state: S,
40    error_flags: ErrorFlag,
41}
42
43// Holds FSM as its type changes.
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub(crate) enum StateMachine {
46    Init(State<Init>),
47    Preflight(State<Preflight>),
48    Calibrating(State<Calibrating>),
49    Armed(State<Armed>),
50    Failsafe(State<Failsafe>),
51    ErrorPresent(State<ErrorPresent>),
52    ErrorFailsafe(State<ErrorFailsafe>),
53}
54
55impl StateMachine {
56    // New state machine starts in the Preflight state.
57    pub fn new() -> Self {
58        StateMachine::Init(State::<Init>::default())
59    }
60
61    // gets a mutable reference to the error flags
62    fn error_flags_mut(&mut self) -> &mut ErrorFlag {
63        match self {
64            StateMachine::Init(sm) => &mut sm.error_flags,
65            StateMachine::Preflight(sm) => &mut sm.error_flags,
66            StateMachine::Calibrating(sm) => &mut sm.error_flags,
67            StateMachine::Armed(sm) => &mut sm.error_flags,
68            StateMachine::Failsafe(sm) => &mut sm.error_flags,
69            StateMachine::ErrorPresent(sm) => &mut sm.error_flags,
70            StateMachine::ErrorFailsafe(sm) => &mut sm.error_flags,
71        }
72    }
73
74    // Transitions machine based on Event
75    pub fn update(&mut self, event: Event, params: &Params) {
76        // Handle errors
77        match event {
78            Event::ERROR_OCCURRED(flag) => self.error_flags_mut().insert(flag),
79            Event::ERROR_CLEARED(flag) => self.error_flags_mut().remove(flag),
80            _ => (),
81        }
82
83        // Consume old state, replace with new one
84        let machine = take(self);
85        *self = machine.transition(event, params);
86    }
87
88    fn transition(self, event: Event, params: &Params) -> Self {
89        match self {
90            StateMachine::Init(sm) => sm.state.on_event(sm, event),
91            StateMachine::Preflight(sm) => sm.state.on_event(sm, event, params),
92            StateMachine::Calibrating(sm) => sm.state.on_event(sm, event, params),
93            StateMachine::Armed(sm) => sm.state.on_event(sm, event, params),
94            StateMachine::Failsafe(sm) => sm.state.on_event(sm, event, params),
95            StateMachine::ErrorPresent(sm) => sm.state.on_event(sm, event, params),
96            StateMachine::ErrorFailsafe(sm) => sm.state.on_event(sm, event, params),
97        }
98    }
99
100    pub fn get_errors(&self) -> ErrorFlag {
101        match self {
102            StateMachine::Init(sm) => sm.error_flags,
103            StateMachine::Preflight(sm) => sm.error_flags,
104            StateMachine::Calibrating(sm) => sm.error_flags,
105            StateMachine::Armed(sm) => sm.error_flags,
106            StateMachine::Failsafe(sm) => sm.error_flags,
107            StateMachine::ErrorPresent(sm) => sm.error_flags,
108            StateMachine::ErrorFailsafe(sm) => sm.error_flags,
109        }
110    }
111
112    pub fn is_armed(&self) -> bool {
113        matches!(self, StateMachine::Armed(_) | StateMachine::Failsafe(_))
114    }
115
116    pub fn is_in_failsafe(&self) -> bool {
117        matches!(
118            self,
119            StateMachine::Failsafe(_) | StateMachine::ErrorFailsafe(_)
120        )
121    }
122
123    pub fn is_in_error_state(&self) -> bool {
124        matches!(
125            self,
126            StateMachine::ErrorPresent(_) | StateMachine::ErrorFailsafe(_)
127        )
128    }
129}
130
131impl Default for StateMachine {
132    fn default() -> Self {
133        Self::new()
134    }
135}
136
137// State structs
138#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
139pub(crate) struct Init;
140#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
141pub(crate) struct Preflight;
142#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
143pub(crate) struct Calibrating;
144#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
145pub(crate) struct Armed;
146#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
147pub(crate) struct Failsafe;
148#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
149pub(crate) struct ErrorPresent;
150#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
151pub(crate) struct ErrorFailsafe;
152
153// State transition logic
154impl Init {
155    fn on_event(self, sm: State<Self>, event: Event) -> StateMachine {
156        match event {
157            Event::INITIALIZED => StateMachine::Preflight(State {
158                state: Preflight,
159                error_flags: sm.error_flags,
160            }),
161            Event::HARDFAULT_REARM_REQUESTED => StateMachine::Init(sm),
162            _ => StateMachine::Init(sm),
163        }
164    }
165}
166
167impl Preflight {
168    fn on_event(self, sm: State<Self>, event: Event, params: &Params) -> StateMachine {
169        match event {
170            Event::REQUEST_ARM => arm_from_preflight(sm.error_flags, params),
171            Event::HARDFAULT_REARM_REQUESTED => StateMachine::Armed(State {
172                state: Armed,
173                error_flags: sm.error_flags,
174            }),
175            Event::ERROR_OCCURRED(_) => StateMachine::ErrorPresent(State {
176                state: ErrorPresent,
177                error_flags: sm.error_flags,
178            }),
179            _ => StateMachine::Preflight(sm),
180        }
181    }
182}
183
184impl Calibrating {
185    fn on_event(self, sm: State<Self>, event: Event, _params: &Params) -> StateMachine {
186        match event {
187            Event::CALIBRATION_COMPLETE => {
188                let mut error_flags = sm.error_flags;
189                error_flags.remove(ErrorFlag::UNCALIBRATED_IMU);
190                StateMachine::Armed(State {
191                    state: Armed,
192                    error_flags: error_flags,
193                })
194            }
195            Event::CALIBRATION_FAILED => StateMachine::Preflight(State {
196                state: Preflight,
197                error_flags: sm.error_flags,
198            }),
199            Event::ERROR_OCCURRED(_) => StateMachine::ErrorPresent(State {
200                state: ErrorPresent,
201                error_flags: sm.error_flags,
202            }),
203            Event::HARDFAULT_REARM_REQUESTED => StateMachine::Armed(State {
204                state: Armed,
205                error_flags: sm.error_flags,
206            }),
207            _ => StateMachine::Calibrating(sm),
208        }
209    }
210}
211
212impl Armed {
213    fn on_event(self, sm: State<Self>, event: Event, _params: &Params) -> StateMachine {
214        match event {
215            Event::REQUEST_DISARM => {
216                if !sm.error_flags.is_empty() {
217                    StateMachine::ErrorPresent(State {
218                        state: ErrorPresent,
219                        error_flags: sm.error_flags,
220                    })
221                } else {
222                    StateMachine::Preflight(State {
223                        state: Preflight,
224                        error_flags: sm.error_flags,
225                    })
226                }
227            }
228            Event::ERROR_OCCURRED(ErrorFlag::RC_LOST) => StateMachine::Failsafe(State {
229                state: Failsafe,
230                error_flags: sm.error_flags,
231            }),
232            Event::HARDFAULT_REARM_REQUESTED => StateMachine::Armed(sm),
233            _ => StateMachine::Armed(sm),
234        }
235    }
236}
237
238impl Failsafe {
239    fn on_event(self, sm: State<Self>, event: Event, _params: &Params) -> StateMachine {
240        match event {
241            Event::ERROR_CLEARED(ErrorFlag::RC_LOST) => StateMachine::Armed(State {
242                state: Armed,
243                error_flags: sm.error_flags,
244            }),
245            Event::REQUEST_DISARM => StateMachine::ErrorFailsafe(State {
246                state: ErrorFailsafe,
247                error_flags: sm.error_flags,
248            }),
249            Event::HARDFAULT_REARM_REQUESTED => StateMachine::Armed(State {
250                state: Armed,
251                error_flags: sm.error_flags,
252            }),
253            _ => StateMachine::Failsafe(sm),
254        }
255    }
256}
257
258impl ErrorFailsafe {
259    fn on_event(self, sm: State<Self>, event: Event, _params: &Params) -> StateMachine {
260        match event {
261            Event::REQUEST_ARM => {
262                log_arming_errors(sm.error_flags);
263                StateMachine::ErrorFailsafe(sm)
264            }
265            Event::HARDFAULT_REARM_REQUESTED => StateMachine::Armed(State {
266                state: Armed,
267                error_flags: sm.error_flags,
268            }),
269            Event::ERROR_CLEARED(ErrorFlag::RC_LOST) => {
270                if sm.error_flags.is_empty() {
271                    StateMachine::Preflight(State {
272                        state: Preflight,
273                        error_flags: sm.error_flags,
274                    })
275                } else {
276                    StateMachine::ErrorPresent(State {
277                        state: ErrorPresent,
278                        error_flags: sm.error_flags,
279                    })
280                }
281            }
282            Event::ERROR_CLEARED(_) => {
283                if sm.error_flags.is_empty() {
284                    StateMachine::Preflight(State {
285                        state: Preflight,
286                        error_flags: sm.error_flags,
287                    })
288                } else {
289                    StateMachine::ErrorFailsafe(sm)
290                }
291            }
292            _ => StateMachine::ErrorFailsafe(sm),
293        }
294    }
295}
296
297impl ErrorPresent {
298    fn on_event(self, sm: State<Self>, event: Event, params: &Params) -> StateMachine {
299        match event {
300            Event::REQUEST_ARM => {
301                let blocking_errors = arming_blocking_errors(sm.error_flags, params);
302                if blocking_errors.is_empty() {
303                    arm_from_preflight(sm.error_flags, params)
304                } else {
305                    log_arming_errors(blocking_errors);
306                    StateMachine::ErrorPresent(sm)
307                }
308            }
309            Event::HARDFAULT_REARM_REQUESTED => StateMachine::Armed(State {
310                state: Armed,
311                error_flags: sm.error_flags,
312            }),
313            Event::ERROR_CLEARED(_) => {
314                if sm.error_flags.is_empty() {
315                    StateMachine::Preflight(State {
316                        state: Preflight,
317                        error_flags: sm.error_flags,
318                    })
319                } else {
320                    StateMachine::ErrorPresent(sm)
321                }
322            }
323            _ => StateMachine::ErrorPresent(sm),
324        }
325    }
326}
327
328fn arm_from_preflight(error_flags: ErrorFlag, params: &Params) -> StateMachine {
329    if matches!(
330        params.get_by_id(ParamId::PARAM_CALIBRATE_GYRO_ON_ARM),
331        ParamValue::Int(value) if value != 0
332    ) {
333        let mut error_flags = error_flags;
334        error_flags.remove(ErrorFlag::UNCALIBRATED_IMU);
335        StateMachine::Calibrating(State {
336            state: Calibrating,
337            error_flags,
338        })
339    } else {
340        StateMachine::Armed(State {
341            state: Armed,
342            error_flags,
343        })
344    }
345}
346
347fn arming_blocking_errors(error_flags: ErrorFlag, params: &Params) -> ErrorFlag {
348    if matches!(
349        params.get_by_id(ParamId::PARAM_ALLOW_UNHEALTHY_ESTIMATOR),
350        ParamValue::Int(value) if value != 0
351    ) {
352        error_flags - ErrorFlag::UNHEALTHY_ESTIMATOR
353    } else {
354        error_flags
355    }
356}
357
358fn log_arming_errors(error_flags: ErrorFlag) {
359    if error_flags.contains(ErrorFlag::INVALID_MIXER) {
360        crate::log_error!("Unable to arm: Invalid mixer");
361    }
362    if error_flags.contains(ErrorFlag::IMU_NOT_RESPONDING) {
363        crate::log_error!("Unable to arm: IMU not responding");
364    }
365    if error_flags.contains(ErrorFlag::RC_LOST) {
366        crate::log_error!("Unable to arm: RC signal lost");
367    }
368    if error_flags.contains(ErrorFlag::UNHEALTHY_ESTIMATOR) {
369        crate::log_error!("Unable to arm: Unhealthy estimator");
370    }
371    if error_flags.contains(ErrorFlag::TIME_GOING_BACKWARDS) {
372        crate::log_error!("Unable to arm: Time going backwards");
373    }
374    if error_flags.contains(ErrorFlag::UNCALIBRATED_IMU) {
375        crate::log_error!("Unable to arm: IMU not calibrated");
376    }
377    if error_flags.contains(ErrorFlag::INVALID_FAILSAFE) {
378        crate::log_error!("Unable to arm: Invalid failsafe setting");
379    }
380}
381
382// Struct for state management
383pub struct StateManager {
384    machine: StateMachine,
385    arming_safety: ArmingSafety,
386}
387
388#[derive(Debug, Clone, Copy, Default)]
389struct ArmingSafety {
390    rc_throttle_low: bool,
391    rc_throttle_override_switch_on: bool,
392}
393
394impl StateManager {
395    pub fn new() -> Self {
396        StateManager {
397            machine: StateMachine::new(),
398            arming_safety: ArmingSafety::default(),
399        }
400    }
401
402    pub fn is_calibrating(&self) -> bool {
403        matches!(self.machine, StateMachine::Calibrating(_))
404    }
405
406    pub fn update_arming_safety(
407        &mut self,
408        rc_throttle_low: bool,
409        rc_throttle_override_switch_on: bool,
410    ) {
411        self.arming_safety = ArmingSafety {
412            rc_throttle_low,
413            rc_throttle_override_switch_on,
414        };
415    }
416
417    fn arming_safety_allows_arm(&self, params: &Params) -> bool {
418        if !self.arming_safety.rc_throttle_low {
419            crate::log_error!("Cannot arm with RC throttle high");
420            return false;
421        }
422
423        let take_min_throttle = matches!(
424            params.get_by_id(ParamId::PARAM_RC_OVERRIDE_TAKE_MIN_THROTTLE),
425            ParamValue::Int(value) if value != 0
426        );
427
428        if !take_min_throttle && !self.arming_safety.rc_throttle_override_switch_on {
429            crate::log_error!("RC throttle override must be active to arm");
430            return false;
431        }
432
433        true
434    }
435
436    // The main update loop. Takes an event and applies it to the internal state machine.
437    pub fn update(&mut self, event: Event, params: &Params) {
438        let arming_attempt_can_transition = matches!(self.machine, StateMachine::Preflight(_))
439            || (matches!(self.machine, StateMachine::ErrorPresent(_))
440                && arming_blocking_errors(self.machine.get_errors(), params).is_empty());
441
442        if matches!(event, Event::REQUEST_ARM)
443            && arming_attempt_can_transition
444            && !self.arming_safety_allows_arm(params)
445        {
446            return;
447        }
448
449        self.machine.update(event, params);
450    }
451
452    pub fn set_error_flag(&mut self, flag: ErrorFlag, is_present: bool, params: &Params) {
453        let already_present = self.get_errors().contains(flag);
454        if is_present {
455            if !already_present {
456                self.update(Event::ERROR_OCCURRED(flag), params);
457            }
458        } else if already_present {
459            self.update(Event::ERROR_CLEARED(flag), params);
460        }
461    }
462
463    pub fn run(&mut self, params: &Params) {
464        // process errors
465        if self.get_errors().is_empty() {
466            // Move out of error state if cleared
467            self.update(Event::ERROR_CLEARED(ErrorFlag::default()), params);
468        } else {
469            // Retry entering error state if errors still present
470            self.update(Event::ERROR_OCCURRED(self.machine.get_errors()), params);
471        }
472        self.update_leds();
473    }
474
475    pub fn is_armed(&self) -> bool {
476        self.machine.is_armed()
477    }
478
479    pub fn is_in_failsafe(&self) -> bool {
480        self.machine.is_in_failsafe()
481    }
482
483    pub fn is_in_error_state(&self) -> bool {
484        self.machine.is_in_error_state()
485    }
486
487    pub fn get_errors(&self) -> ErrorFlag {
488        self.machine.get_errors()
489    }
490
491    fn update_leds(&self) {
492        // LED hardware output is synchronized by the world board stage.
493    }
494}