Skip to main content

BACnetServer

Struct BACnetServer 

Source
pub struct BACnetServer<T: TransportPort = AnyTransport> { /* private fields */ }
Expand description

BACnet server with APDU dispatch and service handling.

T is the transport the server runs over. It defaults to AnyTransport, so BACnetServer alone names a server over any built-in transport; BACnetServer<BipTransport> and the like name one over a concrete transport.

Implementations§

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub fn generic_builder() -> ServerBuilder<T>

Start a server builder for a caller-supplied transport type, with default configuration and stock Device generation unless a database is supplied.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub async fn start( config: ServerConfig, db: ObjectDatabase, transport: T, ) -> Result<Self, Error>

Start with the default system-UTC Device clock.

Source

pub async fn start_with_clock( config: ServerConfig, db: ObjectDatabase, transport: T, clock_config: ClockConfig, ) -> Result<Self, Error>

Start with a validated Device clock configuration.

Source

pub async fn start_clockless( config: ServerConfig, db: ObjectDatabase, transport: T, ) -> Result<Self, Error>

Start without a Device wall clock or time-synchronization execution.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub async fn report_device_reset_local(&self) -> Result<(), Error>

Report a reset the application carries out for its own reasons, not at a peer’s ReinitializeDevice request: a DEVICE_RESET Audit record from this device, with no invoke ID (Clause 19.6, Table 19-5 covers resets for any reason, #1564).

An accepted WARMSTART or COLDSTART is recorded already; don’t report it again. The record goes through the Reporter that monitors the local Device, under that Device’s audit policy, as a peer’s reset’s does, and does nothing when no Reporter selects it. It returns once the record is admitted, not delivered, so call it before the restart begins and give delivery time before the process exits.

§Errors

The stopped-server error once stop has run.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub fn group_source_request_drops(&self) -> u64

Confirmed requests ignored since start because the link-layer address they came from is a group destination of the transport (TransportPort::is_group_destination), such as a B/IP broadcast or multicast address (#1504). The answer, and the confirmed COV notifications of a subscription such a request makes, would go back to that address, so the request is neither executed nor answered. No built-in transport hands one up; a custom one can.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub fn dcc_outcome_counters(&self) -> DccOutcomeCounters

Sample completed admitted DCC outcomes, including after stop(). Excludes pre-handler rejections, duplicates, incomplete cancelled handlers, timer expiry and response delivery outcomes. No history is retained.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub async fn broadcast_i_am(&self) -> Result<(), Error>

Broadcast I-Am through this server’s bounded local-send owner. Cancelling the waiter leaves admitted work owned until completion or stop. While DeviceCommunicationControl restricts initiation nothing is sent and this fails with SERVICES / COMMUNICATION_DISABLED (Clause 16.1).

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub async fn purge_audit_log(&self, oid: &ObjectIdentifier) -> Result<(), Error>

Purge an Audit Log: clear its records and append a BUFFER_PURGED status record, as AuditLogObject::purge describes.

Peers have no way to do this, so it is the application’s to call. The log builds the purged state and queues its commit; the server waits for the commit with the object database guard dropped, so other requests carry on, and the log serves the purged state only once it is durable. Changes to one log land one at a time, in the order they were staged: a notification batch whose commit is running lands first and is purged with the rest, and one that arrives while the purge commits waits for it and follows the purge record.

§Errors
  • OBJECT / UNKNOWN_OBJECT when oid names no object, and OBJECT / OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED when the object cannot be purged (it is not a built-in Audit Log).
  • DEVICE / OPERATIONAL_PROBLEM without a valid local clock, or when the commit fails; the log’s writer logs the storage error.
  • The stopped-server error once stop has run.

On any error the log is left as it was. A purge whose future is dropped before it finishes is handled as an abandoned write request is: the log keeps serving its state, drops the staged purge once it has waited long enough, and sets storage back to that state.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub fn event_notification_counters(&self) -> EventNotificationCounters

Sample the lifetime totals of undelivered event notifications, including after stop(). See EventNotificationCounters for what each field counts and what is left out.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub fn local_mac(&self) -> &[u8] ⓘ

Return the last bound local MAC snapshot, including after stop. This retained address does not assert that the transport is still active.

Source

pub fn transport(&self) -> Option<&T>

The transport the server’s network layer runs on, for its transport-specific state: a B/IPv6 server’s local_address(), which a peer turns into this server’s VMAC with resolve_virtual_address, or a transport’s counters. None once the server no longer holds its network layer.

Source

pub fn database(&self) -> &Arc<RwLock<ObjectDatabase>> ⓘ

Get a reference to the shared object database.

Objects read through this handle return raw standalone data. A built-in Device follows its declared service profile, with absent or empty COV lists. Served Device definitions and live lists belong to the executor: use read_local for that view. Normal Device mutation and replacement cannot change the served execution profile. Install Device objects before startup: changing their membership through this handle does not rebind the discovery limiter’s startup identity.

A clone the application keeps can outlive the server’s own handle, and whichever goes last drops every object, waiting for the saves durable objects have queued. In async code, let go of such a clone with drop_database_off_runtime, so that wait runs on Tokio’s blocking pool instead of a runtime worker (#1513).

Source

pub async fn read_local( &self, oid: &ObjectIdentifier, property: PropertyIdentifier, array_index: Option<u32>, ) -> Result<PropertyValue, Error>

Read one local property through the server’s ReadProperty evaluator.

Device wildcard reads target the configured identity; removal returns UNKNOWN_OBJECT even if another Device is present. The database’s lowest Device instance still owns the live COV lists, independent of insertion order; other Device objects receive empty lists. Every served Device uses the executor’s services, COV presence and property definitions, even after public profile mutation, replacement or custom object installation.

This is the live local read boundary: it applies the same Device wildcard resolution, UNKNOWN_OBJECT and PROPERTY_IS_NOT_AN_ARRAY checks, and value source as network ReadProperty. The selected Device’s Active_COV_Subscriptions (ordinary and single-property subscriptions) and Active_COV_Multiple_Subscriptions (SubscribeCOVPropertyMultiple contexts) are projected from the COV subscription table at one sampled instant, returned as their encoded BACnetLIST of BACnetCOVSubscription and BACnetLIST of BACnetCOVMultipleSubscription (PropertyValue::ApplicationData). After stop the server services no subscriptions, so both properties read as empty lists.

The selected Device’s Device_Address_Binding lists the server’s device bindings at the time of the read (#1369): each configured DeviceBinding and each device whose I-Am was heard in the last ten minutes, in Device instance order, as a PropertyValue::List whose items are encoded BACnetAddressBindings (PropertyValue::ApplicationData). A device on this network has network number 0. The bindings outlast stop, so they still read then.

A Group’s Present_Value is rebuilt from its members, whose rows count against the ReadPropertyMultiple work limit (ReadPropertyMultipleBudget::max_result_elements) as for network ReadProperty; past it the read fails with Error::abort carrying OUT_OF_RESOURCES.

Source

pub async fn write_local_encoded( &self, oid: &ObjectIdentifier, property: PropertyIdentifier, array_index: Option<u32>, value: &[u8], priority: Option<u8>, source: LocalCommandSource, ) -> Result<(), Error>

Write one local property from its encoded value: the octets a network WriteProperty would carry for it.

The octets take the steps the WriteProperty handler takes before an object sees a value. An array index is refused as it is over the network (UNKNOWN_PROPERTY for a property the object doesn’t hold, PROPERTY_IS_NOT_AN_ARRAY for one that isn’t an array), and the octets are decoded with that handler’s per-property framing, which hands a Schedule’s Effective_Period or a Staging’s Stages to the object in the form it decodes. The value then takes the write_local path, with its COV, event and audit work. A value read with read_local and encoded therefore writes back, and any value a network client could write is accepted here.

Like write_local, it must be awaited inside a Tokio runtime, failing before anything is written outside one, and a caller dropped once the write has committed skips none of the work the write owes (#1367).

Source

pub fn i_am_broadcaster(&self) -> IAmBroadcaster<T>

Create a cloneable handle for unsolicited I-Am announcements.

Source

pub fn comm_state(&self) -> DccState

Get the communication state per DeviceCommunicationControl.

The server refuses the deprecated DISABLE, so this is DccState::Enable or DccState::DisableInitiation.

Source

pub async fn generate_pics(&self, pics_config: &PicsConfig) -> Pics

Generate PICS from the database and the server’s effective Device execution view. Standalone PicsGenerator keeps raw-object semantics.

The caller must supply a PicsConfig for fields not available from the server (device profile, data links, network roles, etc.). The vendor and model names, the firmware and application versions, and the protocol version and revision come from the served Device object, so the PICS can’t contradict what the device reports.

Source§

impl<T: TransportPort + AsBip + 'static> BACnetServer<T>

Source

pub fn foreign_registration(&self) -> Result<ForeignRegistrationSnapshot, Error>

How this server’s foreign-device registration is going (#938): whether it holds a live lease, the time left on it, and the last attempt’s result and outcome, a BBMD’s refusal included. It reads BipTransport::bvlc_client_snapshot and sends nothing. Fails on a transport that isn’t B/IP, and once the server has stopped.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub async fn authorize_access_local( &self, request: AuthorizationRequest<'_>, tag: u64, ) -> Result<LocalAuthorizationResult, Error>

Evaluate an authenticated local presentation and record a terminal result.

A grant or denial installs all its event intents in order; pending, unresolved and no-authorization results publish nothing, including their intermediate intents. The full request supplies every evaluation policy and operational fact. The caller owns retries, tag progression and replay handling: repeating a terminal attempt is a new publication, even when its tag is unchanged. Evaluation and event installation share one DB guard.

Each event names its associated credential (the companion for BY) and is stamped by the point. No physical factor is supplied, so the absent-factor UNDEFINED representation applies (Clauses 12.31.27–31). Equal/backward clocks advance the event trigger, and an exhausted timestamp representation refuses the entire batch. This route performs no physical door action.

Runtime, stopped-network, unknown/wrong object, unsupported batch, malformed admission and out-of-service/fault errors leave state unchanged. Contention retries release all guards before waiting and observe fresh state; progress is cooperative once contention quiesces, not a starvation/latency guarantee.

A committed attempt retains snapshots for ordered COV fanout through the existing notification lifecycle. Caller cancellation after commit does not discard owned work. DCC, budgets, send failures and shutdown still apply; success is not remote acknowledgment or durable delivery. Independent transactions have no global notification-order guarantee.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub async fn report_access_event_local( &self, oid: &ObjectIdentifier, report: AccessEventReport, ) -> Result<(), Error>

Report an access event at an Access Point: Access_Event, Access_Event_Tag, Access_Event_Time, Access_Event_Credential and Access_Event_Authentication_Factor change together (Clause 12.31.27.1), checked as AccessPointObject::set_access_event checks them (VALUE_OUT_OF_RANGE for a credential reference, factor or event it refuses; an event is a named BACnetAccessEvent or a proprietary one from 512 to 65535). A report without a time is stamped from the Device clock.

Access_Event_Time is the point’s Table 13-1 trigger, so each event with a new time sends the SubscribeCOV report, carrying all five values. A time the point stamps itself is strictly after the one it served, so several events of one transaction each move the time and each report. While Out_Of_Service is TRUE (Clause 12.31.8), or Reliability isn’t NO_FAULT_DETECTED (Clause 12.31.7), the point performs no authentication, so an event is refused with PROPERTY / WRITE_ACCESS_DENIED and nothing changes. An unknown object fails with OBJECT / UNKNOWN_OBJECT and any object other than an Access Point with OBJECT / OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED. The Python binding exposes this as BACnetServer.report_access_event_local.

Like write_local, it must be awaited inside a Tokio runtime, failing before anything is written outside one, and a caller dropped once the event has committed skips none of the work it owes (#1367).

Source

pub async fn report_credential_read_local( &self, oid: &ObjectIdentifier, report: CredentialReadReport, ) -> Result<(), Error>

Report a factor a Credential Data Input’s reader has read: Present_Value and Update_Time change together (Clause 12.36.11). The factor must name one of the reader’s declared formats with its class, or be the UNDEFINED or ERROR factor with class 0, else VALUE_OUT_OF_RANGE. A report without a time is stamped from the Device clock, or the object’s next sequence number without a usable clock, so reading the same card again still moves Update_Time.

Update_Time is the reader’s Table 13-1 trigger, so each read with a new time sends the SubscribeCOV report. While Out_Of_Service is TRUE a client may be simulating the reader, so the read replaces the reader’s values put aside, as set_present_value does (#1168): the served values, and with them the COV report, stay as they are, and the return to service serves the latest read. The other errors are those of Self::report_access_event_local, for a Credential Data Input. The Python binding exposes this as BACnetServer.report_credential_read_local.

The Tokio-runtime rule of write_local applies.

Source

pub async fn report_door_state_local( &self, oid: &ObjectIdentifier, report: DoorStateReport, ) -> Result<(), Error>

Report an Access Door’s hardware state: whichever of Door_Status, Lock_Status and Door_Alarm_State the report gives, together. Each must fall in its production, and the alarm state be NORMAL or a member of Alarm_Values or Fault_Values and no member of Masked_Alarm_Values, else VALUE_OUT_OF_RANGE and nothing changes.

Door_Alarm_State is the door’s Table 13-1 trigger, so a change of it sends the SubscribeCOV report, and the event pass after the write runs the door’s CHANGE_OF_STATE and FAULT_STATE algorithms on it at once. Secured_Status follows the new values on its next read. While Out_Of_Service is TRUE a client may be simulating the door, so the report replaces the device values put aside, as the door’s setters do (#1131): the served values stay, so no COV report or event follows, and the return to service serves the latest values reported. The other errors are those of Self::report_access_event_local, for an Access Door. The Python binding exposes this as BACnetServer.report_door_state_local.

The Tokio-runtime rule of write_local applies.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub async fn set_life_safety_operation_expected_local( &self, oid: &ObjectIdentifier, operation: LifeSafetyOperation, ) -> Result<(), Error>

Arm or rearm a Life Safety object from trusted local application logic.

This uses the object-internal state channel under the database write lock. Network WriteProperty and WritePropertyMultiple remain unable to forge Operation_Expected. After the lock is released, an actual Operation_Expected readback change uses the exact Life Safety COV path; rearming to the current value emits no notification.

Like write_local, it must be awaited inside a Tokio runtime, failing before anything changes outside one. Once the object holds the operation, the timestamped capture and the COV fanout the change owes run as a task of their own in the server’s request task set, which this future only waits for, so a caller dropped then skips none of it (#1520). stop() aborts that task with the other request tasks.

Source

pub async fn write_local( &self, oid: &ObjectIdentifier, property: PropertyIdentifier, array_index: Option<u32>, value: PropertyValue, priority: Option<u8>, source: LocalCommandSource, ) -> Result<(), Error>

Write a property on a local object and fire the same post-write COV and event notifications that a network WriteProperty does. When target Audit reporting is configured, the same observer records eligible local writes with local Device provenance and no invoke ID; Device recipient changes retain their sole old/new pair owner. Eligible actual AV/BV Audit policy changes reserve immediate delivery before assignment; unavailable resources return SERVICES/SERVICE_REQUEST_DENIED without a policy change.

This is the server-owned local-mutation entry point: it performs the write under the database lock — routing OBJECT_NAME through the name uniqueness check and index refresh, exactly like the network handler — then releases the lock and runs the COV/event trigger path so a subscription observes a local mutation just as it would a network one. source explicitly selects the Device or an existing initiating object. Tracked commands require a concrete selected Device; the selected Device retains correction ownership regardless of the local initiator. Unrelated properties preserve their behavior without a usable command origin. Low-level object setters deliberately bypass this notification owner.

A NULL to a property that isn’t commandable and has no NULL in its datatype succeeds and changes nothing, as over the network (#1396): Audit records it, and no COV, event or Schedule work follows it.

An array index is checked first, as the WriteProperty handler checks it (#1426): UNKNOWN_PROPERTY for a property the object doesn’t hold, PROPERTY_IS_NOT_AN_ARRAY for one that isn’t an array. Neither the object nor Audit sees such a write. The writes a Command or Channel run makes here get the same check.

§Cancellation

Dropped before the write commits, the future makes no change. Once it has committed, the work the write owes (the event pass, the COV fanout, a re-evaluated Schedule’s target fanout, a Staging plan, and the Command or Channel runs it started) runs as a task of its own in the server’s request task set, which this future only waits for, so dropping the future skips none of it (#1367): subscribers hear of the change, and a run ends as it would have, reported to its property subscribers. stop() aborts that task with the other request tasks, as it does a request handler: what it hadn’t sent by then is not sent, and a run it hadn’t started ends as if none of its writes were made (#1324), In_Process FALSE with each command unsuccessful, or Write_Status FAILED. The call still returns Ok(()) then: the write was made. A panic in that work is raised again in the caller, as it would be were the work done in place.

The future must be awaited inside a Tokio runtime, which the task is spawned onto: outside one it fails with Error::Encoding before anything is written.

Source

pub async fn set_present_value_local( &self, oid: &ObjectIdentifier, value: PropertyValue, ) -> Result<(), Error>

Supply a supported object’s logical Present_Value from Rust application code.

Analog, Binary and Multi-state Inputs, noncommandable Values, Loop (the control algorithm’s output) and Life Safety Point and Zone opt in. Binary Input values are logical INACTIVE/ACTIVE states after Polarity, not raw physical states. The update runs the existing post-write intrinsic-event and COV processing after the database lock is released; configured delays and distribution policy still control delivery, and a COV_Increment still filters Present_Value reports.

Applications are denied while Out_Of_Service is TRUE to protect a client’s simulation value. This is local policy for Inputs and required by the object clauses for the supported Values and Loop. NULL is an invalid application value. Other object families fail closed; use BACnetServer::write_local for network-equivalent writes and sourced commands on commandable objects. The Python binding exposes this as BACnetServer.set_present_value_local. A noncommandable Value that tracks its source (set_value_source_tracking, #1552) then publishes this server’s Device as Value_Source, as Clause 19.5 asks of a write the local device makes.

A Life Safety Point or Zone takes an Enumerated BACnetLifeSafetyState, a standard value or one from 256 to 65535, in or out of service, since clients never write its Present_Value. The update leaves Tracking_Value, Silenced and Operation_Expected alone, so any latching until reset is the application’s (see BACnetServer::set_tracking_value_local). A change notifies SubscribeCOV and Present_Value property subscribers through the Life Safety COV path. The built-in objects run no intrinsic reporting, so the post-write event pass raises nothing for them.

Like write_local, it must be awaited inside a Tokio runtime, failing before anything is written outside one, and a caller dropped once the write has committed skips none of the work the write owes (#1367).

Source

pub async fn set_controlled_variable_value_local( &self, oid: &ObjectIdentifier, value: PropertyValue, ) -> Result<(), Error>

Supply a Loop’s measured Controlled_Variable_Value from the application that runs its control algorithm.

The value is a finite REAL: another datatype fails with PROPERTY / INVALID_DATA_TYPE and NaN or an infinity with PROPERTY / VALUE_OUT_OF_RANGE. An unknown object fails with OBJECT / UNKNOWN_OBJECT and any object other than a Loop with OBJECT / OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED, the same errors as BACnetServer::set_present_value_local. Unlike Present_Value it is accepted while Out_Of_Service is TRUE, because Out_Of_Service decouples only the output and Reliability from the algorithm.

The update runs the same post-write COV processing as set_present_value_local once the database lock is released. A SubscribeCOVProperty on Controlled_Variable_Value is notified of the change; a SubscribeCOV on the Loop is not, since the Loop’s COV report carries this value without being triggered by it, so its next report carries the new value. The property stays read-only over the network.

Like write_local, it must be awaited inside a Tokio runtime, failing before anything is written outside one, and a caller dropped once the write has committed skips none of the work the write owes (#1367).

Source

pub async fn add_averaging_sample_local( &self, oid: &ObjectIdentifier, sample: Option<PropertyValue>, ) -> Result<(), Error>

Record one sample for an Averaging object, taken by the application.

The server samples an object that holds an Object_Property_Reference itself, every Window_Interval / Window_Samples seconds (#1144). For an object without one, the application samples whatever it averages, spacing its readings that far apart, and passes each result here. On an object the server samples, a call is one more attempt in the window and doesn’t move the server’s schedule. A value may be a BOOLEAN (FALSE and TRUE count as 0 and 1), Signed, Unsigned, Enumerated or finite REAL; the object keeps its statistics in REAL (Clause 12.5). None records an attempt that produced no value, such as a failed read: it counts toward Attempted_Samples but not Valid_Samples. Another datatype, Double included, fails with PROPERTY / INVALID_DATA_TYPE and NaN or an infinity with PROPERTY / VALUE_OUT_OF_RANGE, and a refused sample counts as neither attempted nor valid. An unknown object fails with OBJECT / UNKNOWN_OBJECT and any object other than an Averaging object with OBJECT / OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED, as with BACnetServer::set_present_value_local.

Each call fills the next slot of the object’s window, dropping the oldest once Window_Samples are held, and Minimum_Value, Maximum_Value, Average_Value, Attempted_Samples and Valid_Samples change together under the database lock; the server’s COV processing runs once the lock is released. Averaging has no Table 13-1 row, so SubscribeCOV on it is refused, but SubscribeCOVProperty and SubscribeCOVPropertyMultiple are admitted: a numeric property is reported when it moves by the subscription’s COV increment, or on any change when the subscription gives none (Table 13-1a). A move to or from the NaN or infinite value of an empty window is always reported, and staying at it never is. The report has no Status_Flags because the object has none. The Python binding exposes this as BACnetServer.add_averaging_sample_local.

Like write_local, it must be awaited inside a Tokio runtime, failing before anything is written outside one, and a caller dropped once the write has committed skips none of the work the write owes (#1367).

Source

pub async fn set_tracking_value_local( &self, oid: &ObjectIdentifier, value: PropertyValue, ) -> Result<(), Error>

Supply a Life Safety Point’s or Zone’s Tracking_Value, the live state the application derived, while the server holds the object.

The value is an Enumerated naming a standard BACnetLifeSafetyState or one from the proprietary range 256..=65535. A reserved or larger number fails with PROPERTY / VALUE_OUT_OF_RANGE and another datatype with PROPERTY / INVALID_DATA_TYPE. An unknown object fails with OBJECT / UNKNOWN_OBJECT and any object other than a Life Safety Point or Zone with OBJECT / OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED, as with BACnetServer::set_present_value_local. Present_Value, Silenced and Operation_Expected are left as they are: the object derives nothing from Tracking_Value.

While Out_Of_Service is TRUE the value replaces the one set aside for the return to service, and a client’s simulated Tracking_Value keeps being served (#1108); the return to service then serves the latest application value and notifies its subscribers. In service, a change reaches SubscribeCOVProperty and SubscribeCOVPropertyMultiple subscribers of Tracking_Value through the Life Safety COV path once the database lock is released; whole-object SubscribeCOV reports don’t carry it. The property stays read-only over the network in service. The Python binding exposes this as BACnetServer.set_tracking_value_local.

Like write_local, it must be awaited inside a Tokio runtime, failing before anything is written outside one, and a caller dropped once the write has committed skips none of the work the write owes (#1367).

Source§

impl BACnetServer<ScTransport<TlsWebSocket>>

Source

pub fn sc_builder() -> ScServerBuilder

Available on crate feature sc-tls only.

Create an SC-specific builder that connects to a BACnet/SC hub.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub fn request_admission_counters(&self) -> RequestAdmissionCounters

Sample admission counters. Available after stop; active counts then are zero.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub async fn stop(&mut self) -> Result<(), Error>

Seal egress, join admitted work, and stop the owned transport.

Call it before dropping the server. A server dropped without it in async code aborts its tasks and lets the object database go on Tokio’s blocking pool once they have let go of it (#1409), so the drop returns at once, but nothing waits for the objects’ last saves. Storage may still change after such a drop returns, as those saves and the put-back of a staged write (#1363) land, so stop().await before building another server on the same storage.

Once its requests are joined, a write a Notification Forwarder, Notification Class, Access Rights object or Audit Log still holds staged for one of them is dropped, and stop waits until every save those objects have queued has run, so storage holds the state each object serves (#1363); see DurableWrites::settle_forgotten_writes. A Trend Log, Trend Log Multiple or Event Log kept in storage commits once more there, if it changed since its last commit, and stop waits for that commit too (#1541). That wait has no limit: storage that stalls holds stop up, and a warning naming the objects still saving is logged after 5 s and every 30 s after that. Stop does not wait while the application holds the database: the objects then settle from a task once it lets go, and put storage back when they are dropped. That task lets go of its handle through drop_database_off_runtime, so if it holds the last one the objects drop on Tokio’s blocking pool.

A request’s abort lands when its task next yields, so a request already running when stop begins may find its save done and make its write first. Its answer is sealed, but the object serves that write, and storage holds it (#1457).

Cancelling this waiter leaves cleanup owned by the server. A later stop joins it; after transport cleanup begins, dropping the server lets that cleanup finish. Local mutation and broadcasts are rejected from the first stop poll; local reads and database inspection remain available.

Source§

impl BACnetServer<BipTransport>

Source

pub fn bip_builder() -> BipServerBuilder

Create a BIP-specific builder with interface/port/broadcast fields.

Source§

impl<T: TransportPort + 'static> BACnetServer<T>

Source

pub fn discovery_counters(&self) -> DiscoveryCounters

Get a snapshot of discovery rate-limiting counters.

Source

pub fn cov_counters(&self) -> CovCounters

Get a snapshot of COV operational and telemetry counters.

Source

pub fn mutation_decision_counters(&self) -> MutationDecisionCounters

Sample lifetime mutation authorization decisions, including after stop(). Counts decisions, not completed handlers or response delivery; see MutationDecisionCounters.

Source

pub async fn remove_peer_subscriptions( &self, mac: &[u8], network: Option<&NpduAddress>, ) -> usize

Remove COV subscriptions using this current immediate MAC and routed source. Cleanup of an obsolete router leaves subscriptions renewed onto another route. Only removed entries release their contribution to the shared quota group.

Trait Implementations§

Source§

impl<T: TransportPort> Drop for BACnetServer<T>

Source§

fn drop(&mut self)

Seal the server and abort its tasks. In async code the object database then drops on Tokio’s blocking pool (#1409): the drop returns before the objects’ last saves land, so storage may still change after it. Call stop first to wait for them. A clone of database the application still holds keeps the objects alive; let go of it with drop_database_off_runtime (#1513).

Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl<T = AnyTransport> !RefUnwindSafe for BACnetServer<T>

§

impl<T = AnyTransport> !UnwindSafe for BACnetServer<T>

§

impl<T> Freeze for BACnetServer<T>
where Option<Arc<TargetAudit<T>>>: Freeze, Option<Arc<NetworkLayer<T>>>: Freeze, Arc<BroadcasterState<T>>: Freeze, Option<JoinHandle<(NetworkLayer<T>, Result<(), Error>)>>: Freeze,

§

impl<T> Send for BACnetServer<T>
where Option<Arc<TargetAudit<T>>>: Send, Option<Arc<NetworkLayer<T>>>: Send, Arc<BroadcasterState<T>>: Send, Option<JoinHandle<(NetworkLayer<T>, Result<(), Error>)>>: Send,

§

impl<T> Sync for BACnetServer<T>
where Option<Arc<TargetAudit<T>>>: Sync, Option<Arc<NetworkLayer<T>>>: Sync, Arc<BroadcasterState<T>>: Sync, Option<JoinHandle<(NetworkLayer<T>, Result<(), Error>)>>: Sync,

§

impl<T> Unpin for BACnetServer<T>
where Option<Arc<TargetAudit<T>>>: Unpin, Option<Arc<NetworkLayer<T>>>: Unpin, Arc<BroadcasterState<T>>: Unpin, Option<JoinHandle<(NetworkLayer<T>, Result<(), Error>)>>: Unpin,

§

impl<T> UnsafeUnpin for BACnetServer<T>
where Option<Arc<TargetAudit<T>>>: UnsafeUnpin, Option<Arc<NetworkLayer<T>>>: UnsafeUnpin, Arc<BroadcasterState<T>>: UnsafeUnpin, Option<JoinHandle<(NetworkLayer<T>, Result<(), Error>)>>: UnsafeUnpin,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more