use bacnet_client::client::{DeviceEvent, DiscoveredRouter};
use bacnet_client::discovery::DiscoveredDevice;
use bacnet_services::who_is::{DeviceInstanceRange, WhoIsRequest};
use bacnet_transport::port::TransportPort;
use bacnet_types::enums::UnconfirmedServiceChoice;
use bacnet_types::error::Error;
use bytes::BytesMut;
use super::Engine;
use crate::target::{self, AddressStyle};
const NOT_A_STATION: &str =
"a directed Who-Is needs a station address, not a device instance or routed target";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WhoIsScope<'a> {
Local,
Network(u16),
Directed(&'a str),
Global,
}
impl<T: TransportPort + 'static> Engine<T> {
pub async fn who_is(
&self,
scope: WhoIsScope<'_>,
range: Option<DeviceInstanceRange>,
) -> Result<(), Error> {
let mac = match scope {
WhoIsScope::Directed(address) => Some(
target::address_mac(self.client.transport(), address, NOT_A_STATION)
.await
.map_err(Error::Encoding)?,
),
_ => None,
};
let network = match scope {
WhoIsScope::Local | WhoIsScope::Directed(_) => Some(0),
WhoIsScope::Network(network) => Some(network),
WhoIsScope::Global => None,
};
self.limits.slot(network).await;
let sent = match (scope, mac) {
(WhoIsScope::Local, _) => {
let mut buf = BytesMut::new();
WhoIsRequest { range }.encode(&mut buf);
self.client
.broadcast_unconfirmed(UnconfirmedServiceChoice::WHO_IS, &buf)
.await
}
(WhoIsScope::Network(network), _) => self.client.who_is_network(network, range).await,
(WhoIsScope::Directed(_), Some(mac)) => self.client.who_is_directed(&mac, range).await,
(WhoIsScope::Directed(_), None) => unreachable!("a directed Who-Is has its address"),
(WhoIsScope::Global, _) => self.client.who_is(range).await,
};
if sent.is_err() {
crate::limits::refund();
}
sent
}
pub async fn discover_routers(
&self,
network: Option<u16>,
wait: std::time::Duration,
deadline: Option<tokio::time::Instant>,
) -> Result<Vec<DiscoveredRouter>, Error> {
self.limits.slot(Some(0)).await;
let wait = match deadline {
Some(deadline) => {
wait.min(deadline.saturating_duration_since(tokio::time::Instant::now()))
}
None => wait,
};
let found = self.client.discover_routers(network, wait).await;
if found.is_err() {
crate::limits::refund();
}
found
}
pub async fn station_mac(&self, address: &str) -> Result<Vec<u8>, Error> {
target::address_mac(self.client.transport(), address, NOT_A_STATION)
.await
.map_err(Error::Encoding)
}
pub async fn devices(&self) -> Vec<DiscoveredDevice> {
self.client.discovered_devices().await
}
pub async fn device(&self, instance: u32) -> Option<DiscoveredDevice> {
self.client.get_device(instance).await
}
pub fn device_events(&self) -> tokio::sync::broadcast::Receiver<DeviceEvent> {
self.client.device_events()
}
pub fn address_style(&self) -> AddressStyle {
AddressStyle::of(self.client.transport())
}
}