bacnet_network/network_number.rs
1//! Shared packet contract for the two local nonrouter Network Number controls.
2use crate::layer::ReceivedNetworkControl;
3use bacnet_types::{enums::NetworkMessageType, network_number::NetworkNumber};
4use std::sync::atomic::{AtomicU16, Ordering};
5use std::sync::Arc;
6
7/// A validated local control, independent of transport and configured authority.
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum NumberControl {
10 /// A local query received by unicast or logical broadcast.
11 WhatIs,
12 /// A valid logical-broadcast announcement.
13 NumberIs {
14 /// Peer-reported number, in 1..=65534.
15 number: u16,
16 /// Peer-reported configured flag, zero or one.
17 flag: u8,
18 },
19}
20impl NumberControl {
21 /// Refuse routed, malformed or ineligible controls without changing state.
22 pub fn parse(control: &ReceivedNetworkControl) -> Option<Self> {
23 let npdu = &control.npdu;
24 if !npdu.is_network_message || npdu.source.is_some() || npdu.destination.is_some() {
25 return None;
26 }
27 match npdu.message_type {
28 Some(t)
29 if t == NetworkMessageType::WHAT_IS_NETWORK_NUMBER.to_raw()
30 && npdu.payload.is_empty() =>
31 {
32 Some(Self::WhatIs)
33 }
34 Some(t)
35 if t == NetworkMessageType::NETWORK_NUMBER_IS.to_raw()
36 && control.link_layer_group
37 && npdu.payload.len() == 3 =>
38 {
39 let number = u16::from_be_bytes([npdu.payload[0], npdu.payload[1]]);
40 let flag = npdu.payload[2];
41 (number != 0 && number != u16::MAX && flag <= 1)
42 .then_some(Self::NumberIs { number, flag })
43 }
44 _ => None,
45 }
46 }
47}
48
49/// Encode a known number as a complete local-broadcast control NPDU.
50/// Learned-configured state still transmits flag zero; unknown has no reply.
51pub fn number_is_reply(state: NetworkNumber) -> Option<[u8; 6]> {
52 let (number, quality) = state.snapshot();
53 (number != 0).then_some([
54 1,
55 0x80,
56 NetworkMessageType::NETWORK_NUMBER_IS.to_raw(),
57 (number >> 8) as u8,
58 number as u8,
59 u8::from(quality == 3),
60 ])
61}
62
63/// The number of the network a single-port node is attached to, shared
64/// between the task that owns its Number controls and the senders that read
65/// it. Each [`NetworkLayer`](crate::layer::NetworkLayer) holds one
66/// ([`NetworkLayer::local_network_number`](crate::layer::NetworkLayer::local_network_number)),
67/// and clones share one value. Reading or publishing takes no lock.
68///
69/// It starts unknown. The layer never learns a number by itself: the owner of
70/// its Number controls publishes the state it holds whenever an announcement
71/// may have changed it, and a registered Network Port's owner also publishes
72/// the port's number at startup. The value is a copy of that state, never a
73/// second authority.
74#[derive(Clone, Debug, Default)]
75pub struct LocalNetworkNumber(Arc<AtomicU16>);
76
77impl LocalNetworkNumber {
78 /// Record the number `state` holds. An unknown state records unknown;
79 /// a known number is always in 1..=65534.
80 pub fn publish(&self, state: NetworkNumber) {
81 self.0.store(state.snapshot().0, Ordering::Release);
82 }
83
84 /// The last number published, or `None` while it is unknown.
85 pub fn get(&self) -> Option<u16> {
86 match self.0.load(Ordering::Acquire) {
87 0 => None,
88 number => Some(number),
89 }
90 }
91}
92
93#[cfg(test)]
94mod tests;