use crate::DataType;
use crate::blocks::ConversionBlock;
pub fn signal_to_data_type(signal: &dbc_rs::Signal) -> DataType {
let length = signal.length();
let is_le = signal.byte_order() == dbc_rs::ByteOrder::LittleEndian;
let is_unsigned = signal.is_unsigned();
match (is_unsigned, is_le, length) {
(true, true, 1..=64) => DataType::UnsignedIntegerLE,
(true, false, 1..=64) => DataType::UnsignedIntegerBE,
(false, true, 1..=64) => DataType::SignedIntegerLE,
(false, false, 1..=64) => DataType::SignedIntegerBE,
_ => DataType::ByteArray,
}
}
pub fn signal_to_bit_count(signal: &dbc_rs::Signal) -> u32 {
let length = signal.length();
match length {
0..=8 => 8,
9..=16 => 16,
17..=32 => 32,
_ => 64,
}
}
#[allow(dead_code)] pub fn signal_to_conversion(signal: &dbc_rs::Signal) -> Option<ConversionBlock> {
let factor = signal.factor();
let offset = signal.offset();
if factor == 1.0 && offset == 0.0 {
return None;
}
Some(ConversionBlock::linear(offset, factor))
}
pub fn signal_to_conversion_with_range(signal: &dbc_rs::Signal) -> Option<ConversionBlock> {
let factor = signal.factor();
let offset = signal.offset();
let min = signal.min();
let max = signal.max();
if factor == 1.0 && offset == 0.0 && min == 0.0 && max == 0.0 {
return None;
}
let mut conv = ConversionBlock::linear(offset, factor);
if min != 0.0 || max != 0.0 {
conv = conv.with_physical_range(min, max);
}
Some(conv)
}
#[derive(Debug, Clone)]
pub struct SignalInfo {
pub name: alloc::string::String,
pub unit: Option<alloc::string::String>,
pub data_type: DataType,
pub bit_count: u32,
pub conversion: Option<ConversionBlock>,
pub min: f64,
pub max: f64,
#[allow(dead_code)] pub factor: f64,
#[allow(dead_code)] pub offset: f64,
pub unsigned: bool,
}
impl SignalInfo {
pub fn from_signal(signal: &dbc_rs::Signal) -> Self {
Self {
name: alloc::string::String::from(signal.name()),
unit: signal.unit().map(alloc::string::String::from),
data_type: signal_to_data_type(signal),
bit_count: signal_to_bit_count(signal),
conversion: signal_to_conversion_with_range(signal),
min: signal.min(),
max: signal.max(),
factor: signal.factor(),
offset: signal.offset(),
unsigned: signal.is_unsigned(),
}
}
#[allow(dead_code)] pub fn needs_conversion(&self) -> bool {
self.conversion.is_some()
}
#[allow(dead_code)] pub fn is_identity(&self) -> bool {
self.factor == 1.0 && self.offset == 0.0
}
#[allow(dead_code)] #[inline]
pub fn raw_to_physical(&self, raw: i64) -> f64 {
self.offset + self.factor * (raw as f64)
}
#[allow(dead_code)] #[inline]
pub fn physical_to_raw(&self, physical: f64) -> i64 {
let raw = (physical - self.offset) / self.factor;
if raw >= 0.0 {
(raw + 0.5) as i64
} else {
(raw - 0.5) as i64
}
}
}
#[allow(dead_code)] #[derive(Debug, Clone)]
pub struct MessageInfo {
pub id: u32,
pub name: alloc::string::String,
pub dlc: u8,
pub sender: alloc::string::String,
pub signals: alloc::vec::Vec<SignalInfo>,
pub is_extended: bool,
}
#[allow(dead_code)] impl MessageInfo {
pub fn from_message(message: &dbc_rs::Message) -> Self {
let id = message.id();
let is_extended = (id & 0x8000_0000) != 0;
let raw_id = if is_extended { id & 0x1FFF_FFFF } else { id };
Self {
id: raw_id,
name: alloc::string::String::from(message.name()),
dlc: message.dlc(),
sender: alloc::string::String::from(message.sender()),
signals: message
.signals()
.iter()
.map(SignalInfo::from_signal)
.collect(),
is_extended,
}
}
pub fn signal_count(&self) -> usize {
self.signals.len()
}
}
#[allow(dead_code)] pub fn extract_message_info(dbc: &dbc_rs::Dbc) -> alloc::vec::Vec<MessageInfo> {
dbc.messages()
.iter()
.filter(|m| m.signals().iter().next().is_some()) .map(MessageInfo::from_message)
.collect()
}
use alloc::string::String;
use alloc::vec::Vec;
#[allow(dead_code)] pub fn value_descriptions_to_mapping<'a>(
descriptions: impl Iterator<Item = (i64, &'a str)>,
default_text: &str,
) -> (Vec<(i64, String)>, String) {
let mapping: Vec<(i64, String)> = descriptions.map(|(v, t)| (v, String::from(t))).collect();
(mapping, String::from(default_text))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signal_to_data_type_unsigned_le() {
let dbc = dbc_rs::Dbc::parse(
r#"VERSION "1.0"
BU_:
BO_ 100 TestMsg: 8 Vector__XXX
SG_ TestSig : 0|16@1+ (1,0) [0|65535] "" Vector__XXX
"#,
)
.unwrap();
let msg = dbc.messages().find_by_id(100).unwrap();
let sig = msg.signals().find("TestSig").unwrap();
let dt = signal_to_data_type(sig);
assert!(matches!(dt, DataType::UnsignedIntegerLE));
}
#[test]
fn test_signal_to_conversion_identity() {
let dbc = dbc_rs::Dbc::parse(
r#"VERSION "1.0"
BU_:
BO_ 100 TestMsg: 8 Vector__XXX
SG_ TestSig : 0|16@1+ (1,0) [0|65535] "" Vector__XXX
"#,
)
.unwrap();
let msg = dbc.messages().find_by_id(100).unwrap();
let sig = msg.signals().find("TestSig").unwrap();
assert!(signal_to_conversion(sig).is_none());
}
#[test]
fn test_signal_to_conversion_linear() {
let dbc = dbc_rs::Dbc::parse(
r#"VERSION "1.0"
BU_:
BO_ 100 TestMsg: 8 Vector__XXX
SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" Vector__XXX
"#,
)
.unwrap();
let msg = dbc.messages().find_by_id(100).unwrap();
let sig = msg.signals().find("RPM").unwrap();
let conv = signal_to_conversion(sig);
assert!(conv.is_some());
let conv = conv.unwrap();
assert_eq!(conv.values.len(), 2);
assert_eq!(conv.values[0], 0.0); assert_eq!(conv.values[1], 0.25); }
#[test]
fn test_signal_info_conversion() {
let dbc = dbc_rs::Dbc::parse(
r#"VERSION "1.0"
BU_:
BO_ 100 TestMsg: 8 Vector__XXX
SG_ Temp : 0|8@1- (0.5,-40) [-40|85] "C" Vector__XXX
"#,
)
.unwrap();
let msg = dbc.messages().find_by_id(100).unwrap();
let sig = msg.signals().find("Temp").unwrap();
let info = SignalInfo::from_signal(sig);
assert_eq!(info.name, "Temp");
assert_eq!(info.unit, Some(String::from("C")));
assert_eq!(info.factor, 0.5);
assert_eq!(info.offset, -40.0);
assert_eq!(info.min, -40.0);
assert_eq!(info.max, 85.0);
assert!(!info.unsigned);
assert_eq!(info.raw_to_physical(0), -40.0);
assert_eq!(info.raw_to_physical(160), 40.0); }
#[test]
fn test_message_info() {
let dbc = dbc_rs::Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 Engine: 8 ECM
SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" Vector__XXX
SG_ Temp : 16|8@1- (1,-40) [-40|215] "C" Vector__XXX
"#,
)
.unwrap();
let msg = dbc.messages().find_by_id(256).unwrap();
let info = MessageInfo::from_message(msg);
assert_eq!(info.id, 256);
assert_eq!(info.name, "Engine");
assert_eq!(info.dlc, 8);
assert_eq!(info.sender, "ECM");
assert_eq!(info.signal_count(), 2);
assert!(!info.is_extended);
}
}