///|
pub struct DecodedSignal {
name_value : String
raw_bits_value : UInt64
signed_raw_value : Int64
physical_value : Double
label_value : String?
} derive(Eq, Debug)
///|
pub struct SignalAssignment {
name_value : String
physical_value : Double
} derive(Eq, Debug)
///|
pub fn DecodedSignal::name(self : DecodedSignal) -> String {
self.name_value
}
///|
pub fn DecodedSignal::raw_bits(self : DecodedSignal) -> UInt64 {
self.raw_bits_value
}
///|
pub fn DecodedSignal::signed_raw(self : DecodedSignal) -> Int64 {
self.signed_raw_value
}
///|
pub fn DecodedSignal::physical(self : DecodedSignal) -> Double {
self.physical_value
}
///|
pub fn DecodedSignal::label(self : DecodedSignal) -> String? {
self.label_value
}
///|
pub fn SignalAssignment::new(
name : String,
physical : Double,
) -> SignalAssignment {
{ name_value: name, physical_value: physical }
}
///|
pub fn SignalAssignment::name(self : SignalAssignment) -> String {
self.name_value
}
///|
pub fn SignalAssignment::physical(self : SignalAssignment) -> Double {
self.physical_value
}
///|
fn codec_error(
code : DbcErrorCode,
message : String,
signal : DbcSignal,
) -> DbcError {
DbcError::new(code, message, signal.source_line(), 1, context=signal.name())
}
///|
fn bit_mask(length : Int) -> UInt64 {
if length >= 64 {
0xFFFFFFFFFFFFFFFFUL
} else {
(1UL << length) - 1UL
}
}
///|
fn ensure_frame_length(
message : DbcMessage,
data : Bytes,
) -> Result[Unit, DbcError] {
if data.length() != message.size() {
Err(
DbcError::new(
InvalidDataLength,
"frame length does not match the DBC message size",
message.source_line(),
1,
context="expected \{message.size()}, received \{data.length()}",
),
)
} else {
Ok(())
}
}
///|
fn raw_bit_offset(signal : DbcSignal, sequence_index : Int) -> Int {
match signal.byte_order() {
Intel => sequence_index
Motorola => signal.bit_length() - 1 - sequence_index
}
}
///|
fn extract_raw_bits(
signal : DbcSignal,
data : Bytes,
) -> Result[UInt64, DbcError] {
let positions = match signal_bit_positions(signal) {
Ok(value) => value
Err(error) => return Err(error)
}
let mut raw = 0UL
for sequence_index, position in positions {
let byte_index = position / 8
let bit_index = position % 8
if byte_index < 0 || byte_index >= data.length() {
return Err(
codec_error(
InvalidBitRange,
"signal extends beyond supplied frame",
signal,
),
)
}
if (data[byte_index].to_int() & (1 << bit_index)) != 0 {
raw = raw | (1UL << raw_bit_offset(signal, sequence_index))
}
}
Ok(raw)
}
///|
fn insert_raw_bits(
signal : DbcSignal,
raw : UInt64,
data : Array[Byte],
) -> Result[Unit, DbcError] {
let positions = match signal_bit_positions(signal) {
Ok(value) => value
Err(error) => return Err(error)
}
for sequence_index, position in positions {
let byte_index = position / 8
let bit_index = position % 8
if byte_index < 0 || byte_index >= data.length() {
return Err(
codec_error(
InvalidBitRange,
"signal extends beyond supplied frame",
signal,
),
)
}
let mask = 1 << bit_index
let raw_offset = raw_bit_offset(signal, sequence_index)
let current = data[byte_index].to_int()
data[byte_index] = if (raw & (1UL << raw_offset)) != 0UL {
(current | mask).to_byte()
} else {
(current & (0xFF ^ mask)).to_byte()
}
}
Ok(())
}
///|
fn sign_extend(raw : UInt64, length : Int) -> Int64 {
if length >= 64 {
raw.reinterpret_as_int64()
} else if (raw & (1UL << (length - 1))) != 0UL {
(raw | (0xFFFFFFFFFFFFFFFFUL ^ bit_mask(length))).reinterpret_as_int64()
} else {
raw.reinterpret_as_int64()
}
}
///|
fn raw_label(signal : DbcSignal, signed : Int64) -> String? {
if signed < -2147483648L || signed > 2147483647L {
return None
}
signal.value_label(signed.to_int())
}
///|
pub fn decode_signal(
message : DbcMessage,
signal : DbcSignal,
data : Bytes,
) -> Result[DecodedSignal, DbcError] {
match ensure_frame_length(message, data) {
Err(error) => return Err(error)
Ok(_) => ()
}
let raw = match extract_raw_bits(signal, data) {
Ok(value) => value
Err(error) => return Err(error)
}
let signed = match signal.value_type() {
Signed => sign_extend(raw, signal.bit_length())
Unsigned => {
if signal.bit_length() == 64 && raw > 0x7FFFFFFFFFFFFFFFUL {
return Err(
codec_error(
ValueOutOfRange,
"unsigned 64-bit value cannot be represented as an exact physical Double",
signal,
),
)
}
raw.reinterpret_as_int64()
}
}
let physical = signed.to_double() * signal.factor() + signal.offset()
Ok({
name_value: signal.name(),
raw_bits_value: raw,
signed_raw_value: signed,
physical_value: physical,
label_value: raw_label(signal, signed),
})
}
///|
pub fn decode_message(
message : DbcMessage,
data : Bytes,
) -> Result[Array[DecodedSignal], DbcError] {
match ensure_frame_length(message, data) {
Err(error) => return Err(error)
Ok(_) => ()
}
let decoded : Array[DecodedSignal] = []
for signal in message.signals() {
match decode_signal(message, signal, data) {
Ok(value) => decoded.push(value)
Err(error) => return Err(error)
}
}
Ok(decoded)
}
///|
fn unsigned_raw_fits(signal : DbcSignal, raw : UInt64) -> Bool {
raw <= bit_mask(signal.bit_length())
}
///|
fn signed_raw_fits(signal : DbcSignal, raw : Int64) -> Bool {
if signal.bit_length() >= 64 {
return true
}
let maximum = (1L << (signal.bit_length() - 1)) - 1L
let minimum = -(1L << (signal.bit_length() - 1))
raw >= minimum && raw <= maximum
}
///|
pub fn encode_unsigned_raw_signal(
message : DbcMessage,
signal : DbcSignal,
raw : UInt64,
data : Array[Byte],
) -> Result[Unit, DbcError] {
if data.length() != message.size() {
return Err(
DbcError::new(
InvalidDataLength,
"frame length does not match the DBC message size",
message.source_line(),
1,
),
)
}
if signal.value_type() != Unsigned {
return Err(
codec_error(ValueOutOfRange, "signal is declared as signed", signal),
)
}
if !unsigned_raw_fits(signal, raw) {
return Err(
codec_error(
ValueOutOfRange,
"raw value does not fit signal width",
signal,
),
)
}
insert_raw_bits(signal, raw, data)
}
///|
pub fn encode_signed_raw_signal(
message : DbcMessage,
signal : DbcSignal,
raw : Int64,
data : Array[Byte],
) -> Result[Unit, DbcError] {
if data.length() != message.size() {
return Err(
DbcError::new(
InvalidDataLength,
"frame length does not match the DBC message size",
message.source_line(),
1,
),
)
}
if signal.value_type() != Signed {
return Err(
codec_error(ValueOutOfRange, "signal is declared as unsigned", signal),
)
}
if !signed_raw_fits(signal, raw) {
return Err(
codec_error(
ValueOutOfRange,
"raw value does not fit signal width",
signal,
),
)
}
insert_raw_bits(
signal,
raw.reinterpret_as_uint64() & bit_mask(signal.bit_length()),
data,
)
}
///|
fn absolute_double(value : Double) -> Double {
if value < 0.0 {
-value
} else {
value
}
}
///|
pub fn encode_physical_signal(
message : DbcMessage,
signal : DbcSignal,
physical : Double,
data : Array[Byte],
tolerance? : Double = 0.000000001,
) -> Result[Unit, DbcError] {
if data.length() != message.size() {
return Err(
DbcError::new(
InvalidDataLength,
"frame length does not match the DBC message size",
message.source_line(),
1,
),
)
}
if tolerance < 0.0 {
return Err(
codec_error(
ValueOutOfRange,
"quantization tolerance cannot be negative",
signal,
),
)
}
if physical < signal.minimum() || physical > signal.maximum() {
return Err(
codec_error(
ValueOutOfRange,
"physical value is outside declared signal range",
signal,
),
)
}
let exact_raw = signal.raw_from_physical(physical)
let rounded = (if exact_raw < 0.0 {
exact_raw - 0.5
} else {
exact_raw + 0.5
}).to_int64()
let reconstructed = rounded.to_double() * signal.factor() + signal.offset()
if absolute_double(reconstructed - physical) > tolerance {
return Err(
codec_error(
ValueOutOfRange,
"physical value is not representable within quantization tolerance",
signal,
),
)
}
match signal.value_type() {
Signed => encode_signed_raw_signal(message, signal, rounded, data)
Unsigned => {
if rounded < 0L {
return Err(
codec_error(
ValueOutOfRange,
"unsigned signal cannot encode a negative raw value",
signal,
),
)
}
encode_unsigned_raw_signal(
message,
signal,
rounded.reinterpret_as_uint64(),
data,
)
}
}
}
///|
fn assignment_seen(assignments : Array[SignalAssignment], index : Int) -> Bool {
for previous in 0.. Result[Bytes, DbcError] {
let data : Array[Byte] = Array::make(message.size(), (0 : Byte))
for index, assignment in assignments {
if assignment_seen(assignments, index) {
return Err(
DbcError::new(
DuplicateSignalName,
"signal assignment is repeated",
message.source_line(),
1,
context=assignment.name(),
),
)
}
let signal = match message.find_signal(assignment.name()) {
Some(value) => value
None =>
return Err(
DbcError::new(
UnknownSignal,
"assignment references an unknown signal",
message.source_line(),
1,
context=assignment.name(),
),
)
}
match encode_physical_signal(message, signal, assignment.physical(), data) {
Ok(_) => ()
Err(error) => return Err(error)
}
}
Ok(Bytes::from_array(data))
}