Skip to main content

Assembler

Struct Assembler 

Source
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 for PARTIAL_TIMEOUT_MS (3 s) is dropped. Plain accept has 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

Source

pub fn new() -> Self

An assembler with no partial messages.

Source

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.

Source

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.

Source

pub fn partial_count(&self) -> usize

Number of partial messages currently buffered.

Source

pub fn forget(&mut self, alias: u16)

Forget partial messages from alias (e.g. after it reset).

Node calls this when it sees an AMR for the alias.

Trait Implementations§

Source§

impl Debug for Assembler

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Assembler

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.