bacnet-bip-core
Portable BACnet/IP behaviour from Annex J of ASHRAE 135, for
rusty-bacnet. It sits above
bacnet-bip-codec, which has the wire format, and below a socket.
#![no_std], no allocator, no dependencies besidesbacnet-bip-codec,bacnet-npdu(forMAX_NPDU, the longest NPDU B/IP carries) andbacnet-time. The codec is re-exported asbacnet_bip_core::codec, since the API speaks its types.- Tables live in arrays whose size you pick with a const generic.
- Time is an
Instantyou pass to each call; the crate reads no clock. It isbacnet-time's, in nanoseconds, re-exported here, the same typebacnet-mstp-coreandbacnet-router-coretake, so firmware feeds them one clock. Anowearlier than the latest one passed in is taken as that latest one: time never runs backwards, so a clock that steps back holds the timers still instead of failing. - The types that may grow are
#[non_exhaustive]: build aDatagram, abbmd::Context, aLocalSubnetor abbmd::Registrationwithnew; start aLinkor aForeignfromnew, or theRegistrationLimitsfromDEFAULT, and set the fields to change; and give amatchon an error, a refusal, aDiscardor another of these enums a wildcard arm.
What it has
The node module is a sans-IO B/IP node in normal, foreign-device and BBMD
mode:
Link, its fixed configuration, decides what to do with each received datagram (the receive rules, own echoes, the NAKs a node that is not a BBMD owes, a Forwarded-NPDU's origin) and what to send for an NPDU, within a datagram budget. Its decisions are pure.Nodekeeps the rest: its own management exchanges, matched by peer and operation; a foreign device's registration, with backoff after a loss and a lease that never counts on the BBMD's grace period; broadcasts held until the first registration resolves; and the timers. You poll its actions after every call.- A node can be a B/IP-M member (Annex J.8):
LocalSubnet::memberputs its broadcasts on a multicast group in place of the subnet's broadcast address, and it takes broadcast functions from that group.
The bbmd module holds a BBMD's broadcast distribution table and foreign
device table, and decides where each broadcast goes:
- an Original-Broadcast-NPDU from the local subnet, to the peer subnets (one-hop or two-hop, from each BDT row's mask) and the foreign devices;
- a peer BBMD's Forwarded-NPDU, to the foreign devices and, when it came to this BBMD alone (its own BDT row is two-hop and it arrived by unicast), the local subnet;
- a registered foreign device's Distribute-Broadcast-To-Network;
- the BBMD's own broadcasts.
Each decision writes its destinations into storage you pass and returns the
Forwarded-NPDU to send them. Foreign-device registration applies
time-to-live limits, a per-address quota and reserved capacity, and asks
your own admission check (an access list, a rate limit) in between.
Registrations expire on your clock, with the 30-second grace period.
Tables::serve answers the management requests a BBMD serves from its
tables (Register-Foreign-Device, the two table reads and
Delete-Foreign-Device-Table-Entry), with the result codes Annex J gives
them, under your own rules.
A BBMD can also be the one for a B/IP-M group (Annex J.8.3), and one behind a NAT router
(Annex J.7.5) takes its public address with Tables::set_nat: the BDT must
then name that global address as its own row, every row two-hop, and the
Forwarded-NPDUs it builds name the global address as their origin.
The port module is the driver that joins a node to a socket and does
what it asks, so firmware doesn't write that loop:
NodePortruns a node in normal or foreign-device mode: it sends the NAKs and the registrations, and holds a foreign device's broadcasts, in slots you lend, until its first registration resolves.BbmdPortruns a BBMD's link over tables you lend: it forwards each broadcast where the tables say and answers each management request itself.
Datagrams leave through a sink you implement, which encodes each straight into your socket's buffer.
The Tokio B/IP transport in bacnet-transport runs its node and its BBMD on
this crate.
Example
use ;
use ;
use ;
use Instant;
let own = new;
let peer = new;
let mut tables: = new;
tables
.bdt
.set
.unwrap;
let cx = new;
let source = new;
let mut out = ;
let sent = tables.original_broadcast;
for in sent.transmissions
License
MIT