///|
/// Link-layer state used by a master or outstation session.
pub enum LinkState {
Disconnected
Connecting
Started
Stopped
TestPending
} derive(Eq, Debug)
///|
/// A deterministic session state machine. Transport I/O is intentionally injected by callers.
pub struct Session {
mut state : LinkState
mut send_sequence : Int
mut receive_sequence : Int
window_size : Int
mut pending : Int
} derive(Debug)
///|
pub fn Session::new(window_size? : Int = 12) -> Session {
{
state: Disconnected,
send_sequence: 0,
receive_sequence: 0,
window_size: if window_size < 1 {
1
} else {
window_size
},
pending: 0,
}
}
///|
pub fn Session::start(self : Session) -> Frame {
self.state = Connecting
unnumbered_frame(UCommand::control(StartDataTransfer))
}
///|
pub fn Session::stop(self : Session) -> Frame {
self.state = Stopped
unnumbered_frame(UCommand::control(StopDataTransfer))
}
///|
pub fn Session::test_link(self : Session) -> Frame {
self.state = TestPending
unnumbered_frame(UCommand::control(TestFrame))
}
///|
pub fn Session::send(self : Session, payload : Bytes) -> Result[Frame, String] {
if self.state != Started {
return Err("data transfer is not started")
}
if self.pending >= self.window_size {
return Err("send window is full")
}
let frame = information_frame(
self.send_sequence,
self.receive_sequence,
payload,
)
self.send_sequence = (self.send_sequence + 1) % 32768
self.pending += 1
Ok(frame)
}
///|
pub fn Session::receive(self : Session, frame : Frame) -> Result[Unit, String] {
match frame.kind {
Information => {
if frame.send_sequence != self.receive_sequence {
return Err("receive sequence mismatch")
}
self.receive_sequence = (self.receive_sequence + 1) % 32768
}
Supervisory => {
if frame.receive_sequence > self.send_sequence {
return Err("acknowledgement exceeds send sequence")
}
self.pending = self.send_sequence - frame.receive_sequence
}
Unnumbered =>
match u_command(frame.control) {
StartDataTransfer => self.state = Started
StopDataTransfer => self.state = Stopped
TestFrame => self.state = Started
Unknown(_) => ()
}
}
Ok(())
}