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 + 4 + 4 + 4 }
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 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 + 1 + 2 + 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 }
190
191
192#[derive(Debug, Clone, Copy)]
193pub enum ControlInfo {
194 Request = 0x1,
195 Response = 0x2,
196 }
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 + 1 + 4 + 4 }
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}