Expand description
An allocation-free codec for BACnet/IP BVLL messages, on IPv4 (Annex J)
at the crate root and on IPv6 (Annex U) in ipv6.
The crate covers the wire format and nothing above it. For B/IP that is
the 4-octet BVLL header, the twelve messages Annex J.2 defines, six-octet
B/IP addresses (J.1.2), and the entries of BDTs and FDTs; ipv6 does
the same for the twelve messages of Annex U.2, with B/IPv6 addresses and
virtual MAC addresses. Sockets, BBMD forwarding, the foreign-device
registration lifecycle and which device answers what belong to the layer
that uses it. Both message families share the BvllSink trait and the
header constants.
The default build never allocates. Bvll borrows its NPDU or table
from the datagram, tables are read through iterators
(Entries::iter), and Bvll::encode_into writes into caller
storage. Only the optional alloc and bytes sinks grow on the heap.
§Validation
Decoding is strict about shape and lenient about values.
BvllFrame::decode requires BVLC type 0x81 and a Length field equal to
the datagram’s length: the field counts the whole message (J.2), so a
truncated datagram and one with trailing octets are both refused.
BvllFrame::message then requires a function J.2 defines and a payload
of the length its format clause gives (see BvlcFunction::payload_shape).
It does not judge field values: an undefined result code, a zero
time-to-live, any table entry and an empty NPDU all decode, and the
receiver decides what to do with them. decode runs both steps.
Function 0x0C is unknown here: the clause that once defined it (J.2.13) is deleted in this edition.
Encoding is checked. A Bvll variant holds exactly its function’s
fields, so most malformed shapes can’t be built; Bvll::encoded_len
also refuses a message longer than the Length field can state and a
BVLC-Result with a code J.2.1.1 doesn’t define.
§Addresses
A B/IP address is a plain SocketAddrV4, and
address_to_octets, address_from_octets and
address_from_slice convert it to and from its six wire octets. Every
field of a SocketAddrV4 travels, so a type of its own would add
nothing. B/IPv6 does get one, ipv6::Bip6Address: a SocketAddrV6
also holds a flow label and a scope ID, which never go on the wire, and
the type keeps only the 18 octets that do.
§Features
No feature needs std. Each adds a BvllSink implementation, which
both encoders use:
alloc: foralloc::vec::Vec<u8>.bytes: forbytes::BytesMut; impliesalloc.heapless: forheapless::Vec<u8, N>.
§Example
use bacnet_bip_codec::{decode, Bvll, BvllError, BvlcFunction};
let npdu = [0x01, 0x20, 0xFF, 0xFF, 0x00, 0xFF];
let message = Bvll::OriginalBroadcastNpdu(&npdu);
let mut wire = [0u8; 64];
let length = message.encode_into(&mut wire)?;
assert_eq!(wire[..4], [0x81, 0x0B, 0x00, 0x0A]);
let decoded = decode(&wire[..length])?;
assert_eq!(decoded, message);
assert_eq!(decoded.function(), BvlcFunction::ORIGINAL_BROADCAST_NPDU);
assert_eq!(decoded.npdu(), Some(&npdu[..]));Modules§
- ipv6
- The B/IPv6 BVLL codec: BACnet/IPv6 messages (Annex U).
Structs§
- BdtEntry
- One row of a broadcast distribution table: a BBMD’s B/IP address and the broadcast distribution mask that says how to reach its subnet (Annex J.2.2.1, J.4.3.2).
- Bvlc
Function - A BVLC function code: the second octet of every B/IP BVLL message, which says what the message is (Annex J.2).
- Bvlc
Result Code - The result code a BVLC-Result carries (Annex J.2.1.1): success, or a NAK naming the request that failed.
- Bvll
Frame - A BVLL message whose header checks out, before its payload is read.
- Entries
- A table carried in a BVLL payload, borrowed rather than copied: either
the wire octets of a decoded message, or entries a caller wants to
encode. Both read the same through
Entries::iter, and two tables compare equal when their entries do. - Entry
Iter - Iterator over a table’s entries; see
Entries::iter. - FdtEntry
- One row of a foreign device table as Read-Foreign-Device-Table-Ack returns it (Annex J.2.8.1): the registrant’s B/IP address, the time-to-live it registered with, and the seconds left before the BBMD purges it, which include the 30-second grace period of J.5.2.3.
- Sink
Full - A
BvllSinkhad no room for a write.
Enums§
- Bvll
- One B/IP BVLL message (Annex J.2), borrowing its NPDU or table.
- Bvll
Error - A BVLL message could not be decoded or encoded.
- Payload
Shape - How long the payload of a BVLL message may be: the octets after the 4-octet header, which depend on the function alone (Annex J.2, U.2).
Constants§
- BDT_
ENTRY_ LENGTH - Octets in a broadcast distribution table entry: B/IP address and mask (Annex J.2.2.1).
- BIP_
ADDRESS_ LENGTH - Octets in a B/IP address: IPv4 address and UDP port (Annex J.1.2).
- BVLC_
TYPE_ BIP - The BVLC Type octet of a B/IP message (Annex J.2).
- DEFAULT_
PORT - The default UDP port, 47808 (0xBAC0), for B/IP and B/IPv6 alike (Annex J.1.2, U.1.1.2).
- FDT_
ENTRY_ LENGTH - Octets in a foreign device table entry: B/IP address, time-to-live and seconds remaining (Annex J.2.8.1).
- HEADER_
LENGTH - The BVLL header: type, function and the two Length octets. B/IP and B/IPv6 share it (Annex J.2, U.2).
- MAX_
BVLL_ LENGTH - The largest message the 16-bit Length field can state, header included, in either annex.
Traits§
- Bvll
Sink - A destination for encoded BVLL octets, shared by the B/IP and the B/IPv6 encoders.
- Table
Entry - A table entry type:
BdtEntryorFdtEntry. Sealed.
Functions§
- address_
from_ octets - The B/IP address six octets hold (Annex J.1.2).
- address_
from_ slice - The B/IP address in
octets, which must be exactly six octets long. - address_
to_ octets - The six octets of a B/IP address: the IPv4 address, then the UDP port, both most significant octet first (Annex J.1.2).
- decode
- Decode one datagram as a BVLL message:
BvllFrame::decode, thenBvllFrame::message.
Type Aliases§
- BdtEntries
- A broadcast distribution table (Annex J.2.2.1, J.2.4.1).
- FdtEntries
- A foreign device table (Annex J.2.8.1).