pub use can_dbc;
macro_rules! low_bits_mask {
($bits:expr, $ty:ty) => {{
debug_assert!($bits <= <$ty>::BITS as usize);
match $bits {
0 => 0 as $ty,
n if n == <$ty>::BITS as usize => <$ty>::MAX,
n => <$ty>::MAX >> (<$ty>::BITS as usize - n),
}
}};
}
pub type SignalMap = indexmap::map::IndexMap<String, DecodedSignal>;
#[derive(Debug, Clone)]
pub struct DecodedMessage {
pub name: String,
pub msg_id: u32,
pub is_extended: bool,
pub tx_node: String,
pub signals: SignalMap,
}
#[derive(Debug, Clone)]
pub struct DecodedSignalValue {
pub physical: f64,
pub raw: Option<i128>,
pub enum_label: Option<String>,
}
impl DecodedSignalValue {
pub fn new_integer_backed_numeric(physical: f64, raw_value: i128) -> Self {
Self {
physical,
raw: Some(raw_value),
enum_label: None,
}
}
pub fn new_float_backed_numeric(physical: f64) -> Self {
Self {
physical,
raw: None,
enum_label: None,
}
}
pub fn new_enum(physical: f64, raw_value: i128, enum_label: String) -> Self {
Self {
physical,
raw: Some(raw_value),
enum_label: Some(enum_label),
}
}
}
#[derive(Debug, Clone)]
pub struct DecodedSignal {
pub name: String,
pub value: DecodedSignalValue,
pub unit: String,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FloatFormat {
F32,
F64,
}
impl FloatFormat {
pub fn from_dbc_def(def: can_dbc::SignalExtendedValueType) -> Option<Self> {
match def {
can_dbc::SignalExtendedValueType::IEEEfloat32Bit => Some(FloatFormat::F32),
can_dbc::SignalExtendedValueType::IEEEdouble64bit => Some(FloatFormat::F64),
can_dbc::SignalExtendedValueType::SignedOrUnsignedInteger => None,
}
}
pub fn bit_size(&self) -> usize {
match self {
FloatFormat::F32 => 32,
FloatFormat::F64 => 64,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct SignalMeta {
pub enum_map: std::collections::HashMap<i128, String>,
pub float_format: Option<FloatFormat>,
pub sig_comment: Option<String>,
}
#[derive(Debug, Clone)]
pub struct MsgEntry {
pub msg_def: can_dbc::Message,
pub msg_desc: Option<String>,
pub signal_meta: std::collections::HashMap<String, SignalMeta>,
}
impl MsgEntry {
fn new(msg_def: can_dbc::Message) -> Self {
Self {
msg_def,
msg_desc: None,
signal_meta: std::collections::HashMap::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct Parser {
msg_entries: std::collections::HashMap<u32, MsgEntry>,
}
impl Parser {
pub fn new() -> Self {
Self {
msg_entries: std::collections::HashMap::new(),
}
}
pub fn from_dbc_file(path: &std::path::Path) -> Result<Self, Box<dyn std::error::Error>> {
let mut parser = Self::new();
parser.add_from_dbc_file(path)?;
Ok(parser)
}
pub fn add_from_str(&mut self, buffer: &str) -> Result<(), Box<dyn std::error::Error>> {
let dbc = can_dbc::Dbc::try_from(buffer).map_err(|e| {
log::error!("Failed to parse DBC: {:?}", e);
format!("{:?}", e)
})?;
for msg_def in dbc.messages {
let msg_id = msg_def.id.raw();
if self.msg_entries.contains_key(&msg_id) {
log::warn!(
"Duplicate message ID {msg_id:#X} ({}). Overwriting existing definition.",
msg_def.name
);
}
self.msg_entries.insert(msg_id, MsgEntry::new(msg_def));
}
for val_desc in dbc.value_descriptions {
let can_dbc::ValueDescription::Signal {
message_id,
name,
value_descriptions,
} = val_desc
else {
continue;
};
let msg_id = message_id.raw();
let Some(msg_entry) = self.msg_entries.get_mut(&msg_id) else {
log::warn!(
"Value description for signal '{}' references unknown message ID {:#X}. \
Skipping.",
name,
msg_id
);
continue;
};
let enum_def = SignalMeta {
enum_map: value_descriptions
.iter()
.map(|vd| (vd.id as i128, vd.description.clone()))
.collect(),
..Default::default()
};
if let Some(existing) = msg_entry.signal_meta.get_mut(&name) {
existing.enum_map = enum_def.enum_map;
log::warn!(
"Duplicate value description for signal '{}' in message ID {:#X}. \
Overwriting existing enum definition.",
name,
msg_id
);
} else {
msg_entry.signal_meta.insert(name.clone(), enum_def);
}
}
for sig_ext_val_typ in dbc.signal_extended_value_type_list {
let Some(float_format) =
FloatFormat::from_dbc_def(sig_ext_val_typ.signal_extended_value_type)
else {
continue;
};
let msg_id = sig_ext_val_typ.message_id.raw();
let signal_name = &sig_ext_val_typ.signal_name;
let Some(msg_entry) = self.msg_entries.get_mut(&msg_id) else {
log::warn!(
"Float definition for signal '{}' references unknown message ID {:#X}. \
Skipping.",
signal_name,
msg_id
);
continue;
};
let Some(signal_def) = msg_entry
.msg_def
.signals
.iter()
.find(|s| s.name == *signal_name)
else {
log::warn!(
"Float definition for signal '{}' references unknown signal in message ID {:#X}. \
Skipping.",
signal_name,
msg_id
);
continue;
};
if signal_def.size != float_format.bit_size() as u64 {
log::warn!(
"Signal '{}' in message ID {:#X} marked as {} but size is {} bits. \
Skipping float definition.",
signal_name,
msg_id,
match float_format {
FloatFormat::F32 => "f32",
FloatFormat::F64 => "f64",
},
signal_def.size
);
continue;
}
let format_def = SignalMeta {
float_format: Some(float_format),
..Default::default()
};
if let Some(existing) = msg_entry.signal_meta.get_mut(signal_name) {
existing.float_format = format_def.float_format;
log::warn!(
"Duplicate float definition for signal '{}' in message ID {:#X}. \
Overwriting existing float definition.",
signal_name,
msg_id
);
} else {
msg_entry
.signal_meta
.insert(signal_name.clone(), format_def);
}
}
for comment in dbc.comments {
match comment {
can_dbc::Comment::Message { id, comment } => {
let msg_id = id.raw();
let Some(msg_entry) = self.msg_entries.get_mut(&msg_id) else {
log::warn!("Comment for unknown message ID {:#X}. Skipping.", msg_id);
continue;
};
if msg_entry.msg_desc.is_some() {
log::warn!(
"Duplicate comment for message ID {:#X}. \
Overwriting existing message comment.",
msg_id
);
}
msg_entry.msg_desc = Some(comment);
}
can_dbc::Comment::Signal {
message_id,
name,
comment,
} => {
let msg_id = message_id.raw();
let Some(msg_entry) = self.msg_entries.get_mut(&msg_id) else {
log::warn!(
"Comment for signal '{}' references unknown message ID {:#X}. Skipping.",
name,
msg_id
);
continue;
};
let signal_meta = msg_entry.signal_meta.entry(name.clone()).or_default();
if signal_meta.sig_comment.is_some() {
log::warn!(
"Duplicate comment for signal '{}' in message ID {:#X}. \
Overwriting existing signal comment.",
name,
msg_id
);
}
signal_meta.sig_comment = Some(comment);
}
_ => {}
}
}
Ok(())
}
pub fn add_from_dbc_file(
&mut self,
path: &std::path::Path,
) -> Result<(), Box<dyn std::error::Error>> {
let buffer = std::fs::read(path)?;
let s = String::from_utf8(buffer)?;
self.add_from_str(&s)?;
Ok(())
}
pub fn decode_msg(&self, msg_id: u32, data: &[u8]) -> Option<DecodedMessage> {
let msg_entry = self.msg_entries.get(&msg_id)?;
let is_extended = matches!(msg_entry.msg_def.id, can_dbc::MessageId::Extended(_));
let tx_node = match &msg_entry.msg_def.transmitter {
can_dbc::Transmitter::NodeName(name) => name.clone(),
can_dbc::Transmitter::VectorXXX => "Unknown".to_string(),
};
let mut decoded_signals = SignalMap::new();
for signal_def in &msg_entry.msg_def.signals {
match self.decode_signal(msg_id, signal_def, data) {
Some(decoded_signal) => {
decoded_signals.insert(decoded_signal.name.to_string(), decoded_signal);
}
_ => {
log::error!(
"Failed to decode signal {} from message {}",
signal_def.name,
msg_entry.msg_def.name
);
return None;
}
}
}
Some(DecodedMessage {
name: msg_entry.msg_def.name.clone(),
msg_id,
is_extended,
tx_node,
signals: decoded_signals,
})
}
fn decode_signal(
&self,
msg_id: u32,
signal_def: &can_dbc::Signal,
data: &[u8],
) -> Option<DecodedSignal> {
let raw_value = self.extract_signal_value(
data,
signal_def.start_bit as usize,
signal_def.size as usize,
signal_def.byte_order,
)?;
let raw_value_with_sign: i128 = if signal_def.value_type == can_dbc::ValueType::Signed {
let shift = 128u32.saturating_sub(signal_def.size as u32);
((raw_value as i128) << shift) >> shift
} else {
raw_value as i128
};
let format_def = self
.msg_entries
.get(&msg_id)
.and_then(|entry| entry.signal_meta.get(&signal_def.name));
if let Some(format_def) = format_def {
if let Some(enum_str) = format_def.enum_map.get(&raw_value_with_sign) {
let physical = raw_value_with_sign as f64 * signal_def.factor + signal_def.offset;
return Some(DecodedSignal {
name: signal_def.name.clone(),
value: DecodedSignalValue::new_enum(
physical,
raw_value_with_sign,
enum_str.clone(),
),
unit: signal_def.unit.clone(),
});
} else {
log::warn!(
"Raw value {} for signal '{}' in message ID {:#X} does not have a corresponding enum label. \
Returning raw value as numeric.",
raw_value_with_sign,
signal_def.name,
msg_id
);
}
}
let float_def = self
.msg_entries
.get(&msg_id)
.and_then(|entry| entry.signal_meta.get(&signal_def.name))
.and_then(|format_def| format_def.float_format);
if let Some(float_format) = float_def {
let float_value = match float_format {
FloatFormat::F32 => f32::from_bits(raw_value as u32) as f64,
FloatFormat::F64 => f64::from_bits(raw_value),
};
let scaled_value = float_value * signal_def.factor + signal_def.offset;
return Some(DecodedSignal {
name: signal_def.name.clone(),
value: DecodedSignalValue::new_float_backed_numeric(scaled_value),
unit: signal_def.unit.clone(),
});
}
let scaled_value = raw_value_with_sign as f64 * signal_def.factor + signal_def.offset;
Some(DecodedSignal {
name: signal_def.name.clone(),
value: DecodedSignalValue::new_integer_backed_numeric(
scaled_value,
raw_value_with_sign,
),
unit: signal_def.unit.clone(),
})
}
fn extract_signal_value(
&self,
data: &[u8],
start_bit: usize,
size: usize,
byte_order: can_dbc::ByteOrder,
) -> Option<u64> {
if data.is_empty() || size == 0 {
return None;
}
let mut result = 0u64;
match byte_order {
can_dbc::ByteOrder::LittleEndian => {
let start_byte = start_bit / 8;
let start_bit_in_byte = start_bit % 8;
let mut remaining_bits = size;
let mut current_byte = start_byte;
let mut bit_offset = start_bit_in_byte;
while remaining_bits > 0 {
if current_byte >= data.len() {
return None;
}
let bits_in_this_byte = std::cmp::min(remaining_bits, 8 - bit_offset);
let mask = low_bits_mask!(bits_in_this_byte, u64) << bit_offset;
let byte_value = ((data[current_byte] as u64) & mask) >> bit_offset;
result |= byte_value << (size - remaining_bits);
remaining_bits -= bits_in_this_byte;
current_byte += 1;
bit_offset = 0;
}
}
can_dbc::ByteOrder::BigEndian => {
let start_byte = start_bit / 8;
let start_bit_in_byte = start_bit % 8;
let mut byte_idx = start_byte;
let mut bit_in_byte = start_bit_in_byte as i32;
for _i in 0..size {
if byte_idx >= data.len() {
return None;
}
let bit_val = (data[byte_idx] >> bit_in_byte) & 1;
result = (result << 1) | (bit_val as u64);
bit_in_byte -= 1;
if bit_in_byte < 0 {
bit_in_byte = 7;
byte_idx += 1;
}
}
}
}
Some(result)
}
pub fn encode_msg(
&self,
msg_id: u32,
signal_values: &std::collections::HashMap<String, f64>,
) -> Option<Vec<u8>> {
let msg_entry = self.msg_entries.get(&msg_id)?;
let msg_size = msg_entry.msg_def.size as usize;
let mut data = vec![0u8; msg_size];
for signal_def in &msg_entry.msg_def.signals {
let physical_value = match signal_values.get(&signal_def.name) {
Some(&v) => v,
_ => {
log::error!(
"Signal {} not provided for message {} during encoding",
signal_def.name,
msg_entry.msg_def.name
);
return None;
}
};
if self
.encode_signal(msg_id, signal_def, physical_value, &mut data)
.is_none()
{
log::error!(
"Failed to encode signal {} for message {}",
signal_def.name,
msg_entry.msg_def.name
);
return None;
}
}
Some(data)
}
pub fn encode_msg_by_name(
&self,
msg_name: &str,
signal_values: &std::collections::HashMap<String, f64>,
) -> Option<(u32, Vec<u8>)> {
let (msg_id, _msg_entry) = self
.msg_entries
.iter()
.find(|(_id, entry)| entry.msg_def.name == msg_name)?;
let data = self.encode_msg(*msg_id, signal_values)?;
Some((*msg_id, data))
}
fn encode_signal(
&self,
msg_id: u32,
signal_def: &can_dbc::Signal,
physical_value: f64,
data: &mut [u8],
) -> Option<()> {
let scaled_value = (physical_value - signal_def.offset) / signal_def.factor;
let float_def = self
.msg_entries
.get(&msg_id)
.and_then(|entry| entry.signal_meta.get(&signal_def.name))
.and_then(|format_def| format_def.float_format);
if let Some(float_format) = float_def {
let float_data = match float_format {
FloatFormat::F32 => (scaled_value as f32).to_bits() as u64,
FloatFormat::F64 => scaled_value.to_bits(),
};
return self.insert_signal_value(
data,
signal_def.start_bit as usize,
signal_def.size as usize,
signal_def.byte_order,
float_data,
);
}
let raw_int = if signal_def.value_type == can_dbc::ValueType::Signed {
let signed_val = scaled_value.round() as i64;
let max_value = ((1i128 << (signal_def.size - 1)) - 1) as i64;
let min_value = -(1i128 << (signal_def.size - 1)) as i64;
let clamped = signed_val.max(min_value).min(max_value);
let mask = low_bits_mask!(signal_def.size as usize, u64);
(clamped as u64) & mask
} else {
let unsigned_val = scaled_value.round() as u64;
let max_value = low_bits_mask!(signal_def.size as usize, u64);
unsigned_val.min(max_value)
};
self.insert_signal_value(
data,
signal_def.start_bit as usize,
signal_def.size as usize,
signal_def.byte_order,
raw_int,
)
}
fn insert_signal_value(
&self,
data: &mut [u8],
start_bit: usize,
size: usize,
byte_order: can_dbc::ByteOrder,
value: u64,
) -> Option<()> {
if data.is_empty() || size == 0 {
return None;
}
let total_bits = data.len() * 8;
if start_bit + size > total_bits {
return None;
}
match byte_order {
can_dbc::ByteOrder::LittleEndian => {
let start_byte = start_bit / 8;
let start_bit_in_byte = start_bit % 8;
let mut remaining_bits = size;
let mut current_byte = start_byte;
let mut bit_offset = start_bit_in_byte;
let mut value_offset = 0;
while remaining_bits > 0 && current_byte < data.len() {
let bits_in_this_byte = std::cmp::min(remaining_bits, 8 - bit_offset);
let mask = low_bits_mask!(bits_in_this_byte, u8) << bit_offset;
let value_mask = low_bits_mask!(bits_in_this_byte, u64);
let value_bits = ((value >> value_offset) & value_mask) as u8;
data[current_byte] =
(data[current_byte] & !mask) | ((value_bits << bit_offset) & mask);
remaining_bits -= bits_in_this_byte;
value_offset += bits_in_this_byte;
current_byte += 1;
bit_offset = 0;
}
}
can_dbc::ByteOrder::BigEndian => {
let start_byte = start_bit / 8;
let start_bit_in_byte = start_bit % 8;
let mut byte_idx = start_byte;
let mut bit_in_byte = start_bit_in_byte as i32;
for i in 0..size {
if byte_idx >= data.len() {
return None;
}
let bit_val = ((value >> (size - 1 - i)) & 1) as u8;
let mask = 1u8 << bit_in_byte;
data[byte_idx] = (data[byte_idx] & !mask) | (bit_val << bit_in_byte);
bit_in_byte -= 1;
if bit_in_byte < 0 {
bit_in_byte = 7;
byte_idx += 1;
}
}
}
}
Some(())
}
pub fn signal_defs(&self, msg_id: u32) -> Option<Vec<can_dbc::Signal>> {
let msg_entry = self.msg_entries.get(&msg_id)?;
Some(msg_entry.msg_def.signals.clone())
}
pub fn msg_desc(&self, msg_id: u32) -> Option<&str> {
self.msg_entries
.get(&msg_id)
.and_then(|entry| entry.msg_desc.as_deref())
}
pub fn signal_desc(&self, msg_id: u32, signal_name: &str) -> Option<&str> {
self.msg_entries
.get(&msg_id)
.and_then(|entry| entry.signal_meta.get(signal_name))
.and_then(|meta| meta.sig_comment.as_deref())
}
pub fn msg_defs(&self) -> Vec<can_dbc::Message> {
self.msg_entries
.values()
.map(|entry| entry.msg_def.clone())
.collect()
}
pub fn msg_def(&self, msg_id: u32) -> Option<&can_dbc::Message> {
self.msg_entries.get(&msg_id).map(|entry| &entry.msg_def)
}
pub fn msg_entries(&self) -> &std::collections::HashMap<u32, MsgEntry> {
&self.msg_entries
}
pub fn msg_entry(&self, msg_id: u32) -> Option<&MsgEntry> {
self.msg_entries.get(&msg_id)
}
pub fn clear(&mut self) {
self.msg_entries.clear();
}
}
impl Default for Parser {
fn default() -> Self {
Self::new()
}
}