use std::net::{Ipv4Addr, SocketAddrV4};
use std::time::Duration;
use bacnet_client::client::BACnetClient;
use bacnet_transport::bip::{
BipTransport, ForeignDeviceConfig, ForeignRegistrationOutcome, DEFAULT_BACNET_PORT,
};
const REGISTRATION_WAIT: Duration = Duration::from_secs(5);
pub(crate) fn parse_bbmd_arg(value: &str) -> Result<SocketAddrV4, String> {
let address = match value.parse::<SocketAddrV4>() {
Ok(address) => address,
Err(_) => {
let ip = value
.parse::<Ipv4Addr>()
.map_err(|_| format!("'{value}' is not an IPv4 address or ip:port"))?;
SocketAddrV4::new(ip, DEFAULT_BACNET_PORT)
}
};
if address.port() == 0 || address.ip().is_unspecified() {
return Err(format!(
"'{value}' is not a BBMD a device can register with"
));
}
Ok(address)
}
pub(crate) fn config(
bbmd: Option<SocketAddrV4>,
ttl: u16,
renewal_secs: Option<u64>,
) -> Option<ForeignDeviceConfig> {
bbmd.map(|bbmd| ForeignDeviceConfig {
bbmd_ip: *bbmd.ip(),
bbmd_port: bbmd.port(),
ttl,
renewal_interval: renewal_secs.map(Duration::from_secs),
})
}
pub(crate) async fn await_registration(
client: &BACnetClient<BipTransport>,
bbmd: SocketAddrV4,
) -> Result<(), String> {
let deadline = tokio::time::Instant::now() + REGISTRATION_WAIT;
loop {
let registration = client.foreign_registration().map_err(|e| e.to_string())?;
match registration.last_outcome {
Some(ForeignRegistrationOutcome::Accepted) => return Ok(()),
Some(ForeignRegistrationOutcome::Refused) => {
let result = registration
.last_result
.map_or_else(String::new, |result| format!(" ({result})"));
return Err(format!(
"BBMD {bbmd} refused the foreign-device registration{result}"
));
}
Some(ForeignRegistrationOutcome::LocalError) => {
return Err(format!(
"could not send the foreign-device registration to BBMD {bbmd}"
))
}
Some(ForeignRegistrationOutcome::TimedOut | ForeignRegistrationOutcome::Cancelled) => {
return Err(format!(
"BBMD {bbmd} did not answer the foreign-device registration"
))
}
Some(ForeignRegistrationOutcome::Pending) | None => {}
}
if tokio::time::Instant::now() >= deadline {
return Err(format!(
"the foreign-device registration with BBMD {bbmd} had no outcome after {} s",
REGISTRATION_WAIT.as_secs()
));
}
tokio::time::sleep(Duration::from_millis(10)).await;
}
}