pub struct Assembler { /* private fields */ }Expand description
Reassembles multi-frame addressed messages and datagrams.
Feed every frame to Assembler::accept_at (or Assembler::accept).
Single-frame messages come straight back; multi-frame ones are buffered
until their last frame. Partial messages are keyed by (source alias,
destination alias, MTI), so interleaved transfers from different nodes
are handled.
The assembler is defensive about memory:
- at most 32 partial messages are kept; when a new one starts at the cap, the one that started longest ago is dropped;
- a partial message that would exceed 256 bytes is discarded;
- with
accept_at, a partial message that gets no new frame forPARTIAL_TIMEOUT_MS(3 s) is dropped. Plainaccepthas no clock and never expires anything.
A middle or last frame with no matching first frame is ignored. It is a passive listener, so it reassembles messages for every destination, not just one node.
§Legacy SNIP replies
Some early nodes send a multi-frame SNIP reply with every frame flagged
“only” (0x0_) instead of first/middle/last. A complete SNIP payload
never fits in one addressed frame (it has at least eight bytes), so the
assembler concatenates “only” SNIP reply frames from the same source to
the same destination until the payload holds six NUL terminators, then
returns it. A truncated reply of that kind is held until it expires or
is evicted rather than returned early.
use rolcb::message::{Assembler, Message};
use rolcb::traction::Command;
use rolcb::NodeId;
let cmd = Message::TractionCommand {
dest: 0x6A1,
payload: Command::AssignController { flags: 0, controller: NodeId::new(0x0501_0101_2299) }
.encode(),
};
let frames = cmd.to_frames(0x285);
assert_eq!(frames.len(), 2); // 9 bytes → 6 + 3
let mut asm = Assembler::new();
assert!(asm.accept_at(&frames[0], 1_000).is_none());
let raw = asm.accept_at(&frames[1], 1_010).expect("complete");
assert_eq!(raw.dest, Some(0x6A1));
assert_eq!(Message::from(raw), cmd);
// A first frame whose continuation never arrives is forgotten after 3 s.
assert!(asm.accept_at(&frames[0], 2_000).is_none());
assert!(asm.accept_at(&frames[1], 5_000).is_none());Implementations§
Source§impl Assembler
impl Assembler
Sourcepub fn accept(&mut self, frame: &CanFrame) -> Option<RawMessage>
pub fn accept(&mut self, frame: &CanFrame) -> Option<RawMessage>
Accept one frame without a clock. Returns a message when one is
complete. Control frames (CID/RID/AMD…) return None; the node
handles those.
Partial messages accepted this way never expire; they leave only
when completed, evicted at the cap, or forget
is called. Prefer Assembler::accept_at in long-running programs.
Sourcepub fn accept_at(&mut self, frame: &CanFrame, now_ms: u64) -> Option<RawMessage>
pub fn accept_at(&mut self, frame: &CanFrame, now_ms: u64) -> Option<RawMessage>
Accept one frame received at now_ms (any monotonic millisecond
clock). Like Assembler::accept, but first drops partial messages
that have had no new frame for PARTIAL_TIMEOUT_MS.
Sourcepub fn partial_count(&self) -> usize
pub fn partial_count(&self) -> usize
Number of partial messages currently buffered.