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