///|
/// ExpectedSubmoduleBlockReq — the controller's expected module/submodule
/// layout, matched by the device against its real configuration during Connect.
/// Structure mirrors the C# reference chain
/// `ExpectedSubmoduleBlockReq` → `IoSlotInformation` → `IoSubslotInformation`
/// → `IoDataInformation`, all big-endian.
///|
/// DataDescription kinds (submodule direction).
pub let data_description_input : Int = 1
///|
pub let data_description_output : Int = 2
///|
/// One submodule data description: direction, data length, and the IOCS/IOPS
/// status byte lengths (1 each by default).
pub(all) struct DataDescription {
data_description : Int
data_length : Int
iocs_length : Int
iops_length : Int
} derive(Eq, Debug)
///|
/// Submodule entry within a slot.
pub(all) struct ExpectedSubmodule {
subslot_number : Int
submodule_ident : Int
submodule_properties : Int
data_descriptions : Array[DataDescription]
} derive(Eq, Debug)
///|
/// Slot entry within an API. `api` is the C# `IoSlotInformation.ID` field
/// (the API number) emitted first on the wire.
pub(all) struct ExpectedSlot {
api : Int
slot_number : Int
module_ident : Int
module_properties : Int
submodules : Array[ExpectedSubmodule]
} derive(Eq, Debug)
///|
/// ExpectedSubmoduleBlockReq body: a count-prefixed list of slot entries.
pub(all) struct ExpectedSubmoduleBlockReq {
apis : Array[ExpectedSlot]
} derive(Eq, Debug)
///|
fn encode_data_description(output : Array[Byte], dd : DataDescription) -> Unit {
push_u16_be(output, dd.data_description)
push_u16_be(output, dd.data_length)
output.push((dd.iocs_length & 0xFF).to_byte())
output.push((dd.iops_length & 0xFF).to_byte())
}
///|
fn encode_expected_submodule(
output : Array[Byte],
sub : ExpectedSubmodule,
) -> Unit {
push_u16_be(output, sub.subslot_number)
push_u32_be(output, sub.submodule_ident)
push_u16_be(output, sub.submodule_properties)
for dd in sub.data_descriptions {
encode_data_description(output, dd)
}
}
///|
fn encode_expected_slot(output : Array[Byte], slot : ExpectedSlot) -> Unit {
push_u32_be(output, slot.api)
push_u16_be(output, slot.slot_number)
push_u32_be(output, slot.module_ident)
push_u16_be(output, slot.module_properties)
push_u16_be(output, slot.submodules.length())
for sub in slot.submodules {
encode_expected_submodule(output, sub)
}
}
///|
/// Encode the ExpectedSubmodule block body (without the IO block header).
pub fn encode_expected_submodule_block_req(
req : ExpectedSubmoduleBlockReq,
) -> Bytes {
let output : Array[Byte] = []
push_u16_be(output, req.apis.length())
for slot in req.apis {
encode_expected_slot(output, slot)
}
Bytes::from_array(output)
}