Skip to main content

rustbac_client/
lib.rs

1//! High-level async BACnet client.
2//!
3//! [`BacnetClient`] wraps any [`DataLink`](rustbac_datalink::DataLink)
4//! transport and exposes ergonomic methods for common BACnet operations
5//! such as reading properties, discovering devices, and subscribing to
6//! change-of-value (COV) notifications.
7
8/// Alarm and event services (GetAlarmSummary, GetEventInformation, etc.).
9pub mod alarm;
10/// Core [`BacnetClient`] type and transport setup.
11pub mod client;
12/// Change-of-value (COV) notification types.
13pub mod cov;
14/// COV subscriptions with renewal and polling failover.
15pub mod cov_manager;
16/// Device and object discovery (Who-Is / I-Am / Who-Has).
17pub mod discovery;
18/// Client-level error type.
19pub mod error;
20/// Atomic file read/write operations.
21pub mod file;
22/// Long-running async notification listener.
23pub mod listener;
24/// Point type inference for BACnet objects.
25pub mod point;
26/// ReadRange results and related types.
27pub mod range;
28/// Schedule and Calendar convenience helpers.
29pub mod schedule;
30/// BACnet server/responder implementation.
31pub mod server;
32/// Lightweight simulated BACnet device.
33pub mod simulator;
34/// Per-device request throttling utility.
35pub mod throttle;
36/// Owned application-data values for client-side use.
37pub mod value;
38/// Device discovery walk — reads all objects and their properties.
39pub mod walk;
40/// Server-side intrinsic reporting engine.
41pub mod intrinsic;
42/// PICS (Protocol Implementation Conformance Statement) document generation.
43pub mod pics;
44
45pub use alarm::{
46    AlarmSummaryItem, EnrollmentSummaryItem, EventInformationItem, EventInformationResult,
47    EventNotification,
48};
49pub use client::{BacnetClient, ForeignDeviceRenewal};
50pub use cov::{CovNotification, CovPropertyValue};
51pub use cov_manager::{
52    CovManager, CovManagerBuilder, CovSubscriptionSpec, CovUpdate, UpdateSource,
53};
54pub use discovery::{DiscoveredDevice, DiscoveredObject};
55pub use error::ClientError;
56pub use file::{AtomicReadFileResult, AtomicWriteFileResult};
57pub use listener::{create_notification_listener, Notification, NotificationListener};
58pub use point::{PointClassification, PointDirection, PointKind};
59pub use range::{ClientBitString, ReadRangeResult};
60pub use rustbac_bacnet_sc::BacnetScTransport;
61pub use rustbac_core::services::acknowledge_alarm::{EventState, TimeStamp};
62pub use rustbac_core::services::device_management::{DeviceCommunicationState, ReinitializeState};
63pub use rustbac_datalink::bip::transport::{BroadcastDistributionEntry, ForeignDeviceTableEntry};
64pub use schedule::{CalendarEntry, DateRange, TimeValue};
65pub use server::{
66    encode_unconfirmed_cov_notification, BacnetServer, BacnetServiceError, CovSubscriptionManager,
67    ObjectStore, ObjectStoreHandler, ServiceHandler,
68};
69pub use simulator::SimulatedDevice;
70pub use throttle::DeviceThrottle;
71pub use value::ClientDataValue;
72pub use walk::{DeviceInfo, DeviceWalkResult, ObjectSummary};
73pub use intrinsic::{
74    AckedTransitions, IntrinsicAlgorithm, IntrinsicEnrollment, IntrinsicEventState,
75    IntrinsicReportingEngine, PendingEventNotification,
76};
77pub use pics::{PicsDocument, SegmentationSupport, SupportedObjectType, SupportedService};
78
79// Internal helpers used by simulator module.
80use rustbac_core::encoding::{primitives::decode_unsigned, reader::Reader, tag::Tag};
81use rustbac_core::types::ObjectId;
82
83fn decode_ctx_unsigned(r: &mut Reader<'_>) -> Result<u32, ClientError> {
84    match Tag::decode(r)? {
85        Tag::Context { len, .. } => Ok(decode_unsigned(r, len as usize)?),
86        _ => Err(ClientError::UnsupportedResponse),
87    }
88}
89
90fn decode_ctx_object_id(r: &mut Reader<'_>) -> Result<ObjectId, ClientError> {
91    Ok(ObjectId::from_raw(decode_ctx_unsigned(r)?))
92}
93
94fn data_value_to_client(value: rustbac_core::types::DataValue<'_>) -> ClientDataValue {
95    match value {
96        rustbac_core::types::DataValue::Null => ClientDataValue::Null,
97        rustbac_core::types::DataValue::Boolean(v) => ClientDataValue::Boolean(v),
98        rustbac_core::types::DataValue::Unsigned(v) => ClientDataValue::Unsigned(v),
99        rustbac_core::types::DataValue::Signed(v) => ClientDataValue::Signed(v),
100        rustbac_core::types::DataValue::Real(v) => ClientDataValue::Real(v),
101        rustbac_core::types::DataValue::Double(v) => ClientDataValue::Double(v),
102        rustbac_core::types::DataValue::OctetString(v) => ClientDataValue::OctetString(v.to_vec()),
103        rustbac_core::types::DataValue::CharacterString(v) => {
104            ClientDataValue::CharacterString(v.to_string())
105        }
106        rustbac_core::types::DataValue::BitString(v) => ClientDataValue::BitString {
107            unused_bits: v.unused_bits,
108            data: v.data.to_vec(),
109        },
110        rustbac_core::types::DataValue::Enumerated(v) => ClientDataValue::Enumerated(v),
111        rustbac_core::types::DataValue::Date(v) => ClientDataValue::Date(v),
112        rustbac_core::types::DataValue::Time(v) => ClientDataValue::Time(v),
113        rustbac_core::types::DataValue::ObjectId(v) => ClientDataValue::ObjectId(v),
114        rustbac_core::types::DataValue::Constructed { tag_num, values } => {
115            ClientDataValue::Constructed {
116                tag_num,
117                values: values.into_iter().map(data_value_to_client).collect(),
118            }
119        }
120    }
121}