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 enum DecodedSignalValue {
Numeric(f64),
Enum(i64, String),
}
#[derive(Debug, Clone)]
pub struct DecodedSignal {
pub name: String,
pub value: DecodedSignalValue,
pub unit: String,
}
#[derive(Debug, Clone)]
pub struct EnumDef {
pub signal_name: String,
pub enum_map: std::collections::HashMap<i64, String>,
}
pub struct Parser {
msg_defs: std::collections::HashMap<u32, can_dbc::Message>,
enum_defs: std::collections::HashMap<u32, Vec<EnumDef>>,
}
impl Parser {
pub fn new() -> Self {
Self {
msg_defs: std::collections::HashMap::new(),
enum_defs: 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_defs.contains_key(&msg_id) {
log::warn!(
"Duplicate message ID {msg_id:#X} ({}). Overwriting existing definition.",
msg_def.name
);
}
self.msg_defs.insert(msg_id, msg_def.clone());
}
for val_desc in dbc.value_descriptions {
match val_desc {
can_dbc::ValueDescription::Signal {
message_id,
name,
value_descriptions,
} => {
let msg_id = message_id.raw();
let enum_def = EnumDef {
signal_name: name.clone(),
enum_map: value_descriptions
.iter()
.map(|vd| (vd.id, vd.description.clone()))
.collect(),
};
let entry = self.enum_defs.entry(msg_id).or_default();
if let Some(existing) = entry
.iter_mut()
.find(|e| e.signal_name == enum_def.signal_name)
{
*existing = enum_def;
log::warn!(
"Duplicate value description for signal '{}' in message ID {:#X}. \
Overwriting existing enum definition.",
name,
msg_id
);
} else {
entry.push(enum_def);
}
}
can_dbc::ValueDescription::EnvironmentVariable { .. } => {}
}
}
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_def = self.msg_defs.get(&msg_id)?;
let is_extended = matches!(msg_def.id, can_dbc::MessageId::Extended(_));
let tx_node = match &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_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_def.name
);
return None;
}
}
}
Some(DecodedMessage {
name: 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 = if signal_def.value_type == can_dbc::ValueType::Signed {
let max_unsigned = low_bits_mask!(signal_def.size as usize, u64);
let sign_bit = 1u64 << (signal_def.size - 1);
if raw_value & sign_bit != 0 {
(raw_value | (!max_unsigned)) as i64
} else {
raw_value as i64
}
} else {
raw_value as i64
};
let enum_name = self
.enum_defs
.get(&msg_id)
.and_then(|enums| {
enums
.iter()
.find(|e| e.signal_name == signal_def.name)
.and_then(|e| e.enum_map.get(&raw_value_with_sign))
})
.cloned();
if let Some(enum_str) = enum_name {
Some(DecodedSignal {
name: signal_def.name.clone(),
value: DecodedSignalValue::Enum(raw_value_with_sign, enum_str),
unit: signal_def.unit.clone(),
})
} else {
let scaled_value = raw_value_with_sign as f64 * signal_def.factor + signal_def.offset;
Some(DecodedSignal {
name: signal_def.name.clone(),
value: DecodedSignalValue::Numeric(scaled_value),
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_def = self.msg_defs.get(&msg_id)?;
let msg_size = msg_def.size as usize;
let mut data = vec![0u8; msg_size];
for signal_def in &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_def.name
);
return None;
}
};
if self
.encode_signal(signal_def, physical_value, &mut data)
.is_none()
{
log::error!(
"Failed to encode signal {} for message {}",
signal_def.name,
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_def) = self
.msg_defs
.iter()
.find(|(_id, msg)| msg.name == msg_name)?;
let data = self.encode_msg(*msg_id, signal_values)?;
Some((*msg_id, data))
}
fn encode_signal(
&self,
signal_def: &can_dbc::Signal,
physical_value: f64,
data: &mut [u8],
) -> Option<()> {
let raw_value = (physical_value - signal_def.offset) / signal_def.factor;
let raw_int = if signal_def.value_type == can_dbc::ValueType::Signed {
let signed_val = raw_value.round() as i64;
let max_value = (1i64 << (signal_def.size - 1)) - 1;
let min_value = -(1i64 << (signal_def.size - 1));
let clamped = signed_val.max(min_value).min(max_value);
if clamped < 0 {
let mask = low_bits_mask!(signal_def.size as usize, u64);
(clamped as u64) & mask
} else {
clamped as u64
}
} else {
let unsigned_val = raw_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_def = self.msg_defs.get(&msg_id)?;
Some(msg_def.signals.to_vec())
}
pub fn msg_defs(&self) -> Vec<can_dbc::Message> {
self.msg_defs.values().cloned().collect()
}
pub fn msg_def(&self, msg_id: u32) -> Option<&can_dbc::Message> {
self.msg_defs.get(&msg_id)
}
pub fn clear(&mut self) {
self.msg_defs.clear();
self.enum_defs.clear();
}
}
impl Default for Parser {
fn default() -> Self {
Self::new()
}
}