blackbox_log/
event.rs

1//! Types for blackbox log events.
2
3use tracing::instrument;
4
5use crate::parser::{decode, InternalError, InternalResult};
6use crate::Reader;
7
8/// A decoded blackbox log event.
9#[derive(Debug, Clone, PartialEq)]
10#[cfg_attr(feature = "_serde", derive(serde::Serialize))]
11pub enum Event {
12    SyncBeep(u64),
13    InflightAdjustment {
14        function: u8,
15        new_value: AdjustedValue,
16    },
17    Resume {
18        log_iteration: u32,
19        time: u32,
20    },
21    Disarm(u32),
22    FlightMode {
23        flags: u32,
24        last_flags: u32,
25    },
26    ImuFailure {
27        error: u32,
28    },
29    End {
30        disarm_reason: Option<u32>,
31    },
32}
33
34/// A new value decoded from an
35/// [`InflightAdjustment`](`Event::InflightAdjustment`) event.
36#[derive(Debug, Clone, Copy, PartialEq)]
37#[cfg_attr(feature = "_serde", derive(serde::Serialize))]
38pub enum AdjustedValue {
39    Float(f32),
40    Int(i32),
41}
42
43impl Event {
44    #[instrument(level = "debug", name = "Event::parse", skip_all, fields(kind))]
45    pub(crate) fn parse(data: &mut Reader) -> InternalResult<Self> {
46        let byte = data.read_u8().ok_or(InternalError::Eof)?;
47        let kind = EventKind::from_byte(byte).ok_or_else(|| {
48            tracing::debug!("found invalid event: {byte:0>#2x}");
49            InternalError::Retry
50        })?;
51
52        let event = match kind {
53            EventKind::SyncBeep => {
54                // TODO: SyncBeep handle time rollover
55
56                let time = decode::variable(data)?;
57                Self::SyncBeep(time.into())
58            }
59
60            EventKind::InflightAdjustment => {
61                let function = data.read_u8().ok_or(InternalError::Eof)?;
62
63                let new_value = if (function & 0x80) > 0 {
64                    AdjustedValue::Float(data.read_f32().ok_or(InternalError::Eof)?)
65                } else {
66                    AdjustedValue::Int(decode::variable_signed(data)?)
67                };
68
69                Self::InflightAdjustment {
70                    function: function & 0x7F,
71                    new_value,
72                }
73            }
74
75            EventKind::Resume => {
76                let log_iteration = decode::variable(data)?;
77                let time = decode::variable(data)?;
78
79                Self::Resume {
80                    log_iteration,
81                    time,
82                }
83            }
84
85            EventKind::Disarm => {
86                let reason = decode::variable(data)?;
87                Self::Disarm(reason)
88            }
89
90            EventKind::FlightMode => {
91                let flags = decode::variable(data)?;
92                let last_flags = decode::variable(data)?;
93                Self::FlightMode { flags, last_flags }
94            }
95
96            EventKind::ImuFailure => {
97                let error = decode::variable(data)?;
98                Self::ImuFailure { error }
99            }
100
101            EventKind::End => {
102                check_message(data, b"End of log")?;
103
104                let disarm_reason = if data.peek() == Some(b' ') {
105                    // Assume INAV's new format:
106                    // `End of log (disarm reason:x)\0`
107
108                    check_message(data, b" (disarm reason:")?;
109
110                    let reason = data.read_u8().ok_or(InternalError::Eof)?.into();
111
112                    if data.read_u8() != Some(b')') {
113                        return Err(InternalError::Retry);
114                    }
115
116                    Some(reason)
117                } else {
118                    None
119                };
120
121                if data.read_u8() != Some(0) {
122                    return Err(InternalError::Retry);
123                }
124
125                Self::End { disarm_reason }
126            }
127        };
128
129        Ok(event)
130    }
131}
132
133byte_enum! {
134    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
135    #[repr(u8)]
136    pub(crate) enum EventKind {
137        SyncBeep = 0,
138        InflightAdjustment = 13,
139        Resume = 14,
140        Disarm = 15,
141        FlightMode = 30,
142        ImuFailure = 40,
143        End = 255,
144    }
145}
146
147fn check_message(bytes: &mut Reader, message: &[u8]) -> InternalResult<()> {
148    let bytes = bytes.read_n_bytes(message.len());
149
150    if bytes.len() != message.len() {
151        return Err(InternalError::Eof);
152    }
153
154    if bytes != message {
155        return Err(InternalError::Retry);
156    }
157
158    Ok(())
159}