use crate::{Dbc, Error, MAX_SIGNALS_PER_MESSAGE, Message, Result, compat::Vec};
#[cfg(feature = "embedded-can")]
use embedded_can::{Frame, Id};
#[derive(Debug, Clone, PartialEq)]
pub struct DecodedSignal<'a> {
pub name: &'a str,
pub value: f64,
pub raw_value: i64,
pub min: f64,
pub max: f64,
pub unit: Option<&'a str>,
pub description: Option<&'a str>,
}
impl<'a> DecodedSignal<'a> {
#[inline]
pub fn new(
name: &'a str,
value: f64,
raw_value: i64,
min: f64,
max: f64,
unit: Option<&'a str>,
description: Option<&'a str>,
) -> Self {
Self {
name,
value,
raw_value,
min,
max,
unit,
description,
}
}
#[inline]
pub fn is_in_range(&self) -> bool {
self.value >= self.min && self.value <= self.max
}
}
const MAX_SWITCHES: usize = 8;
struct SwitchValues<'a> {
names: [Option<&'a str>; MAX_SWITCHES],
values: [u64; MAX_SWITCHES],
count: usize,
}
impl<'a> SwitchValues<'a> {
#[inline]
const fn new() -> Self {
Self {
names: [None; MAX_SWITCHES],
values: [0; MAX_SWITCHES],
count: 0,
}
}
#[inline]
fn push(&mut self, name: &'a str, value: u64) -> Result<()> {
if self.count >= MAX_SWITCHES {
return Err(Error::Decoding(Error::MESSAGE_TOO_MANY_SIGNALS));
}
let idx = self.count;
self.names[idx] = Some(name);
self.values[idx] = value;
self.count += 1;
Ok(())
}
#[inline]
fn get_by_name(&self, name: &str) -> Option<u64> {
for i in 0..self.count {
if self.names[i] == Some(name) {
return Some(self.values[i]);
}
}
None
}
#[inline]
fn any_has_value(&self, target: u64) -> bool {
for i in 0..self.count {
if self.values[i] == target && self.names[i].is_some() {
return true;
}
}
false
}
}
impl Dbc {
#[inline]
pub fn decode(
&self,
id: u32,
payload: &[u8],
is_extended: bool,
) -> Result<Vec<DecodedSignal<'_>, { MAX_SIGNALS_PER_MESSAGE }>> {
let id = if is_extended {
id | Message::EXTENDED_ID_FLAG
} else {
id
};
let message = self
.messages()
.find_by_id(id)
.ok_or(Error::Decoding(Error::MESSAGE_NOT_FOUND))?;
let min_bytes = message.min_bytes_required() as usize;
if payload.len() < min_bytes {
return Err(Error::Decoding(Error::PAYLOAD_LENGTH_MISMATCH));
}
let mut decoded_signals: Vec<DecodedSignal<'_>, { MAX_SIGNALS_PER_MESSAGE }> = Vec::new();
let mut switch_values = SwitchValues::<'_>::new();
let signals = message.signals();
let message_has_extended_mux = !self.extended_multiplexing.is_empty()
&& self.has_extended_multiplexing_for_message(id);
let has_any_value_descriptions = !self.value_descriptions.is_empty();
for signal in signals.iter() {
if signal.is_multiplexer_switch() {
let (raw_value, physical_value) = signal.decode_raw(payload)?;
if raw_value < 0 {
return Err(Error::Decoding(Error::MULTIPLEXER_SWITCH_NEGATIVE));
}
switch_values.push(signal.name(), raw_value as u64)?;
let description = if has_any_value_descriptions {
self.value_descriptions_for_signal(id, signal.name())
.and_then(|vd| vd.get(raw_value as u64))
} else {
None
};
decoded_signals
.push(DecodedSignal::new(
signal.name(),
physical_value,
raw_value,
signal.min(),
signal.max(),
signal.unit(),
description,
))
.map_err(|_| Error::Decoding(Error::MESSAGE_TOO_MANY_SIGNALS))?;
}
}
for signal in signals.iter() {
if signal.is_multiplexer_switch() {
continue;
}
let should_decode = if let Some(mux_value) = signal.multiplexer_switch_value() {
if message_has_extended_mux {
self.check_extended_multiplexing(id, signal.name(), &switch_values)
.unwrap_or_else(|| {
switch_values.any_has_value(mux_value)
})
} else {
switch_values.any_has_value(mux_value)
}
} else {
true
};
if should_decode {
let (raw_value, physical_value) = signal.decode_raw(payload)?;
let description = if has_any_value_descriptions {
self.value_descriptions_for_signal(id, signal.name())
.and_then(|vd| vd.get(raw_value as u64))
} else {
None
};
decoded_signals
.push(DecodedSignal::new(
signal.name(),
physical_value,
raw_value,
signal.min(),
signal.max(),
signal.unit(),
description,
))
.map_err(|_| Error::Decoding(Error::MESSAGE_TOO_MANY_SIGNALS))?;
}
}
Ok(decoded_signals)
}
#[inline]
fn check_extended_multiplexing(
&self,
message_id: u32,
signal_name: &str,
switch_values: &SwitchValues,
) -> Option<bool> {
let indices = self.ext_mux_index.get(message_id, signal_name)?;
if indices.is_empty() {
return None;
}
let mut unique_switches: [Option<&str>; MAX_SWITCHES] = [None; MAX_SWITCHES];
let mut unique_count = 0;
for &idx in indices {
if let Some(entry) = self.extended_multiplexing.get(idx) {
let switch_name = entry.multiplexer_switch();
let found =
unique_switches.iter().take(unique_count).any(|&s| s == Some(switch_name));
if !found && unique_count < MAX_SWITCHES {
unique_switches[unique_count] = Some(switch_name);
unique_count += 1;
}
}
}
for switch_opt in unique_switches.iter().take(unique_count) {
let switch_name = match switch_opt {
Some(name) => *name,
None => continue,
};
let switch_val = match switch_values.get_by_name(switch_name) {
Some(v) => v,
None => return Some(false), };
let mut has_match = false;
for &idx in indices {
if let Some(entry) = self.extended_multiplexing.get(idx) {
if entry.multiplexer_switch() == switch_name {
for &(min, max) in entry.value_ranges() {
if switch_val >= min && switch_val <= max {
has_match = true;
break;
}
}
if has_match {
break;
}
}
}
}
if !has_match {
return Some(false); }
}
Some(true) }
#[inline]
fn has_extended_multiplexing_for_message(&self, message_id: u32) -> bool {
self.extended_multiplexing
.iter()
.any(|ext_mux| ext_mux.message_id() == message_id)
}
#[cfg(feature = "embedded-can")]
#[inline]
pub fn decode_frame<T: Frame>(
&self,
frame: T,
) -> Result<Vec<DecodedSignal<'_>, { MAX_SIGNALS_PER_MESSAGE }>> {
let payload = frame.data();
match frame.id() {
Id::Standard(id) => self.decode(id.as_raw() as u32, payload, false),
Id::Extended(id) => self.decode(id.as_raw(), payload, true),
}
}
}
#[cfg(test)]
mod tests {
use crate::Dbc;
#[test]
fn test_decode_basic() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine : 8 ECM
SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
"#,
)
.unwrap();
let payload = [0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(256, &payload, false).unwrap();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].name, "RPM");
assert_eq!(decoded[0].value, 2000.0);
assert_eq!(decoded[0].raw_value, 8000); assert_eq!(decoded[0].min, 0.0);
assert_eq!(decoded[0].max, 8000.0);
assert!(decoded[0].is_in_range());
assert_eq!(decoded[0].unit, Some("rpm"));
}
#[test]
fn test_decode_message_not_found() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine : 8 ECM
"#,
)
.unwrap();
let payload = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let result = dbc.decode(512, &payload, false);
assert!(result.is_err());
}
#[test]
fn test_decode_message() {
let data = r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine : 8 ECM
SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
SG_ Temp : 16|8@1- (1,-40) [-40|215] "°C" *
"#;
let dbc = Dbc::parse(data).unwrap();
let payload = [0x40, 0x1F, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(256, &payload, false).unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].name, "RPM");
assert_eq!(decoded[0].value, 2000.0);
assert_eq!(decoded[0].unit, Some("rpm"));
assert_eq!(decoded[1].name, "Temp");
assert_eq!(decoded[1].value, 50.0);
assert_eq!(decoded[1].unit, Some("°C"));
}
#[test]
fn test_decode_payload_length_mismatch() {
use crate::Error;
let data = r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine : 8 ECM
SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
"#;
let dbc = Dbc::parse(data).unwrap();
let payload = [0x40];
let result = dbc.decode(256, &payload, false);
assert!(result.is_err());
match result.unwrap_err() {
Error::Decoding(msg) => {
assert!(msg.contains(Error::PAYLOAD_LENGTH_MISMATCH));
}
_ => panic!("Expected Error::Decoding"),
}
let payload = [0x40, 0x1F];
let result = dbc.decode(256, &payload, false);
assert!(result.is_ok());
let payload = [0x40, 0x1F, 0x00, 0x00];
let result = dbc.decode(256, &payload, false);
assert!(result.is_ok());
}
#[test]
fn test_decode_dlc_larger_than_signal_coverage() {
let data = r#"VERSION "1.0"
BU_: ECM
BO_ 1024 NewMessage : 8 ECM
SG_ Temp : 0|8@1+ (1,0) [0|255] "" ECM
SG_ Pressure : 8|8@1+ (1,0) [0|255] "" ECM
SG_ Heel : 16|4@1+ (1,0) [0|15] "" ECM
SG_ Rest : 20|4@1+ (1,0) [0|15] "" ECM
"#;
let dbc = Dbc::parse(data).unwrap();
let message = dbc.messages().find("NewMessage").unwrap();
assert_eq!(message.dlc(), 8);
assert_eq!(message.min_bytes_required(), 3);
let payload = [0xAB, 0xCD, 0xEF, 0x00, 0x00, 0x00];
let decoded = dbc.decode(1024, &payload, false).unwrap();
assert_eq!(decoded.len(), 4);
assert_eq!(
decoded.iter().find(|s| s.name == "Temp").unwrap().raw_value,
0xAB
);
assert_eq!(
decoded.iter().find(|s| s.name == "Pressure").unwrap().raw_value,
0xCD
);
assert_eq!(
decoded.iter().find(|s| s.name == "Heel").unwrap().raw_value,
0xF
);
assert_eq!(
decoded.iter().find(|s| s.name == "Rest").unwrap().raw_value,
0xE
);
}
#[test]
fn test_decode_big_endian_signal() {
let data = r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine : 8 ECM
SG_ RPM : 0|16@0+ (1.0,0) [0|65535] "rpm" *
"#;
let dbc = Dbc::parse(data).unwrap();
let payload = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(256, &payload, false).unwrap();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].name, "RPM");
assert!(decoded[0].value >= 0.0);
assert_eq!(decoded[0].unit, Some("rpm"));
}
#[test]
fn test_decode_multiplexed_signal() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine : 8 ECM
SG_ MuxId M : 0|8@1+ (1,0) [0|255] ""
SG_ Signal0 m0 : 8|16@1+ (0.1,0) [0|6553.5] "unit" *
SG_ Signal1 m1 : 24|16@1+ (0.01,0) [0|655.35] "unit" *
SG_ NormalSignal : 40|8@1+ (1,0) [0|255] ""
"#,
)
.unwrap();
let payload = [0x00, 0x64, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00];
let decoded = dbc.decode(256, &payload, false).unwrap();
let find_signal = |name: &str| decoded.iter().find(|s| s.name == name).map(|s| s.value);
assert!(find_signal("MuxId").is_some());
assert!(find_signal("Signal0").is_some());
assert!(find_signal("Signal1").is_none());
assert!(find_signal("NormalSignal").is_some());
}
#[test]
fn test_decode_multiplexed_signal_switch_one() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine : 8 ECM
SG_ MuxId M : 0|8@1+ (1,0) [0|255] ""
SG_ Signal0 m0 : 8|16@1+ (0.1,0) [0|6553.5] "unit" *
SG_ Signal1 m1 : 24|16@1+ (0.01,0) [0|655.35] "unit" *
"#,
)
.unwrap();
let payload = [0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(256, &payload, false).unwrap();
let find_signal = |name: &str| decoded.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find_signal("MuxId"), Some(1.0));
assert!(find_signal("Signal0").is_none());
assert!(find_signal("Signal1").is_some());
}
#[test]
fn test_decode_mixed_byte_order() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 MixedByteOrder : 8 ECM
SG_ LittleEndianSignal : 0|16@1+ (1.0,0) [0|65535] ""
SG_ BigEndianSignal : 23|16@0+ (1.0,0) [0|65535] ""
SG_ AnotherLittleEndian : 32|8@1+ (1.0,0) [0|255] ""
SG_ AnotherBigEndian : 47|8@0+ (1.0,0) [0|255] ""
"#,
)
.unwrap();
let payload = [
0x34, 0x12, 0x00, 0x01, 0xAB, 0xCD, 0x00, 0x00, ];
let decoded = dbc.decode(256, &payload, false).unwrap();
let find_signal = |name: &str| decoded.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find_signal("LittleEndianSignal"), Some(4660.0));
let big_endian_value = find_signal("BigEndianSignal").unwrap();
assert!((0.0..=65535.0).contains(&big_endian_value));
assert_eq!(find_signal("AnotherLittleEndian"), Some(171.0));
let big_endian_8bit = find_signal("AnotherBigEndian");
assert!(big_endian_8bit.is_some());
assert!(big_endian_8bit.unwrap() >= 0.0 && big_endian_8bit.unwrap() <= 255.0);
assert_eq!(decoded.len(), 4);
assert!(find_signal("LittleEndianSignal").is_some());
assert!(find_signal("BigEndianSignal").is_some());
}
#[test]
fn test_decode_extended_multiplexing_simple() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 500 ComplexMux : 8 ECM
SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
SG_ Signal_A m0 : 16|16@1+ (0.1,0) [0|100] "unit" *
SG_MUL_VAL_ 500 Signal_A Mux1 5-10 ;
"#,
)
.unwrap();
let payload = [0x05, 0x00, 0xE8, 0x03, 0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(500, &payload, false).unwrap();
let find_signal = |name: &str| decoded.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find_signal("Mux1"), Some(5.0));
assert_eq!(
dbc.extended_multiplexing_for_message(500).count(),
1,
"Extended multiplexing entries should be parsed"
);
assert!(
find_signal("Signal_A").is_some(),
"Signal_A should be decoded when Mux1=5 (within range 5-10)"
);
assert_eq!(find_signal("Signal_A").unwrap(), 100.0);
let payload2 = [0x0F, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00];
let decoded2 = dbc.decode(500, &payload2, false).unwrap();
let find_signal2 = |name: &str| decoded2.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find_signal2("Mux1"), Some(15.0));
assert!(find_signal2("Signal_A").is_none());
}
#[test]
fn test_decode_extended_multiplexing_multiple_ranges() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 501 MultiRangeMux : 8 ECM
SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
SG_ Signal_B m0 : 16|16@1+ (1,0) [0|65535] "unit" *
SG_MUL_VAL_ 501 Signal_B Mux1 0-5,10-15,20-25 ;
"#,
)
.unwrap();
let payload1 = [0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00];
let decoded1 = dbc.decode(501, &payload1, false).unwrap();
let find1 = |name: &str| decoded1.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find1("Mux1"), Some(3.0));
assert!(find1("Signal_B").is_some());
let payload2 = [0x0C, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00];
let decoded2 = dbc.decode(501, &payload2, false).unwrap();
let find2 = |name: &str| decoded2.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find2("Mux1"), Some(12.0));
assert!(find2("Signal_B").is_some());
let payload3 = [0x16, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00];
let decoded3 = dbc.decode(501, &payload3, false).unwrap();
let find3 = |name: &str| decoded3.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find3("Mux1"), Some(22.0));
assert!(find3("Signal_B").is_some());
let payload4 = [0x08, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00];
let decoded4 = dbc.decode(501, &payload4, false).unwrap();
let find4 = |name: &str| decoded4.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find4("Mux1"), Some(8.0));
assert!(find4("Signal_B").is_none());
}
#[test]
fn test_decode_extended_multiplexing_multiple_switches() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 502 MultiSwitchMux : 8 ECM
SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
SG_ Mux2 M : 8|8@1+ (1,0) [0|255] ""
SG_ Signal_C m0 : 16|16@1+ (1,0) [0|65535] "unit" *
SG_MUL_VAL_ 502 Signal_C Mux1 5-10 ;
SG_MUL_VAL_ 502 Signal_C Mux2 20-25 ;
"#,
)
.unwrap();
let payload1 = [0x07, 0x16, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00];
let decoded1 = dbc.decode(502, &payload1, false).unwrap();
let find1 = |name: &str| decoded1.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find1("Mux1"), Some(7.0));
assert_eq!(find1("Mux2"), Some(22.0));
assert!(find1("Signal_C").is_some());
let payload2 = [0x07, 0x1E, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00];
let decoded2 = dbc.decode(502, &payload2, false).unwrap();
let find2 = |name: &str| decoded2.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find2("Mux1"), Some(7.0));
assert_eq!(find2("Mux2"), Some(30.0));
assert!(find2("Signal_C").is_none());
let payload3 = [0x0F, 0x16, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00];
let decoded3 = dbc.decode(502, &payload3, false).unwrap();
let find3 = |name: &str| decoded3.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find3("Mux1"), Some(15.0));
assert_eq!(find3("Mux2"), Some(22.0));
assert!(find3("Signal_C").is_none());
}
#[test]
fn test_decode_extended_multiplexing_takes_precedence() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 503 PrecedenceTest : 8 ECM
SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
SG_ Signal_D m0 : 16|16@1+ (1,0) [0|65535] "unit" *
SG_MUL_VAL_ 503 Signal_D Mux1 10-15 ;
"#,
)
.unwrap();
let payload1 = [0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00];
let decoded1 = dbc.decode(503, &payload1, false).unwrap();
let find1 = |name: &str| decoded1.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find1("Mux1"), Some(0.0));
assert!(find1("Signal_D").is_none());
let payload2 = [0x0C, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00];
let decoded2 = dbc.decode(503, &payload2, false).unwrap();
let find2 = |name: &str| decoded2.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find2("Mux1"), Some(12.0));
assert!(find2("Signal_D").is_some());
}
#[test]
fn test_decode_extended_multiplexing_with_extended_mux_signal() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 504 ExtendedMuxSignal : 8 ECM
SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
SG_ Mux2 m65M : 8|8@1+ (1,0) [0|255] ""
SG_ Signal_E m0 : 16|16@1+ (1,0) [0|65535] "unit" *
SG_MUL_VAL_ 504 Signal_E Mux1 65-65 ;
SG_MUL_VAL_ 504 Signal_E Mux2 10-15 ;
"#,
)
.unwrap();
let payload = [0x41, 0x0C, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(504, &payload, false).unwrap();
let find = |name: &str| decoded.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find("Mux1"), Some(65.0));
assert_eq!(find("Mux2"), Some(12.0));
assert!(find("Signal_E").is_some());
let payload2 = [0x40, 0x0C, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00];
let decoded2 = dbc.decode(504, &payload2, false).unwrap();
let find2 = |name: &str| decoded2.iter().find(|s| s.name == name).map(|s| s.value);
assert_eq!(find2("Mux1"), Some(64.0));
assert_eq!(find2("Mux2"), Some(12.0));
assert!(find2("Signal_E").is_none());
}
#[test]
fn test_decode_negative_multiplexer_switch() {
use crate::Error;
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 MuxMessage : 8 ECM
SG_ MuxSwitch M : 0|8@1- (1,0) [-128|127] ""
SG_ SignalA m0 : 8|8@1+ (1,0) [0|255] ""
"#,
)
.unwrap();
let payload = [0xFB, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let result = dbc.decode(256, &payload, false);
assert!(result.is_err());
match result.unwrap_err() {
Error::Decoding(msg) => {
assert_eq!(msg, Error::MULTIPLEXER_SWITCH_NEGATIVE);
}
_ => panic!("Expected Error::Decoding with MULTIPLEXER_SWITCH_NEGATIVE"),
}
}
#[test]
fn test_decode_too_many_unique_switches() {
use crate::{Error, MAX_SIGNALS_PER_MESSAGE};
if MAX_SIGNALS_PER_MESSAGE < 17 {
return;
}
let dbc_str = r#"VERSION "1.0"
BU_: ECM
BO_ 600 TooManySwitches : 18 ECM
SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
SG_ Mux2 M : 8|8@1+ (1,0) [0|255] ""
SG_ Mux3 M : 16|8@1+ (1,0) [0|255] ""
SG_ Mux4 M : 24|8@1+ (1,0) [0|255] ""
SG_ Mux5 M : 32|8@1+ (1,0) [0|255] ""
SG_ Mux6 M : 40|8@1+ (1,0) [0|255] ""
SG_ Mux7 M : 48|8@1+ (1,0) [0|255] ""
SG_ Mux8 M : 56|8@1+ (1,0) [0|255] ""
SG_ Mux9 M : 64|8@1+ (1,0) [0|255] ""
SG_ Mux10 M : 72|8@1+ (1,0) [0|255] ""
SG_ Mux11 M : 80|8@1+ (1,0) [0|255] ""
SG_ Mux12 M : 88|8@1+ (1,0) [0|255] ""
SG_ Mux13 M : 96|8@1+ (1,0) [0|255] ""
SG_ Mux14 M : 104|8@1+ (1,0) [0|255] ""
SG_ Mux15 M : 112|8@1+ (1,0) [0|255] ""
SG_ Mux16 M : 120|8@1+ (1,0) [0|255] ""
SG_ Mux17 M : 128|8@1+ (1,0) [0|255] ""
SG_ Signal_X m0 : 136|8@1+ (1,0) [0|255] "unit" *
SG_MUL_VAL_ 600 Signal_X Mux1 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux2 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux3 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux4 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux5 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux6 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux7 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux8 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux9 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux10 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux11 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux12 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux13 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux14 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux15 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux16 0-255 ;
SG_MUL_VAL_ 600 Signal_X Mux17 0-255 ;
"#;
let dbc = Dbc::parse(dbc_str).unwrap();
let payload = [0x00; 18];
let result = dbc.decode(600, &payload, false);
assert!(
result.is_err(),
"Decode should fail when there are more than 16 unique switches"
);
match result.unwrap_err() {
Error::Decoding(msg) => {
assert_eq!(
msg,
Error::MESSAGE_TOO_MANY_SIGNALS,
"Expected MESSAGE_TOO_MANY_SIGNALS error, got: {}",
msg
);
}
e => panic!(
"Expected Error::Decoding with MESSAGE_TOO_MANY_SIGNALS, got: {:?}",
e
),
}
}
#[test]
fn test_decode_extended_can_id() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 2147484672 ExtendedMsg : 8 ECM
SG_ Speed : 0|16@1+ (0.1,0) [0|6553.5] "km/h" *
"#,
)
.unwrap();
let payload = [0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(0x400, &payload, true).unwrap();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].name, "Speed");
assert_eq!(decoded[0].value, 100.0);
assert_eq!(decoded[0].unit, Some("km/h"));
}
#[test]
fn test_decode_extended_can_id_not_found_without_flag() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 2147484672 ExtendedMsg : 8 ECM
SG_ Speed : 0|16@1+ (0.1,0) [0|6553.5] "km/h" *
"#,
)
.unwrap();
let payload = [0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let result = dbc.decode(0x400, &payload, false);
assert!(result.is_err());
}
#[test]
fn test_decode_standard_vs_extended_same_base_id() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 StandardMsg : 8 ECM
SG_ StdSignal : 0|8@1+ (1,0) [0|255] "" *
BO_ 2147483904 ExtendedMsg : 8 ECM
SG_ ExtSignal : 0|8@1+ (2,0) [0|510] "" *
"#,
)
.unwrap();
let payload = [0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let decoded_std = dbc.decode(256, &payload, false).unwrap();
assert_eq!(decoded_std.len(), 1);
assert_eq!(decoded_std[0].name, "StdSignal");
assert_eq!(decoded_std[0].value, 100.0);
let decoded_ext = dbc.decode(256, &payload, true).unwrap();
assert_eq!(decoded_ext.len(), 1);
assert_eq!(decoded_ext[0].name, "ExtSignal");
assert_eq!(decoded_ext[0].value, 200.0); }
#[test]
fn test_decode_with_value_descriptions() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 200 GearboxData : 4 ECM
SG_ GearActual : 0|8@1+ (1,0) [0|5] "" *
VAL_ 200 GearActual 0 "Park" 1 "Reverse" 2 "Neutral" 3 "Drive" 4 "Sport" 5 "Manual" ;
"#,
)
.unwrap();
let payload = [0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(200, &payload, false).unwrap();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].name, "GearActual");
assert_eq!(decoded[0].value, 0.0);
assert_eq!(decoded[0].description, Some("Park"));
let payload = [0x03, 0x00, 0x00, 0x00];
let decoded = dbc.decode(200, &payload, false).unwrap();
assert_eq!(decoded[0].value, 3.0);
assert_eq!(decoded[0].description, Some("Drive"));
let payload = [0x05, 0x00, 0x00, 0x00];
let decoded = dbc.decode(200, &payload, false).unwrap();
assert_eq!(decoded[0].value, 5.0);
assert_eq!(decoded[0].description, Some("Manual"));
}
#[test]
fn test_decode_without_value_descriptions() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine : 8 ECM
SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
"#,
)
.unwrap();
let payload = [0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(256, &payload, false).unwrap();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].name, "RPM");
assert_eq!(decoded[0].value, 2000.0);
assert_eq!(decoded[0].unit, Some("rpm"));
assert_eq!(decoded[0].description, None);
}
#[test]
fn test_decode_value_description_not_found() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 200 GearboxData : 4 ECM
SG_ GearActual : 0|8@1+ (1,0) [0|255] "" *
VAL_ 200 GearActual 0 "Park" 1 "Reverse" 2 "Neutral" ;
"#,
)
.unwrap();
let payload = [0x0A, 0x00, 0x00, 0x00];
let decoded = dbc.decode(200, &payload, false).unwrap();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].value, 10.0);
assert_eq!(decoded[0].description, None); }
#[test]
fn test_decode_multiplexer_with_value_descriptions() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 300 MultiplexedSensors : 8 ECM
SG_ SensorID M : 0|8@1+ (1,0) [0|3] "" *
SG_ Temperature m0 : 8|16@1- (0.1,-40) [-40|125] "°C" *
SG_ Pressure m1 : 8|16@1+ (0.01,0) [0|655.35] "kPa" *
VAL_ 300 SensorID 0 "Temperature Sensor" 1 "Pressure Sensor" 2 "Humidity Sensor" 3 "Voltage Sensor" ;
"#,
)
.unwrap();
let payload = [0x00, 0xF4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(300, &payload, false).unwrap();
let sensor_id = decoded.iter().find(|s| s.name == "SensorID").unwrap();
assert_eq!(sensor_id.value, 0.0);
assert_eq!(sensor_id.description, Some("Temperature Sensor"));
let temp = decoded.iter().find(|s| s.name == "Temperature").unwrap();
assert!(temp.description.is_none());
let payload = [0x01, 0x10, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00];
let decoded = dbc.decode(300, &payload, false).unwrap();
let sensor_id = decoded.iter().find(|s| s.name == "SensorID").unwrap();
assert_eq!(sensor_id.value, 1.0);
assert_eq!(sensor_id.description, Some("Pressure Sensor"));
}
#[cfg(feature = "embedded-can")]
mod embedded_can_tests {
use super::*;
use embedded_can::{ExtendedId, Frame, Id, StandardId};
struct TestFrame {
id: Id,
data: [u8; 8],
dlc: usize,
}
impl TestFrame {
fn new_standard(id: u16, data: &[u8]) -> Self {
let mut frame_data = [0u8; 8];
let dlc = data.len().min(8);
frame_data[..dlc].copy_from_slice(&data[..dlc]);
Self {
id: Id::Standard(StandardId::new(id).unwrap()),
data: frame_data,
dlc,
}
}
fn new_extended(id: u32, data: &[u8]) -> Self {
let mut frame_data = [0u8; 8];
let dlc = data.len().min(8);
frame_data[..dlc].copy_from_slice(&data[..dlc]);
Self {
id: Id::Extended(ExtendedId::new(id).unwrap()),
data: frame_data,
dlc,
}
}
}
impl Frame for TestFrame {
fn new(id: impl Into<Id>, data: &[u8]) -> Option<Self> {
let mut frame_data = [0u8; 8];
let dlc = data.len().min(8);
frame_data[..dlc].copy_from_slice(&data[..dlc]);
Some(Self {
id: id.into(),
data: frame_data,
dlc,
})
}
fn new_remote(_id: impl Into<Id>, _dlc: usize) -> Option<Self> {
None }
fn is_extended(&self) -> bool {
matches!(self.id, Id::Extended(_))
}
fn is_remote_frame(&self) -> bool {
false
}
fn id(&self) -> Id {
self.id
}
fn dlc(&self) -> usize {
self.dlc
}
fn data(&self) -> &[u8] {
&self.data[..self.dlc]
}
}
#[test]
fn test_decode_frame_standard() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine : 8 ECM
SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
"#,
)
.unwrap();
let frame =
TestFrame::new_standard(256, &[0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
let decoded = dbc.decode_frame(frame).unwrap();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].name, "RPM");
assert_eq!(decoded[0].value, 2000.0);
assert_eq!(decoded[0].unit, Some("rpm"));
}
#[test]
fn test_decode_frame_extended() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 2147484672 ExtendedMsg : 8 ECM
SG_ Speed : 0|16@1+ (0.1,0) [0|6553.5] "km/h" *
"#,
)
.unwrap();
let frame =
TestFrame::new_extended(0x400, &[0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
let decoded = dbc.decode_frame(frame).unwrap();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].name, "Speed");
assert_eq!(decoded[0].value, 100.0);
assert_eq!(decoded[0].unit, Some("km/h"));
}
#[test]
fn test_decode_frame_standard_vs_extended() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 StandardMsg : 8 ECM
SG_ StdSignal : 0|8@1+ (1,0) [0|255] "" *
BO_ 2147483904 ExtendedMsg : 8 ECM
SG_ ExtSignal : 0|8@1+ (2,0) [0|510] "" *
"#,
)
.unwrap();
let payload = [0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let std_frame = TestFrame::new_standard(256, &payload);
let decoded_std = dbc.decode_frame(std_frame).unwrap();
assert_eq!(decoded_std[0].name, "StdSignal");
assert_eq!(decoded_std[0].value, 100.0);
let ext_frame = TestFrame::new_extended(256, &payload);
let decoded_ext = dbc.decode_frame(ext_frame).unwrap();
assert_eq!(decoded_ext[0].name, "ExtSignal");
assert_eq!(decoded_ext[0].value, 200.0);
}
#[test]
fn test_decode_frame_message_not_found() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine : 8 ECM
"#,
)
.unwrap();
let frame =
TestFrame::new_standard(512, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
let result = dbc.decode_frame(frame);
assert!(result.is_err());
}
}
}