fc_blackbox/
lib.rs

1use frame::event;
2use itertools::Itertools;
3use nom::FindSubstring;
4use stream::{
5    data::parse_next_frame,
6    header::{parse_headers, Header},
7    predictor::{LogProcessor, LogRecord},
8};
9use thiserror::Error;
10
11extern crate itertools;
12
13pub mod frame;
14pub(crate) mod stream;
15
16pub enum BlackboxRecord<'a> {
17    Main(&'a [i64]),
18    GNSS(&'a [i64]),
19    Slow(Vec<i64>),
20    Event(event::Frame),
21    Garbage(usize),
22}
23
24#[derive(Copy, Clone)]
25pub enum Strictness {
26    Strict,
27    Lenient,
28}
29
30pub struct BlackboxReader<'a> {
31    strictness: Strictness,
32    last_values: Vec<i64>,
33    remaining_bytes: &'a [u8],
34    original_length: usize,
35    pub header: Header,
36    processor: LogProcessor,
37    pub last_loop_iteration: i64,
38    pub last_time: i64,
39    loop_iteration_field_ix: usize,
40    time_field_ix: usize,
41}
42
43#[derive(Error, Debug)]
44#[cfg_attr(test, derive(serde::Serialize))]
45pub enum BlackboxReaderError {
46    #[error("couldn't parse header")]
47    ParseHeader,
48    #[error("loopIteration or time I/P fields have not been found")]
49    NoLoopIterationAndTime,
50    #[error("log is truncated")]
51    Incomplete,
52}
53
54impl<'a> BlackboxReader<'a> {
55    pub fn new(bytes: &'a [u8], strictness: Strictness) -> Result<BlackboxReader<'a>, BlackboxReaderError> {
56        let original_length = bytes.len();
57        let (remaining_bytes, header) = parse_headers(bytes).map_err(|e| match e {
58            nom::Err::Error(_e) => BlackboxReaderError::ParseHeader,
59            nom::Err::Failure(_e) => BlackboxReaderError::ParseHeader,
60            nom::Err::Incomplete(_) => BlackboxReaderError::Incomplete,
61        })?;
62
63        let loop_iteration_field_ix = header
64            .ip_fields_in_order
65            .iter()
66            .find_position(|f| f.name == "loopIteration")
67            .ok_or(BlackboxReaderError::NoLoopIterationAndTime)?
68            .0;
69
70        let time_field_ix = header
71            .ip_fields_in_order
72            .iter()
73            .find_position(|f| f.name == "time")
74            .ok_or(BlackboxReaderError::NoLoopIterationAndTime)?
75            .0;
76
77        let last_values = Vec::with_capacity(
78            header
79                .ip_fields_in_order
80                .len()
81                .max(header.s_fields_in_order.len())
82                .max(header.g_fields_in_order.len()),
83        );
84
85        Ok(BlackboxReader {
86            remaining_bytes,
87            original_length,
88            processor: LogProcessor::new(&header),
89            last_values,
90            loop_iteration_field_ix,
91            time_field_ix,
92            header,
93            last_loop_iteration: 0,
94            last_time: 0,
95            strictness,
96        })
97    }
98
99    pub fn from_bytes(bytes: &'a [u8]) -> Result<BlackboxReader<'a>, BlackboxReaderError> {
100        Self::new(bytes, Strictness::Lenient)
101    }
102
103    pub fn next(&mut self) -> Option<BlackboxRecord> {
104        loop {
105            match parse_next_frame(&self.header, self.remaining_bytes) {
106                Ok((remaining_bytes, frame)) => {
107                    self.remaining_bytes = remaining_bytes;
108                    if let Some(record) = self.processor.process_frame(frame) {
109                        return Some(match record {
110                            LogRecord::Main(values) => {
111                                self.last_loop_iteration = values[self.loop_iteration_field_ix];
112                                self.last_time = values[self.time_field_ix];
113                                self.last_values.clear();
114                                self.last_values.extend_from_slice(values);
115                                BlackboxRecord::Main(&self.last_values)
116                            }
117                            LogRecord::GNSS(values) => {
118                                self.last_values.clear();
119                                self.last_values.extend_from_slice(values);
120                                BlackboxRecord::GNSS(&self.last_values)
121                            }
122                            LogRecord::Slow(values) => BlackboxRecord::Slow(values),
123                            LogRecord::Event(event) => BlackboxRecord::Event(event),
124                        });
125                    }
126                }
127                Err(e) => match e {
128                    nom::Err::Error(e) => {
129                        match self.strictness {
130                            Strictness::Strict => return None,
131                            Strictness::Lenient => if e.input.len() > 0 {
132                                self.remaining_bytes = &e.input[1..];
133                            }
134                        }
135                    }
136                    nom::Err::Failure(_) => {
137                        return None;
138                    }
139                    nom::Err::Incomplete(_) => {
140                        return None;
141                    }
142                },
143            }
144        }
145    }
146
147    pub fn bytes_read(&self) -> usize {
148        self.original_length - self.remaining_bytes.len()
149    }
150}
151
152pub struct MultiSegmentBlackboxReader<'a> {
153    remaining_bytes: &'a [u8],
154    strictness: Strictness,
155}
156
157impl<'a> MultiSegmentBlackboxReader<'a> {
158    pub fn new(bytes: &'a [u8], strictness: Strictness) -> Self {
159        Self {
160            remaining_bytes: bytes,
161            strictness,
162        }
163    }
164
165    pub fn from_bytes(bytes: &'a [u8]) -> Self {
166        Self::new(bytes, Strictness::Lenient)
167    }
168
169    pub fn successful_only(self) -> impl Iterator<Item = BlackboxReader<'a>> {
170        self.filter_map(|r| {
171            r.ok()
172        })
173    }
174}
175
176impl<'a> Iterator for MultiSegmentBlackboxReader<'a> {
177    type Item = Result<BlackboxReader<'a>, BlackboxReaderError>;
178
179    fn next(&mut self) -> Option<Self::Item> {
180        let pos = self.remaining_bytes.find_substring(&b"H Product:Blackbox"[..])?;
181        self.remaining_bytes = &self.remaining_bytes[pos..];
182        let reader = BlackboxReader::new(self.remaining_bytes, self.strictness);
183        if let Ok(reader) = &reader {
184            self.remaining_bytes = &self.remaining_bytes[reader.bytes_read()..];
185        } else {
186            self.remaining_bytes = &self.remaining_bytes[1..];
187        }
188        Some(reader)
189    }
190}
191
192#[cfg(test)]
193mod tests;