///|
pub(all) enum FrameLayoutCellKind {
LayoutUnused
LayoutOccupied
LayoutMultiplexed
LayoutConflict
} derive(Eq, Debug)
///|
pub(all) struct FrameLayoutOwner {
signal_index : Int
signal_name : String
/// Bit position within the raw signal value, where zero is the least
/// significant bit.
signal_bit : Int
multiplex : MultiplexRole
source : SourceSpan?
} derive(Eq, Debug)
///|
pub(all) struct FrameLayoutCell {
absolute_bit : Int
byte_index : Int
bit_in_byte : Int
kind : FrameLayoutCellKind
owners : Array[FrameLayoutOwner]
} derive(Eq, Debug)
///|
pub(all) enum FrameLayoutIssue {
LayoutInvalidPayloadSize(Int)
LayoutInvalidSignal(String, Int, Int, SourceSpan?)
LayoutSignalOutsidePayload(String, SourceSpan?)
LayoutConflictingSignals(String, String, Array[Int], SourceSpan?, SourceSpan?)
} derive(Eq, Debug)
///|
pub(all) struct FrameLayoutAnalysis {
message_id : UInt
payload_size : Int
cells : Array[FrameLayoutCell]
issues : Array[FrameLayoutIssue]
} derive(Eq, Debug)
///|
pub(all) enum FrameLayoutError {
LayoutUnknownMessage(UInt)
} derive(Eq, Debug)
///|
fn layout_roles_can_coexist(
left : MultiplexRole,
right : MultiplexRole,
) -> Bool {
signals_can_coexist(left, right)
}
///|
fn layout_signal_source(
source_map : DbcSourceMap?,
message_id : UInt,
signal_name : StringView,
) -> SourceSpan? {
match source_map {
Some(map) => map.find_signal(message_id, signal_name)
None => None
}
}
///|
fn layout_signal_bits(
signal : Signal,
available_bits : Int,
) -> Array[(Int, Int)]? {
let bits : Array[(Int, Int)] = []
match signal.byte_order {
Intel => {
if signal.bit_length > available_bits ||
signal.start_bit > available_bits - signal.bit_length {
return None
}
for offset = 0; offset < signal.bit_length; offset = offset + 1 {
bits.push((signal.start_bit + offset, offset))
}
}
Motorola => {
let mut absolute_bit = signal.start_bit
for offset = 0; offset < signal.bit_length; offset = offset + 1 {
if absolute_bit >= available_bits {
return None
}
bits.push((absolute_bit, signal.bit_length - 1 - offset))
if offset + 1 < signal.bit_length {
absolute_bit = if absolute_bit % 8 == 0 {
absolute_bit + 15
} else {
absolute_bit - 1
}
}
}
}
}
Some(bits)
}
///|
fn shared_layout_bits(
left : Array[(Int, Int)],
right : Array[(Int, Int)],
) -> Array[Int] {
let shared : Array[Int] = []
for left_bit in left {
for right_bit in right {
if left_bit.0 == right_bit.0 {
shared.push(left_bit.0)
}
}
}
shared
}
///|
fn analyze_message_layout(
message : Message,
source_map : DbcSourceMap?,
) -> FrameLayoutAnalysis {
let issues : Array[FrameLayoutIssue] = []
let available_bits = if message.payload_size >= 0 &&
message.payload_size <= 64 {
message.payload_size * 8
} else {
issues.push(LayoutInvalidPayloadSize(message.payload_size))
0
}
let cells : Array[FrameLayoutCell] = []
for absolute_bit = 0
absolute_bit < available_bits
absolute_bit = absolute_bit + 1 {
cells.push({
absolute_bit,
byte_index: absolute_bit / 8,
bit_in_byte: absolute_bit % 8,
kind: LayoutUnused,
owners: [],
})
}
let signal_bits : Array[Array[(Int, Int)]?] = []
for signal_index, signal in message.signals {
let source = layout_signal_source(source_map, message.id, signal.name)
if signal.start_bit < 0 || signal.bit_length < 1 || signal.bit_length > 64 {
issues.push(
LayoutInvalidSignal(
signal.name,
signal.start_bit,
signal.bit_length,
source,
),
)
signal_bits.push(None)
continue
}
let bits = layout_signal_bits(signal, available_bits)
match bits {
None => {
issues.push(LayoutSignalOutsidePayload(signal.name, source))
signal_bits.push(None)
}
Some(bits) => {
for bit in bits {
cells[bit.0].owners.push({
signal_index,
signal_name: signal.name,
signal_bit: bit.1,
multiplex: signal.multiplex,
source,
})
}
signal_bits.push(Some(bits))
}
}
}
for right = 0; right < message.signals.length(); right = right + 1 {
for left = 0; left < right; left = left + 1 {
if layout_roles_can_coexist(
message.signals[left].multiplex,
message.signals[right].multiplex,
) {
match (signal_bits[left], signal_bits[right]) {
(Some(left_bits), Some(right_bits)) => {
let shared = shared_layout_bits(left_bits, right_bits)
if !shared.is_empty() {
issues.push(
LayoutConflictingSignals(
message.signals[left].name,
message.signals[right].name,
shared,
layout_signal_source(
source_map,
message.id,
message.signals[left].name,
),
layout_signal_source(
source_map,
message.id,
message.signals[right].name,
),
),
)
}
}
_ => ()
}
}
}
}
for index, cell in cells {
let kind = if cell.owners.is_empty() {
LayoutUnused
} else if cell.owners.length() == 1 {
LayoutOccupied
} else {
let mut conflict = false
for right = 0; right < cell.owners.length(); right = right + 1 {
for left = 0; left < right; left = left + 1 {
if layout_roles_can_coexist(
cell.owners[left].multiplex,
cell.owners[right].multiplex,
) {
conflict = true
}
}
}
if conflict {
LayoutConflict
} else {
LayoutMultiplexed
}
}
cells[index] = { ..cell, kind, }
}
{ message_id: message.id, payload_size: message.payload_size, cells, issues, }
}
///|
/// Analyze the payload bits occupied by every signal. Source ranges are absent
/// for databases that were constructed manually or returned by `parse`.
pub fn Database::analyze_frame_layout(
self : Database,
message_id : UInt,
) -> Result[FrameLayoutAnalysis, FrameLayoutError] {
match self.find_message(message_id) {
Some(message) => Ok(analyze_message_layout(message, None))
None => Err(LayoutUnknownMessage(message_id))
}
}
///|
/// Analyze a parsed message and attach its original DBC declaration ranges.
pub fn SourceParseResult::analyze_frame_layout(
self : SourceParseResult,
message_id : UInt,
) -> Result[FrameLayoutAnalysis, FrameLayoutError] {
match self.database.find_message(message_id) {
Some(message) => Ok(analyze_message_layout(message, Some(self.source_map)))
None => Err(LayoutUnknownMessage(message_id))
}
}
///|
pub fn FrameLayoutAnalysis::find_cell(
self : FrameLayoutAnalysis,
byte_index : Int,
bit_in_byte : Int,
) -> FrameLayoutCell? {
if byte_index < 0 ||
byte_index >= self.cells.length() / 8 ||
bit_in_byte < 0 ||
bit_in_byte > 7 {
return None
}
let absolute_bit = byte_index * 8 + bit_in_byte
if absolute_bit >= self.cells.length() {
None
} else {
Some(self.cells[absolute_bit])
}
}