///|
pub(all) enum ValidationSeverity {
Error
Warning
} derive(Eq, Debug)
///|
pub struct DbcValidationIssue {
severity_value : ValidationSeverity
code_value : String
message_value : String
message_id_value : Int?
signal_name_value : String?
source_line_value : Int
} derive(Eq, Debug)
///|
pub(all) struct DbcValidationPolicy {
allow_extended_ids : Bool
allow_can_fd : Bool
require_known_nodes : Bool
detect_signal_overlap : Bool
warn_empty_units : Bool
}
///|
pub struct DbcValidationResult {
issues_value : Array[DbcValidationIssue]
error_count_value : Int
warning_count_value : Int
} derive(Eq, Debug)
///|
pub fn DbcValidationPolicy::strict_classic_can() -> DbcValidationPolicy {
{
allow_extended_ids: true,
allow_can_fd: false,
require_known_nodes: true,
detect_signal_overlap: true,
warn_empty_units: false,
}
}
///|
pub fn DbcValidationPolicy::can_fd() -> DbcValidationPolicy {
{ ..DbcValidationPolicy::strict_classic_can(), allow_can_fd: true }
}
///|
pub fn DbcValidationIssue::severity(
self : DbcValidationIssue,
) -> ValidationSeverity {
self.severity_value
}
///|
pub fn DbcValidationIssue::code(self : DbcValidationIssue) -> String {
self.code_value
}
///|
pub fn DbcValidationIssue::message(self : DbcValidationIssue) -> String {
self.message_value
}
///|
pub fn DbcValidationIssue::message_id(self : DbcValidationIssue) -> Int? {
self.message_id_value
}
///|
pub fn DbcValidationIssue::signal_name(self : DbcValidationIssue) -> String? {
self.signal_name_value
}
///|
pub fn DbcValidationIssue::source_line(self : DbcValidationIssue) -> Int {
self.source_line_value
}
///|
pub fn DbcValidationResult::issues(
self : DbcValidationResult,
) -> Array[DbcValidationIssue] {
self.issues_value.copy()
}
///|
pub fn DbcValidationResult::is_valid(self : DbcValidationResult) -> Bool {
self.error_count_value == 0
}
///|
pub fn DbcValidationResult::error_count(self : DbcValidationResult) -> Int {
self.error_count_value
}
///|
pub fn DbcValidationResult::warning_count(self : DbcValidationResult) -> Int {
self.warning_count_value
}
///|
fn validation_issue(
severity : ValidationSeverity,
code : String,
message : String,
source_line : Int,
message_id? : Int? = None,
signal_name? : String? = None,
) -> DbcValidationIssue {
{
severity_value: severity,
code_value: code,
message_value: message,
message_id_value: message_id,
signal_name_value: signal_name,
source_line_value: source_line,
}
}
///|
fn known_node(database : DbcDatabase, name : String) -> Bool {
if name == "Vector__XXX" {
return true
}
for node in database.nodes() {
if node.name() == name {
return true
}
}
false
}
///|
fn signal_bit_positions(signal : DbcSignal) -> Result[Array[Int], DbcError] {
if signal.start_bit() < 0 ||
signal.bit_length() <= 0 ||
signal.bit_length() > 64 {
return Err(
DbcError::new(
InvalidBitRange,
"signal bit layout is outside supported bounds",
signal.source_line(),
1,
context=signal.name(),
),
)
}
let positions : Array[Int] = []
match signal.byte_order() {
Intel =>
for offset in 0.. {
let mut current = signal.start_bit()
for _ in 0.. Unit {
for index, message in messages {
for previous in 0.. Unit {
let signals = message.signals()
for index, signal in signals {
for previous in 0.. Unit {
let maximum_id = if policy.allow_extended_ids { 536870911 } else { 2047 }
if message.id() < 0 || message.id() > maximum_id {
issues.push(
validation_issue(
Error,
"message.id_out_of_range",
"message id exceeds the configured CAN identifier range",
message.source_line(),
message_id=Some(message.id()),
),
)
}
let maximum_size = if policy.allow_can_fd { 64 } else { 8 }
if message.size() < 0 || message.size() > maximum_size {
issues.push(
validation_issue(
Error,
InvalidDataLength.code(),
"message size exceeds the configured frame size",
message.source_line(),
message_id=Some(message.id()),
),
)
}
if policy.require_known_nodes && !known_node(database, message.transmitter()) {
issues.push(
validation_issue(
Error,
"message.unknown_transmitter",
"message transmitter is not declared in BU_",
message.source_line(),
message_id=Some(message.id()),
),
)
}
}
///|
fn add_signal_policy_issues(
database : DbcDatabase,
message : DbcMessage,
signal : DbcSignal,
policy : DbcValidationPolicy,
issues : Array[DbcValidationIssue],
) -> Unit {
if signal.factor() == 0.0 {
issues.push(
validation_issue(
Error,
"signal.zero_factor",
"signal factor cannot be zero",
signal.source_line(),
message_id=Some(message.id()),
signal_name=Some(signal.name()),
),
)
}
if signal.minimum() > signal.maximum() {
issues.push(
validation_issue(
Error,
"signal.invalid_physical_range",
"signal minimum exceeds maximum",
signal.source_line(),
message_id=Some(message.id()),
signal_name=Some(signal.name()),
),
)
}
if policy.warn_empty_units && signal.unit() == "" {
issues.push(
validation_issue(
Warning,
"signal.empty_unit",
"signal has no engineering unit",
signal.source_line(),
message_id=Some(message.id()),
signal_name=Some(signal.name()),
),
)
}
if policy.require_known_nodes {
for receiver in signal.receivers() {
if !known_node(database, receiver) {
issues.push(
validation_issue(
Error,
"signal.unknown_receiver",
"signal receiver is not declared in BU_",
signal.source_line(),
message_id=Some(message.id()),
signal_name=Some(signal.name()),
),
)
}
}
}
match signal_bit_positions(signal) {
Err(error) =>
issues.push(
validation_issue(
Error,
error.code(),
error.message(),
signal.source_line(),
message_id=Some(message.id()),
signal_name=Some(signal.name()),
),
)
Ok(positions) =>
for position in positions {
if position >= message.size() * 8 {
issues.push(
validation_issue(
Error,
InvalidBitRange.code(),
"signal extends beyond the message payload",
signal.source_line(),
message_id=Some(message.id()),
signal_name=Some(signal.name()),
),
)
break
}
}
}
}
///|
fn add_overlap_issues(
message : DbcMessage,
issues : Array[DbcValidationIssue],
) -> Unit {
let owners = Array::make(message.size() * 8, "")
for signal in message.signals() {
let positions = match signal_bit_positions(signal) {
Ok(value) => value
Err(_) => continue
}
for position in positions {
if position < 0 || position >= owners.length() {
continue
}
if owners[position] != "" && owners[position] != signal.name() {
issues.push(
validation_issue(
Error,
OverlappingSignal.code(),
"signal overlaps " +
owners[position] +
" at frame bit " +
position.to_string(),
signal.source_line(),
message_id=Some(message.id()),
signal_name=Some(signal.name()),
),
)
break
}
owners[position] = signal.name()
}
}
}
///|
pub fn validate_dbc(
database : DbcDatabase,
policy? : DbcValidationPolicy = DbcValidationPolicy::strict_classic_can(),
) -> DbcValidationResult {
let issues : Array[DbcValidationIssue] = []
let messages = database.messages()
add_message_identity_issues(messages, issues)
for message in messages {
add_message_policy_issues(database, message, policy, issues)
add_signal_identity_issues(message, issues)
for signal in message.signals() {
add_signal_policy_issues(database, message, signal, policy, issues)
}
if policy.detect_signal_overlap {
add_overlap_issues(message, issues)
}
}
let mut errors = 0
let mut warnings = 0
for issue in issues {
match issue.severity() {
Error => errors = errors + 1
Warning => warnings = warnings + 1
}
}
{
issues_value: issues,
error_count_value: errors,
warning_count_value: warnings,
}
}