use crate::layer::ReceivedNetworkControl;
use bacnet_types::{enums::NetworkMessageType, network_number::NetworkNumber};
use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::Arc;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NumberControl {
WhatIs,
NumberIs {
number: u16,
flag: u8,
},
}
impl NumberControl {
pub fn parse(control: &ReceivedNetworkControl) -> Option<Self> {
let npdu = &control.npdu;
if !npdu.is_network_message || npdu.source.is_some() || npdu.destination.is_some() {
return None;
}
match npdu.message_type {
Some(t)
if t == NetworkMessageType::WHAT_IS_NETWORK_NUMBER.to_raw()
&& npdu.payload.is_empty() =>
{
Some(Self::WhatIs)
}
Some(t)
if t == NetworkMessageType::NETWORK_NUMBER_IS.to_raw()
&& control.link_layer_group
&& npdu.payload.len() == 3 =>
{
let number = u16::from_be_bytes([npdu.payload[0], npdu.payload[1]]);
let flag = npdu.payload[2];
(number != 0 && number != u16::MAX && flag <= 1)
.then_some(Self::NumberIs { number, flag })
}
_ => None,
}
}
}
pub fn number_is_reply(state: NetworkNumber) -> Option<[u8; 6]> {
let (number, quality) = state.snapshot();
(number != 0).then_some([
1,
0x80,
NetworkMessageType::NETWORK_NUMBER_IS.to_raw(),
(number >> 8) as u8,
number as u8,
u8::from(quality == 3),
])
}
#[derive(Clone, Debug, Default)]
pub struct LocalNetworkNumber(Arc<AtomicU16>);
impl LocalNetworkNumber {
pub fn publish(&self, state: NetworkNumber) {
self.0.store(state.snapshot().0, Ordering::Release);
}
pub fn get(&self) -> Option<u16> {
match self.0.load(Ordering::Acquire) {
0 => None,
number => Some(number),
}
}
}
#[cfg(test)]
mod tests;