1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use frame::event;
use itertools::Itertools;
use stream::{
    data::parse_next_frame,
    header::{parse_headers, Header},
    predictor::{LogProcessor, LogRecord},
};
use thiserror::Error;

extern crate itertools;

pub(crate) mod frame;
pub(crate) mod stream;

pub enum BlackboxRecord<'a> {
    Main(&'a [i64]),
    GNSS(&'a [i64]),
    Slow(Vec<i64>),
    Event(event::Frame),
    Garbage(usize),
}

pub struct BlackboxReader<'a> {
    last_values: Vec<i64>,
    remaining_bytes: &'a [u8],
    pub header: Header,
    processor: LogProcessor,
    pub last_loop_iteration: i64,
    pub last_time: i64,
    loop_iteration_field_ix: usize,
    time_field_ix: usize,
}

#[derive(Error, Debug)]
pub enum BlackboxReaderError {
    #[error("couldn't parse header")]
    ParseHeader,
    #[error("loopIteration or time I/P fields have not been found")]
    NoLoopIterationAndTime,
    #[error("log is truncated")]
    Incomplete,
}

impl<'a> BlackboxReader<'a> {
    pub fn from_bytes(bytes: &'a [u8]) -> Result<BlackboxReader<'a>, BlackboxReaderError> {
        let (remaining_bytes, header) = parse_headers(bytes).map_err(|e| match e {
            nom::Err::Error(_e) => BlackboxReaderError::ParseHeader,
            nom::Err::Failure(_e) => BlackboxReaderError::ParseHeader,
            nom::Err::Incomplete(_) => BlackboxReaderError::Incomplete,
        })?;

        let loop_iteration_field_ix = header
            .ip_fields_in_order
            .iter()
            .find_position(|f| f.name == "loopIteration")
            .ok_or(BlackboxReaderError::NoLoopIterationAndTime)?
            .0;

        let time_field_ix = header
            .ip_fields_in_order
            .iter()
            .find_position(|f| f.name == "time")
            .ok_or(BlackboxReaderError::NoLoopIterationAndTime)?
            .0;

        let last_values = Vec::with_capacity(
            header
                .ip_fields_in_order
                .len()
                .max(header.s_fields_in_order.len())
                .max(header.g_fields_in_order.len()),
        );

        Ok(BlackboxReader {
            remaining_bytes,
            processor: LogProcessor::new(&header),
            last_values,
            loop_iteration_field_ix,
            time_field_ix,
            header,
            last_loop_iteration: 0,
            last_time: 0,
        })
    }

    pub fn next(&mut self) -> Option<BlackboxRecord> {
        loop {
            match parse_next_frame(&self.header, self.remaining_bytes) {
                Ok((remaining_bytes, frame)) => {
                    self.remaining_bytes = remaining_bytes;
                    if let Some(record) = self.processor.process_frame(frame) {
                        return Some(match record {
                            LogRecord::Main(values) => {
                                self.last_loop_iteration = values[self.loop_iteration_field_ix];
                                self.last_time = values[self.time_field_ix];
                                self.last_values.clear();
                                self.last_values.extend_from_slice(values);
                                BlackboxRecord::Main(&self.last_values)
                            }
                            LogRecord::GNSS(values) => {
                                self.last_values.clear();
                                self.last_values.extend_from_slice(values);
                                BlackboxRecord::GNSS(&self.last_values)
                            }
                            LogRecord::Slow(values) => BlackboxRecord::Slow(values),
                            LogRecord::Event(event) => BlackboxRecord::Event(event),
                        });
                    }
                }
                Err(e) => match e {
                    nom::Err::Error(e) => {
                        if e.input.len() > 0 {
                            self.remaining_bytes = &e.input[1..];
                        }
                    }
                    nom::Err::Failure(e) => {
                        if e.input.len() > 0 {
                            self.remaining_bytes = &e.input[1..];
                        }
                    }
                    nom::Err::Incomplete(_) => {
                        return None;
                    }
                },
            }
        }
    }
}

#[cfg(test)]
mod tests {}