///|
/// Errors raised by the portable frame codec.
pub suberror FrameCodecError {
UnsupportedVersion
Truncated
InvalidFlags
InvalidPayloadLength
InvalidHex
} derive(Debug)
///|
/// A stable binary representation for trace files and test fixtures.
///
/// The format is: version, flags, four-byte identifier, payload length, data.
/// All multi-byte values are big-endian so the format is identical on every
/// MoonBit backend.
pub fn encode_frame(frame : Frame) -> Array[Byte] {
let flags : Byte = (if frame.is_extended() { 1 } else { 0 }) |
(if frame.protocol() is CanFd { 2 } else { 0 }) |
(match frame.kind() {
Data => 0
Remote => 4
Error => 8
}) |
(if frame.bitrate_switch() { 16 } else { 0 }) |
(if frame.error_state_indicator() { 32 } else { 0 })
let id = frame.id()
[
1,
flags,
(id >> 24).to_byte(),
(id >> 16).to_byte(),
(id >> 8).to_byte(),
id.to_byte(),
frame.data().length().to_byte(),
] +
frame.data()
}
///|
/// Decode one frame from the stable binary representation.
pub fn decode_frame(bytes : Array[Byte]) -> Frame raise FrameCodecError {
if bytes.length() < 7 {
raise Truncated
}
if bytes[0] != 1 {
raise UnsupportedVersion
}
let flags = bytes[1].to_int()
if (flags & 0xC0) != 0 {
raise InvalidFlags
}
let extended = (flags & 1) != 0
let fd = (flags & 2) != 0
let kind_code = flags & 12
let kind = if kind_code == 0 {
Data
} else if kind_code == 4 {
Remote
} else if kind_code == 8 {
Error
} else {
raise InvalidFlags
}
let id : UInt = (bytes[2].to_uint() << 24) |
(bytes[3].to_uint() << 16) |
(bytes[4].to_uint() << 8) |
bytes[5].to_uint()
let length = bytes[6].to_int()
if bytes.length() != 7 + length {
raise InvalidPayloadLength
}
if kind is Remote && length != 0 {
raise InvalidPayloadLength
}
if fd && kind_code != 0 {
raise InvalidFlags
}
let data = bytes[7:].to_owned()
if fd {
fd_frame(
id,
data,
extended~,
bitrate_switch=(flags & 16) != 0,
error_state_indicator=(flags & 32) != 0,
) catch {
_ => raise InvalidPayloadLength
}
} else if kind is Data {
data_frame(id, data, extended~) catch {
_ => raise InvalidPayloadLength
}
} else if kind is Remote {
remote_frame(id, extended~) catch {
_ => raise InvalidPayloadLength
}
} else {
if id != 0 || length != 1 {
raise InvalidFlags
}
error_frame(data[0])
}
}
///|
/// Encode a frame as lowercase hexadecimal bytes.
pub fn frame_to_hex(frame : Frame) -> String {
let builder = StringBuilder()
for byte in encode_frame(frame) {
builder.write_string(hex_digit((byte.to_int() >> 4) & 15))
builder.write_string(hex_digit(byte.to_int() & 15))
}
builder.to_string()
}
///|
/// Decode a hexadecimal frame representation.
pub fn frame_from_hex(text : String) -> Frame raise FrameCodecError {
if text.length() % 2 != 0 {
raise InvalidHex
}
let bytes : Array[Byte] = []
for index in 0..<(text.length() / 2) {
let high = hex_value(text[index * 2])
let low = hex_value(text[index * 2 + 1])
match (high, low) {
(Some(a), Some(b)) => bytes.push(((a << 4) | b).to_byte())
_ => raise InvalidHex
}
}
decode_frame(bytes)
}
///|
/// Return a compact human-readable frame summary.
pub fn frame_summary(frame : Frame) -> String {
let kind = match frame.kind() {
Data => "data"
Remote => "remote"
Error => "error"
}
let protocol = match frame.protocol() {
Can20 => "can20"
CanFd => "canfd"
}
"\{protocol} \{kind} id=0x\{frame.id().to_string()} len=\{frame.data().length()}"
}
///|
/// Compare frames in the order used by deterministic arbitration.
pub fn compare_frames(left : Frame, right : Frame) -> Int {
if left.id() < right.id() {
-1
} else if left.id() > right.id() {
1
} else if left.is_extended() && !right.is_extended() {
1
} else if !left.is_extended() && right.is_extended() {
-1
} else {
0
}
}
///|
/// Compare frame payloads and all control flags.
pub fn frame_equal(left : Frame, right : Frame) -> Bool {
left.id() == right.id() &&
left.is_extended() == right.is_extended() &&
same_kind(left.kind(), right.kind()) &&
same_protocol(left.protocol(), right.protocol()) &&
left.bitrate_switch() == right.bitrate_switch() &&
left.error_state_indicator() == right.error_state_indicator() &&
left.data() == right.data()
}
///|
/// Return the number of encoded frame bytes, including the codec header.
pub fn frame_encoded_size(frame : Frame) -> Int {
7 + frame.data().length()
}
///|
/// Return the CRC for the unstuffed arbitration/control/data sequence.
pub fn frame_crc(frame : Frame) -> UInt {
let bits = encode_bits(frame)
match frame.protocol() {
Can20 => crc15(bits)
CanFd => if frame.data().length() <= 16 { crc17(bits) } else { crc21(bits) }
}
}
///|
/// Return the approximate wire bit count before inter-frame spacing.
pub fn frame_wire_bits(frame : Frame) -> Int {
let base = if frame.is_extended() { 29 } else { 11 }
let control = if frame.protocol() is CanFd { 11 } else { 6 }
let payload = frame.data().length() * 8
base +
control +
payload +
(match frame.protocol() {
Can20 => 15
CanFd => if frame.data().length() <= 16 { 17 } else { 21 }
}) +
13
}
///|
/// Return a hexadecimal nibble.
fn hex_digit(value : Int) -> String {
match value {
0 => "0"
1 => "1"
2 => "2"
3 => "3"
4 => "4"
5 => "5"
6 => "6"
7 => "7"
8 => "8"
9 => "9"
10 => "a"
11 => "b"
12 => "c"
13 => "d"
14 => "e"
_ => "f"
}
}
///|
/// Decode one UTF-16 code unit containing an ASCII hexadecimal digit.
fn hex_value(value : UInt16) -> Int? {
if value >= '0' && value <= '9' {
Some(value.to_int() - '0'.to_int())
} else if value >= 'a' && value <= 'f' {
Some(value.to_int() - 'a'.to_int() + 10)
} else if value >= 'A' && value <= 'F' {
Some(value.to_int() - 'A'.to_int() + 10)
} else {
None
}
}
///|
fn same_kind(left : FrameKind, right : FrameKind) -> Bool {
match (left, right) {
(Data, Data) => true
(Remote, Remote) => true
(Error, Error) => true
(_, _) => false
}
}
///|
fn same_protocol(left : Protocol, right : Protocol) -> Bool {
match (left, right) {
(Can20, Can20) => true
(CanFd, CanFd) => true
(_, _) => false
}
}