///|
fn dbc_escape(value : String) -> String {
let mut output = ""
for character in value {
let code = character.to_int()
if code == 34 {
output = output + "\\\""
} else if code == 92 {
output = output + "\\\\"
} else if code == 10 {
output = output + "\\n"
} else if code == 13 {
output = output + "\\r"
} else if code == 9 {
output = output + "\\t"
} else if code < 32 {
output = output + "?"
} else {
output = output + character.to_string()
}
}
output
}
///|
fn dbc_quoted(value : String) -> String {
"\"" + dbc_escape(value) + "\""
}
///|
fn render_double(value : Double) -> String {
let text = value.to_string()
if text.has_suffix(".0") {
text[:text.length() - 2].to_owned()
} else {
text
}
}
///|
fn render_receivers(receivers : Array[String]) -> String {
let mut output = ""
for index, receiver in receivers {
if index > 0 {
output = output + ","
}
output = output + receiver
}
output
}
///|
fn render_signal_line(signal : DbcSignal) -> String {
let byte_order = match signal.byte_order() {
Motorola => "0"
Intel => "1"
}
let value_type = match signal.value_type() {
Unsigned => "+"
Signed => "-"
}
" SG_ \{signal.name()} : \{signal.start_bit()}|\{signal.bit_length()}@\{byte_order}\{value_type} (\{render_double(signal.factor())},\{render_double(signal.offset())}) [\{render_double(signal.minimum())}|\{render_double(signal.maximum())}] \{dbc_quoted(signal.unit())} \{render_receivers(signal.receivers())}\n"
}
///|
fn render_message_block(message : DbcMessage) -> String {
let mut output = "BO_ \{message.id()} \{message.name()}: \{message.size()} \{message.transmitter()}\n"
for signal in message.signals() {
output = output + render_signal_line(signal)
}
output + "\n"
}
///|
fn render_nodes(nodes : Array[DbcNode]) -> String {
let mut output = "BU_:"
for node in nodes {
output = output + " " + node.name()
}
output + "\n\n"
}
///|
fn render_comments(messages : Array[DbcMessage]) -> String {
let mut output = ""
for message in messages {
if message.comment() != "" {
output = output +
"CM_ BO_ \{message.id()} \{dbc_quoted(message.comment())};\n"
}
for signal in message.signals() {
if signal.comment() != "" {
output = output +
"CM_ SG_ \{message.id()} \{signal.name()} \{dbc_quoted(signal.comment())};\n"
}
}
}
if output != "" {
output + "\n"
} else {
output
}
}
///|
fn render_value_tables(messages : Array[DbcMessage]) -> String {
let mut output = ""
for message in messages {
for signal in message.signals() {
let values = signal.values()
if values.is_empty() {
continue
}
output = output + "VAL_ \{message.id()} \{signal.name()}"
for value in values {
output = output + " \{value.raw()} \{dbc_quoted(value.label())}"
}
output = output + ";\n"
}
}
output
}
///|
fn has_duplicate_database_identity(database : DbcDatabase) -> Bool {
let messages = database.messages()
for index, message in messages {
for previous in 0.. Result[String, DbcError] {
if has_duplicate_database_identity(database) {
return Err(
DbcError::new(
InvalidMessage,
"database contains duplicate message or signal identities",
1,
1,
),
)
}
let mut output = "VERSION " + dbc_quoted(database.version()) + "\n\n"
output = output + "NS_ :\n\nBS_:\n\n"
output = output + render_nodes(database.nodes())
for message in database.messages() {
output = output + render_message_block(message)
}
output = output + render_comments(database.messages())
output = output + render_value_tables(database.messages())
Ok(output)
}
///|
fn signal_round_trip_equal(left : DbcSignal, right : DbcSignal) -> Bool {
left.name() == right.name() &&
left.start_bit() == right.start_bit() &&
left.bit_length() == right.bit_length() &&
left.byte_order() == right.byte_order() &&
left.value_type() == right.value_type() &&
round_trip_double_equal(left.factor(), right.factor()) &&
round_trip_double_equal(left.offset(), right.offset()) &&
round_trip_double_equal(left.minimum(), right.minimum()) &&
round_trip_double_equal(left.maximum(), right.maximum()) &&
left.unit() == right.unit() &&
left.receivers() == right.receivers() &&
left.values() == right.values() &&
left.comment() == right.comment()
}
///|
fn round_trip_double_equal(left : Double, right : Double) -> Bool {
if left == right {
return true
}
let difference = if left > right { left - right } else { right - left }
let left_magnitude = if left < 0.0 { -left } else { left }
let right_magnitude = if right < 0.0 { -right } else { right }
let mut scale = if left_magnitude > right_magnitude {
left_magnitude
} else {
right_magnitude
}
if scale < 1.0 {
scale = 1.0
}
difference <= scale * 0.000000000001
}
///|
fn message_round_trip_equal(left : DbcMessage, right : DbcMessage) -> Bool {
if left.id() != right.id() ||
left.name() != right.name() ||
left.size() != right.size() ||
left.transmitter() != right.transmitter() ||
left.comment() != right.comment() {
return false
}
let left_signals = left.signals()
let right_signals = right.signals()
if left_signals.length() != right_signals.length() {
return false
}
for index, signal in left_signals {
if !signal_round_trip_equal(signal, right_signals[index]) {
return false
}
}
true
}
///|
pub fn database_content_equal(left : DbcDatabase, right : DbcDatabase) -> Bool {
if left.version() != right.version() || left.nodes() != right.nodes() {
return false
}
let left_messages = left.messages()
let right_messages = right.messages()
if left_messages.length() != right_messages.length() {
return false
}
for index, message in left_messages {
if !message_round_trip_equal(message, right_messages[index]) {
return false
}
}
true
}