Skip to main content

Crate bacnet_mstp_codec

Crate bacnet_mstp_codec 

Source
Expand description

An allocation-free codec for BACnet MS/TP frames (Clause 9): the standard frames and the two extended data frames.

The crate covers the wire format and nothing above it: frame types, the header and data CRCs, the COBS encoding and CRC-32K of extended frames, one-shot encode and decode, and an incremental decoder for a serial byte stream. Token passing, timers and line control belong to the layer that uses it.

The default build never allocates. Frame borrows its data, Frame::encode_into writes into caller storage, and StreamDecoder keeps its receive state in a buffer the caller lends it. Only the optional alloc and bytes sinks grow on the heap.

§Extended frames

Types 32 and 33 (FrameType::BACNET_EXTENDED_DATA_EXPECTING_REPLY and FrameType::BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY) carry NPDUs too long for a standard frame (Clauses 9.3.9, 9.3.10). On the wire their data is COBS-encoded with a CRC-32K, so no preamble can appear inside them (Clause 9.10), and the header’s Length counts the two encoded fields less two, which lets a receiver without COBS skip them by count (Clause 9.3). Frame::data is always the decoded data: encoding takes it plain, and decoding hands it back plain. Decoding an extended frame writes its data over its encoded form, so it takes writable input: decode_frame_in_place, or a StreamDecoder, which does it in its own storage. decode_frame borrows read-only input and refuses an extended frame with FrameError::ExtendedFrame as soon as its header checks out.

The other COBS-encoded types, 34-127, are reserved: both directions refuse them with FrameError::UnsupportedCobsFrame, after the header’s other checks. The exception is a StreamDecoder built with_reserved_cobs, for a receiver that supports COBS: it checks their CRC-32K as an extended frame’s and yields a good one as a frame of a type it doesn’t know. Test_Request and Test_Response stay standard frames.

§Validation

Decoding is a raw, link-level check. decode_frame verifies the preamble, the header CRC, the header fields Clause 9.5.8 checks, then the frame’s data. The header checks run in this order: the source, which may not be 255, the broadcast address no station may send from (Clause 9.3); the Length, at most MAX_STANDARD_MPDU_DATA on a standard type and within MIN_COBS_LENGTH..=MAX_COBS_LENGTH on a COBS-encoded one; and last whether the codec supports the type. Decoding accepts every other source, including the slave-only range 128-254: whether a station may use its address in its role is for the receiver to judge. It also accepts data on a frame type that never carries any, and a proprietary frame too short for its vendor identifier, so a receiver can see what arrived.

Encoding is strict. Frame::encode_into and Frame::encode_to_sink apply the same header checks in the same order, then refuse data on the four frame types defined without any (see FrameType::allows_data) and a proprietary frame (128-255) with less than its 2-octet vendor identifier (Clause 9.3.11). The reserved types 8-31 get the same CRC, source and length checks as any frame in both directions, but the codec doesn’t police their content. An extended frame needs data, since an empty one would fall below MIN_COBS_LENGTH; the BACnet range of 502 to MAX_EXTENDED_MPDU_DATA octets for it is the sending layer’s to keep.

§Storage

Each frame class has a constant for its longest frame on the wire, which is the StreamDecoder storage it takes (plus room for the slices pushed into it):

A frame longer than the storage is reported once and skipped; see StreamDecoder.

§Features

No feature needs std. Most add a FrameSink implementation:

  • alloc: for alloc::vec::Vec<u8>.
  • bytes: for bytes::BytesMut; implies alloc.
  • heapless: for heapless::Vec<u8, N>.
  • crc32k-table: works out the CRC-32K with a 256-entry table, about 1 KiB more flash than the default 16-entry one and half the lookups (see crc::crc32k).

§Example

use bacnet_mstp_codec::{decode_frame, Frame, FrameType, MAX_STANDARD_FRAME_LENGTH};

let frame = Frame::new(FrameType::BACNET_DATA_NOT_EXPECTING_REPLY, 0x10, 0x05, &[0x01, 0x00]);
let mut wire = [0u8; MAX_STANDARD_FRAME_LENGTH];
let length = frame.encode_into(&mut wire)?;

let (decoded, consumed) = decode_frame(&wire[..length])?;
assert_eq!(decoded, frame);
assert_eq!(consumed, length);

Modules§

crc
The CRCs of Clause 9.6: the header CRC-8, the data CRC-16, and the CRC-32K of extended frames.

Structs§

Frame
A frame whose data borrows from the caller or from decoder storage.
FrameHeader
The addressing fields of an MS/TP frame header.
FrameType
An MS/TP frame type: the first octet of the header after the preamble, which says what the frame is (Clause 9.3).
SinkFull
A FrameSink had no room for a write.
StreamDecoder
Incremental frame decoder over a buffer the caller lends it.

Enums§

DecodeEvent
The outcome of one StreamDecoder::push_slice call.
FrameError
A frame could not be encoded or decoded.
StreamPhase
Receive phase represented by the decoder’s retained bytes.

Constants§

BROADCAST_MAC
The broadcast address. It is valid only as a destination.
HEADER_LENGTH
Header length after the preamble: type, destination, source, the two length octets and the header CRC.
MAX_COBS_FRAME_LENGTH
Longest frame whose header passes validation: a COBS-encoded frame of the largest Length.
MAX_COBS_LENGTH
Largest Length a COBS-encoded frame’s header may declare (Clause 9.5.3, Nmax_COBS_length).
MAX_EXTENDED_FRAME_LENGTH
Longest extended frame carrying a BACnet NPDU: preamble, header, the largest NPDU with COBS’s worst-case overhead of one octet per 254, and the Encoded CRC-32K (Clause 9.5.2 sizes a receive buffer the same way).
MAX_EXTENDED_MPDU_DATA
Largest NPDU an extended frame carries (Clauses 9.3.9, 9.3.10).
MAX_MASTER
Highest station address a master may use. Slaves may also use 128-254.
MAX_STANDARD_FRAME_LENGTH
Longest standard frame on the wire: preamble, header, the largest data field and its CRC.
MAX_STANDARD_MPDU_DATA
Largest data field a standard (non-COBS) frame can carry.
MIN_COBS_LENGTH
Smallest Length a COBS-encoded frame’s header may declare: one data octet takes two encoded, the CRC field always takes five, and the Length leaves out two (Clause 9.5.3, Nmin_COBS_length).
PREAMBLE
The two preamble octets that start every frame.

Traits§

FrameSink
A destination for encoded frame octets.

Functions§

decode_frame
Decode one frame from input, which must start with the preamble, and borrow its data from input.
decode_frame_in_place
Decode one frame of any type the codec supports from input, which must start with the preamble.
find_preamble
Return the offset of the first preamble in data, if it holds one.