use super::ValueDescriptionsMap;
use crate::{Error, ExtendedMultiplexing, Message, Nodes, Result, VECTOR_XXX};
pub(crate) struct Validate;
impl Validate {
pub fn validate(
nodes: &Nodes,
messages: &[Message],
value_descriptions: Option<&ValueDescriptionsMap>,
extended_multiplexing: Option<&[ExtendedMultiplexing]>,
) -> Result<()> {
Self::validate_common(nodes, messages)?;
if let Some(value_descriptions) = value_descriptions {
Self::validate_value_descriptions(messages, value_descriptions)?;
}
if let Some(ext_mux_entries) = extended_multiplexing {
Self::validate_extended_multiplexing(messages, ext_mux_entries)?;
}
Ok(())
}
fn validate_common(nodes: &Nodes, messages: &[Message]) -> Result<()> {
for (i, msg1) in messages.iter().enumerate() {
for msg2 in messages.iter().skip(i + 1) {
if msg1.id_with_flag() == msg2.id_with_flag() {
return Err(Error::Validation(Error::DUPLICATE_MESSAGE_ID));
}
}
}
if !nodes.is_empty() {
for msg in messages {
let sender = msg.sender();
if sender != VECTOR_XXX && !nodes.contains(sender) {
return Err(Error::Validation(Error::SENDER_NOT_IN_NODES));
}
}
}
Ok(())
}
fn validate_value_descriptions(
messages: &[Message],
value_descriptions: &ValueDescriptionsMap,
) -> Result<()> {
for ((message_id_opt, signal_name), _) in value_descriptions.iter() {
if let Some(message_id) = message_id_opt {
let message_exists = messages.iter().any(|msg| msg.id_with_flag() == message_id);
if !message_exists {
return Err(Error::Validation(
Error::VALUE_DESCRIPTION_MESSAGE_NOT_FOUND,
));
}
let signal_exists = messages.iter().any(|msg| {
msg.id_with_flag() == message_id && msg.signals().find(signal_name).is_some()
});
if !signal_exists {
return Err(Error::Validation(Error::VALUE_DESCRIPTION_SIGNAL_NOT_FOUND));
}
} else {
let signal_exists =
messages.iter().any(|msg| msg.signals().find(signal_name).is_some());
if !signal_exists {
return Err(Error::Validation(Error::VALUE_DESCRIPTION_SIGNAL_NOT_FOUND));
}
}
}
Ok(())
}
fn validate_extended_multiplexing(
messages: &[Message],
ext_mux_entries: &[ExtendedMultiplexing],
) -> Result<()> {
for ext_mux in ext_mux_entries {
let message_id = ext_mux.message_id();
let signal_name = ext_mux.signal_name();
let multiplexer_switch = ext_mux.multiplexer_switch();
let message = messages
.iter()
.find(|msg| msg.id_with_flag() == message_id)
.ok_or(Error::Validation(Error::EXT_MUX_MESSAGE_NOT_FOUND))?;
if message.signals().find(signal_name).is_none() {
return Err(Error::Validation(Error::EXT_MUX_SIGNAL_NOT_FOUND));
}
if message.signals().find(multiplexer_switch).is_none() {
return Err(Error::Validation(Error::EXT_MUX_SWITCH_NOT_FOUND));
}
for (min, max) in ext_mux.value_ranges() {
if min > max {
return Err(Error::Validation(Error::EXT_MUX_INVALID_RANGE));
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::Dbc;
#[test]
fn test_validate_standard_and_extended_same_base_id_not_duplicate() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 256 StandardMsg : 8 ECM
SG_ Signal1 : 0|8@1+ (1,0) [0|255] "" Vector__XXX
BO_ 2147483904 ExtendedMsg : 8 ECM
SG_ Signal2 : 0|8@1+ (1,0) [0|255] "" Vector__XXX
"#,
);
assert!(
dbc.is_ok(),
"Standard and extended IDs with same base should not be duplicates"
);
let dbc = dbc.unwrap();
assert_eq!(dbc.messages().len(), 2);
let std_msg = dbc.messages().find("StandardMsg").unwrap();
let ext_msg = dbc.messages().find("ExtendedMsg").unwrap();
assert_eq!(std_msg.id(), 256);
assert!(!std_msg.is_extended());
assert_eq!(ext_msg.id(), 256); assert!(ext_msg.is_extended());
}
#[test]
fn test_validate_duplicate_extended_ids_rejected() {
let result = Dbc::parse(
r#"VERSION "1.0"
BU_: ECM
BO_ 2147483904 ExtendedMsg1 : 8 ECM
SG_ Signal1 : 0|8@1+ (1,0) [0|255] "" Vector__XXX
BO_ 2147483904 ExtendedMsg2 : 8 ECM
SG_ Signal2 : 0|8@1+ (1,0) [0|255] "" Vector__XXX
"#,
);
assert!(result.is_err());
}
#[test]
fn test_validate_value_descriptions_extended_id() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_:
BO_ 2564485397 OBD2 : 8 Vector__XXX
SG_ Mode : 0|8@1+ (1,0) [0|255] "" Vector__XXX
VAL_ 2564485397 Mode 1 "CurrentData" 2 "FreezeFrame" 3 "StoredDTCs" ;
"#,
);
assert!(
dbc.is_ok(),
"Value descriptions with extended ID should parse: {:?}",
dbc.err()
);
let dbc = dbc.unwrap();
let msg = dbc.messages().iter().next().unwrap();
assert_eq!(msg.id(), 0x18DAF115); assert!(msg.is_extended());
}
#[test]
fn test_validate_extended_multiplexing_extended_id() {
let dbc = Dbc::parse(
r#"VERSION "1.0"
BU_:
BO_ 2564485397 OBD2 : 8 Vector__XXX
SG_ Service M : 0|8@1+ (1,0) [0|255] "" Vector__XXX
SG_ PID m1 : 8|8@1+ (1,0) [0|255] "" Vector__XXX
SG_ Data m1 : 16|16@1+ (1,0) [0|65535] "" Vector__XXX
SG_MUL_VAL_ 2564485397 PID Service 1-1;
SG_MUL_VAL_ 2564485397 Data PID 12-12;
"#,
);
assert!(
dbc.is_ok(),
"Extended multiplexing with extended ID should parse: {:?}",
dbc.err()
);
let dbc = dbc.unwrap();
let msg = dbc.messages().iter().next().unwrap();
assert_eq!(msg.id(), 0x18DAF115); assert!(msg.is_extended());
assert_eq!(msg.signals().len(), 3);
}
#[test]
fn test_validate_real_obd2_extended_id_pattern() {
let dbc = Dbc::parse(
r#"VERSION ""
NS_ :
BS_:
BU_:
BO_ 2564485397 OBD2: 8 Vector__XXX
SG_ S M : 15|8@0+ (1,0) [0|15] "" Vector__XXX
SG_ S01PID m65M : 23|8@0+ (1,0) [0|255] "" Vector__XXX
SG_ S01PID0D_VehicleSpeed m13 : 31|8@0+ (1,0) [0|255] "km/h" Vector__XXX
SG_MUL_VAL_ 2564485397 S01PID S 65-65;
SG_MUL_VAL_ 2564485397 S01PID0D_VehicleSpeed S01PID 13-13;
"#,
);
assert!(
dbc.is_ok(),
"Real OBD2 pattern should parse: {:?}",
dbc.err()
);
let dbc = dbc.unwrap();
let msg = dbc.messages().iter().next().unwrap();
assert_eq!(msg.id(), 417001749);
assert!(msg.is_extended());
}
}