socketcan_hal/
dump.rs

1//! candump format parsing
2//!
3//! Parses the text format emitted by the `candump` utility, which is part of
4//! [can-utils](https://github.com/linux-can/can-utils).
5//!
6//! Example:
7//!
8//! ```text
9//! (1469439874.299654) can1 701#7F
10//! ```
11//!
12//! Can be parsed by a `Reader` object. The API is inspired by the
13//! [csv](https://crates.io/crates/csv) crate.
14
15use std::{fs, io, path};
16use hex::FromHex;
17
18// cannot be generic, because from_str_radix is not part of any Trait
19fn parse_raw(bytes: &[u8], radix: u32) -> Option<u64> {
20    ::std::str::from_utf8(bytes)
21        .ok()
22        .and_then(|s| u64::from_str_radix(s, radix).ok())
23}
24
25#[derive(Debug)]
26/// A CAN log reader.
27pub struct Reader<R> {
28    rdr: R,
29    line_buf: Vec<u8>,
30}
31
32impl<R: io::Read> Reader<R> {
33    pub fn from_reader(rdr: R) -> Reader<io::BufReader<R>> {
34        Reader {
35            rdr: io::BufReader::new(rdr),
36            line_buf: Vec::new(),
37        }
38    }
39}
40
41impl Reader<fs::File> {
42    pub fn from_file<P: AsRef<path::Path>>(path: P) -> io::Result<Reader<io::BufReader<fs::File>>> {
43        Ok(Reader::from_reader(fs::File::open(path)?))
44    }
45}
46
47/// Record iterator
48#[derive(Debug)]
49pub struct CanDumpRecords<'a, R: 'a> {
50    src: &'a mut Reader<R>,
51}
52
53/// Recorded CAN frame.
54#[derive(Debug)]
55pub struct CanDumpRecord<'a> {
56    pub t_us: u64,
57    pub device: &'a str,
58    pub frame: super::CanFrame,
59}
60
61#[derive(Debug)]
62/// candump line parse error
63pub enum ParseError {
64    Io(io::Error),
65    UnexpectedEndOfLine,
66    InvalidTimestamp,
67    InvalidDeviceName,
68    InvalidCanFrame,
69    ConstructionError(super::ConstructionError),
70}
71
72impl From<io::Error> for ParseError {
73    fn from(e: io::Error) -> ParseError {
74        ParseError::Io(e)
75    }
76}
77
78impl From<super::ConstructionError> for ParseError {
79    fn from(e: super::ConstructionError) -> ParseError {
80        ParseError::ConstructionError(e)
81    }
82}
83
84impl<R: io::BufRead> Reader<R> {
85    /// Returns an iterator over all records
86    pub fn records(&mut self) -> CanDumpRecords<R> {
87        CanDumpRecords { src: self }
88    }
89
90    /// Advance state, returning next record.
91    pub fn next_record(&mut self) -> Result<Option<CanDumpRecord>, ParseError> {
92        self.line_buf.clear();
93        let bytes_read = self.rdr.read_until(b'\n', &mut self.line_buf)?;
94
95        // reached EOF
96        if bytes_read == 0 {
97            return Ok(None);
98        }
99
100        let mut field_iter = self.line_buf.split(|&c| c == b' ');
101
102        // parse time field
103        let f = field_iter.next().ok_or(ParseError::UnexpectedEndOfLine)?;
104
105        if f.len() < 3 || f[0] != b'(' || f[f.len() - 1] != b')' {
106            return Err(ParseError::InvalidTimestamp);
107        }
108
109        let inner = &f[1..f.len() - 1];
110
111        // split at dot, read both parts
112        let dot = inner.iter()
113            .position(|&c| c == b'.')
114            .ok_or(ParseError::InvalidTimestamp)?;
115
116        let (num, mant) = inner.split_at(dot);
117
118        // parse number and multiply
119        let n_num: u64 = parse_raw(num, 10).ok_or(ParseError::InvalidTimestamp)?;
120        let n_mant: u64 = parse_raw(&mant[1..], 10).ok_or(ParseError::InvalidTimestamp)?;
121        let t_us = n_num.saturating_mul(1_000_000).saturating_add(n_mant);
122
123        let f = field_iter.next().ok_or(ParseError::UnexpectedEndOfLine)?;
124
125        // device name
126        let device = ::std::str::from_utf8(f).map_err(|_| ParseError::InvalidDeviceName)?;
127
128        // parse packet
129        let can_raw = field_iter.next()
130            .ok_or(ParseError::UnexpectedEndOfLine)?;
131
132        let sep_idx = can_raw.iter()
133            .position(|&c| c == b'#')
134            .ok_or(ParseError::InvalidCanFrame)?;
135        let (can_id, mut can_data) = can_raw.split_at(sep_idx);
136
137        // cut of linefeed and skip seperator
138        can_data = &can_data[1..];
139        if let Some(&b'\n') = can_data.last() {
140            can_data = &can_data[..can_data.len() - 1];
141        };
142
143        let rtr = b"R" == can_data;
144
145        let data = if rtr {
146            Vec::new()
147        } else {
148            Vec::from_hex(&can_data).map_err(|_| ParseError::InvalidCanFrame)?
149        };
150        let frame = super::CanFrame::new((parse_raw(can_id, 16)
151                                                  .ok_or
152
153
154                                                  (ParseError::InvalidCanFrame))?
155                                              as u32,
156                                              &data,
157                                              rtr,
158                                              // FIXME: how are error frames saved?
159                                              false)?;
160
161        Ok(Some(CanDumpRecord {
162            t_us,
163            device,
164            frame,
165        }))
166    }
167}
168
169impl<'a, R: io::Read> Iterator for CanDumpRecords<'a, io::BufReader<R>> {
170    type Item = Result<(u64, super::CanFrame), ParseError>;
171
172    fn next(&mut self) -> Option<Self::Item> {
173        // lift Option:
174        match self.src.next_record() {
175            Ok(Some(CanDumpRecord { t_us, frame, .. })) => Some(Ok((t_us, frame))),
176            Ok(None) => None,
177            Err(e) => Some(Err(e)),
178        }
179    }
180}
181
182#[cfg(test)]
183mod test {
184    use super::Reader;
185
186    #[test]
187    fn test_simple_example() {
188        let input: &[u8] = b"(1469439874.299591) can1 080#\n\
189                             (1469439874.299654) can1 701#7F";
190
191        let mut reader = Reader::from_reader(input);
192
193        {
194            let rec1 = reader.next_record().unwrap().unwrap();
195
196            assert_eq!(rec1.t_us, 1469439874299591);
197            assert_eq!(rec1.device, "can1");
198            assert_eq!(rec1.frame.id(), 0x080);
199            assert_eq!(rec1.frame.is_rtr(), false);
200            assert_eq!(rec1.frame.is_error(), false);
201            assert_eq!(rec1.frame.is_extended(), false);
202            assert_eq!(rec1.frame.data(), &[]);
203        }
204
205        {
206            let rec2 = reader.next_record().unwrap().unwrap();
207            assert_eq!(rec2.t_us, 1469439874299654);
208            assert_eq!(rec2.device, "can1");
209            assert_eq!(rec2.frame.id(), 0x701);
210            assert_eq!(rec2.frame.is_rtr(), false);
211            assert_eq!(rec2.frame.is_error(), false);
212            assert_eq!(rec2.frame.is_extended(), false);
213            assert_eq!(rec2.frame.data(), &[0x7F]);
214        }
215
216        assert!(reader.next_record().unwrap().is_none());
217    }
218
219
220}