use bacnet_network::layer::ReceivedNetworkControl;
use bacnet_network::network_number::{LocalNetworkNumber, NumberControl, number_is_reply};
use bacnet_objects::database::ObjectDatabase;
use bacnet_types::network_number::NetworkNumber;
use bacnet_types::primitives::ObjectIdentifier;
use std::sync::Arc;
use tokio::sync::RwLock;
enum State {
Registered(Arc<RwLock<ObjectDatabase>>, ObjectIdentifier),
Unregistered(NetworkNumber),
}
#[doc(hidden)]
pub struct NetworkNumberOwner {
state: State,
published: Option<LocalNetworkNumber>,
}
impl NetworkNumberOwner {
#[doc(hidden)]
pub fn new(selected: Option<(Arc<RwLock<ObjectDatabase>>, ObjectIdentifier)>) -> Self {
Self {
state: match selected {
Some((db, oid)) => State::Registered(db, oid),
None => State::Unregistered(NetworkNumber::default()),
},
published: None,
}
}
#[doc(hidden)]
pub fn publishing_to(mut self, slot: LocalNetworkNumber) -> Self {
self.published = Some(slot);
self
}
#[doc(hidden)]
pub async fn handle(&mut self, control: ReceivedNetworkControl) -> Option<Vec<u8>> {
let announcement = match NumberControl::parse(&control)? {
NumberControl::WhatIs => None,
NumberControl::NumberIs { number, flag } => Some((number, flag)),
};
let publish = |state| {
if let Some(slot) = &self.published {
slot.publish(state);
}
};
let state = match &mut self.state {
State::Registered(db, oid) => {
let mut db = db.write().await;
let state = db.network_number_internal(*oid, announcement)?;
publish(state);
state
}
State::Unregistered(state) => {
if let Some((number, flag)) = announcement {
state.observe(number, flag);
}
publish(*state);
*state
}
};
if announcement.is_some() {
return None;
}
number_is_reply(state).map(|npdu| npdu.to_vec())
}
}
impl Drop for NetworkNumberOwner {
fn drop(&mut self) {
let state = std::mem::replace(
&mut self.state,
State::Unregistered(NetworkNumber::default()),
);
if let State::Registered(db, _) = state {
drop(crate::server::drop_database_off_runtime(db));
}
}
}