use chrono::{Local, TimeZone};
use serde::ser::{Serialize, Serializer};
use std::convert::TryInto;
use std::fmt;
use std::fmt::Write;
use std::io::BufRead;
#[derive(Clone, PartialEq, Eq, Copy, Hash)] pub struct DltChar4 {
char4: [u8; 4], }
impl DltChar4 {
pub fn from_buf(buf: &[u8]) -> DltChar4 {
DltChar4 {
char4: [buf[0], buf[1], buf[2], buf[3]],
}
}
pub fn from_str(astr: &str) -> Option<DltChar4> {
if !astr.is_ascii() {
return None;
}
let bytes = astr.as_bytes();
let mut chars: [u8; 4] = [0, 0, 0, 0]; for n in 0..std::cmp::min(bytes.len(), 4) {
chars[n] = bytes[n];
}
Some(DltChar4 { char4: chars })
}
}
impl Serialize for DltChar4 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl fmt::Debug for DltChar4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(&self.char4))
}
}
impl fmt::Display for DltChar4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let a_str: &str = if self.char4[0] > 0 {
if self.char4[1] > 0 {
if self.char4[2] > 0 {
if self.char4[3] > 0 {
std::str::from_utf8(&self.char4).unwrap()
} else {
std::str::from_utf8(&self.char4[0..3]).unwrap()
}
} else {
std::str::from_utf8(&self.char4[0..2]).unwrap()
}
} else {
std::str::from_utf8(&self.char4[0..1]).unwrap()
}
} else {
""
};
f.pad(a_str) }
}
#[derive(Debug)]
pub struct DltStorageHeader {
pub secs: u32,
pub micros: u32,
pub ecu: DltChar4,
}
pub const DLT_STORAGE_HEADER_PATTERN: u32 = 0x01544c44; const DLT_STORAGE_HEADER_SIZE: usize = 16;
const DLT_MIN_STD_HEADER_SIZE: usize = 4;
const MIN_DLT_MSG_SIZE: usize = DLT_STORAGE_HEADER_SIZE + DLT_MIN_STD_HEADER_SIZE;
const DLT_EXT_HEADER_SIZE: usize = 10;
impl DltStorageHeader {
fn from_buf(buf: &[u8]) -> Option<DltStorageHeader> {
if buf.len() < 16 {
return None;
}
let pat = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
if pat != DLT_STORAGE_HEADER_PATTERN {
return None;
}
let sh = DltStorageHeader {
secs: u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]),
micros: u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]),
ecu: DltChar4::from_buf(&buf[12..16]),
};
Some(sh)
}
fn from_msg(msg: &DltMessage) -> DltStorageHeader {
DltStorageHeader {
secs: (msg.reception_time_us / crate::utils::US_PER_SEC) as u32,
micros: (msg.reception_time_us % crate::utils::US_PER_SEC) as u32,
ecu: msg.ecu,
}
}
fn to_write(&self, writer: &mut impl std::io::Write) -> Result<usize, std::io::Error> {
let pat = DLT_STORAGE_HEADER_PATTERN;
let b1 = &u32::to_le_bytes(pat);
let b2 = &u32::to_le_bytes(self.secs);
let b3 = &u32::to_le_bytes(self.micros);
let bufs = &mut [
std::io::IoSlice::new(b1),
std::io::IoSlice::new(b2),
std::io::IoSlice::new(b3),
std::io::IoSlice::new(&self.ecu.char4),
];
writer.write_vectored(bufs)
}
fn reception_time_us(&self) -> u64 {
(self.secs as u64 * 1000_000) + self.micros as u64
}
#[allow(dead_code)]
fn reception_time_ms(&self) -> u64 {
(self.secs as u64 * 1000) + (self.micros / 1000) as u64
}
}
const DLT_STD_HDR_HAS_EXT_HDR: u8 = 1;
const DLT_STD_HDR_BIG_ENDIAN: u8 = 1 << 1;
const DLT_STD_HDR_HAS_ECU_ID: u8 = 1 << 2;
const DLT_STD_HDR_HAS_SESSION_ID: u8 = 1 << 3;
const DLT_STD_HDR_HAS_TIMESTAMP: u8 = 1 << 4;
const DLT_STD_HDR_VERSION: u8 = 0x1 << 5;
#[derive(Debug)]
pub struct DltStandardHeader {
pub htyp: u8,
pub mcnt: u8,
pub len: u16,
}
impl DltStandardHeader {
fn from_buf(buf: &[u8]) -> Option<DltStandardHeader> {
if buf.len() < 4 {
return None;
}
let htyp = buf[0];
let sh = DltStandardHeader {
htyp,
mcnt: buf[1],
len: u16::from_be_bytes([buf[2], buf[3]]), };
let dlt_vers = (htyp >> 5) & 0x07;
match dlt_vers {
1 => (),
_ => {
eprintln!("DltStandardHeader with unsupported version {}!", dlt_vers);
}
}
Some(sh)
}
fn to_write(
writer: &mut impl std::io::Write,
mcnt: u8,
ext_hdr: &Option<DltExtendedHeader>,
big_endian: bool,
ecu: Option<DltChar4>,
session_id: Option<u32>,
timestamp: Option<u32>,
payload: &Vec<u8>,
) -> Result<(), std::io::Error> {
let mut htyp: u8 = if big_endian {
DLT_STD_HDR_VERSION | DLT_STD_HDR_BIG_ENDIAN
} else {
DLT_STD_HDR_VERSION
};
let mut len: u16 = DLT_MIN_STD_HEADER_SIZE as u16;
if ecu.is_some() {
htyp |= DLT_STD_HDR_HAS_ECU_ID;
len += 4;
}
if session_id.is_some() {
htyp |= DLT_STD_HDR_HAS_SESSION_ID;
len += 4;
}
if timestamp.is_some() {
htyp |= DLT_STD_HDR_HAS_TIMESTAMP;
len += 4;
}
if ext_hdr.is_some() {
htyp |= DLT_STD_HDR_HAS_EXT_HDR;
len += DLT_EXT_HEADER_SIZE as u16;
}
len += payload.len() as u16;
let b1 = &[htyp, mcnt];
let b2 = &u16::to_be_bytes(len);
let bufs = &mut [std::io::IoSlice::new(b1), std::io::IoSlice::new(b2)];
writer.write_vectored(bufs)?;
if let Some(e) = ecu {
writer.write(&e.char4)?;
}
if let Some(s) = session_id {
writer.write(&u32::to_be_bytes(s))?;
}
if let Some(t) = timestamp {
writer.write(&u32::to_be_bytes(t))?;
}
if let Some(e) = ext_hdr {
e.to_write(writer)?;
}
if payload.len() > 0 {
writer.write(payload)?;
}
Ok(())
}
fn std_ext_header_size(&self) -> u16 {
let mut length = DLT_MIN_STD_HEADER_SIZE as u16;
if self.has_ecu_id() {
length += 4;
}
if self.has_session_id() {
length += 4;
}
if self.has_timestamp() {
length += 4;
}
if self.has_ext_hdr() {
length += DLT_EXT_HEADER_SIZE as u16;
}
length
}
fn has_ext_hdr(&self) -> bool {
(self.htyp & DLT_STD_HDR_HAS_EXT_HDR) > 0
}
fn is_big_endian(&self) -> bool {
(self.htyp & DLT_STD_HDR_BIG_ENDIAN) > 0
}
fn has_ecu_id(&self) -> bool {
(self.htyp & DLT_STD_HDR_HAS_ECU_ID) > 0
}
fn has_session_id(&self) -> bool {
(self.htyp & DLT_STD_HDR_HAS_SESSION_ID) > 0
}
fn has_timestamp(&self) -> bool {
(self.htyp & DLT_STD_HDR_HAS_TIMESTAMP) > 0
}
fn ecu(&self, add_header_buf: &[u8]) -> Option<DltChar4> {
if self.has_ecu_id() {
Some(DltChar4::from_buf(&add_header_buf[0..4]))
} else {
None
}
}
fn timestamp_dms(&self, add_header_buf: &[u8]) -> u32 {
if self.has_timestamp() {
let mut offset = if self.has_ecu_id() { 4 } else { 0 };
if self.has_session_id() {
offset += 4;
}
u32::from_be_bytes([
add_header_buf[offset],
add_header_buf[offset + 1],
add_header_buf[offset + 2],
add_header_buf[offset + 3],
])
} else {
0
}
}
}
#[derive(Debug, PartialEq)]
pub enum DltMessageLogType {
Fatal = 1,
Error,
Warn,
Info,
Debug,
Verbose,
}
impl DltMessageLogType {
fn from(mtin: u8) -> DltMessageLogType {
match mtin {
2 => DltMessageLogType::Error,
3 => DltMessageLogType::Warn,
4 => DltMessageLogType::Info,
5 => DltMessageLogType::Debug,
6 => DltMessageLogType::Verbose,
1 | _ => DltMessageLogType::Fatal,
}
}
}
#[derive(Debug, PartialEq)]
pub enum DltMessageTraceType {
Variable = 1,
FunctionIn,
FunctionOut,
State,
Vfb,
}
impl DltMessageTraceType {
fn from(mtin: u8) -> DltMessageTraceType {
match mtin {
2 => DltMessageTraceType::FunctionIn,
3 => DltMessageTraceType::FunctionOut,
4 => DltMessageTraceType::State,
5 => DltMessageTraceType::Vfb,
1 | _ => DltMessageTraceType::Variable,
}
}
}
#[derive(Debug, PartialEq)]
pub enum DltMessageNwType {
Ipc = 1,
Can,
Flexray,
Most,
Ethernet,
SomeIp,
}
impl DltMessageNwType {
fn from(mtin: u8) -> DltMessageNwType {
match mtin {
2 => DltMessageNwType::Can,
3 => DltMessageNwType::Flexray,
4 => DltMessageNwType::Most,
5 => DltMessageNwType::Ethernet,
6 => DltMessageNwType::SomeIp,
1 | _ => DltMessageNwType::Ipc,
}
}
}
#[derive(Debug, PartialEq)]
pub enum DltMessageControlType {
Request = 1,
Response,
Time,
}
impl DltMessageControlType {
fn from(mtin: u8) -> DltMessageControlType {
match mtin {
2 => DltMessageControlType::Response,
3 => DltMessageControlType::Time,
1 | _ => DltMessageControlType::Request,
}
}
}
#[derive(Debug, PartialEq)]
pub enum DltMessageType {
Log(DltMessageLogType),
AppTrace(DltMessageTraceType),
NwTrace(DltMessageNwType),
Control(DltMessageControlType),
}
#[derive(Debug)]
pub struct DltExtendedHeader {
pub(super) verb_mstp_mtin: u8,
pub(super) noar: u8,
pub(super) apid: DltChar4,
pub(super) ctid: DltChar4,
}
impl DltExtendedHeader {
fn from_buf(buf: &[u8]) -> Option<DltExtendedHeader> {
if buf.len() < DLT_EXT_HEADER_SIZE {
return None;
}
let eh = DltExtendedHeader {
verb_mstp_mtin: buf[0],
noar: buf[1],
apid: DltChar4::from_buf(&buf[2..6]),
ctid: DltChar4::from_buf(&buf[6..10]),
};
Some(eh)
}
pub fn is_verbose(&self) -> bool {
self.verb_mstp_mtin & 0x01 == 0x01
}
pub fn mstp(&self) -> DltMessageType {
let mstp = (self.verb_mstp_mtin >> 1) & 0x7;
let mtin = (self.verb_mstp_mtin >> 4) & 0xf;
match mstp {
1 => DltMessageType::AppTrace(DltMessageTraceType::from(mtin)),
2 => DltMessageType::NwTrace(DltMessageNwType::from(mtin)),
3 => DltMessageType::Control(DltMessageControlType::from(mtin)),
0 | _ => DltMessageType::Log(DltMessageLogType::from(mtin)),
}
}
fn to_write(&self, writer: &mut impl std::io::Write) -> Result<usize, std::io::Error> {
let b1 = &[self.verb_mstp_mtin, self.noar];
let bufs = &mut [
std::io::IoSlice::new(b1),
std::io::IoSlice::new(&self.apid.char4),
std::io::IoSlice::new(&self.ctid.char4),
];
writer.write_vectored(bufs)
}
}
pub type DltMessageIndexType = u32;
#[derive(Debug)]
pub struct DltMessage {
pub index: DltMessageIndexType,
pub(super) reception_time_us: u64, pub ecu: DltChar4,
pub timestamp_dms: u32, pub(super) standard_header: DltStandardHeader,
pub(super) extended_header: Option<DltExtendedHeader>, pub(super) payload: Vec<u8>,
pub lifecycle: crate::lifecycle::LifecycleId, }
#[cfg(test)]
static NEXT_TEST_TIMESTAMP: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
static DEFAULT_APID_CTID: DltChar4 = DltChar4 {
char4: ['-' as u8, '-' as u8, '-' as u8, '-' as u8],
};
static LOG_LEVEL_STRS: [&str; 7] = ["", "fatal", "error", "warn", "info", "debug", "verbose"];
static TRACE_TYPE_STRS: [&str; 6] = ["", "variable", "func_in", "func_out", "state", "vfb"];
static NW_TYPE_STRS: [&str; 7] = ["", "ipc", "can", "flexray", "most", "ethernet", "someip"];
static CONTROL_TYPE_STRS: [&str; 4] = ["", "request", "response", "time"];
static SERVICE_ID_NAMES: [&str; 21] = [
"",
"set_log_level",
"set_trace_status",
"get_log_info",
"get_default_log_level",
"store_config",
"reset_to_factory_default",
"set_com_interface_status",
"set_com_interface_max_bandwidth",
"set_verbose_mode",
"set_message_filtering",
"set_timing_packets",
"get_local_time",
"use_ecu_id",
"use_session_id",
"use_timestamp",
"use_extended_header",
"set_default_log_level",
"set_default_trace_status",
"get_software_version",
"message_buffer_overflow",
];
static CTRL_RESPONSE_STRS: [&str; 9] = [
"ok",
"not_supported",
"error",
"perm_denied",
"warning",
"",
"",
"",
"no_matching_context_id",
];
impl DltMessage {
pub fn timestamp_us(&self) -> u64 {
return self.timestamp_dms as u64 * 100;
}
pub fn reception_time(&self) -> chrono::NaiveDateTime {
crate::utils::utc_time_from_us(self.reception_time_us)
}
pub fn mcnt(&self) -> u8 {
self.standard_header.mcnt
}
pub fn mstp(&self) -> DltMessageType {
match &self.extended_header {
Some(e) => e.mstp(),
None => DltMessageType::Log(DltMessageLogType::Fatal), }
}
pub fn is_ctrl_request(&self) -> bool {
match &self.extended_header {
Some(e) => {
if (e.verb_mstp_mtin >> 1) & 0x07 == 3 && (e.verb_mstp_mtin>>4 & 0x0f) == 1
{
true
} else {
false
}
}
None => false,
}
}
pub fn noar(&self) -> u8 {
match &self.extended_header {
Some(e) => e.noar,
None => 0,
}
}
pub fn is_verbose(&self) -> bool {
match &self.extended_header {
Some(e) => e.is_verbose(),
None => false, }
}
fn from(
index: DltMessageIndexType,
storage_header: DltStorageHeader,
standard_header: DltStandardHeader,
add_header_buf: &[u8],
payload: Vec<u8>,
) -> DltMessage {
let ecu = standard_header
.ecu(add_header_buf)
.unwrap_or_else(|| storage_header.ecu.clone());
let timestamp_dms = standard_header.timestamp_dms(add_header_buf);
let extended_header = if standard_header.has_ext_hdr() {
DltExtendedHeader::from_buf(
&add_header_buf[add_header_buf.len() - DLT_EXT_HEADER_SIZE..],
)
} else {
None
};
DltMessage {
index,
reception_time_us: storage_header.reception_time_us(),
ecu: ecu,
timestamp_dms,
standard_header,
extended_header,
payload,
lifecycle: 0,
}
}
pub fn to_write(&self, writer: &mut impl std::io::Write) -> Result<(), std::io::Error> {
let storage_header = DltStorageHeader::from_msg(self);
storage_header.to_write(writer)?;
DltStandardHeader::to_write(
writer,
self.standard_header.mcnt,
&self.extended_header,
self.standard_header.is_big_endian(), None, None, Some(self.timestamp_dms),
&self.payload,
)?;
Ok(())
}
pub fn header_as_text_to_write(
&self,
writer: &mut impl std::io::Write,
) -> Result<(), std::io::Error> {
write!(
writer,
"{index} {reception_time} {timestamp_dms:10} {mcnt:03} {ecu:-<4} {apid:-<4} {ctid:-<4} ",
index = self.index,
reception_time = Local
.from_utc_datetime(&self.reception_time())
.format("%Y/%m/%d %H:%M:%S%.6f"),
timestamp_dms = self.timestamp_dms,
mcnt = self.mcnt(),
ecu = self.ecu,
apid = self.apid().unwrap_or(&DEFAULT_APID_CTID).to_string(),
ctid = self.ctid().unwrap_or(&DEFAULT_APID_CTID).to_string(),
);
if self.extended_header.is_some() {
match self.mstp() {
DltMessageType::Control(ct) => {
write!(writer, "control {type}", type = CONTROL_TYPE_STRS[ct as usize]);
}
DltMessageType::AppTrace(tt) => {
write!(writer, "app_trace {type}", type = TRACE_TYPE_STRS[tt as usize]);
}
DltMessageType::NwTrace(nt) => {
write!(writer, "nw_trace {type}", type = NW_TYPE_STRS[nt as usize]);
}
DltMessageType::Log(lt) => {
write!(writer, "log {level}", level = LOG_LEVEL_STRS[lt as usize]);
}
}
if self.is_verbose() {
writer.write(&[' ' as u8, 'V' as u8])?;
} else {
writer.write(&[' ' as u8, 'N' as u8])?;
}
} else {
write!(writer, "--- --- N -");
}
write!(writer, " {}", self.noar());
Ok(())
}
pub fn payload_as_text(&self) -> String {
let mut text = String::new();
let mut args = self.into_iter();
if self.is_verbose() {
let mut nr_arg = 0;
for arg in args {
if nr_arg > 0 {
write!(text, " ");
}
nr_arg += 1;
let _tyle = arg.type_info & 0x0f;
let is_bool = arg.type_info & 0x10u32 > 0;
let is_sint = arg.type_info & 0x20u32 > 0;
let is_uint = arg.type_info & 0x40u32 > 0;
let is_floa = arg.type_info & 0x80u32 > 0;
let _is_aray = arg.type_info & 0x100u32 > 0;
let is_strg = arg.type_info & 0x200u32 > 0;
let is_rawd = arg.type_info & 0x400u32 > 0;
if is_bool {
let val = arg.payload_raw[0];
if val > 0 {
write!(text, "true");
} else {
write!(text, "false");
}
}
if is_uint {
write!(
text,
"<uint {} {:?}>",
arg.payload_raw.len(),
arg.payload_raw
);
match arg.payload_raw.len() {
1 => {
let val: u8 = arg.payload_raw[0];
write!(text, "{}", val);
}
2 => {
let val: u16 = if arg.is_big_endian {
u16::from_be_bytes(arg.payload_raw.try_into().unwrap())
} else {
u16::from_le_bytes(arg.payload_raw.try_into().unwrap())
};
write!(text, "{}", val);
}
4 => {
let val: u32 = if arg.is_big_endian {
u32::from_be_bytes(arg.payload_raw.try_into().unwrap())
} else {
u32::from_le_bytes(arg.payload_raw.try_into().unwrap())
};
write!(text, "{}", val);
}
8 => {
let val: u64 = if arg.is_big_endian {
u64::from_be_bytes(arg.payload_raw.try_into().unwrap())
} else {
u64::from_le_bytes(arg.payload_raw.try_into().unwrap())
};
write!(text, "{}", val);
}
_ => (),
};
}
if is_sint {
match arg.payload_raw.len() {
1 => {
let val: i8 = arg.payload_raw[0] as i8;
write!(text, "{}", val);
}
2 => {
let val: i16 = if arg.is_big_endian {
i16::from_be_bytes(arg.payload_raw.try_into().unwrap())
} else {
i16::from_le_bytes(arg.payload_raw.try_into().unwrap())
};
write!(text, "{}", val);
}
4 => {
let val: i32 = if arg.is_big_endian {
i32::from_be_bytes(arg.payload_raw.try_into().unwrap())
} else {
i32::from_le_bytes(arg.payload_raw.try_into().unwrap())
};
write!(text, "{}", val);
}
8 => {
let val: i64 = if arg.is_big_endian {
i64::from_be_bytes(arg.payload_raw.try_into().unwrap())
} else {
i64::from_le_bytes(arg.payload_raw.try_into().unwrap())
};
write!(text, "{}", val);
}
_ => (),
};
}
if is_floa {
write!(text, "<floa>");
}
if is_rawd {
write!(text, "<rawd>");
}
if is_strg {
let scod = (arg.type_info >> 15) & 0x03;
match scod {
0 | 1 => {
if arg.payload_raw.len() > 1 {
let s = String::from_utf8(
arg.payload_raw
.get(0..arg.payload_raw.len() - 1)
.unwrap()
.to_vec(),
); match s {
Ok(s) => {
let s = s.replace("\n", " ");
write!(text, "{}", s);
}
Err(e) => {
write!(text, "!utf8-conv error {:?}", e);
}
};
}
}
_ => {
write!(text, "<scod unknown {}>", scod);
}
}
}
}
} else {
let message_id_arg = args.next();
let message_id = match message_id_arg {
Some(a) => {
if a.is_big_endian {
u32::from_be_bytes(a.payload_raw.get(0..4).unwrap().try_into().unwrap())
} else {
u32::from_le_bytes(a.payload_raw.get(0..4).unwrap().try_into().unwrap())
}
}
None => 0,
};
let payload_arg = args.next();
let payload = match payload_arg {
Some(a) => a.payload_raw,
None => &[],
};
match self.mstp() {
DltMessageType::Control(ct) => {
if message_id > 0 && message_id < SERVICE_ID_NAMES.len() as u32 {
write!(&mut text, "{}", SERVICE_ID_NAMES[message_id as usize]);
} else if ct != DltMessageControlType::Time {
write!(&mut text, "service({})", message_id);
}
if payload.len() > 0 {
write!(&mut text, ", ");
}
match ct {
DltMessageControlType::Response => {
if payload.len() > 0 {
let retval = payload.get(0).unwrap();
if *retval < 5u8 || *retval == 8u8 {
write!(&mut text, "{}", CTRL_RESPONSE_STRS[*retval as usize]);
} else {
write!(&mut text, "{:02x}", *retval);
}
write!(&mut text, ", ");
if payload.len() > 1 {
crate::utils::buf_as_hex_to_write(
&mut text,
payload.get(1..).unwrap(),
)
.unwrap(); }
}
}
_ => {
crate::utils::buf_as_hex_to_write(&mut text, payload).unwrap();
}
}
}
_ => {
write!(&mut text, "{} {:x?}", message_id, &payload);
}
}
}
text
}
#[cfg(test)]
pub fn for_test() -> DltMessage {
let timestamp_us =
100 * NEXT_TEST_TIMESTAMP.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
DltMessage {
index: 0,
reception_time_us: 100_000 + timestamp_us,
ecu: DltChar4::from_buf(b"TEST"),
timestamp_dms: (timestamp_us / 100) as u32,
standard_header: DltStandardHeader {
htyp: 1,
len: 0,
mcnt: 0,
},
extended_header: None,
payload: [].to_vec(),
lifecycle: 0,
}
}
pub fn is_big_endian(&self) -> bool {
self.standard_header.is_big_endian()
}
pub fn apid(&self) -> Option<&DltChar4> {
match &self.extended_header {
None => None,
Some(e) => Some(&e.apid),
}
}
pub fn ctid(&self) -> Option<&DltChar4> {
match &self.extended_header {
None => None,
Some(e) => Some(&e.ctid),
}
}
}
pub struct DltMessageArgIterator<'a> {
msg: &'a DltMessage,
is_verbose: bool,
is_big_endian: bool,
index: usize,
}
#[derive(Debug)]
pub struct DltArg<'a> {
type_info: u32, is_big_endian: bool, payload_raw: &'a [u8], }
impl<'a> IntoIterator for &'a DltMessage {
type Item = DltArg<'a>; type IntoIter = DltMessageArgIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
DltMessageArgIterator {
msg: self,
is_verbose: self.is_verbose(),
is_big_endian: self.is_big_endian(),
index: 0,
}
}
}
impl<'a> Iterator for DltMessageArgIterator<'a> {
type Item = DltArg<'a>; fn next(&mut self) -> Option<Self::Item> {
if self.is_verbose {
if self.msg.payload.len() >= self.index + 4 {
let type_info = if self.is_big_endian {
u32::from_be_bytes(
self.msg
.payload
.get(self.index..self.index + 4)
.unwrap()
.try_into()
.unwrap(),
)
} else {
u32::from_le_bytes(
self.msg
.payload
.get(self.index..self.index + 4)
.unwrap()
.try_into()
.unwrap(),
)
};
self.index += 4;
let tyle = type_info & 0x0f; let mut len: usize = match tyle {
1 => 1,
2 => 2,
3 => 4,
4 => 8,
5 => 16,
_ => 0,
};
if type_info & (0x1u32 << 11) != 0 {
assert!(false, "type_info VARI not supported yet!");
}
if type_info & (0x1u32 << 12) != 0 {
assert!(false, "type_info FIXP not supported yet!");
}
if type_info & 0x10u32 != 0 {
if len != 1 {
if len != 0 {
println!(
"type bool expects to have len 1 has len {} index={} msg={:?}",
len, self.index, self.msg
);
}
len = 1;
}
} else if type_info & (0x60u32) != 0 {
assert!(len > 0);
} else if type_info & (0x80u32) != 0 {
assert!(
len == 4 || len == 8,
"unexpected len={} for FLOA type_info=0x{:x} index={}, msg={:?}",
len,
type_info,
self.index,
self.msg
);
} else if type_info & (0x03u32 << 9) != 0 {
if self.msg.payload.len() < self.index + 2 {
return None;
}
len = if self.is_big_endian {
u16::from_be_bytes(
self.msg
.payload
.get(self.index..self.index + 2)
.unwrap()
.try_into()
.unwrap(),
) as usize
} else {
u16::from_le_bytes(
self.msg
.payload
.get(self.index..self.index + 2)
.unwrap()
.try_into()
.unwrap(),
) as usize
};
self.index += 2;
let to_ret = if len > 0 && self.msg.payload.len() >= self.index + len {
Some(DltArg {
type_info,
is_big_endian: self.is_big_endian,
payload_raw: self
.msg
.payload
.get(self.index..self.index + len)
.unwrap(),
})
} else {
assert!(
false,
"not enough payload for the string. expected len={} got={}",
len,
self.msg.payload.len() - self.index
);
None
};
self.index += len; return to_ret;
} else {
assert!(
false,
"type_info=0x{:x} unhandled! is_big_endian={}, index={}, msg={:?}",
type_info, self.is_big_endian, self.index, self.msg
);
}
let to_ret = if len > 0 && self.msg.payload.len() >= self.index + len {
Some(DltArg {
type_info,
is_big_endian: self.is_big_endian,
payload_raw: self.msg.payload.get(self.index..self.index + len).unwrap(),
})
} else {
None
};
self.index += len; return to_ret;
} else {
if self.msg.payload.len() > self.index {
assert!(false, "have unhandled payload data");
}
}
} else {
return match self.index {
0 => {
if self.msg.payload.len() >= 4 {
self.index += 4;
Some(DltArg {
type_info: 0,
is_big_endian: self.is_big_endian,
payload_raw: self.msg.payload.get(0..4).unwrap(),
})
} else {
None
}
}
4 => {
if self.msg.payload.len() > 4 {
self.index = self.msg.payload.len();
Some(DltArg {
type_info: 0,
is_big_endian: self.is_big_endian,
payload_raw: self.msg.payload.get(4..).unwrap(),
}) } else {
None
}
}
_ => None,
};
}
None
}
}
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
}
impl Error {
pub fn new(kind: ErrorKind) -> Error {
Error { kind }
}
pub fn kind(&self) -> &ErrorKind {
return &self.kind;
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind {
ErrorKind::NotEnoughData(amount) => {
write!(f, "not enough data - missing at least {} bytes", amount)
}
ErrorKind::InvalidData(ref desc) => {
write!(f, "invalid data - {}", desc)
}
ErrorKind::OtherFatal(ref desc) => {
write!(f, "other fatal error! - {}", desc)
}
}
}
}
#[derive(Debug)]
pub enum ErrorKind {
InvalidData(String),
NotEnoughData(usize),
OtherFatal(String),
}
pub fn parse_dlt_with_storage_header(
index: DltMessageIndexType,
data: &mut impl BufRead,
) -> Result<(usize, DltMessage), Error> {
let peek_buf = data.fill_buf().unwrap(); let mut remaining = peek_buf.len();
if remaining >= MIN_DLT_MSG_SIZE {
match DltStorageHeader::from_buf(peek_buf) {
Some(sh) => {
remaining -= DLT_STORAGE_HEADER_SIZE;
let stdh = DltStandardHeader::from_buf(&peek_buf[DLT_STORAGE_HEADER_SIZE..])
.expect("no valid stdheader!");
let std_ext_header_size = stdh.std_ext_header_size();
if stdh.len >= std_ext_header_size {
if remaining >= stdh.len as usize {
remaining -= std_ext_header_size as usize;
let payload_offset = DLT_STORAGE_HEADER_SIZE + std_ext_header_size as usize;
let payload_size = stdh.len - std_ext_header_size as u16;
remaining -= payload_size as usize;
let to_consume = peek_buf.len() - remaining;
let payload = Vec::from(
&peek_buf[payload_offset..payload_offset + payload_size as usize],
);
let msg = DltMessage::from(
index,
sh,
stdh,
&peek_buf
[DLT_STORAGE_HEADER_SIZE + DLT_MIN_STD_HEADER_SIZE..payload_offset],
payload,
);
data.consume(to_consume);
Ok((to_consume, msg))
} else {
Err(Error::new(ErrorKind::NotEnoughData(
stdh.len as usize - remaining,
)))
}
} else {
Err(Error::new(ErrorKind::InvalidData(String::from(
"stdh.len too small",
))))
}
}
None => Err(Error::new(ErrorKind::InvalidData(String::from(
"no storageheader",
)))),
}
} else {
Err(Error::new(ErrorKind::NotEnoughData(
MIN_DLT_MSG_SIZE - remaining,
)))
}
}
#[cfg(test)]
mod tests {
use super::*;
mod dlt_storage_header {
use super::*;
#[test]
fn from_buf_invalid() {
let buf: Vec<u8> = vec![
0x41, 0x4c, 0x54, 0x01, 224, 181, 124, 94, 0, 0, 0, 0, 0x45, 0x43, 0x55, 0x31,
];
let shdr = DltStorageHeader::from_buf(&buf);
assert_eq!(shdr.is_none(), true);
let buf: Vec<u8> = vec![
0x41, 0x4c, 0x54, 0x01, 224, 181, 124, 94, 0, 0, 0, 0, 0x45, 0x43, 0x55,
];
let shdr = DltStorageHeader::from_buf(&buf);
assert_eq!(shdr.is_none(), true);
}
#[test]
fn from_buf_valid1() {
let buf: Vec<u8> = vec![
0x44, 0x4c, 0x54, 0x01, 224, 181, 124, 94, 0, 0, 0, 0, 0x45, 0x43, 0x55, 0x31,
];
let shdr =
DltStorageHeader::from_buf(&buf).expect("failed to parse valid storage header");
assert_eq!(shdr.secs, 1585231328); assert_eq!(shdr.micros, 0);
assert_eq!(&shdr.ecu.char4, b"ECU1");
assert_eq!(
shdr.reception_time_ms() as u64 * 1000,
shdr.reception_time_us()
);
}
#[test]
fn dltchar4_format() {
assert_eq!(format!("{}", DltChar4::from_str("----").unwrap()), "----");
assert_eq!(format!("{}", DltChar4::from_str("ECU").unwrap()), "ECU");
assert_eq!(format!("{}", DltChar4::from_str("EC").unwrap()), "EC");
assert_eq!(format!("{}", DltChar4::from_str("E").unwrap()), "E");
assert_eq!(format!("{:4}", DltChar4::from_str("E").unwrap()), "E ");
assert_eq!(
format!("{:-<4}", DltChar4::from_str("E").unwrap().to_string()),
"E---"
);
assert_eq!(format!("{:-<4}", DltChar4::from_str("E").unwrap()), "E---");
}
}
mod dlt_extended_header {
use super::*;
#[test]
fn verbose() {
let eh = DltExtendedHeader::from_buf(&[0, 0, 1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
assert_eq!(eh.is_verbose(), false);
let eh = DltExtendedHeader::from_buf(&[1, 0, 1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
assert_eq!(eh.is_verbose(), true);
let eh = DltExtendedHeader::from_buf(&[0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
assert_eq!(eh.is_verbose(), true);
let eh = DltExtendedHeader::from_buf(&[0xfe, 0, 1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
assert_eq!(eh.is_verbose(), false);
}
#[test]
fn mstp() {
let eh = DltExtendedHeader::from_buf(&[0, 0, 1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
assert_eq!(eh.mstp(), DltMessageType::Log(DltMessageLogType::Fatal)); let eh = DltExtendedHeader::from_buf(&[1u8 << 4, 0, 1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
assert_eq!(eh.mstp(), DltMessageType::Log(DltMessageLogType::Fatal));
let eh =
DltExtendedHeader::from_buf(&[(0 << 1) | (3 << 4) | 1, 0, 1, 2, 3, 4, 5, 6, 7, 8])
.unwrap();
assert_eq!(eh.mstp(), DltMessageType::Log(DltMessageLogType::Warn));
let eh = DltExtendedHeader::from_buf(&[(1 << 1) | (1 << 4), 0, 1, 2, 3, 4, 5, 6, 7, 8])
.unwrap();
assert_eq!(
eh.mstp(),
DltMessageType::AppTrace(DltMessageTraceType::Variable)
);
let eh = DltExtendedHeader::from_buf(&[(3 << 1) | (2 << 4), 0, 1, 2, 3, 4, 5, 6, 7, 8])
.unwrap();
assert_eq!(
eh.mstp(),
DltMessageType::Control(DltMessageControlType::Response)
);
}
}
mod dlt_message {
use super::*;
#[test]
fn arg_iter() {
let m = DltMessage::for_test();
assert_eq!(m.is_verbose(), false);
let args_iter = m.into_iter();
assert_eq!(args_iter.count(), 0);
let m = DltMessage {
index: 0,
reception_time_us: 0,
ecu: DltChar4::from_buf(b"ECU1"),
timestamp_dms: 0,
standard_header: DltStandardHeader {
htyp: 1,
len: 100,
mcnt: 0,
},
extended_header: Some(DltExtendedHeader {
verb_mstp_mtin: 0, noar: 2,
apid: DltChar4::from_buf(b"APID"),
ctid: DltChar4::from_buf(b"CTID"),
}),
lifecycle: 0,
payload: vec![1, 2, 3, 4, 5],
};
let args_iter = m.into_iter();
assert_eq!(args_iter.count(), 2);
let mut args_iter = m.into_iter();
assert_eq!(args_iter.next().unwrap().payload_raw, vec!(1, 2, 3, 4));
assert_eq!(args_iter.next().unwrap().payload_raw, vec!(5));
assert_eq!(args_iter.next().is_none(), true);
let m = DltMessage {
index: 0,
reception_time_us: 0,
ecu: DltChar4::from_buf(b"ECU1"),
timestamp_dms: 0,
standard_header: DltStandardHeader {
htyp: 1,
len: 100,
mcnt: 0,
},
extended_header: Some(DltExtendedHeader {
verb_mstp_mtin: 0, noar: 2,
apid: DltChar4::from_buf(b"APID"),
ctid: DltChar4::from_buf(b"CTID"),
}),
lifecycle: 0,
payload: vec![1, 2, 3, 4],
};
let args_iter = m.into_iter();
assert_eq!(args_iter.count(), 1);
let m = DltMessage {
index: 0,
reception_time_us: 0,
ecu: DltChar4::from_buf(b"ECU1"),
timestamp_dms: 0,
standard_header: DltStandardHeader {
htyp: 0, len: 100,
mcnt: 0,
},
extended_header: Some(DltExtendedHeader {
verb_mstp_mtin: 1, noar: 2,
apid: DltChar4::from_buf(b"APID"),
ctid: DltChar4::from_buf(b"CTID"),
}),
lifecycle: 0,
payload: vec![0x11, 0, 0, 0, 1, 0x11, 0, 0, 0, 0], };
assert_eq!(u32::from_be_bytes([0, 0, 0, 0x10]), 0x10);
assert_eq!(u32::from_le_bytes([0x10, 0, 0, 0]), 0x10);
let args_iter = m.into_iter();
assert_eq!(args_iter.count(), 2);
let mut args_iter = m.into_iter();
assert_eq!(args_iter.next().unwrap().payload_raw, vec!(1));
assert_eq!(args_iter.next().unwrap().payload_raw, vec!(0));
}
}
}