use std::any::Any;
use std::net::SocketAddrV4;
use bacnet_transport::bip::{BipTransport, BvlcExchangeError};
use bacnet_transport::port::TransportPort;
use super::Engine;
use crate::bbmd::{BdtRow, FdtRow, Outcome, Probe, mac};
impl<T: TransportPort + 'static> Engine<T> {
pub fn bip(&self) -> Option<&BipTransport> {
(self.client.transport() as &dyn Any).downcast_ref()
}
pub fn registration_bbmd(&self) -> Option<SocketAddrV4> {
self.bip()?
.bvlc_client_snapshot()
.foreign_registration
.last_bbmd
}
pub async fn read_bdt(&self, bbmd: SocketAddrV4) -> Probe {
let Some(bip) = self.bip() else {
return Outcome::Unreachable(NOT_BIP.into());
};
self.limits.slot(Some(0)).await;
settle(bip.read_bdt(&mac(bbmd)).await, |e| BdtRow::from(e))
}
pub async fn read_fdt(&self, bbmd: SocketAddrV4) -> Outcome<Vec<FdtRow>> {
let Some(bip) = self.bip() else {
return Outcome::Unreachable(NOT_BIP.into());
};
self.limits.slot(Some(0)).await;
settle(bip.read_fdt(&mac(bbmd)).await, |e| FdtRow::from(e))
}
}
const NOT_BIP: &str = "BBMD tables need BACnet/IP";
fn settle<E, R>(read: Result<Vec<E>, BvlcExchangeError>, row: fn(&E) -> R) -> Outcome<Vec<R>> {
match read {
Ok(entries) => Outcome::Table(entries.iter().map(row).collect()),
Err(error) => {
if matches!(error, BvlcExchangeError::Refused(_)) {
crate::limits::refund();
}
Outcome::from_error(&error)
}
}
}