///|
/// IO data object — references a slot/subslot within an IOCR.
pub(all) struct IoDataObject {
slot_number : Int
subslot_number : Int
frame_offset : Int
} derive(Eq, Debug)
///|
/// API (Application Process Identifier) with its IO data objects.
pub(all) struct IocrApi {
api : Int
io_data_objects : Array[IoDataObject]
iocs : Array[IoDataObject]
} derive(Eq, Debug)
///|
/// IOCR (IO Communication Relationship) block request.
pub(all) struct IOCRBlockReq {
iocr_type : Int
iocr_reference : Int
lt : Int
iocr_properties : Int
data_length : Int
frame_id : Int
send_clock_factor : Int
reduction_ratio : Int
phase : Int
sequence : Int
frame_send_offset : Int
watchdog_factor : Int
data_hold_factor : Int
iocr_tag_header : Int
iocr_multicast_mac : Bytes
apis : Array[IocrApi]
} derive(Eq, Debug)
///|
fn default_iocr_multicast_mac() -> Bytes {
Bytes::make(6, b'\x00')
}
///|
/// Check the IOCR Phase invariant from PN-AL: 1 <= Phase <= ReductionRatio.
pub fn iocr_phase_valid(phase : Int, reduction_ratio : Int) -> Bool {
phase > 0 && phase <= reduction_ratio
}
///|
/// Return true when Sequence uses the mandatory random-sequence mode.
pub fn iocr_sequence_is_random(sequence : Int) -> Bool {
sequence == 0
}
///|
/// Label the IOCR Sequence mode. Non-zero values are defined optional sequences.
pub fn iocr_sequence_label(sequence : Int) -> String {
if iocr_sequence_is_random(sequence) {
"random"
} else {
"defined"
}
}
///|
pub fn IOCRBlockReq::phase_valid(self : IOCRBlockReq) -> Bool {
iocr_phase_valid(self.phase, self.reduction_ratio)
}
///|
pub fn IOCRBlockReq::sequence_is_random(self : IOCRBlockReq) -> Bool {
iocr_sequence_is_random(self.sequence)
}
///|
pub fn IOCRBlockReq::default_input(frame_id : Int) -> IOCRBlockReq {
IOCRBlockReq::{
iocr_type: iocr_type_input,
iocr_reference: 1,
lt: 0x8892,
iocr_properties: 2,
data_length: 40,
frame_id,
send_clock_factor: 32,
reduction_ratio: 2,
phase: 1,
sequence: 0,
frame_send_offset: 0xFFFFFFFF,
watchdog_factor: 3,
data_hold_factor: 3,
iocr_tag_header: 0xC000,
iocr_multicast_mac: default_iocr_multicast_mac(),
apis: [],
}
}
///|
pub fn IOCRBlockReq::default_output(frame_id : Int) -> IOCRBlockReq {
IOCRBlockReq::{
iocr_type: iocr_type_output,
iocr_reference: 2,
lt: 0x8892,
iocr_properties: 2,
data_length: 40,
frame_id,
send_clock_factor: 32,
reduction_ratio: 2,
phase: 1,
sequence: 0,
frame_send_offset: 0xFFFFFFFF,
watchdog_factor: 3,
data_hold_factor: 3,
iocr_tag_header: 0xC000,
iocr_multicast_mac: default_iocr_multicast_mac(),
apis: [],
}
}
///|
fn encode_io_data_object(output : Array[Byte], obj : IoDataObject) -> Unit {
push_u16_be(output, obj.slot_number)
push_u16_be(output, obj.subslot_number)
push_u16_be(output, obj.frame_offset)
}
///|
/// Encode IOCR block request body.
pub fn encode_iocr_block_req(req : IOCRBlockReq) -> Bytes {
let output : Array[Byte] = []
push_u16_be(output, req.iocr_type)
push_u16_be(output, req.iocr_reference)
push_u16_be(output, req.lt)
push_u32_be(output, req.iocr_properties)
push_u16_be(output, req.data_length)
push_u16_be(output, req.frame_id)
push_u16_be(output, req.send_clock_factor)
push_u16_be(output, req.reduction_ratio)
push_u16_be(output, req.phase)
push_u16_be(output, req.sequence)
push_u32_be(output, req.frame_send_offset)
push_u16_be(output, req.watchdog_factor)
push_u16_be(output, req.data_hold_factor)
push_u16_be(output, req.iocr_tag_header)
append_bytes(output, req.iocr_multicast_mac)
// number of APIs
push_u16_be(output, req.apis.length())
for api in req.apis {
push_u32_be(output, api.api)
// number of IO data objects
push_u16_be(output, api.io_data_objects.length())
for obj in api.io_data_objects {
encode_io_data_object(output, obj)
}
// number of IOCs
push_u16_be(output, api.iocs.length())
for obj in api.iocs {
encode_io_data_object(output, obj)
}
}
Bytes::from_array(output)
}
///|
/// IOCR block response.
pub(all) struct IOCRBlockRes {
iocr_type : Int
iocr_reference : Int
frame_id : Int
} derive(Eq, Debug)
///|
/// Parse IOCR block response from body bytes.
pub fn parse_iocr_block_res(
data : Bytes,
offset : Int,
) -> IOCRBlockRes raise @frame.FrameError {
guard data.length() >= offset + 6 else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
IOCRBlockRes::{
iocr_type: read_u16_be(data, offset),
iocr_reference: read_u16_be(data, offset + 2),
frame_id: read_u16_be(data, offset + 4),
}
}
///|
/// Format IOCR block request summary.
pub fn format_iocr_block_req(req : IOCRBlockReq) -> String {
let lines : Array[String] = []
lines.push("iocr_type=" + iocr_type_label(req.iocr_type))
lines.push("iocr_reference=" + req.iocr_reference.to_string())
lines.push("frame_id=0x" + @frame.uint16_to_hex(req.frame_id))
lines.push("data_length=" + req.data_length.to_string())
lines.push("send_clock_factor=" + req.send_clock_factor.to_string())
lines.push("reduction_ratio=" + req.reduction_ratio.to_string())
lines.push("phase=" + req.phase.to_string())
lines.push(
"sequence=" +
req.sequence.to_string() +
" (" +
iocr_sequence_label(req.sequence) +
")",
)
lines.push("watchdog_factor=" + req.watchdog_factor.to_string())
lines.push("api_count=" + req.apis.length().to_string())
lines.join("\n")
}