use crate::{
dlt::{
float_width_to_type_length, ApplicationTraceType, Argument, ControlType, DltTimeStamp,
Endianness, ExtendedHeader, FixedPoint, LogLevel, Message, MessageType, NetworkTraceType,
PayloadContent, StandardHeader, StorageHeader, StringCoding, TypeInfo, TypeInfoKind, Value,
},
fibex::{ApplicationId, ContextId, FibexMetadata, FrameId, FrameMetadata},
parse::{dlt_fint, dlt_fixed_point, dlt_sint, dlt_uint},
service_id::SERVICE_ID_MAPPING,
};
use byteorder::{BigEndian, ByteOrder, LittleEndian};
use chrono::{
prelude::{DateTime, Utc},
NaiveDateTime,
};
use chrono_tz::Tz;
use std::{
fmt::{self, Formatter},
str,
};
const DLT_COLUMN_SENTINAL: char = '\u{0004}';
const DLT_ARGUMENT_SENTINAL: char = '\u{0005}';
const DLT_NEWLINE_SENTINAL_SLICE: &[u8] = &[0x6];
lazy_static::lazy_static! {
static ref DLT_NEWLINE_SENTINAL_STR: &'static str =
unsafe { str::from_utf8_unchecked(DLT_NEWLINE_SENTINAL_SLICE) };
}
impl fmt::Display for MessageType {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match self {
MessageType::ApplicationTrace(app_type) => app_type.fmt(f),
MessageType::Control(c) => c.fmt(f),
MessageType::Log(log_level) => log_level.fmt(f),
MessageType::NetworkTrace(trace_type) => trace_type.fmt(f),
MessageType::Unknown(v) => write!(f, "Unkown MSTP {:?}", v),
}
}
}
impl fmt::Display for StorageHeader {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(
f,
"{}{}{}",
self.timestamp, DLT_COLUMN_SENTINAL, self.ecu_id
)
}
}
impl StorageHeader {
pub fn as_tz_string(&self, tz: &Tz) -> String {
format!(
"{}{}{}",
self.timestamp.tz_string(tz),
DLT_COLUMN_SENTINAL,
self.ecu_id
)
}
}
impl fmt::Display for ApplicationTraceType {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match self {
ApplicationTraceType::Variable => f.write_str("VARIABLE"),
ApplicationTraceType::FunctionIn => f.write_str("FUNC_IN"),
ApplicationTraceType::FunctionOut => f.write_str("FUNC_OUT"),
ApplicationTraceType::State => f.write_str("STATE"),
ApplicationTraceType::Vfb => f.write_str("VFB"),
ApplicationTraceType::Invalid(n) => write!(f, "invalid({})", n),
}
}
}
impl fmt::Display for NetworkTraceType {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match self {
NetworkTraceType::Invalid => f.write_str("INVALID"),
NetworkTraceType::Ipc => f.write_str("IPC"),
NetworkTraceType::Can => f.write_str("CAN"),
NetworkTraceType::Flexray => f.write_str("FLEXRAY"),
NetworkTraceType::Most => f.write_str("MOST"),
NetworkTraceType::Ethernet => f.write_str("ETHERNET"),
NetworkTraceType::Someip => f.write_str("SOMEIP"),
NetworkTraceType::UserDefined(v) => write!(f, "USERDEFINED({})", v),
}
}
}
impl fmt::Display for ControlType {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match self {
ControlType::Request => f.write_str("REQ"),
ControlType::Response => f.write_str("RES"),
ControlType::Unknown(n) => write!(f, "{}", n),
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match self {
Value::Bool(value) => value.fmt(f),
Value::U8(value) => value.fmt(f),
Value::U16(value) => value.fmt(f),
Value::U32(value) => value.fmt(f),
Value::U64(value) => value.fmt(f),
Value::U128(value) => value.fmt(f),
Value::I8(value) => value.fmt(f),
Value::I16(value) => value.fmt(f),
Value::I32(value) => value.fmt(f),
Value::I64(value) => value.fmt(f),
Value::I128(value) => value.fmt(f),
Value::F32(value) => value.fmt(f),
Value::F64(value) => value.fmt(f),
Value::StringVal(s) => write!(
f,
"{}",
s.lines()
.collect::<Vec<&str>>()
.join(&DLT_NEWLINE_SENTINAL_STR)
),
Value::Raw(value) => write!(f, "{:02X?}", value),
}
}
}
impl fmt::Display for Argument {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
if let Some(n) = &self.name {
write!(f, "{}: ", n)?;
}
if let Some(u) = &self.unit {
u.fmt(f)?;
}
if let Some(v) = self.to_real_value() {
write!(f, "{}", v)?;
} else {
self.value.fmt(f)?;
}
Ok(())
}
}
impl fmt::Display for DltTimeStamp {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let naive: Option<NaiveDateTime> =
NaiveDateTime::from_timestamp_opt(i64::from(self.seconds), self.microseconds * 1000);
match naive {
Some(n) => {
let datetime: DateTime<Utc> = DateTime::from_utc(n, Utc);
let system_time: std::time::SystemTime = std::time::SystemTime::from(datetime);
write!(f, "{}", humantime::format_rfc3339(system_time))
}
None => write!(
f,
"no valid timestamp for {}s/{}us",
self.seconds, self.microseconds
),
}
}
}
impl fmt::Display for StandardHeader {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{}{}", self.version, DLT_COLUMN_SENTINAL,)?;
if let Some(id) = &self.session_id {
write!(f, "{}", id)?;
}
write!(
f,
"{}{}{}",
DLT_COLUMN_SENTINAL, self.message_counter, DLT_COLUMN_SENTINAL,
)?;
if let Some(t) = &self.timestamp {
write!(f, "{}", t)?;
}
write!(f, "{}", DLT_COLUMN_SENTINAL,)?;
if let Some(id) = &self.ecu_id {
write!(f, "{}", id)?;
}
Ok(())
}
}
impl fmt::Display for LogLevel {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match self {
LogLevel::Fatal => f.write_str("FATAL"),
LogLevel::Error => f.write_str("Error"),
LogLevel::Warn => f.write_str("WARN"),
LogLevel::Info => f.write_str("INFO"),
LogLevel::Debug => f.write_str("DEBUG"),
LogLevel::Verbose => f.write_str("VERBOSE"),
LogLevel::Invalid(v) => write!(f, "INVALID (0x{:02X?})", v),
}
}
}
#[derive(Default)]
pub struct FormatOptions {
pub tz: Option<Tz>,
}
pub struct FormattableMessage<'a> {
pub message: Message,
pub fibex_metadata: Option<&'a FibexMetadata>,
pub options: Option<&'a FormatOptions>,
}
impl<'a> From<Message> for FormattableMessage<'a> {
fn from(message: Message) -> Self {
FormattableMessage {
message,
fibex_metadata: None,
options: None,
}
}
}
impl<'a> fmt::Display for FormattableMessage<'a> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
self.message
.fmt_as_text(f, self.fibex_metadata, self.options)
}
}
impl DltTimeStamp {
pub fn tz_string(&self, tz: &Tz) -> String {
let naive: Option<NaiveDateTime> =
NaiveDateTime::from_timestamp_opt(i64::from(self.seconds), self.microseconds * 1000);
match naive {
Some(n) => DateTime::<Utc>::from_utc(n, Utc)
.with_timezone(tz)
.to_string(),
None => format!(
"no valid timestamp for {}s/{}us",
self.seconds, self.microseconds,
),
}
}
pub fn utc_string(&self) -> String {
let naive: Option<NaiveDateTime> =
NaiveDateTime::from_timestamp_opt(i64::from(self.seconds), self.microseconds * 1000);
match naive {
Some(n) => {
let datetime: DateTime<Utc> = DateTime::from_utc(n, Utc);
let system_time: std::time::SystemTime = std::time::SystemTime::from(datetime);
humantime::format_rfc3339(system_time).to_string()
}
None => format!(
"no valid timestamp for {}s/{}us",
self.seconds, self.microseconds,
),
}
}
}
impl Message {
pub fn fmt_as_text(
&self,
f: &mut fmt::Formatter,
fibex_metadata: Option<&FibexMetadata>,
options: Option<&FormatOptions>,
) -> fmt::Result {
if let Some(h) = &self.storage_header {
let tz = options.map(|o| o.tz);
match tz {
Some(Some(tz)) => write!(f, "{}", h.as_tz_string(&tz))?,
_ => write!(f, "{}", h)?,
};
}
write!(f, "{}", DLT_COLUMN_SENTINAL,)?;
write!(f, "{}", self.header)?;
write!(f, "{}", DLT_COLUMN_SENTINAL,)?;
match &self.payload {
PayloadContent::Verbose(arguments) => {
self.write_app_id_context_id_and_message_type(f)?;
arguments
.iter()
.try_for_each(|arg| write!(f, "{}{}", DLT_ARGUMENT_SENTINAL, arg))
}
PayloadContent::NonVerbose(id, data) => {
self.format_nonverbose_data(*id, data, f, fibex_metadata)
}
PayloadContent::ControlMsg(ctrl_id, _data) => {
self.write_app_id_context_id_and_message_type(f)?;
match SERVICE_ID_MAPPING.get(&ctrl_id.value()) {
Some((name, _desc)) => write!(f, "[{}]", name),
None => write!(f, "[Unknown CtrlCommand]"),
}
}
}
}
fn write_app_id_context_id_and_message_type(
&self,
f: &mut fmt::Formatter,
) -> Result<(), fmt::Error> {
match self.extended_header.as_ref() {
Some(ext) => {
write!(
f,
"{}{}{}{}{}{}",
ext.application_id,
DLT_COLUMN_SENTINAL,
ext.context_id,
DLT_COLUMN_SENTINAL,
ext.message_type,
DLT_COLUMN_SENTINAL,
)?;
}
None => {
write!(
f,
"-{}-{}-{}",
DLT_COLUMN_SENTINAL, DLT_COLUMN_SENTINAL, DLT_COLUMN_SENTINAL,
)?;
}
};
Ok(())
}
pub(crate) fn format_nonverbose_data(
&self,
id: u32,
data: &[u8],
f: &mut fmt::Formatter,
fibex_metadata: Option<&FibexMetadata>,
) -> fmt::Result {
trace!("format_nonverbose_data");
let mut fibex_info_added = false;
if let Some(fibex_metadata) = fibex_metadata {
let id_text = format!("ID_{}", id);
let frame_metadata = if let Some(extended_header) = &self.extended_header {
fibex_metadata.frame_map_with_key.get(&(
ContextId(extended_header.context_id.clone()),
ApplicationId(extended_header.application_id.clone()),
FrameId(id_text),
)) } else {
fibex_metadata.frame_map.get(&FrameId(id_text))
};
if let Some(frame_metadata) = frame_metadata {
let FrameMetadata {
application_id,
context_id,
message_info,
..
} = &frame_metadata;
write!(
f,
"{}{}{}{}",
application_id
.as_ref()
.map(|id| &*id.as_str())
.or_else(|| self
.extended_header
.as_ref()
.map(|h| h.application_id.as_str()))
.unwrap_or("-"),
DLT_COLUMN_SENTINAL,
context_id
.as_ref()
.map(|id| &*id.as_str())
.or_else(|| self.extended_header.as_ref().map(|h| h.context_id.as_ref()))
.unwrap_or("-"),
DLT_COLUMN_SENTINAL
)?;
if let Some(v) = message_info
.as_ref()
.and_then(|mi| MessageType::try_new_from_fibex_message_info(&*mi))
{
write!(f, "{}", v)?;
} else if let Some(message_type) =
self.extended_header.as_ref().map(|h| &h.message_type)
{
write!(f, "{}", message_type)?;
} else {
write!(f, "-")?;
}
write!(f, "{}", DLT_COLUMN_SENTINAL)?;
for pdu in &frame_metadata.pdus {
if let Some(description) = &pdu.description {
let arg = Argument {
type_info: TypeInfo {
kind: TypeInfoKind::StringType,
coding: StringCoding::UTF8,
has_trace_info: false,
has_variable_info: false,
},
name: None,
unit: None,
fixed_point: None,
value: Value::StringVal(description.to_string()),
};
write!(f, "{}{} ", DLT_ARGUMENT_SENTINAL, arg)?;
} else {
let arguments = self.construct_arguments(&pdu.signal_types, data)?;
trace!("Constructed {} arguments", arguments.len());
for arg in arguments {
write!(f, "{}{} ", DLT_ARGUMENT_SENTINAL, arg)?;
}
};
fibex_info_added = true;
}
} else {
self.write_app_id_context_id_and_message_type(f)?;
}
} else {
self.write_app_id_context_id_and_message_type(f)?;
}
if !fibex_info_added {
let _ = match get_message_type_string(&self.extended_header) {
Some(v) => f.write_str(
&format!(
"{}[{}]{} {}",
DLT_ARGUMENT_SENTINAL, id, DLT_ARGUMENT_SENTINAL, v
)[..],
),
None => f.write_str(
&format!(
"{}[{}]{} {:02X?}",
DLT_ARGUMENT_SENTINAL, id, DLT_ARGUMENT_SENTINAL, data
)[..],
),
};
}
Ok(())
}
fn construct_arguments(
&self,
pdu_signal_types: &[TypeInfo],
data: &[u8],
) -> Result<Vec<Argument>, fmt::Error> {
let mut offset = 0;
let mut arguments = vec![];
for signal_type in pdu_signal_types {
let argument = {
let (value, fixed_point) = {
let mut fixed_point = None;
match signal_type.kind {
TypeInfoKind::StringType | TypeInfoKind::Raw => {
if data.len() < offset + 2 {
return Err(fmt::Error);
}
let length = if self.header.endianness == Endianness::Big {
BigEndian::read_u16(&data[offset..offset + 2]) as usize
} else {
LittleEndian::read_u16(&data[offset..offset + 2]) as usize
};
offset += 2;
if data.len() < offset + length {
return Err(fmt::Error);
}
let v = if signal_type.kind == TypeInfoKind::StringType {
Value::StringVal(
String::from_utf8(data[offset..offset + length].to_vec())
.map_err(|_| fmt::Error)?,
)
} else {
Value::Raw(Vec::from(&data[offset..offset + length]))
};
offset += length;
Ok((v, fixed_point))
}
TypeInfoKind::Bool => {
offset += 1;
if data.len() < offset {
return Err(fmt::Error);
}
Ok((Value::Bool(data[offset - 1]), fixed_point))
}
TypeInfoKind::Float(width) => {
let length = width as usize / 8;
if data.len() < offset + length {
return Err(fmt::Error);
}
let v = if self.header.endianness == Endianness::Big {
dlt_fint::<BigEndian>(width)(&data[offset..offset + length])
} else {
dlt_fint::<LittleEndian>(width)(&data[offset..offset + length])
}
.map_err(|_| fmt::Error)?
.1;
offset += length;
Ok((v, fixed_point)) as Result<(Value, Option<FixedPoint>), fmt::Error>
}
TypeInfoKind::Signed(length) => {
let byte_length = length as usize / 8;
if data.len() < offset + byte_length {
return Err(fmt::Error);
}
let value_offset = &data[offset..];
let (_, v) = if self.header.endianness == Endianness::Big {
dlt_sint::<BigEndian>(length)(value_offset)
} else {
dlt_sint::<LittleEndian>(length)(value_offset)
}
.map_err(|_| fmt::Error)?;
offset += byte_length;
Ok((v, fixed_point))
}
TypeInfoKind::SignedFixedPoint(length) => {
let byte_length = length as usize / 8;
if data.len() < offset + byte_length {
return Err(fmt::Error);
}
let (value_offset, fp) = if self.header.endianness == Endianness::Big {
dlt_fixed_point::<BigEndian>(
&data[offset..offset + byte_length],
length,
)
} else {
dlt_fixed_point::<LittleEndian>(
&data[offset..offset + byte_length],
length,
)
}
.map_err(|_| fmt::Error)?;
fixed_point = Some(fp);
let (_, v) = if self.header.endianness == Endianness::Big {
dlt_sint::<BigEndian>(float_width_to_type_length(length))(
value_offset,
)
} else {
dlt_sint::<LittleEndian>(float_width_to_type_length(length))(
value_offset,
)
}
.map_err(|_| fmt::Error)?;
offset += byte_length;
Ok((v, fixed_point))
}
TypeInfoKind::Unsigned(length) => {
let byte_length = length as usize / 8;
if data.len() < offset + byte_length {
return Err(fmt::Error);
}
let value_offset = &data[offset..];
let (_, v) = if self.header.endianness == Endianness::Big {
dlt_uint::<BigEndian>(length)(value_offset)
} else {
dlt_uint::<LittleEndian>(length)(value_offset)
}
.map_err(|_| fmt::Error)?;
offset += byte_length;
Ok((v, fixed_point))
}
TypeInfoKind::UnsignedFixedPoint(length) => {
let byte_length = length as usize / 8;
if data.len() < offset + byte_length {
return Err(fmt::Error);
}
let value_offset = {
let (r, fp) = if self.header.endianness == Endianness::Big {
dlt_fixed_point::<BigEndian>(
&data[offset..offset + byte_length],
length,
)
} else {
dlt_fixed_point::<LittleEndian>(
&data[offset..offset + byte_length],
length,
)
}
.map_err(|_| fmt::Error)?;
fixed_point = Some(fp);
r
};
let (_, v) = if self.header.endianness == Endianness::Big {
dlt_uint::<BigEndian>(float_width_to_type_length(length))(
value_offset,
)
} else {
dlt_uint::<LittleEndian>(float_width_to_type_length(length))(
value_offset,
)
}
.map_err(|_| fmt::Error)?;
offset += byte_length;
Ok((v, fixed_point))
}
}
}?;
Argument {
type_info: signal_type.clone(),
name: None,
unit: None,
fixed_point,
value,
}
};
arguments.push(argument);
}
Ok(arguments)
}
}
impl fmt::Display for TypeInfo {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let kind = match self.kind {
TypeInfoKind::Bool => "Bool".into(),
TypeInfoKind::Signed(type_length) => format!("Signed ({:?})", type_length),
TypeInfoKind::SignedFixedPoint(float_width) => format!("Signed FP ({:?})", float_width),
TypeInfoKind::Unsigned(type_length) => format!("Unsigned ({:?})", type_length),
TypeInfoKind::UnsignedFixedPoint(float_width) => {
format!("Signed FP ({:?})", float_width)
}
TypeInfoKind::Float(float_width) => format!("Float ({:?})", float_width),
TypeInfoKind::StringType => "String".into(),
TypeInfoKind::Raw => "Raw".into(),
};
write!(f, "TypeInfo {}", kind)
}
}
fn get_message_type_string(extended_header: &Option<ExtendedHeader>) -> Option<&str> {
if let Some(ext) = extended_header {
match &ext.message_type {
MessageType::Control(ct) => match ct {
ControlType::Request => Some("control request"),
ControlType::Response => Some("control response"),
ControlType::Unknown(_) => Some("unknown control"),
},
MessageType::NetworkTrace(ntt) => match ntt {
NetworkTraceType::Ipc => Some("Ipc"),
NetworkTraceType::Can => Some("Can"),
NetworkTraceType::Flexray => Some("Flexray"),
NetworkTraceType::Most => Some("Most"),
NetworkTraceType::Ethernet => Some("Ethernet"),
NetworkTraceType::Someip => Some("Someip"),
NetworkTraceType::Invalid => Some("Invalid"),
_ => Some("unknown network trace"),
},
_ => None,
}
} else {
None
}
}