use std::sync::Arc;
use bacnet_encoding::constructed::{encode_bdt_entry, encode_fdt_entry, encode_host_n_port};
use bacnet_types::bip_port::{BbmdTables, BipPortMode};
use bacnet_types::constructed::BACnetHostNPort;
use bacnet_types::enums::{IPMode, PropertyIdentifier as P};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use bytes::BytesMut;
#[derive(Clone, Default)]
pub(super) enum LiveMode {
#[default]
Normal,
Foreign {
bbmd: BACnetHostNPort,
lifetime: u16,
},
Bbmd(Arc<dyn BbmdTables>),
}
impl LiveMode {
pub(super) fn from_port(mode: BipPortMode) -> Result<Self, Error> {
Ok(match mode {
BipPortMode::Normal => Self::Normal,
BipPortMode::Foreign {
bbmd,
subscription_lifetime,
} => Self::Foreign {
bbmd,
lifetime: subscription_lifetime,
},
BipPortMode::Bbmd {
tables: Some(tables),
} => Self::Bbmd(tables),
BipPortMode::Bbmd { tables: None } => {
return Err(Error::Encoding(
"a registered BBMD must lend its tables once started".into(),
))
}
})
}
pub(super) fn ip_mode(&self) -> IPMode {
match self {
Self::Normal => IPMode::NORMAL,
Self::Foreign { .. } => IPMode::FOREIGN,
Self::Bbmd(_) => IPMode::BBMD,
}
}
pub(super) fn read(&self, property: P) -> Option<Result<PropertyValue, Error>> {
match (self, property) {
(Self::Foreign { bbmd, .. }, P::FD_BBMD_ADDRESS) => {
let mut buf = BytesMut::new();
Some(encode_host_n_port(&mut buf, bbmd).map(|()| framed(buf)))
}
(Self::Foreign { lifetime, .. }, P::FD_SUBSCRIPTION_LIFETIME) => {
Some(Ok(PropertyValue::Unsigned((*lifetime).into())))
}
(Self::Bbmd(tables), P::BBMD_BROADCAST_DISTRIBUTION_TABLE) => Some(
tables
.broadcast_distribution_table()
.iter()
.map(|entry| {
let mut buf = BytesMut::new();
encode_bdt_entry(&mut buf, entry).map(|()| framed(buf))
})
.collect::<Result<_, _>>()
.map(PropertyValue::List),
),
(Self::Bbmd(tables), P::BBMD_ACCEPT_FD_REGISTRATIONS) => Some(Ok(
PropertyValue::Boolean(tables.accepts_foreign_device_registrations()),
)),
(Self::Bbmd(tables), P::BBMD_FOREIGN_DEVICE_TABLE) => Some(Ok(PropertyValue::List(
tables
.foreign_device_table()
.iter()
.map(|entry| {
let mut buf = BytesMut::new();
encode_fdt_entry(&mut buf, entry);
framed(buf)
})
.collect(),
))),
_ => None,
}
}
}
fn framed(buf: BytesMut) -> PropertyValue {
PropertyValue::ApplicationData(buf.to_vec())
}