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#[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
22bitflags! {
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#[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 pub fn new() -> Self {
58 StateMachine::Init(State::<Init>::default())
59 }
60
61 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 pub fn update(&mut self, event: Event, params: &Params) {
76 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 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#[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
153impl 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
382pub 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 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 if self.get_errors().is_empty() {
466 self.update(Event::ERROR_CLEARED(ErrorFlag::default()), params);
468 } else {
469 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 }
494}