fast_dlt/
header.rs

1use crate::{error::DltError, error::Result};
2use std::{str};
3
4use simdutf8::basic::from_utf8;
5
6#[derive(Debug, PartialEq, Eq)]
7pub struct StorageHeader<'a> {
8    pub seconds: u32,
9    pub microseconds: i32,
10    pub ecu_id: &'a str,
11}
12impl<'a> StorageHeader<'a> {
13    pub fn new(buf: &'a [u8]) -> Result<Self> {
14
15        if buf.len() < 4 + 4 + 4 + 4 {
16            return Err(DltError::NotEnoughData)
17        }
18
19        if &buf[..4] != b"DLT\x01" {
20            return Err(DltError::MissingDltPattern);
21        }
22        let seconds = u32::from_le_bytes(buf[4..8].try_into()?);
23        let microseconds = i32::from_le_bytes(buf[8..12].try_into()?);
24        let ecu_id = from_utf8(&buf[12..16])?.trim_end_matches('\0');
25        Ok(Self {
26            seconds,
27            microseconds,
28            ecu_id,
29        })
30    }
31
32    pub fn num_bytes(&self) -> usize {
33        4 /*DLT pattern*/ 
34        + 4 /*seconds*/ 
35        + 4 /*microseconds*/ 
36        + 4 /*ecu id*/
37    }
38}
39
40#[rustfmt::skip]
41#[derive(Debug)]
42#[repr(u8)]
43enum StdHeaderMask {
44    UseExtendedHeader = 0b00000001,
45    MsbFirst =          0b00000010,
46    WithEcuId =         0b00000100,
47    WithSessionId =     0b00001000,
48    WithTimestamp =     0b00010000,
49    VersionNumber =     0b11100000,
50}
51
52#[derive(Debug, PartialEq, Eq)]
53pub struct StandardHeader<'a> {
54    header_type: u8,
55    pub message_counter: u8,
56    pub length: u16,
57    pub ecu_id: Option<&'a str>,
58    pub session_id: Option<u32>,
59    pub timestamp: Option<u32>,
60}
61
62impl<'a> StandardHeader<'a> {
63    pub fn new(buf: &'a [u8]) -> Result<Self> {
64        let Some(header_type) = buf.first() else {
65            return Err(DltError::NotEnoughData)
66        };
67
68        let with_ecu_id = header_type & StdHeaderMask::WithEcuId as u8 == 0;
69        let with_session_id = header_type & StdHeaderMask::WithSessionId as u8 == 0;
70        let with_timestamp = header_type & StdHeaderMask::WithTimestamp as u8 == 0;
71
72        let min_size = 1+1 + 2 + with_ecu_id as usize * 4 + with_session_id as usize * 4 + with_timestamp as usize * 4;
73        if buf.len() < min_size {
74            return Err(DltError::NotEnoughData)
75        }
76
77        let message_counter = buf[1];
78
79        // use mem::transmute to convert to [u8;2]? only if todo is implemented
80        let length = u16::from_be_bytes(buf[2..4].try_into()?);
81
82        let mut optionals_offset = 0;
83        let ecu_id = if with_ecu_id {
84            None
85        } else {
86            optionals_offset += 4;
87            Some(from_utf8(
88                &buf[optionals_offset..optionals_offset + 4],
89            )?.trim_end_matches('\0'))
90        };
91        let session_id = if with_session_id {
92            None
93        } else {
94            optionals_offset += 4;
95            Some(u32::from_be_bytes(
96                buf[optionals_offset..optionals_offset + 4].try_into()?,
97            ))
98        };
99        let timestamp = if with_timestamp {
100            None
101        } else {
102            optionals_offset += 4;
103            Some(u32::from_be_bytes(
104                buf[optionals_offset..optionals_offset + 4].try_into()?,
105            ))
106        };
107
108        Ok(Self {
109            header_type: *header_type,
110            message_counter,
111            length,
112            ecu_id,
113            session_id,
114            timestamp,
115        })
116    }
117
118    pub fn use_extended_header(&self) -> bool {
119        self.header_type & StdHeaderMask::UseExtendedHeader as u8 != 0
120    }
121    pub fn msb_first(&self) -> bool {
122        self.header_type & StdHeaderMask::MsbFirst as u8 != 0
123    }
124    pub fn with_ecu_id(&self) -> bool {
125        self.header_type & StdHeaderMask::WithEcuId as u8 != 0
126    }
127    pub fn with_session_id(&self) -> bool {
128        self.header_type & StdHeaderMask::WithSessionId as u8 != 0
129    }
130    pub fn with_timestamp(&self) -> bool {
131        self.header_type & StdHeaderMask::WithTimestamp as u8 != 0
132    }
133    pub fn version(&self) -> u8 {
134        self.header_type & StdHeaderMask::VersionNumber as u8
135    }
136
137    pub fn big_endian(&self) -> bool {
138        self.header_type & StdHeaderMask::MsbFirst as u8 != 0
139    }
140
141    pub fn num_bytes(&self) -> usize {
142        1 /*header type*/
143        + 1 /*message_counter */
144        + 2 /*length */
145        + self.ecu_id.is_some() as usize * 4
146        + self.session_id.is_some() as usize * 4
147        + self.timestamp.is_some() as usize * 4
148    }
149}
150
151
152#[derive(Debug, Clone, Copy)]
153pub enum MessageType {
154    Log = 0x0,
155    AppTrace = 0x1,
156    NwTrace = 0x2,
157    Control = 0x3
158}
159
160#[derive(Debug, Clone, Copy)]
161pub enum LogInfo {
162    Fatal = 0x1,
163    Error = 0x2,
164    Warn = 0x3,
165    Info = 0x4,
166    Debug = 0x5,
167    Verbose = 0x6
168}
169
170#[derive(Debug, Clone, Copy)]
171pub enum TraceInfo {
172    Variable = 0x1,
173    FunctionIn = 0x2,
174    FunctionOut = 0x3,
175    State = 0x4,
176    Vfb = 0x5
177}
178
179#[derive(Debug, Clone, Copy)]
180pub enum BusInfo {
181    Ipc = 0x1,
182    Can = 0x2,
183    Flexray = 0x3,
184    Most = 0x4,
185    Ethernet = 0x5,
186    SomeIP = 0x6,
187    // UserDefined
188
189}
190
191
192#[derive(Debug, Clone, Copy)]
193pub enum ControlInfo {
194    Request = 0x1,
195    Response = 0x2,
196    // Time = 0x3 ??
197}
198
199#[derive(Debug, Clone, Copy)]
200pub enum MessageTypeInfo {
201    Log(LogInfo),
202    Trace(TraceInfo),
203    Bus(BusInfo),
204    Control(ControlInfo)
205}
206
207
208
209#[derive(Debug, PartialEq, Eq)]
210pub struct ExtendedHeader<'a> {
211    message_info: u8,
212    pub number_of_arguments: u8,
213    pub application_id: &'a str,
214    pub context_id: &'a str,
215}
216
217impl<'a> ExtendedHeader<'a> {
218    pub fn new(buf: &'a [u8]) -> Result<Self> {
219        if buf.len() < 1 + 1 + 4 + 4 {
220            return Err(DltError::NotEnoughData)
221        }
222
223        let message_info = buf[0];
224        let number_of_arguments = buf[1];
225        let application_id = from_utf8(&buf[2..6])?.trim_end_matches('\0');
226        let context_id = from_utf8(&buf[6..10])?.trim_end_matches('\0');
227        Ok(Self {
228            message_info,
229            number_of_arguments,
230            application_id,
231            context_id,
232        })
233    }
234
235    pub fn verbose(&self) -> bool {
236        self.message_info & 0b00000001 != 0
237    }
238
239    pub fn message_type(&self) -> MessageType {
240        match (self.message_info & 0b00001110) >> 1 {
241            0x0 => MessageType::Log,
242            0x1 => MessageType::AppTrace,
243            0x2 => MessageType::NwTrace,
244            0x3 => MessageType::Control,
245            _ => unreachable!()
246        }
247
248    }
249
250    pub fn type_info(&self) -> MessageTypeInfo {
251        match (self.message_type(), (self.message_info & 0b11110000) >> 4) {
252            (MessageType::Log, 0x1) => MessageTypeInfo::Log(LogInfo::Fatal),
253            (MessageType::Log, 0x2) => MessageTypeInfo::Log(LogInfo::Error),
254            (MessageType::Log, 0x3) => MessageTypeInfo::Log(LogInfo::Warn),
255            (MessageType::Log, 0x4) => MessageTypeInfo::Log(LogInfo::Info),
256            (MessageType::Log, 0x5) => MessageTypeInfo::Log(LogInfo::Debug),
257            (MessageType::Log, 0x6) => MessageTypeInfo::Log(LogInfo::Verbose),
258            (MessageType::AppTrace, 0x1) => MessageTypeInfo::Trace(TraceInfo::Variable),
259            (MessageType::AppTrace, 0x2) => MessageTypeInfo::Trace(TraceInfo::FunctionIn),
260            (MessageType::AppTrace, 0x3) => MessageTypeInfo::Trace(TraceInfo::FunctionOut),
261            (MessageType::AppTrace, 0x4) => MessageTypeInfo::Trace(TraceInfo::State),
262            (MessageType::AppTrace, 0x5) => MessageTypeInfo::Trace(TraceInfo::Vfb),
263            (MessageType::NwTrace, 0x1)=> MessageTypeInfo::Bus(BusInfo::Ipc),
264            (MessageType::NwTrace, 0x2)=> MessageTypeInfo::Bus(BusInfo::Can),
265            (MessageType::NwTrace, 0x3)=> MessageTypeInfo::Bus(BusInfo::Flexray),
266            (MessageType::NwTrace, 0x4)=> MessageTypeInfo::Bus(BusInfo::Most),
267            (MessageType::NwTrace, 0x5)=> MessageTypeInfo::Bus(BusInfo::Ethernet),
268            (MessageType::NwTrace, 0x6)=> MessageTypeInfo::Bus(BusInfo::SomeIP),
269            (MessageType::Control, 0x1) => MessageTypeInfo::Control(ControlInfo::Request),
270            (MessageType::Control, 0x2) => MessageTypeInfo::Control(ControlInfo::Response),
271            (message_type, info) => unreachable!("Unexpected: ({message_type:?}, {info})")
272        }
273    }
274
275    pub fn num_bytes(&self) -> usize {
276        1 /*message_info*/
277        + 1 /*number_of_arguments*/
278        + 4 /*application_id*/
279        + 4 /*context id*/
280    }
281}
282
283#[cfg(test)]
284mod test {
285
286    use super::*;
287
288    #[test]
289    fn storage_header() {
290        let bytes = b"DLT\x01\r\x00\x00\x00%\x00\x00\x00TEST";
291        let header = StorageHeader::new(bytes).unwrap();
292        assert_eq!(
293            header,
294            StorageHeader {
295                seconds: 13,
296                microseconds: 37,
297                ecu_id: "TEST"
298            }
299        );
300    }
301
302    #[test]
303    fn standard_header() {
304        let bytes = b"|\n\x00dTEST\x00\x00\x00\x03\x00\x00\x059";
305        let header = StandardHeader::new(bytes).unwrap();
306        assert_eq!(
307            header,
308            StandardHeader {
309                header_type: 124,
310                message_counter: 10,
311                length: 100,
312                ecu_id: Some("TEST"),
313                session_id: Some(3),
314                timestamp: Some(1337)
315            }
316        )
317    }
318    #[test]
319    fn extended_header() {
320        let bytes = b"@\x07APPLCONT";
321        let header = ExtendedHeader::new(bytes).unwrap();
322        assert_eq!(
323            header,
324            ExtendedHeader {
325                message_info: 64,
326                number_of_arguments: 7,
327                application_id: "APPL",
328                context_id: "CONT"
329            }
330        )
331    }
332}