use super::*;
use bacnet_transport::port::BipPort;
impl<T: TransportPort + 'static> EndpointSession<T> {
pub fn with_registered_network_port(mut self, oid: ObjectIdentifier) -> Self {
self.assert_configurable();
self.registered_network_port = Some(oid);
self
}
pub(super) async fn prepare_registered_port(&mut self) -> Result<(), Error> {
let Some(oid) = self.registered_network_port else {
return Ok(());
};
let ingress = self
.ingress
.as_mut()
.ok_or_else(|| invalid("missing ingress"))?;
let address = match ingress.bip_port() {
Some(port) => port.endpoint,
None if ingress.bip_broadcast_endpoint().is_some() => {
return Err(invalid(
"registered Network Port refused: a BBMD that also registers as a \
foreign device has no single B/IP mode",
))
}
None => return Err(invalid("registration requires B/IP")),
};
let ip = *address.ip();
if ip.is_unspecified()
|| ip.is_multicast()
|| ip.is_broadcast()
|| ingress
.bip_broadcast_endpoint()
.is_some_and(|broadcast| *broadcast.ip() == ip)
{
return Err(invalid(
"registered B/IP requires a concrete unicast interface",
));
}
let db = self
.database
.as_ref()
.ok_or_else(|| invalid("registered port requires database"))?;
let mut db = db.write().await;
let configured = db
.configured_bip_port_internal(&oid)
.ok_or_else(|| invalid("selected built-in B/IP port missing"))?;
if let Some(identity) = &self.identity {
identity.validate_registered_bip(oid, &configured)?;
}
let (_, lease) = db.reserve_bip_port_internal(oid, ip.octets(), address.port())?;
self.registered_port_lease = Arc::downgrade(&lease);
ingress.retain_network_port_lease_internal(lease)
}
pub(super) async fn publish_registered_port(
&mut self,
actual: Option<(BipPort, u16)>,
local_network: &bacnet_network::network_number::LocalNetworkNumber,
) -> Result<(), Error> {
let Some(oid) = self.registered_network_port else {
return Ok(());
};
let (port, capacity) =
actual.ok_or_else(|| invalid("registered B/IP mode changed during bind"))?;
let address = port.endpoint;
let mut db = self
.database
.as_ref()
.expect("validated database")
.write()
.await;
db.publish_bip_port_internal(
oid,
address.ip().octets(),
address.port(),
capacity as u32,
port.mode,
)?;
if let Some(state) = db.network_number_internal(oid, None) {
local_network.publish(state);
}
if let Some(identity) = &mut self.identity {
identity.publish_registered_bip(oid, address);
}
Ok(())
}
}
fn invalid(message: &str) -> Error {
Error::Encoding(message.into())
}