Skip to main content

bacnet_server/server/
mod.rs

1//! BACnetServer: builder, APDU dispatch, and lifecycle management.
2//!
3//! The server wraps a NetworkLayer behind Arc (shared with the dispatch task),
4//! owns an ObjectDatabase via `Arc<RwLock>`, and spawns a dispatch task that
5//! routes incoming APDUs to service handlers.
6
7use std::collections::HashMap;
8use std::net::Ipv4Addr;
9#[cfg(test)]
10pub(crate) use std::sync::atomic::AtomicBool;
11use std::sync::atomic::Ordering;
12use std::sync::Arc;
13use std::time::Instant;
14
15use bytes::{Bytes, BytesMut};
16use tokio::sync::{mpsc, oneshot, watch, Mutex, RwLock, Semaphore};
17use tokio::task::JoinHandle;
18use tokio::time::Duration;
19use tracing::{debug, warn};
20
21use event_delivery::EventDelivery;
22
23use bacnet_encoding::apdu::{
24    self, encode_apdu, AbortPdu, Apdu, ComplexAck, ConfirmedRequest as ConfirmedRequestPdu,
25    ErrorPdu, RejectPdu, SegmentAck as SegmentAckPdu, SimpleAck,
26    UnconfirmedRequest as UnconfirmedRequestPdu,
27};
28use bacnet_encoding::npdu::NpduAddress;
29use bacnet_encoding::primitives::encode_property_value;
30use bacnet_encoding::segmentation::{
31    duplicate_in_window, max_segment_payload, split_payload, SegmentReceiver, SegmentedPduType,
32};
33use bacnet_network::layer::NetworkLayer;
34use bacnet_objects::database::ObjectDatabase;
35use bacnet_objects::notification_class::{
36    lookup_notification_recipients, resolve_transition_priority_ack,
37};
38use bacnet_services::alarm_event::EventNotificationRequest;
39use bacnet_services::common::BACnetPropertyValue;
40use bacnet_services::cov::COVNotificationRequest;
41use bacnet_services::cov_multiple::COVNotificationMultipleRequest;
42use bacnet_services::who_is::{DeviceInstanceRange, IAmRequest, WhoIsRequest};
43use bacnet_transport::bip::{BipMode, BipTransport};
44use bacnet_transport::port::TransportPort;
45use bacnet_types::enums::{
46    AbortReason, ConfirmedServiceChoice, ErrorClass, ErrorCode, LifeSafetyOperation,
47    NetworkPriority, NotifyType, ObjectType, PropertyIdentifier, RejectReason, Segmentation,
48    UnconfirmedServiceChoice,
49};
50use bacnet_types::error::Error;
51use bacnet_types::primitives::{ObjectIdentifier, PropertyValue};
52use bacnet_types::MacAddr;
53
54use crate::audit_notification::{
55    AuditNotificationAuthorizationContext, AuditNotificationAuthorizer,
56    UnconfirmedAuditNotificationAuthorizationContext, UnconfirmedAuditNotificationAuthorizer,
57    MAX_AUDIT_NOTIFICATIONS, MAX_AUDIT_NOTIFICATION_BYTES,
58};
59pub use crate::cov::{CovCounters, CovPolicy};
60use crate::cov::{
61    CovNotificationKind, CovSubscription, CovSubscriptionSnapshot, CovSubscriptionTable,
62};
63use crate::life_safety::{LifeSafetyOperationAuthorizationContext, LifeSafetyOperationAuthorizer};
64use crate::{handlers, runtime_clock};
65use confirmed_request_tracker::{
66    ConfirmedRequestAdmission, ConfirmedRequestTracker, PendingConfirmedRequest,
67};
68pub use device_bindings::DeviceBinding;
69use device_bindings::{register_configured_binding, DeviceBindingTable};
70use learned_router_cache::LearnedRouterCache;
71use lso_replay::{LsoAdmission, PendingLsoReplay};
72use notification_transactions::{
73    canonical_direct_peer, canonical_routed_peer, run_notification_under_dcc,
74    run_notification_worker, InitiationRestricted, NotificationTransactions,
75    NotificationWorkerResult,
76};
77use request_services::{DispatchContext, RequestOrigin, RequestServices, UnconfirmedServices};
78use requests::confirmed_response::ResponseTarget;
79use requests::ConfirmedRequestOwnership;
80use segmentation::{ComplexAckParams, SegmentedSendResources};
81
82/// Maximum number of concurrent segmented reassembly sessions.
83const MAX_SEG_RECEIVERS: usize = 128;
84
85/// Hard per-request reassembly ceiling: the sequence-number space (#364).
86///
87/// A local storage bound, not a protocol one. Clause 20.1.2.7 makes the
88/// request sequence number modulo 256, so a longer request is entirely
89/// representable on the wire — this server simply keys its segment store by
90/// that `u8` and cannot tell segment 256 from segment 0. A Device object
91/// configured to receive segments does publish a tighter advertisement — its
92/// `Max_Segments_Accepted` (Clause 12.11) defaults to `Unsigned(65)` — but
93/// enforcing it here is deliberately not done: accepting more segments than
94/// advertised is permissive, not a violation, while the sequence space is the
95/// line past which acceptance silently corrupts. Exactly 256 segments
96/// reassemble correctly and must keep working; 257 is the first that would
97/// corrupt the payload.
98const MAX_REQUEST_SEGMENTS: usize = 256;
99
100/// Maximum number of concurrent segmented response send sessions.
101const MAX_SEG_SENDERS: usize = 128;
102
103/// Maximum negative SegmentAck retries during segmented response send.
104const MAX_NEG_SEGMENT_ACK_RETRIES: u8 = 3;
105
106/// Default timeout while waiting for SegmentACK during segmented response send.
107const DEFAULT_APDU_SEGMENT_TIMEOUT: Duration = Duration::from_secs(5);
108
109/// Default retransmission budget for segmented response segments.
110const DEFAULT_APDU_SEGMENT_RETRIES: u8 = MAX_NEG_SEGMENT_ACK_RETRIES;
111
112/// Default number of APDU retries for confirmed notifications and the
113/// WriteProperty requests Command actions send to other devices.
114const DEFAULT_APDU_RETRIES: u8 = 3;
115
116/// Data from a TimeSynchronization request.
117#[derive(Debug, Clone)]
118pub struct TimeSyncData {
119    /// Raw service request bytes (caller can decode if needed).
120    pub raw_service_data: Bytes,
121    /// Whether this was a UTC time sync (vs. local).
122    pub is_utc: bool,
123    /// Transport-native source MAC; on SC this is the source VMAC, not a
124    /// certificate principal. This metadata is a claimed identity only.
125    pub source_mac: MacAddr,
126    /// Claimed routed NPDU source, if present; takes precedence for policy matching.
127    pub source_network: Option<NpduAddress>,
128    /// Honest transport + origin provenance, threaded for diagnostics only
129    /// (RB-07 compat mode: decisions unchanged; RB-09 consumes it later).
130    pub provenance: bacnet_transport::port::TransportProvenance,
131}
132
133mod config;
134pub use config::ServerConfig;
135mod audit_batch_queue;
136mod audit_batch_runtime;
137mod audit_context_preparation;
138mod audit_forwarder;
139#[cfg(test)]
140mod audit_forwarder_tests;
141mod audit_recipient;
142mod audit_recipient_routes;
143mod audit_reporter;
144mod audit_reporter_changes;
145mod audit_send_now;
146mod heap_futures;
147pub use audit_reporter::{valid_bip_audit_address, AuditReportersConfig};
148#[cfg(test)]
149mod audit_reporter_tests;
150
151/// Generic builder for BACnetServer with a pre-built transport.
152pub struct ServerBuilder<T: TransportPort> {
153    config: ServerConfig,
154    db: ObjectDatabase,
155    transport: Option<T>,
156    configured_device_bindings: Vec<DeviceBinding>,
157}
158
159impl<T: TransportPort + 'static> ServerBuilder<T> {
160    /// Set the object database (transfers ownership).
161    pub fn database(mut self, db: ObjectDatabase) -> Self {
162        self.db = db;
163        self
164    }
165
166    /// Set the pre-built transport.
167    pub fn transport(mut self, transport: T) -> Self {
168        self.transport = Some(transport);
169        self
170    }
171
172    /// Register one explicit unicast route for a Device recipient.
173    pub fn device_binding(mut self, binding: DeviceBinding) -> Result<Self, Error> {
174        register_configured_binding(&mut self.configured_device_bindings, binding)?;
175        Ok(self)
176    }
177
178    /// Set the policy that authorizes inbound LifeSafetyOperation requests.
179    pub fn life_safety_operation_authorizer<F>(mut self, authorizer: F) -> Self
180    where
181        F: Fn(&LifeSafetyOperationAuthorizationContext) -> bool + Send + Sync + 'static,
182    {
183        self.config.life_safety_operation_authorizer = Some(Arc::new(authorizer));
184        self
185    }
186
187    /// Select the only local Audit Log that receives authorized notifications.
188    pub fn audit_notification_sink(mut self, sink: ObjectIdentifier) -> Self {
189        self.config.audit_notification_sink = Some(sink);
190        self
191    }
192
193    /// Set the fail-closed ConfirmedAuditNotification authorization policy.
194    pub fn audit_notification_authorizer<F>(mut self, authorizer: F) -> Self
195    where
196        F: Fn(&AuditNotificationAuthorizationContext) -> bool + Send + Sync + 'static,
197    {
198        self.config.audit_notification_authorizer = Some(Arc::new(authorizer));
199        self
200    }
201
202    /// Set the fail-closed UnconfirmedAuditNotification authorization policy.
203    pub fn unconfirmed_audit_notification_authorizer<F>(mut self, authorizer: F) -> Self
204    where
205        F: Fn(&UnconfirmedAuditNotificationAuthorizationContext) -> bool + Send + Sync + 'static,
206    {
207        self.config.unconfirmed_audit_notification_authorizer = Some(Arc::new(authorizer));
208        self
209    }
210
211    /// Enable periodic fault detection / reliability evaluation.
212    ///
213    /// When enabled, every object's opt-in reliability hook runs every 10
214    /// seconds; the default hook is a no-op.
215    ///
216    /// Reliability evaluation only; Event Enrollment evaluation is configured
217    /// by [`enable_event_enrollment`](Self::enable_event_enrollment).
218    pub fn enable_fault_detection(mut self, enabled: bool) -> Self {
219        self.config.enable_fault_detection = enabled;
220        self
221    }
222
223    /// Enable periodic Event Enrollment evaluation (default `true`).
224    pub fn enable_event_enrollment(mut self, enabled: bool) -> Self {
225        self.config.enable_event_enrollment = enabled;
226        self
227    }
228
229    /// Set the interval in seconds between Event Enrollment evaluation passes
230    /// (default 10).
231    pub fn event_enrollment_interval_secs(mut self, secs: u64) -> Self {
232        self.config.event_enrollment_interval_secs = secs;
233        self
234    }
235
236    /// Set the segmentation support this device advertises and enforces.
237    ///
238    /// The dispatch loop honors the advertisement (Clause 5.4.5.1): inbound
239    /// segmented requests are reassembled only under `BOTH` or `RECEIVE`, and
240    /// draw a SEGMENTATION_NOT_SUPPORTED Abort otherwise. The default is
241    /// `NONE`.
242    pub fn segmentation_supported(mut self, segmentation: Segmentation) -> Self {
243        self.config.segmentation_supported = segmentation;
244        self
245    }
246
247    /// Set the vendor identifier (used in IAm responses and protocol operations).
248    pub fn vendor_id(mut self, id: u16) -> Self {
249        self.config.vendor_id = id;
250        self
251    }
252
253    /// Set the discovery rate-limiting and duplicate suppression policy.
254    pub fn discovery_policy(mut self, policy: DiscoveryPolicy) -> Self {
255        self.config.discovery_policy = policy;
256        self
257    }
258
259    /// Set the COV quota and notification work budget policy, checked by
260    /// [`CovPolicy::validate`] before transport startup.
261    pub fn cov_policy(mut self, policy: CovPolicy) -> Self {
262        self.config.cov_policy = policy;
263        self
264    }
265
266    /// Build and start the server.
267    pub async fn build(self) -> Result<BACnetServer<T>, Error> {
268        let transport = self
269            .transport
270            .ok_or_else(|| Error::Encoding("transport not set on ServerBuilder".into()))?;
271        BACnetServer::start_with_clock_mode_and_bindings(
272            self.config,
273            self.db,
274            transport,
275            Some(ClockConfig::default()),
276            self.configured_device_bindings,
277        )
278        .await
279    }
280}
281
282/// BIP-specific builder that constructs `BipTransport` from interface/port/broadcast fields.
283pub struct BipServerBuilder {
284    config: ServerConfig,
285    db: ObjectDatabase,
286    configured_device_bindings: Vec<DeviceBinding>,
287    mode: BipMode,
288}
289
290impl BipServerBuilder {
291    /// Set the local interface IP.
292    pub fn interface(mut self, ip: Ipv4Addr) -> Self {
293        self.config.interface = ip;
294        self
295    }
296
297    /// Set the UDP port.
298    pub fn port(mut self, port: u16) -> Self {
299        self.config.port = port;
300        self
301    }
302
303    /// Set the directed broadcast address.
304    pub fn broadcast_address(mut self, addr: Ipv4Addr) -> Self {
305        self.config.broadcast_address = addr;
306        self
307    }
308
309    /// Set the object database (transfers ownership).
310    pub fn database(mut self, db: ObjectDatabase) -> Self {
311        self.db = db;
312        self
313    }
314
315    /// Register one explicit unicast route for a Device recipient.
316    pub fn device_binding(mut self, binding: DeviceBinding) -> Result<Self, Error> {
317        register_configured_binding(&mut self.configured_device_bindings, binding)?;
318        Ok(self)
319    }
320
321    /// Set the policy that authorizes inbound LifeSafetyOperation requests.
322    pub fn life_safety_operation_authorizer<F>(mut self, authorizer: F) -> Self
323    where
324        F: Fn(&LifeSafetyOperationAuthorizationContext) -> bool + Send + Sync + 'static,
325    {
326        self.config.life_safety_operation_authorizer = Some(Arc::new(authorizer));
327        self
328    }
329
330    /// Select the only local Audit Log that receives authorized notifications.
331    pub fn audit_notification_sink(mut self, sink: ObjectIdentifier) -> Self {
332        self.config.audit_notification_sink = Some(sink);
333        self
334    }
335
336    /// Set the fail-closed ConfirmedAuditNotification authorization policy.
337    pub fn audit_notification_authorizer<F>(mut self, authorizer: F) -> Self
338    where
339        F: Fn(&AuditNotificationAuthorizationContext) -> bool + Send + Sync + 'static,
340    {
341        self.config.audit_notification_authorizer = Some(Arc::new(authorizer));
342        self
343    }
344
345    /// Set the fail-closed UnconfirmedAuditNotification authorization policy.
346    pub fn unconfirmed_audit_notification_authorizer<F>(mut self, authorizer: F) -> Self
347    where
348        F: Fn(&UnconfirmedAuditNotificationAuthorizationContext) -> bool + Send + Sync + 'static,
349    {
350        self.config.unconfirmed_audit_notification_authorizer = Some(Arc::new(authorizer));
351        self
352    }
353
354    /// Enable periodic fault detection / reliability evaluation.
355    ///
356    /// When enabled, every object's opt-in reliability hook runs every 10
357    /// seconds; the default hook is a no-op.
358    ///
359    /// Reliability evaluation only; Event Enrollment evaluation is configured
360    /// by [`enable_event_enrollment`](Self::enable_event_enrollment).
361    pub fn enable_fault_detection(mut self, enabled: bool) -> Self {
362        self.config.enable_fault_detection = enabled;
363        self
364    }
365
366    /// Enable periodic Event Enrollment evaluation (default `true`).
367    pub fn enable_event_enrollment(mut self, enabled: bool) -> Self {
368        self.config.enable_event_enrollment = enabled;
369        self
370    }
371
372    /// Set the interval in seconds between Event Enrollment evaluation passes
373    /// (default 10).
374    pub fn event_enrollment_interval_secs(mut self, secs: u64) -> Self {
375        self.config.event_enrollment_interval_secs = secs;
376        self
377    }
378
379    /// Set the segmentation support this device advertises and enforces.
380    ///
381    /// The dispatch loop honors the advertisement (Clause 5.4.5.1): inbound
382    /// segmented requests are reassembled only under `BOTH` or `RECEIVE`, and
383    /// draw a SEGMENTATION_NOT_SUPPORTED Abort otherwise. The default is
384    /// `NONE`.
385    pub fn segmentation_supported(mut self, segmentation: Segmentation) -> Self {
386        self.config.segmentation_supported = segmentation;
387        self
388    }
389
390    /// Set the vendor identifier advertised in I-Am responses.
391    pub fn vendor_id(mut self, id: u16) -> Self {
392        self.config.vendor_id = id;
393        self
394    }
395
396    /// Set the discovery rate-limiting and duplicate suppression policy.
397    pub fn discovery_policy(mut self, policy: DiscoveryPolicy) -> Self {
398        self.config.discovery_policy = policy;
399        self
400    }
401
402    /// Set the COV quota and notification work budget policy, checked by
403    /// [`CovPolicy::validate`] before transport startup.
404    pub fn cov_policy(mut self, policy: CovPolicy) -> Self {
405        self.config.cov_policy = policy;
406        self
407    }
408
409    /// Build and start the server, constructing a BipTransport from the config.
410    pub async fn build(self) -> Result<BACnetServer<BipTransport>, Error> {
411        let config = &self.config;
412        let mut transport =
413            BipTransport::new(config.interface, config.port, config.broadcast_address);
414        transport.set_share_port_by_address(config.share_port_by_address);
415        transport.set_mode(self.mode);
416        BACnetServer::start_with_clock_mode_and_bindings(
417            self.config,
418            self.db,
419            transport,
420            Some(ClockConfig::default()),
421            self.configured_device_bindings,
422        )
423        .await
424    }
425}
426
427type TransportCleanup<T> = JoinHandle<(NetworkLayer<T>, Result<(), Error>)>;
428
429/// BACnet server with APDU dispatch and service handling.
430pub struct BACnetServer<T: TransportPort> {
431    target_audit: Option<Arc<audit_recipient::TargetAudit<T>>>,
432    /// Shared with dispatch and every task the server starts (#1521).
433    config: Arc<ServerConfig>,
434    discovery_limiter: Arc<DiscoveryLimiter>,
435    #[allow(dead_code)] // Retained with the server, including direct dispatch tests.
436    time_sync_limiter: Arc<TimeSyncLimiter>,
437    /// Server-owned clock controller; absent in explicit clockless mode.
438    _clock: Option<Arc<ServerClock>>,
439    /// Shared network layer (also held by dispatch task; read by
440    /// [`write_local`](Self::write_local) for post-write COV/event sends).
441    network: Option<Arc<NetworkLayer<T>>>,
442    broadcaster: Arc<broadcaster::BroadcasterState<T>>,
443    transport_cleanup: Option<TransportCleanup<T>>,
444    transport_cleanup_error: Option<String>,
445    network_number_task: Option<JoinHandle<()>>,
446    /// Shared object database.
447    db: Arc<RwLock<ObjectDatabase>>,
448    /// COV subscription table (also held by dispatch task; read by
449    /// [`write_local`](Self::write_local) to fire post-write notifications).
450    cov_table: Arc<RwLock<CovSubscriptionTable>>,
451    cov_counters: Arc<crate::cov::AtomicCovCounters>,
452    /// Channels for routing segmented-send events to in-progress segmented sends.
453    #[allow(dead_code)]
454    seg_ack_senders: Arc<segmented_send::SegmentedSendRegistry>,
455    /// Permits that cap live segmented response sender tasks, including
456    /// cancelled senders that have not yet exited a transport send.
457    #[allow(dead_code)]
458    seg_send_permits: Arc<Semaphore>,
459    /// Operational cap of 255 concurrent confirmed COV notification workers.
460    /// Invoke-ID ownership is handled by `notification_transactions`.
461    cov_in_flight: Arc<Semaphore>,
462    /// Bounded DNET-to-router cache learned from admitted notification terminals.
463    learned_routers: Arc<Mutex<LearnedRouterCache>>,
464    /// Invoke-ID ownership and terminal admission for confirmed notifications.
465    notification_transactions: Arc<NotificationTransactions>,
466    /// Server-lifetime exact inbound ConfirmedRequest duplicate state.
467    confirmed_request_tracker: Arc<ConfirmedRequestTracker>,
468    /// Shared configured and passively observed Device recipient authority.
469    device_bindings: Arc<RwLock<DeviceBindingTable>>,
470    /// DeviceCommunicationControl state; only `dcc_timer` changes it.
471    comm_state: Arc<CommState>,
472    /// DCC timer owner and replacement/expiry serialization boundary.
473    /// Valid replacement and explicit stop abort and join before clearing it.
474    dcc_timer: Arc<Mutex<crate::server::dcc_timer::TimerSlot>>,
475    dcc_outcomes: Arc<dcc_outcomes::DccOutcomes>,
476    /// Lifetime totals of undelivered event notifications (#1142).
477    event_suppressions: Arc<event_suppression::EventSuppressions>,
478    mutation_decisions: Arc<crate::mutation::MutationDecisions>,
479    dispatch_task: Option<JoinHandle<()>>,
480    request_tasks: Arc<request_tasks::RequestTasks>,
481    cov_purge_task: Option<JoinHandle<()>>,
482    fault_detection_task: Option<JoinHandle<()>>,
483    event_enrollment_task: Option<JoinHandle<()>>,
484    trend_log_task: Option<JoinHandle<()>>,
485    schedule_tick_task: Option<JoinHandle<()>>,
486    /// One-second `Time_Delay` confirmation task for intrinsic reporting.
487    intrinsic_reporting_task: Option<JoinHandle<()>>,
488    /// Monotonic object-operation task: Binary Lighting Output
489    /// WARN_OFF/WARN_RELINQUISH egress and Access Door pulse relock (#1073).
490    binary_lighting_operation_task: Option<JoinHandle<()>>,
491    /// Follow-up fanout after acknowledged confirmed COV reports (#896).
492    cov_revisit_task: Option<JoinHandle<()>>,
493    local_mac: MacAddr,
494}
495
496/// Cloneable handle for unsolicited I-Am announcements while its server runs.
497///
498/// At most 32 local broadcasts may be in flight. Admission fails immediately
499/// at capacity or after shutdown begins. Admitted sends belong to the server:
500/// cancelling their caller only drops its waiter; stop cancels and joins them.
501/// Retaining this handle does not retain the transport after shutdown.
502pub struct IAmBroadcaster<T: TransportPort> {
503    state: std::sync::Weak<broadcaster::BroadcasterState<T>>,
504}
505
506impl<T: TransportPort> Clone for IAmBroadcaster<T> {
507    fn clone(&self) -> Self {
508        Self {
509            state: self.state.clone(),
510        }
511    }
512}
513
514impl BACnetServer<BipTransport> {
515    /// Create a BIP-specific builder with interface/port/broadcast fields.
516    pub fn bip_builder() -> BipServerBuilder {
517        BipServerBuilder {
518            config: ServerConfig::default(),
519            db: ObjectDatabase::new(),
520            configured_device_bindings: Vec::new(),
521            mode: BipMode::Normal,
522        }
523    }
524}
525
526mod clock;
527mod command_runs;
528mod reinitialize;
529mod time_sync_policy;
530pub use reinitialize::{ReinitializeContext, ReinitializeHandler};
531mod write_group;
532#[cfg(test)]
533pub(crate) use clock::clocked_test_database;
534pub use clock::ClockConfig;
535use clock::ServerClock;
536use time_sync_policy::{request_limiters, TimeSyncLimiter};
537pub use time_sync_policy::{
538    TimeSyncPolicy, TimeSyncRateLimit, TimeSyncSource, TimeSyncSourceRestriction,
539};
540mod binary_lighting_lifecycle;
541#[cfg(test)]
542mod confirmed_issuance_tests;
543mod confirmed_request_tracker;
544mod cov_clock;
545mod cov_encoding;
546mod cov_fanout;
547mod cov_notifications;
548mod cov_notify_context;
549mod cov_snapshot;
550mod dcc_disable_rate;
551pub(crate) mod dcc_outcomes;
552mod dcc_policy;
553mod lso_replay;
554pub use dcc_disable_rate::DccDisableRateLimit;
555mod dcc_timer;
556pub use dcc_outcomes::DccOutcomeCounters;
557pub use dcc_policy::{DccPolicy, DccSource, DccSourceRestriction};
558use dcc_timer::CommState;
559pub use dcc_timer::DccState;
560mod binding_probes;
561mod device_bindings;
562mod discovery;
563#[doc(hidden)]
564pub use discovery::iam_request_for as discovery_iam_for_test;
565pub use discovery::{DiscoveryCounters, DiscoveryPolicy};
566pub(crate) use discovery::{DiscoveryLimiter, PreCheckDecision, WhoHasTarget};
567mod audit_log_purge;
568mod dispatch;
569mod durable_writes;
570#[doc(hidden)]
571pub use durable_writes::settle_endpoint as __endpoint_settle_durable_writes;
572mod event_delivery;
573mod event_enrollment_lifecycle;
574mod event_forwarding;
575pub use event_forwarding::MAX_FORWARDED_DESTINATIONS;
576mod event_forwarding_repeats;
577mod event_message_policy;
578pub(crate) mod event_notification_payload;
579mod event_notifications;
580mod event_recipient_route;
581mod event_send;
582mod event_suppression;
583pub use event_suppression::EventNotificationCounters;
584mod received_event_log;
585pub use received_event_log::{RECEIVED_EVENT_LOG_RATE, RECEIVED_EVENT_LOG_SOURCES};
586mod confirmed_answer;
587pub(crate) mod event_timestamp;
588mod handles;
589mod learned_router_cache;
590mod lifecycle;
591mod local_writes;
592mod network_port;
593#[cfg(test)]
594mod network_port_tests;
595pub use confirmed_answer::{CovAckResult, Refusal};
596mod remote_writes;
597pub(crate) use remote_writes::RemoteRequestError;
598mod notification_transactions;
599#[doc(hidden)]
600pub use notification_transactions::{
601    canonical_direct_peer as __endpoint_canonical_direct_peer,
602    canonical_routed_peer as __endpoint_canonical_routed_peer,
603    run_notification_worker as __endpoint_run_notification_worker,
604    AuditFailureContext as __endpoint_AuditFailureContext,
605    AuditFailureQueue as __endpoint_AuditFailureQueue,
606    AuditFailureTicket as __endpoint_AuditFailureTicket,
607    NotificationOperation as __endpoint_NotificationOperation,
608    NotificationReserveError as __endpoint_NotificationReserveError,
609    NotificationTransactions as __endpoint_NotificationTransactions,
610    NotificationWorkerResult as __endpoint_NotificationWorkerResult,
611};
612mod requests;
613#[doc(hidden)]
614pub use requests::endpoint_responder::EndpointResponder as __endpoint_EndpointResponder;
615#[cfg(feature = "sc-tls")]
616mod sc_builder;
617#[cfg(test)]
618pub(crate) use requests::{EXECUTED_CONFIRMED, EXECUTED_UNCONFIRMED};
619#[cfg(test)]
620mod audit_notification_tests;
621#[cfg(test)]
622mod unconfirmed_audit_notification_tests;
623#[cfg(feature = "sc-tls")]
624pub use sc_builder::ScServerBuilder;
625mod responses;
626mod segmentation;
627mod segmented_receive;
628mod segmented_send;
629pub(crate) use segmented_send::*;
630mod request_admission;
631mod rpm_budget;
632pub use rpm_budget::ReadPropertyMultipleBudget;
633mod alarm_summary_budget;
634pub use alarm_summary_budget::GetAlarmSummaryBudget;
635mod atomic_read_file_budget;
636mod atomic_write_file_budget;
637mod read_range_budget;
638pub use read_range_budget::ReadRangeBudget;
639mod event_information_budget;
640pub use event_information_budget::GetEventInformationBudget;
641mod enrollment_summary_budget;
642pub use atomic_read_file_budget::AtomicReadFileBudget;
643pub use atomic_write_file_budget::AtomicWriteFileBudget;
644pub use enrollment_summary_budget::GetEnrollmentSummaryBudget;
645#[cfg(test)]
646mod atomic_read_file_tests;
647#[cfg(test)]
648mod atomic_write_file_tests;
649#[cfg(test)]
650mod enrollment_summary_tests;
651mod request_peer;
652mod request_services;
653mod request_tasks;
654pub use request_admission::{RequestAdmissionCounters, RequestAdmissionPolicy};
655mod shutdown;
656pub use shutdown::drop_database_off_runtime;
657
658#[cfg(test)]
659mod access_door_event_tests;
660#[cfg(test)]
661mod access_door_pulse_task_tests;
662#[cfg(test)]
663mod access_zone_event_tests;
664#[cfg(test)]
665mod acknowledge_alarm_tests;
666#[cfg(test)]
667mod active_cov_subscriptions_tests;
668#[cfg(test)]
669mod audit_log_query_tests;
670#[cfg(test)]
671mod averaging_reference_write_tests;
672#[cfg(test)]
673mod averaging_sample_tests;
674#[cfg(test)]
675mod binary_lighting_task_tests;
676#[cfg(test)]
677mod channel_member_concurrency_tests;
678#[cfg(test)]
679mod channel_reliability_tests;
680#[cfg(test)]
681mod channel_remote_datatype_tests;
682#[cfg(test)]
683mod channel_remote_write_tests;
684#[cfg(test)]
685mod channel_run_tests;
686#[cfg(test)]
687mod channel_wire_tests;
688#[cfg(test)]
689mod color_channel_member_tests;
690#[cfg(test)]
691mod color_engine_task_tests;
692#[cfg(test)]
693mod command_action_run_tests;
694#[cfg(test)]
695mod command_action_wire_tests;
696#[cfg(test)]
697mod command_remote_write_tests;
698#[cfg(test)]
699mod command_run_cancel_tests;
700#[cfg(test)]
701mod command_run_stop_tests;
702#[cfg(test)]
703mod confirmed_broadcast_tests;
704#[cfg(test)]
705mod cov_background_tests;
706#[cfg(test)]
707mod cov_budget_tests;
708#[cfg(test)]
709mod cov_confirmed_baseline_tests;
710#[cfg(test)]
711mod cov_confirmed_context_tests;
712#[cfg(test)]
713mod cov_multiple_admission_tests;
714#[cfg(test)]
715mod cov_notifications_tests;
716#[cfg(test)]
717mod cov_quota_tests;
718#[cfg(test)]
719mod cov_timed_capture_order_tests;
720#[cfg(test)]
721mod cov_timed_chunk_tests;
722#[cfg(test)]
723mod cov_timed_deadline_tests;
724#[cfg(test)]
725mod cov_timed_multiple_tests;
726#[cfg(test)]
727mod cov_timed_producer_tests;
728#[cfg(test)]
729mod cov_timed_value_split_tests;
730#[cfg(test)]
731mod cov_untimed_split_tests;
732#[cfg(test)]
733mod cov_wire_test_support;
734#[cfg(test)]
735mod dcc_event_detection_tests;
736#[cfg(test)]
737mod device_bindings_tests;
738#[cfg(test)]
739mod device_recipient_routing_tests;
740#[cfg(all(test, feature = "sc-tls"))]
741mod direct_principal_tests;
742#[cfg(test)]
743mod discovery_dcc_tests;
744#[cfg(test)]
745mod discovery_tests;
746#[cfg(test)]
747mod event_confirmed_routing_tests;
748#[cfg(test)]
749mod event_enable_distribution_tests;
750#[cfg(test)]
751mod event_enrollment_task_tests;
752#[cfg(test)]
753mod event_forwarding_bounds_tests;
754#[cfg(test)]
755mod event_forwarding_cap_tests;
756#[cfg(test)]
757mod event_forwarding_origin_tests;
758#[cfg(test)]
759mod event_forwarding_rule_tests;
760#[cfg(test)]
761mod event_forwarding_tests;
762#[cfg(test)]
763mod event_log_notification_tests;
764#[cfg(test)]
765mod event_network_priority_tests;
766#[cfg(test)]
767mod event_notifications_tests;
768#[cfg(test)]
769mod event_recipient_routing_tests;
770#[cfg(test)]
771mod life_safety_application_tests;
772#[cfg(test)]
773mod life_safety_cov_tests;
774#[cfg(test)]
775mod life_safety_operation_replay_tests;
776#[cfg(test)]
777mod life_safety_operation_tests;
778#[cfg(test)]
779mod lighting_command_member_tests;
780#[cfg(test)]
781mod lighting_engine_task_tests;
782#[cfg(test)]
783mod lighting_present_value_tests;
784#[cfg(test)]
785mod list_element_event_tests;
786#[cfg(test)]
787mod local_network_number_tests;
788#[cfg(test)]
789mod local_network_outbound_tests;
790#[cfg(test)]
791mod log_reference_resize_tests;
792#[cfg(test)]
793mod loop_controlled_variable_tests;
794#[cfg(test)]
795mod loop_cov_tests;
796#[cfg(test)]
797mod mistagged_request_wire_tests;
798#[cfg(test)]
799mod noncommandable_null_cov_tests;
800#[cfg(test)]
801mod notification_dcc_tests;
802#[cfg(test)]
803mod notification_transactions_tests;
804#[cfg(test)]
805mod pulse_converter_cov_tests;
806#[cfg(test)]
807mod pulse_converter_reference_tests;
808#[cfg(test)]
809mod rb07_provenance_tests;
810#[cfg(test)]
811mod reference_write_wire_tests;
812#[cfg(test)]
813mod remote_write_answer_tests;
814#[cfg(test)]
815mod remote_write_discovery_tests;
816#[cfg(test)]
817mod run_cycle_tests;
818#[cfg(test)]
819mod schedule_create_retry_tests;
820#[cfg(test)]
821mod schedule_reference_list_tests;
822#[cfg(test)]
823mod schedule_reference_write_tests;
824#[cfg(test)]
825mod schedule_write_tests;
826#[cfg(test)]
827mod segmentation_tests;
828#[cfg(test)]
829mod staging_cov_tests;
830#[cfg(test)]
831mod staging_reference_write_tests;
832#[cfg(test)]
833mod subscribed_recipients_expiry_tests;
834#[cfg(test)]
835mod table_13_1_cov_tests;
836#[cfg(test)]
837pub(crate) mod test_forwarder;
838#[cfg(test)]
839pub(crate) mod test_transport;
840#[cfg(test)]
841mod tests;
842#[cfg(test)]
843mod trailing_octet_wire_tests;
844#[cfg(test)]
845mod truncated_request_wire_tests;
846#[cfg(test)]
847mod value_cov_increment_tests;
848#[cfg(test)]
849mod value_source_local_tests;
850
851impl<T: TransportPort + 'static> BACnetServer<T> {
852    /// Start a server builder for a caller-supplied transport type, with default configuration and
853    /// an empty object database.
854    pub fn generic_builder() -> ServerBuilder<T> {
855        ServerBuilder {
856            config: ServerConfig::default(),
857            db: ObjectDatabase::new(),
858            transport: None,
859            configured_device_bindings: Vec::new(),
860        }
861    }
862
863    /// Get a snapshot of discovery rate-limiting counters.
864    pub fn discovery_counters(&self) -> DiscoveryCounters {
865        self.discovery_limiter.counters()
866    }
867
868    /// Get a snapshot of COV operational and telemetry counters.
869    pub fn cov_counters(&self) -> CovCounters {
870        self.cov_counters.snapshot()
871    }
872
873    /// Sample lifetime mutation authorization decisions, including after `stop()`.
874    /// Counts decisions, not completed handlers or response delivery; see
875    /// [`MutationDecisionCounters`](crate::mutation::MutationDecisionCounters).
876    pub fn mutation_decision_counters(&self) -> crate::mutation::MutationDecisionCounters {
877        self.mutation_decisions.snapshot()
878    }
879
880    /// Remove COV subscriptions using this current immediate MAC and routed source.
881    /// Cleanup of an obsolete router leaves subscriptions renewed onto another route.
882    /// Only removed entries release their contribution to the shared quota group.
883    pub async fn remove_peer_subscriptions(
884        &self,
885        mac: &[u8],
886        network: Option<&NpduAddress>,
887    ) -> usize {
888        let mut table = self.cov_table.write().await;
889        table.remove_peer_subscriptions(mac, network)
890    }
891}
892
893#[cfg(test)]
894mod cov_identity_completion_tests;
895
896mod broadcaster;