///|
/// Payload transformation errors.
pub suberror PayloadError {
InvalidOffset
InvalidWidth
InvalidCounter
InvalidFrame
} derive(Debug)
///|
/// Byte ordering used by application payload adapters.
pub(all) enum ByteOrder {
BigEndian
LittleEndian
}
///|
/// Extract an unsigned integer from a payload.
pub fn payload_uint(
data : Array[Byte],
offset : Int,
width : Int,
order : ByteOrder,
) -> UInt raise PayloadError {
if offset < 0 || width <= 0 || width > 4 || offset + width > data.length() {
raise InvalidWidth
}
let mut result : UInt = 0
match order {
BigEndian =>
for index in 0..
for index in 0.. Array[Byte] raise PayloadError {
if offset < 0 || width <= 0 || width > 4 || offset + width > data.length() {
raise InvalidWidth
}
if width < 4 && value >> (width * 8) != 0 {
raise InvalidWidth
}
let result = data.copy()
match order {
BigEndian =>
for index in 0..> (8 * (width - index - 1))).to_byte()
}
LittleEndian =>
for index in 0..> (8 * index)).to_byte()
}
}
result
}
///|
/// Return the CRC-8/SAE-J1850 checksum of a payload.
pub fn crc8_sae_j1850(data : Array[Byte]) -> Byte {
let mut crc : Byte = 0xFF
for byte in data {
crc = crc ^ byte
for _ in 0..<=8 {
crc = if (crc & 0x80) != 0 { (crc << 1) ^ 0x1D } else { crc << 1 }
}
}
crc ^ 0xFF
}
///|
/// Return the CRC-8/AUTOSAR checksum with configurable initial value.
pub fn crc8_autosar(data : Array[Byte], initial : Byte) -> Byte {
let mut crc = initial
for byte in data {
crc = crc ^ byte
for _ in 0..<=8 {
crc = if (crc & 0x80) != 0 { (crc << 1) ^ 0x2F } else { crc << 1 }
}
}
crc
}
///|
/// Insert a rolling counter into a payload nibble.
pub fn set_counter_nibble(
data : Array[Byte],
offset : Int,
counter : Byte,
high_nibble : Bool,
) -> Array[Byte] raise PayloadError {
if offset < 0 || offset >= data.length() || counter > 0x0F {
raise InvalidCounter
}
let result = data.copy()
if high_nibble {
result[offset] = (result[offset] & 0x0F) | (counter << 4)
} else {
result[offset] = (result[offset] & 0xF0) | counter
}
result
}
///|
/// Read a rolling counter nibble.
pub fn counter_nibble(
data : Array[Byte],
offset : Int,
high_nibble : Bool,
) -> Byte raise PayloadError {
if offset < 0 || offset >= data.length() {
raise InvalidCounter
}
if high_nibble {
data[offset] >> 4
} else {
data[offset] & 0x0F
}
}
///|
/// Verify the expected modulo-16 counter progression.
pub fn counter_is_next(previous : Byte, current : Byte) -> Bool {
current == ((previous.to_int() + 1) & 0x0F).to_byte()
}
///|
/// Return a payload with bytes reversed in a selected range.
pub fn reverse_payload(
data : Array[Byte],
start : Int,
end : Int,
) -> Array[Byte] raise PayloadError {
if start < 0 || end < start || end > data.length() {
raise InvalidOffset
}
let result = data.copy()
let mut left = start
let mut right = end - 1
while left < right {
let temp = result[left]
result[left] = result[right]
result[right] = temp
left += 1
right -= 1
}
result
}
///|
/// Mask a payload byte range without changing its length.
pub fn mask_payload(
data : Array[Byte],
offset : Int,
mask : Byte,
) -> Array[Byte] raise PayloadError {
if offset < 0 || offset >= data.length() {
raise InvalidOffset
}
let result = data.copy()
result[offset] = result[offset] & mask
result
}
///|
/// Rebuild a frame with a defensive payload copy.
pub fn frame_with_data(
frame : Frame,
data : Array[Byte],
) -> Frame raise PayloadError {
if frame.protocol() is CanFd {
fd_frame(
frame.id(),
data,
extended=frame.is_extended(),
bitrate_switch=frame.bitrate_switch(),
error_state_indicator=frame.error_state_indicator(),
) catch {
_ => raise InvalidFrame
}
} else if frame.is_data() {
data_frame(frame.id(), data, extended=frame.is_extended()) catch {
_ => raise InvalidFrame
}
} else {
raise InvalidFrame
}
}
///|
/// Rebuild a frame with a different arbitration identifier.
pub fn frame_with_id(frame : Frame, id : UInt) -> Frame raise PayloadError {
frame_with_data(
if frame.protocol() is CanFd {
fd_frame(
id,
frame.data(),
extended=frame.is_extended(),
bitrate_switch=frame.bitrate_switch(),
error_state_indicator=frame.error_state_indicator(),
) catch {
_ => raise InvalidFrame
}
} else {
data_frame(id, frame.data(), extended=frame.is_extended()) catch {
_ => raise InvalidFrame
}
},
frame.data(),
)
}