1use std::collections::HashMap;
8use std::net::Ipv4Addr;
9use std::sync::atomic::{AtomicU8, Ordering};
10use std::sync::Arc;
11use std::time::Instant;
12
13use bytes::{Bytes, BytesMut};
14use tokio::sync::{mpsc, oneshot, Mutex, RwLock, Semaphore};
15use tokio::task::JoinHandle;
16use tokio::time::Duration;
17use tracing::{debug, warn};
18
19use bacnet_encoding::apdu::{
20 self, encode_apdu, validate_max_apdu_length, AbortPdu, Apdu, ComplexAck,
21 ConfirmedRequest as ConfirmedRequestPdu, ErrorPdu, RejectPdu, SegmentAck as SegmentAckPdu,
22 SimpleAck, UnconfirmedRequest as UnconfirmedRequestPdu,
23};
24use bacnet_encoding::npdu::NpduAddress;
25use bacnet_encoding::primitives::{encode_ctx_unsigned, encode_property_value};
26use bacnet_encoding::segmentation::{
27 max_segment_payload, split_payload, SegmentReceiver, SegmentedPduType,
28};
29use bacnet_network::layer::NetworkLayer;
30use bacnet_objects::database::ObjectDatabase;
31use bacnet_objects::notification_class::get_notification_recipients;
32use bacnet_services::alarm_event::EventNotificationRequest;
33use bacnet_services::common::BACnetPropertyValue;
34use bacnet_services::cov::COVNotificationRequest;
35use bacnet_services::cov_multiple::{
36 COVNotificationItem, COVNotificationMultipleRequest, COVNotificationValue,
37};
38use bacnet_services::who_is::{IAmRequest, WhoIsRequest};
39use bacnet_transport::bip::BipTransport;
40use bacnet_transport::port::TransportPort;
41use bacnet_types::enums::{
42 AbortReason, ConfirmedServiceChoice, ErrorClass, ErrorCode, NetworkPriority, NotifyType,
43 ObjectType, PropertyIdentifier, RejectReason, Segmentation, UnconfirmedServiceChoice,
44};
45use bacnet_types::error::Error;
46use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, Time};
47use bacnet_types::MacAddr;
48
49use crate::cov::{CovNotificationKind, CovSubscription, CovSubscriptionTable};
50use crate::handlers;
51
52const MAX_SEG_RECEIVERS: usize = 128;
54
55const SEG_RECEIVER_TIMEOUT: Duration = Duration::from_secs(4);
57
58const MAX_NEG_SEGMENT_ACK_RETRIES: u8 = 3;
60
61const DEFAULT_APDU_RETRIES: u8 = 3;
63
64type TsmPeer = (MacAddr, Option<NpduAddress>);
65type TsmKey = (MacAddr, Option<NpduAddress>, u8);
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum CovAckResult {
74 Ack,
76 Error,
78}
79
80pub struct ServerTsm {
87 next_invoke_id: u8,
88 pending: HashMap<TsmKey, oneshot::Sender<CovAckResult>>,
91}
92
93impl ServerTsm {
94 fn new() -> Self {
95 Self {
96 next_invoke_id: 0,
97 pending: HashMap::new(),
98 }
99 }
100
101 fn allocate(&mut self, peer: TsmPeer) -> Option<(u8, oneshot::Receiver<CovAckResult>)> {
104 for offset in 0..=u8::MAX {
105 let id = self.next_invoke_id.wrapping_add(offset);
106 if !self
107 .pending
108 .contains_key(&(peer.0.clone(), peer.1.clone(), id))
109 {
110 self.next_invoke_id = id.wrapping_add(1);
111 let rx = self.register(peer, id);
112 return Some((id, rx));
113 }
114 }
115 None
116 }
117
118 fn register(&mut self, peer: TsmPeer, invoke_id: u8) -> oneshot::Receiver<CovAckResult> {
120 let (tx, rx) = oneshot::channel();
121 self.pending.insert((peer.0, peer.1, invoke_id), tx);
122 rx
123 }
124
125 fn record_result(
128 &mut self,
129 peer: &MacAddr,
130 network: Option<&NpduAddress>,
131 invoke_id: u8,
132 result: CovAckResult,
133 ) -> bool {
134 if let Some(tx) = self
135 .pending
136 .remove(&(peer.clone(), network.cloned(), invoke_id))
137 {
138 let _ = tx.send(result);
139 true
140 } else {
141 false
142 }
143 }
144
145 fn remove(&mut self, peer: &TsmPeer, invoke_id: u8) {
147 self.pending
148 .remove(&(peer.0.clone(), peer.1.clone(), invoke_id));
149 }
150}
151
152#[derive(Debug, Clone)]
154pub struct TimeSyncData {
155 pub raw_service_data: Bytes,
157 pub is_utc: bool,
159}
160
161#[derive(Clone)]
163pub struct ServerConfig {
164 pub interface: Ipv4Addr,
166 pub port: u16,
168 pub broadcast_address: Ipv4Addr,
170 pub max_apdu_length: u32,
172 pub segmentation_supported: Segmentation,
174 pub vendor_id: u16,
176 pub cov_retry_timeout_ms: u64,
178 pub on_time_sync: Option<Arc<dyn Fn(TimeSyncData) + Send + Sync>>,
180 pub dcc_password: Option<String>,
182 pub reinit_password: Option<String>,
184 pub enable_fault_detection: bool,
188}
189
190impl std::fmt::Debug for ServerConfig {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 f.debug_struct("ServerConfig")
193 .field("interface", &self.interface)
194 .field("port", &self.port)
195 .field("broadcast_address", &self.broadcast_address)
196 .field("max_apdu_length", &self.max_apdu_length)
197 .field("segmentation_supported", &self.segmentation_supported)
198 .field("vendor_id", &self.vendor_id)
199 .field("cov_retry_timeout_ms", &self.cov_retry_timeout_ms)
200 .field(
201 "on_time_sync",
202 &self.on_time_sync.as_ref().map(|_| "<callback>"),
203 )
204 .field("dcc_password", &self.dcc_password.as_ref().map(|_| "***"))
205 .field(
206 "reinit_password",
207 &self.reinit_password.as_ref().map(|_| "***"),
208 )
209 .field("enable_fault_detection", &self.enable_fault_detection)
210 .finish()
211 }
212}
213
214impl Default for ServerConfig {
215 fn default() -> Self {
216 Self {
217 interface: Ipv4Addr::UNSPECIFIED,
218 port: 0xBAC0,
219 broadcast_address: Ipv4Addr::BROADCAST,
220 max_apdu_length: 1476,
221 segmentation_supported: Segmentation::NONE,
222 vendor_id: 0,
223 cov_retry_timeout_ms: 3000,
224 on_time_sync: None,
225 dcc_password: None,
226 reinit_password: None,
227 enable_fault_detection: false,
228 }
229 }
230}
231
232pub struct ServerBuilder<T: TransportPort> {
234 config: ServerConfig,
235 db: ObjectDatabase,
236 transport: Option<T>,
237}
238
239impl<T: TransportPort + 'static> ServerBuilder<T> {
240 pub fn database(mut self, db: ObjectDatabase) -> Self {
242 self.db = db;
243 self
244 }
245
246 pub fn transport(mut self, transport: T) -> Self {
248 self.transport = Some(transport);
249 self
250 }
251
252 pub fn dcc_password(mut self, password: impl Into<String>) -> Self {
254 self.config.dcc_password = Some(password.into());
255 self
256 }
257
258 pub fn reinit_password(mut self, password: impl Into<String>) -> Self {
260 self.config.reinit_password = Some(password.into());
261 self
262 }
263
264 pub fn enable_fault_detection(mut self, enabled: bool) -> Self {
266 self.config.enable_fault_detection = enabled;
267 self
268 }
269
270 pub fn vendor_id(mut self, id: u16) -> Self {
272 self.config.vendor_id = id;
273 self
274 }
275
276 pub async fn build(self) -> Result<BACnetServer<T>, Error> {
278 let transport = self
279 .transport
280 .ok_or_else(|| Error::Encoding("transport not set on ServerBuilder".into()))?;
281 BACnetServer::start(self.config, self.db, transport).await
282 }
283}
284
285pub struct BipServerBuilder {
287 config: ServerConfig,
288 db: ObjectDatabase,
289}
290
291impl BipServerBuilder {
292 pub fn interface(mut self, ip: Ipv4Addr) -> Self {
294 self.config.interface = ip;
295 self
296 }
297
298 pub fn port(mut self, port: u16) -> Self {
300 self.config.port = port;
301 self
302 }
303
304 pub fn broadcast_address(mut self, addr: Ipv4Addr) -> Self {
306 self.config.broadcast_address = addr;
307 self
308 }
309
310 pub fn database(mut self, db: ObjectDatabase) -> Self {
312 self.db = db;
313 self
314 }
315
316 pub fn dcc_password(mut self, password: impl Into<String>) -> Self {
318 self.config.dcc_password = Some(password.into());
319 self
320 }
321
322 pub fn reinit_password(mut self, password: impl Into<String>) -> Self {
324 self.config.reinit_password = Some(password.into());
325 self
326 }
327
328 pub fn enable_fault_detection(mut self, enabled: bool) -> Self {
330 self.config.enable_fault_detection = enabled;
331 self
332 }
333
334 pub fn vendor_id(mut self, id: u16) -> Self {
336 self.config.vendor_id = id;
337 self
338 }
339
340 pub async fn build(self) -> Result<BACnetServer<BipTransport>, Error> {
342 let transport = BipTransport::new(
343 self.config.interface,
344 self.config.port,
345 self.config.broadcast_address,
346 );
347 BACnetServer::start(self.config, self.db, transport).await
348 }
349}
350
351type SegKey = (MacAddr, Option<NpduAddress>, u8);
354
355struct SegmentedRequestState {
356 receiver: SegmentReceiver,
357 first_req: bacnet_encoding::apdu::ConfirmedRequest,
358 last_activity: Instant,
359 expected_seq: u8,
360 last_acked_seq: u8,
361 window_pos: u8,
362 actual_window_size: u8,
363}
364
365pub struct BACnetServer<T: TransportPort> {
367 #[allow(dead_code)]
368 config: ServerConfig,
369 #[allow(dead_code)]
371 network: Arc<NetworkLayer<T>>,
372 db: Arc<RwLock<ObjectDatabase>>,
374 #[allow(dead_code)]
376 cov_table: Arc<RwLock<CovSubscriptionTable>>,
377 #[allow(dead_code)]
379 seg_ack_senders: Arc<Mutex<HashMap<SegKey, mpsc::Sender<SegmentAckPdu>>>>,
380 #[allow(dead_code)]
383 cov_in_flight: Arc<Semaphore>,
384 #[allow(dead_code)]
386 server_tsm: Arc<Mutex<ServerTsm>>,
387 comm_state: Arc<AtomicU8>,
389 #[allow(dead_code)]
392 dcc_timer: Arc<Mutex<Option<JoinHandle<()>>>>,
393 dispatch_task: Option<JoinHandle<()>>,
394 cov_purge_task: Option<JoinHandle<()>>,
395 fault_detection_task: Option<JoinHandle<()>>,
396 event_enrollment_task: Option<JoinHandle<()>>,
397 trend_log_task: Option<JoinHandle<()>>,
398 schedule_tick_task: Option<JoinHandle<()>>,
399 local_mac: MacAddr,
400}
401
402pub struct IAmBroadcaster<T: TransportPort> {
404 config: ServerConfig,
405 network: Arc<NetworkLayer<T>>,
406 db: Arc<RwLock<ObjectDatabase>>,
407}
408
409impl<T: TransportPort> Clone for IAmBroadcaster<T> {
410 fn clone(&self) -> Self {
411 Self {
412 config: self.config.clone(),
413 network: Arc::clone(&self.network),
414 db: Arc::clone(&self.db),
415 }
416 }
417}
418
419impl BACnetServer<BipTransport> {
420 pub fn bip_builder() -> BipServerBuilder {
422 BipServerBuilder {
423 config: ServerConfig::default(),
424 db: ObjectDatabase::new(),
425 }
426 }
427
428 pub fn builder() -> BipServerBuilder {
430 Self::bip_builder()
431 }
432}
433
434#[cfg(feature = "sc-tls")]
435impl BACnetServer<bacnet_transport::sc::ScTransport<bacnet_transport::sc_tls::TlsWebSocket>> {
436 pub fn sc_builder() -> ScServerBuilder {
438 ScServerBuilder {
439 config: ServerConfig::default(),
440 db: ObjectDatabase::new(),
441 hub_url: String::new(),
442 tls_config: None,
443 vmac: [0; 6],
444 heartbeat_interval_ms: 30_000,
445 heartbeat_timeout_ms: 60_000,
446 reconnect: None,
447 }
448 }
449}
450
451#[cfg(feature = "sc-tls")]
455pub struct ScServerBuilder {
456 config: ServerConfig,
457 db: ObjectDatabase,
458 hub_url: String,
459 tls_config: Option<std::sync::Arc<tokio_rustls::rustls::ClientConfig>>,
460 vmac: bacnet_transport::sc_frame::Vmac,
461 heartbeat_interval_ms: u64,
462 heartbeat_timeout_ms: u64,
463 reconnect: Option<bacnet_transport::sc::ScReconnectConfig>,
464}
465
466#[cfg(feature = "sc-tls")]
467impl ScServerBuilder {
468 pub fn hub_url(mut self, url: &str) -> Self {
470 self.hub_url = url.to_string();
471 self
472 }
473
474 pub fn tls_config(
476 mut self,
477 config: std::sync::Arc<tokio_rustls::rustls::ClientConfig>,
478 ) -> Self {
479 self.tls_config = Some(config);
480 self
481 }
482
483 pub fn vmac(mut self, vmac: [u8; 6]) -> Self {
485 self.vmac = vmac;
486 self
487 }
488
489 pub fn database(mut self, db: ObjectDatabase) -> Self {
491 self.db = db;
492 self
493 }
494
495 pub fn heartbeat_interval_ms(mut self, ms: u64) -> Self {
497 self.heartbeat_interval_ms = ms;
498 self
499 }
500
501 pub fn heartbeat_timeout_ms(mut self, ms: u64) -> Self {
503 self.heartbeat_timeout_ms = ms;
504 self
505 }
506
507 pub fn reconnect(mut self, config: bacnet_transport::sc::ScReconnectConfig) -> Self {
509 self.reconnect = Some(config);
510 self
511 }
512
513 pub fn dcc_password(mut self, password: impl Into<String>) -> Self {
515 self.config.dcc_password = Some(password.into());
516 self
517 }
518
519 pub fn reinit_password(mut self, password: impl Into<String>) -> Self {
521 self.config.reinit_password = Some(password.into());
522 self
523 }
524
525 pub fn enable_fault_detection(mut self, enabled: bool) -> Self {
527 self.config.enable_fault_detection = enabled;
528 self
529 }
530
531 pub async fn build(
533 self,
534 ) -> Result<
535 BACnetServer<bacnet_transport::sc::ScTransport<bacnet_transport::sc_tls::TlsWebSocket>>,
536 Error,
537 > {
538 let tls_config = self
539 .tls_config
540 .ok_or_else(|| Error::Encoding("SC server builder: tls_config is required".into()))?;
541
542 let ws = bacnet_transport::sc_tls::TlsWebSocket::connect(&self.hub_url, tls_config.clone())
543 .await?;
544
545 let mut transport = bacnet_transport::sc::ScTransport::new(ws, self.vmac)
546 .with_heartbeat_interval_ms(self.heartbeat_interval_ms)
547 .with_heartbeat_timeout_ms(self.heartbeat_timeout_ms);
548 if let Some(rc) = self.reconnect {
549 let hub_url = self.hub_url.clone();
550 let tls_config = tls_config.clone();
551 #[allow(deprecated)]
552 {
553 transport = transport
554 .with_connector(move || {
555 let hub_url = hub_url.clone();
556 let tls_config = tls_config.clone();
557 async move {
558 bacnet_transport::sc_tls::TlsWebSocket::connect(&hub_url, tls_config)
559 .await
560 }
561 })
562 .with_reconnect(rc);
563 }
564 }
565
566 BACnetServer::start(self.config, self.db, transport).await
567 }
568}
569
570mod cov_notifications;
571mod dispatch;
572mod event_notifications;
573mod lifecycle;
574mod requests;
575mod responses;
576mod segmentation;
577
578#[cfg(test)]
579mod cov_notifications_tests;
580#[cfg(test)]
581mod tests;
582
583impl<T: TransportPort + 'static> BACnetServer<T> {
584 pub fn generic_builder() -> ServerBuilder<T> {
585 ServerBuilder {
586 config: ServerConfig::default(),
587 db: ObjectDatabase::new(),
588 transport: None,
589 }
590 }
591}