///|
/// A bounded ordered collection of frames for gateways and batch adapters.
pub struct FrameSequence {
frames : Array[Frame]
max_frames : Int
}
///|
pub fn FrameSequence::new(
max_frames? : Int = 1024,
) -> Result[FrameSequence, ModbusError] {
if max_frames < 1 {
Err(CapacityExceeded)
} else {
Ok({ frames: [], max_frames })
}
}
///|
pub fn FrameSequence::push(
self : FrameSequence,
frame : Frame,
) -> Result[Unit, ModbusError] {
if self.frames.length() >= self.max_frames {
Err(CapacityExceeded)
} else {
self.frames.push(frame)
Ok(())
}
}
///|
pub fn FrameSequence::length(self : FrameSequence) -> Int {
self.frames.length()
}
///|
pub fn FrameSequence::get(
self : FrameSequence,
index : Int,
) -> Result[Frame, ModbusError] {
if index < 0 || index >= self.frames.length() {
Err(InvalidAddress)
} else {
Ok(self.frames[index])
}
}
///|
pub fn FrameSequence::snapshot(self : FrameSequence) -> Array[Frame] {
let out : Array[Frame] = []
for frame in self.frames {
out.push(frame)
}
out
}
///|
pub fn FrameSequence::validate(
self : FrameSequence,
allow_broadcast : Bool,
) -> Result[Unit, ModbusError] {
for frame in self.frames {
match validate_frame(frame, allow_broadcast) {
Ok(_) => ()
Err(error) => return Err(error)
}
}
Ok(())
}
///|
pub fn FrameSequence::encoded_bytes(self : FrameSequence, mode : Mode) -> Int {
let mut total = 0
for frame in self.frames {
total += encoded_length(mode, frame)
}
total
}
///|
pub fn FrameSequence::filter_function(
self : FrameSequence,
function : Byte,
) -> Array[Frame] {
let out : Array[Frame] = []
for frame in self.frames {
if ordinary_function(frame.pdu.function) == function {
out.push(frame)
}
}
out
}
///|
pub fn FrameSequence::filter_unit(
self : FrameSequence,
unit_id : Byte,
) -> Array[Frame] {
let out : Array[Frame] = []
for frame in self.frames {
if frame.unit_id == unit_id {
out.push(frame)
}
}
out
}
///|
/// Compare frames at the logical PDU level.
pub fn frames_equal(left : Frame, right : Frame) -> Bool {
left.unit_id == right.unit_id &&
left.pdu.function == right.pdu.function &&
left.pdu.data == right.pdu.data
}
///|
pub fn pdus_equal(left : Pdu, right : Pdu) -> Bool {
left.function == right.function && left.data == right.data
}
///|
pub fn frame_data_copy(frame : Frame) -> Array[Byte] {
copy_bytes(frame.pdu.data)
}
///|
pub fn frame_with_data_copy(frame : Frame, data : Array[Byte]) -> Frame {
{
unit_id: frame.unit_id,
pdu: { function: frame.pdu.function, data: copy_bytes(data) },
}
}
///|
/// Add a signed address offset while preserving 16-bit bounds.
pub fn offset_frame_address(
frame : Frame,
delta : Int,
) -> Result[Frame, ModbusError] {
if frame.pdu.data.length() < 2 {
return Err(InvalidLength)
}
let address = ((frame.pdu.data[0].to_uint16() << 8) |
frame.pdu.data[1].to_uint16()).to_int()
let next = address + delta
if next < 0 || next > 65535 {
return Err(InvalidAddress)
}
let data = copy_bytes(frame.pdu.data)
data[0] = (next / 256).to_byte()
data[1] = next.to_byte()
Ok(frame_with_data_copy(frame, data))
}
///|
pub fn replace_frame_unit(
frame : Frame,
unit_id : Byte,
) -> Result[Frame, ModbusError] {
if !is_valid_unit_id(unit_id) {
Err(InvalidUnitId)
} else {
Ok({
unit_id,
pdu: { function: frame.pdu.function, data: copy_bytes(frame.pdu.data) },
})
}
}
///|
pub fn safe_byte_at(
bytes : Array[Byte],
index : Int,
) -> Result[Byte, ModbusError] {
if index < 0 || index >= bytes.length() {
Err(Incomplete)
} else {
Ok(bytes[index])
}
}
///|
pub fn is_read_function(function : Byte) -> Bool {
function == 1 ||
function == 2 ||
function == 3 ||
function == 4 ||
function == 7 ||
function == 8 ||
function == 11 ||
function == 12 ||
function == 17 ||
function == 20 ||
function == 24 ||
function == 43
}
///|
pub fn is_write_function(function : Byte) -> Bool {
function == 5 ||
function == 6 ||
function == 15 ||
function == 16 ||
function == 21 ||
function == 22 ||
function == 23
}
///|
pub fn response_payload_length(frame : Frame) -> Int {
frame.pdu.data.length()
}
///|
pub fn frame_is_empty(frame : Frame) -> Bool {
frame.pdu.data.length() == 0
}
///|
pub fn frame_is_valid(frame : Frame) -> Bool {
validate_frame(frame, true) is Ok(_)
}
///|
pub fn error_or_unit(result : Result[Unit, ModbusError]) -> String {
match result {
Ok(_) => "ok"
Err(error) => error_name(error)
}
}