///|
/// A decoded IO block header plus raw body bytes.
pub(all) struct IoBlockView {
block_type : Int
block_length : Int
high_version : Byte
low_version : Byte
body : Bytes
} derive(Eq, Debug)
///|
pub(all) struct ConnectResponseSummary {
status : Int
args_length : Int
block_count : Int
ar_block : ARBlockRes?
iocr_blocks : Array[IOCRBlockRes]
module_diff_blocks : Array[IoBlockView]
blocks : Array[IoBlockView]
} derive(Eq)
///|
pub(all) struct IoResponseSummary {
status : Int
args_length : Int
actual_count : Int
block_count : Int
blocks : Array[IoBlockView]
} derive(Eq)
///|
fn read_u32_be_local(data : Bytes, offset : Int) -> Int {
(data[offset].to_int() << 24) +
(data[offset + 1].to_int() << 16) +
(data[offset + 2].to_int() << 8) +
data[offset + 3].to_int()
}
///|
pub fn parse_io_blocks(
data : Bytes,
offset : Int,
) -> Array[IoBlockView] raise @frame.FrameError {
let blocks : Array[IoBlockView] = []
let mut cursor = offset
while cursor < data.length() {
guard cursor + 6 <= data.length() else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let block_type = read_u16_be(data, cursor)
let block_length = read_u16_be(data, cursor + 2)
guard block_length >= 2 && cursor + 4 + block_length <= data.length() else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
let body_start = cursor + 6
let body_end = cursor + 4 + block_length
let body_bytes : Array[Byte] = []
for i in body_start..= 38 {
user_data_length = read_u32_be_local(Bytes::from_array(body_bytes), 30)
guard body_end + user_data_length <= data.length() else {
raise @frame.FrameError::InvalidMacLength(data.length())
}
for i = body_end; i < body_end + user_data_length; i = i + 1 {
body_bytes.push(data[i])
}
}
blocks.push(IoBlockView::{
block_type,
block_length,
high_version: data[cursor + 4],
low_version: data[cursor + 5],
body: Bytes::from_array(body_bytes),
})
cursor = cursor + 4 + block_length + user_data_length
}
blocks
}
///|
pub fn summarize_connect_response(
response : @rpc.IoResponse,
) -> ConnectResponseSummary raise @frame.FrameError {
let blocks = parse_io_blocks(response.payload, 0)
let iocrs : Array[IOCRBlockRes] = []
let module_diffs : Array[IoBlockView] = []
let mut ar_block : ARBlockRes? = None
for block in blocks {
match block.block_type {
t if t == block_type_ar_res =>
ar_block = Some(parse_ar_block_res(block.body, 0))
t if t == block_type_iocr_res =>
iocrs.push(parse_iocr_block_res(block.body, 0))
t if t == block_type_module_diff => module_diffs.push(block)
_ => ()
}
}
ConnectResponseSummary::{
status: response.status,
args_length: response.args_length,
block_count: blocks.length(),
ar_block,
iocr_blocks: iocrs,
module_diff_blocks: module_diffs,
blocks,
}
}
///|
fn block_type_name(block_type : Int) -> String {
match block_type {
t if t == block_type_ar_res => "ARBlockRes"
t if t == block_type_iocr_res => "IOCRBlockRes"
t if t == block_type_alarm_cr_res => "AlarmCRBlockRes"
t if t == block_type_module_diff => "ModuleDiffBlock"
t if t == @record.block_type_iod_write_res_header => "IODWriteResHeader"
t if t == @record.block_type_iod_read_res_header => "IODReadResHeader"
t if t == @record.block_type_iod_control_res => "IODControlRes"
t if t == @record.block_type_release_res => "ReleaseBlockRes"
t if t == block_type_iox_control_res => "IOXControlRes0"
_ => "Unknown"
}
}
///|
pub fn summarize_io_response(
response : @rpc.IoResponse,
) -> IoResponseSummary raise @frame.FrameError {
let blocks = parse_io_blocks(response.payload, 0)
IoResponseSummary::{
status: response.status,
args_length: response.args_length,
actual_count: response.actual_count,
block_count: blocks.length(),
blocks,
}
}
///|
pub fn format_io_response_summary(summary : IoResponseSummary) -> String {
let lines : Array[String] = []
lines.push("io_status=0x" + @frame.uint32_to_hex(summary.status))
lines.push("args_length=" + summary.args_length.to_string())
lines.push("actual_count=" + summary.actual_count.to_string())
lines.push("block_count=" + summary.block_count.to_string())
for index, block in summary.blocks {
lines.push(
"block_" +
(index + 1).to_string() +
"=type:" +
block.block_type.to_string() +
",name:" +
block_type_name(block.block_type) +
",length:" +
block.block_length.to_string() +
",version:" +
block.high_version.to_int().to_string() +
"." +
block.low_version.to_int().to_string() +
",body_hex:" +
@frame.bytes_to_hex(block.body, separator=""),
)
}
lines.join("\n")
}
///|
pub fn format_connect_response_summary(
summary : ConnectResponseSummary,
) -> String {
let lines : Array[String] = []
lines.push("io_status=0x" + @frame.uint32_to_hex(summary.status))
lines.push("args_length=" + summary.args_length.to_string())
lines.push("block_count=" + summary.block_count.to_string())
match summary.ar_block {
Some(ar) => {
lines.push("ar_type=" + ar.ar_type.to_string())
lines.push("ar_uuid=" + ar.ar_uuid.to_hex())
lines.push("session_key=" + ar.session_key.to_string())
lines.push("responder_mac=" + ar.cm_responder_mac.to_hex())
lines.push(
"responder_udp_rt_port=" + ar.cm_responder_udp_rt_port.to_string(),
)
}
None => ()
}
for index, iocr in summary.iocr_blocks {
lines.push(
"iocr_" +
(index + 1).to_string() +
"=type:" +
iocr_type_label(iocr.iocr_type) +
",ref:" +
iocr.iocr_reference.to_string() +
",frame_id:0x" +
@frame.uint16_to_hex(iocr.frame_id),
)
}
for index, block in summary.blocks {
lines.push(
"block_" +
(index + 1).to_string() +
"=type:" +
block.block_type.to_string() +
",name:" +
block_type_name(block.block_type) +
",length:" +
block.block_length.to_string() +
",version:" +
block.high_version.to_int().to_string() +
"." +
block.low_version.to_int().to_string() +
",body_hex:" +
@frame.bytes_to_hex(block.body, separator=""),
)
}
lines.join("\n")
}