///|
/// A state change represented by the display form of a VCD value.
pub struct StateTransition {
signal_id : String
time : Int64
from : String
to : String
} derive(Debug, Eq)
///|
/// A dwell interval labelled by its digital state.
pub struct StateInterval {
signal_id : String
start : Int64
end : Int64
state : String
} derive(Debug, Eq)
///|
/// Return all value transitions, including vector and real changes.
pub fn state_transitions(
trace : Trace,
signal_id : String,
) -> Array[StateTransition] {
let result : Array[StateTransition] = []
let mut previous : Value? = None
for change in signal_changes(trace, signal_id) {
match previous {
Some(before) if before != change.value =>
result.push({
signal_id,
time: change.time,
from: before.display(),
to: change.value.display(),
})
_ => ()
}
previous = Some(change.value)
}
result
}
///|
/// Return state intervals for a signal, preserving vector values.
pub fn state_intervals(
trace : Trace,
signal_id : String,
) -> Array[StateInterval] {
value_intervals(trace, signal_id).map(fn(interval) {
{
signal_id,
start: interval.start,
end: interval.end,
state: interval.value.display(),
}
})
}
///|
/// Count dwell time by state label.
pub fn state_dwell(trace : Trace, signal_id : String) -> Array[(String, Int64)] {
let result : Array[(String, Int64)] = []
for interval in state_intervals(trace, signal_id) {
let duration = Int64::max(0L, interval.end - interval.start)
match result.search_by(fn(item) { item.0 == interval.state }) {
Some(index) =>
result[index] = (interval.state, result[index].1 + duration)
None => result.push((interval.state, duration))
}
}
result
}
///|
/// Decode a known integer value through an optional state label table.
pub fn decode_state(value : Value, labels : Array[String]) -> String {
match value.to_int() {
Some(number) => {
let index = number.to_int()
match labels.get(index) {
Some(label) => label
None => "state_" + index.to_string()
}
}
None => value.display()
}
}
///|
/// Decode all observed values with a label table.
pub fn decoded_states(
trace : Trace,
signal_id : String,
labels : Array[String],
) -> Array[(Int64, String)] {
signal_changes(trace, signal_id).map(fn(change) {
(change.time, decode_state(change.value, labels))
})
}
///|
/// Return the sequence of stable state labels without adjacent duplicates.
pub fn state_sequence(trace : Trace, signal_id : String) -> Array[String] {
let result : Array[String] = []
for change in signal_changes(trace, signal_id) {
let state = change.value.display()
match result.last() {
Some(previous) if previous == state => ()
_ => result.push(state)
}
}
result
}
///|
/// Return the most frequently occupied states, ordered by first appearance.
pub fn dominant_states(
trace : Trace,
signal_id : String,
) -> Array[(String, Int64)] {
let result = state_dwell(trace, signal_id)
result.sort_by(fn(left, right) {
if left.1 > right.1 {
-1
} else if left.1 < right.1 {
1
} else {
0
}
})
result
}
///|
/// Render a compact state transition report.
pub fn state_report(trace : Trace, signal_id : String) -> String {
let out : Array[String] = ["state report: " + signal_id]
for item in state_dwell(trace, signal_id) {
out.push(item.0 + "=" + item.1.to_string())
}
out.push(
"transitions=" + state_transitions(trace, signal_id).length().to_string(),
)
out.join("\n")
}