1use super::*;
11use crate::network_port_registration as registration;
12
13impl<T: TransportPort + 'static> ServerBuilder<T> {
14 pub fn registered_network_port(mut self, oid: ObjectIdentifier) -> Self {
22 self.config.registered_network_port = Some(oid);
23 self
24 }
25}
26impl BipServerBuilder {
27 pub fn registered_network_port(mut self, oid: ObjectIdentifier) -> Self {
29 self.config.registered_network_port = Some(oid);
30 self
31 }
32}
33
34pub(super) fn prepare<T: TransportPort>(
37 db: &mut ObjectDatabase,
38 transport: &mut T,
39 oid: Option<ObjectIdentifier>,
40 device_instance: u32,
41) -> Result<(), Error> {
42 let Some(oid) = oid else {
43 return Ok(());
44 };
45 let address = match transport.bip_port() {
46 Some(port) => registration::bip_address(&port, transport.bip_broadcast_endpoint())?,
47 None if transport.bip6_port().is_some() => {
48 return prepare_bip6(db, transport, oid, device_instance);
49 }
50 None if transport.bip_broadcast_endpoint().is_some() => {
52 return Err(Error::Encoding(
53 "registered Network Port refused: a BBMD that also registers as a foreign \
54 device has no single B/IP mode"
55 .into(),
56 ));
57 }
58 None => {
59 return Err(Error::Encoding(
60 "registered port requires B/IP or B/IPv6".into(),
61 ));
62 }
63 };
64 let (_, lease) = db.reserve_bip_port_internal(oid, address.ip().octets(), address.port())?;
65 transport.retain_network_port_lease_internal(lease)
66}
67
68fn prepare_bip6<T: TransportPort>(
72 db: &mut ObjectDatabase,
73 transport: &mut T,
74 oid: ObjectIdentifier,
75 device_instance: u32,
76) -> Result<(), Error> {
77 let port = transport
78 .bip6_port()
79 .ok_or_else(|| Error::Encoding("registered port requires B/IP or B/IPv6".into()))?;
80 let address = registration::bip6_address(&port, Some(device_instance))?;
81 let (_, lease) = db.reserve_bip6_port_internal(oid, address.ip().octets(), address.port())?;
82 transport.retain_network_port_lease_internal(lease)
83}
84
85pub(super) fn publish<T: TransportPort + 'static>(
86 db: &mut ObjectDatabase,
87 network: &NetworkLayer<T>,
88 oid: Option<ObjectIdentifier>,
89 activation: bool,
90) -> Result<(), Error> {
91 let Some(oid) = oid else {
92 return Ok(());
93 };
94 let transport = network.transport();
95 let capacity = transport.local_receive_apdu_capacity() as u32;
96 match transport.bip_port() {
97 Some(port) => {
98 registration::publish_bip(db, oid, port, capacity)?;
99 }
100 _ => {
101 let port = transport.bip6_port().ok_or_else(|| {
102 Error::Encoding("registered B/IP mode changed during bind".into())
103 })?;
104 registration::publish_bip6(db, oid, port, capacity)?;
105 }
106 }
107 db.set_network_port_activation_internal(oid, activation)?;
109 if let Some(state) = db.network_number_internal(oid, None) {
112 network.local_network_number().publish(state);
113 }
114 Ok(())
115}
116
117pub(super) struct StartingNetwork<T: TransportPort + 'static> {
120 network: Option<NetworkLayer<T>>,
121 runtime: tokio::runtime::Handle,
122}
123impl<T: TransportPort + 'static> StartingNetwork<T> {
124 pub(super) fn new(transport: T) -> Self {
125 Self {
126 network: Some(NetworkLayer::new(transport)),
127 runtime: tokio::runtime::Handle::current(),
128 }
129 }
130 pub(super) fn from_network(network: NetworkLayer<T>) -> Self {
131 Self {
132 network: Some(network),
133 runtime: tokio::runtime::Handle::current(),
134 }
135 }
136 pub(super) fn network(&mut self) -> &mut NetworkLayer<T> {
137 self.network.as_mut().expect("startup network owned")
138 }
139 pub(super) fn finish(mut self) -> NetworkLayer<T> {
140 self.network.take().expect("startup network owned")
141 }
142 pub(super) async fn cleanup(mut self) -> Result<(), Error> {
143 let mut network = self.network.take().expect("startup network owned");
144 self.runtime
145 .spawn(async move { network.stop().await })
146 .await
147 .map_err(|error| Error::Encoding(format!("startup cleanup failed: {error}")))?
148 }
149}
150impl<T: TransportPort + 'static> Drop for StartingNetwork<T> {
151 fn drop(&mut self) {
152 if let Some(mut network) = self.network.take() {
153 self.runtime.spawn(async move {
154 if let Err(error) = network.stop().await {
155 tracing::warn!(%error, "cancelled startup cleanup failed");
156 }
157 });
158 }
159 }
160}
161
162pub(super) async fn start<T: TransportPort + 'static>(
164 db: &mut ObjectDatabase,
165 config: &ServerConfig,
166 mut transport: T,
167 audit_routes: super::audit_recipient_routes::AuditRoutes,
168) -> Result<
169 (
170 NetworkLayer<T>,
171 bacnet_network::layer::ApduReceiver,
172 Arc<super::audit_recipient_routes::AuditRoutes>,
173 Option<mpsc::Receiver<bacnet_network::layer::ReceivedNetworkControl>>,
174 ),
175 Error,
176> {
177 prepare(
178 db,
179 &mut transport,
180 config.registered_network_port,
181 config.device.instance(),
182 )?;
183 let mut starting = StartingNetwork::new(transport);
184 let started = async {
185 let controls = if starting
186 .network()
187 .transport()
188 .supports_local_nonrouter_number_controls()
189 {
190 Some(starting.network().enable_network_control_receiver()?)
191 } else {
192 None
193 };
194 let apdu_rx = starting.network().start().await?;
195 publish(
196 db,
197 starting.network(),
198 config.registered_network_port,
199 config.on_reinitialize.is_some(),
200 )?;
201 let routes = audit_routes.finish(db, config, starting.network()).await?;
202 let controls = starting
203 .network()
204 .transport()
205 .supports_local_nonrouter_number_controls()
206 .then_some(controls)
207 .flatten();
208 Ok::<_, Error>((apdu_rx, routes, controls))
209 }
210 .await;
211 match started {
212 Ok((apdu_rx, routes, controls)) => Ok((starting.finish(), apdu_rx, routes, controls)),
213 Err(error) => {
214 starting.cleanup().await?;
215 Err(error)
216 }
217 }
218}
219
220pub(super) fn spawn_number_worker<T: TransportPort + 'static>(
223 network: &Arc<NetworkLayer<T>>,
224 db: &Arc<RwLock<ObjectDatabase>>,
225 selected: Option<ObjectIdentifier>,
226 mut controls: mpsc::Receiver<bacnet_network::layer::ReceivedNetworkControl>,
227 audit: Option<std::sync::Weak<super::audit_recipient::TargetAudit<T>>>,
228) -> JoinHandle<()> {
229 let network = Arc::clone(network);
230 let mut owner =
231 crate::network_number::NetworkNumberOwner::new(selected.map(|oid| (Arc::clone(db), oid)))
232 .publishing_to(network.local_network_number().clone());
233 super::heap_futures::spawn_boxed(move || async move {
234 while let Some(control) = controls.recv().await {
235 let before = network.local_network_number().get();
236 let reply = owner.handle(control).await;
237 if network.local_network_number().get() != before
238 && let Some(audit) = audit.as_ref().and_then(std::sync::Weak::upgrade)
239 {
240 audit.number_changed();
241 }
242 if let Some(npdu) = reply
243 && let Err(error) = network.transport().send_broadcast(&npdu).await
244 {
245 tracing::debug!(%error, "Network-Number-Is broadcast failed");
246 }
247 }
248 })
249}
250
251pub(super) fn validate_apdu_capacity<T: TransportPort>(
262 config: &mut ServerConfig,
263 transport: &T,
264) -> Result<(), Error> {
265 config.device = config
266 .device
267 .resolve(transport.local_receive_apdu_capacity())?;
268 Ok(())
269}