bacnet_types/constructed/mod.rs
1//! BACnet constructed data types per ASHRAE 135-2020.
2//!
3//! This module provides compound/structured types that are used by higher-level
4//! BACnet objects (Calendar, Schedule, TrendLog, NotificationClass, Loop, etc.).
5//! All types follow the same `no_std`-compatible pattern used in `primitives.rs`.
6
7#[cfg(not(feature = "std"))]
8use alloc::{string::String, vec::Vec};
9
10use crate::bitstring::{DaysOfWeek, EventTransitionBits};
11use crate::enums::LifeSafetyState;
12use crate::error::Error;
13use crate::primitives::{Date, ObjectIdentifier, PropertyValue, Time};
14use crate::MacAddr;
15
16mod action;
17pub use action::{BACnetActionCommand, BACnetActionList};
18mod access;
19pub use access::{
20 BACnetAccessRule, BACnetAssignedAccessRights, BACnetAuthenticationFactor,
21 BACnetAuthenticationFactorFormat, BACnetAuthenticationPolicy, BACnetAuthenticationPolicyEntry,
22 BACnetCredentialAuthenticationFactor,
23};
24mod audit;
25pub use audit::{
26 AuditPropertyReference, BACnetAuditLogDatum, BACnetAuditLogQueryParameters,
27 BACnetAuditLogRecord, BACnetAuditLogRecordResult, BACnetAuditNotification,
28 BACnetObjectSelector,
29};
30mod color;
31pub use color::{BACnetColorCommand, BACnetXyColor};
32mod event_notification;
33pub use event_notification::{
34 ChangeOfValueChoice, EventNotificationRequest, NotificationParameters,
35};
36mod lift;
37pub use lift::{
38 AssignedLandingCall, BACnetAssignedLandingCalls, BACnetLandingCallStatus,
39 BACnetLandingDoorStatus, BACnetLiftCarCallList, LandingCallCommand, LandingDoor,
40};
41mod lighting;
42pub use lighting::BACnetLightingCommand;
43mod log;
44pub use log::{
45 BACnetEventLogRecord, BACnetLogMultipleRecord, BACnetLogRecord, EventLogDatum, LogData,
46 LogDatum, LogValue,
47};
48mod name_value;
49pub use name_value::BACnetNameValue;
50mod network_port;
51pub use network_port::{BACnetBDTEntry, BACnetFDTEntry, BACnetHostAddress, BACnetHostNPort};
52mod property_access;
53pub use property_access::{AccessResult, BACnetPropertyAccessResult};
54mod property_value;
55pub use property_value::BACnetPropertyValue;
56mod read_access;
57pub use read_access::{PropertyReference, ReadAccessSpecification};
58mod staging;
59pub use staging::BACnetStageLimitValue;
60
61// ---------------------------------------------------------------------------
62// BACnetDateRange (Clause 21 -- used by CalendarEntry and BACnetSpecialEvent)
63// ---------------------------------------------------------------------------
64
65/// BACnet date range: a SEQUENCE of start and end Date values.
66///
67/// On the wire each Date is application-tagged; the codec is
68/// `bacnet_encoding::constructed::{encode_date_range, decode_date_range}`.
69/// An unspecified start or end date leaves that side of the range open.
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct BACnetDateRange {
72 /// The start of the date range (inclusive).
73 pub start_date: Date,
74 /// The end of the date range (inclusive).
75 pub end_date: Date,
76}
77
78// ---------------------------------------------------------------------------
79// BACnetWeekNDay (Clause 21 -- used by CalendarEntry)
80// ---------------------------------------------------------------------------
81
82/// BACnet Week-And-Day: OCTET STRING(3) encoding month, week_of_month,
83/// and day_of_week.
84///
85/// Each field may be `0xFF` to mean "any" (wildcard).
86///
87/// - `month`: 1-12, 13=odd months, 14=even months, 0xFF=any
88/// - `week_of_month`: 1-5 = the days numbered 1-7, 8-14, 15-21, 22-28 and
89/// 29-31; 6 = the last 7 days of the month; 7, 8 and 9 = the 7 days before
90/// the last 7, 14 and 21 days; 0xFF=any
91/// - `day_of_week`: 1=Monday..7=Sunday, 0xFF=any
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct BACnetWeekNDay {
94 /// Month (1-14, or 0xFF for any).
95 pub month: u8,
96 /// Week of month (1-9, or 0xFF for any).
97 pub week_of_month: u8,
98 /// Day of week (1-7, or 0xFF for any).
99 pub day_of_week: u8,
100}
101
102impl BACnetWeekNDay {
103 /// Wildcard value indicating "any" for any field.
104 pub const ANY: u8 = 0xFF;
105
106 /// Encode to 3 bytes.
107 pub fn encode(&self) -> [u8; 3] {
108 [self.month, self.week_of_month, self.day_of_week]
109 }
110
111 /// Decode from at least 3 bytes.
112 pub fn decode(data: &[u8]) -> Result<Self, Error> {
113 if data.len() < 3 {
114 return Err(Error::buffer_too_short(3, data.len()));
115 }
116 Ok(Self {
117 month: data[0],
118 week_of_month: data[1],
119 day_of_week: data[2],
120 })
121 }
122}
123
124// ---------------------------------------------------------------------------
125// BACnetCalendarEntry (Clause 21.6 -- Calendar Date_List; Calendar object Clause 12.9)
126// ---------------------------------------------------------------------------
127
128/// BACnet calendar entry: a CHOICE between a specific date, a date range,
129/// or a week-and-day pattern.
130///
131/// Context tags per spec:
132/// - `[0]` Date
133/// - `[1]` DateRange
134/// - `[2]` WeekNDay
135///
136/// The wire codec is `bacnet_encoding::constructed::{encode_calendar_entry,
137/// decode_calendar_entry}`.
138#[derive(Debug, Clone, PartialEq, Eq)]
139pub enum BACnetCalendarEntry {
140 /// A single specific date (context tag 0).
141 Date(Date),
142 /// A contiguous date range (context tag 1).
143 DateRange(BACnetDateRange),
144 /// A recurring week-and-day pattern (context tag 2).
145 WeekNDay(BACnetWeekNDay),
146}
147
148// ---------------------------------------------------------------------------
149// BACnetTimeValue (Clause 12.24 -- Schedule Weekly_Schedule; Clause 21.6)
150// ---------------------------------------------------------------------------
151
152/// BACnet time-value pair: a Time followed by a value of any primitive
153/// datatype.
154///
155/// The value is typed, so a Schedule writes it with its own datatype. Only
156/// primitive values ([`PropertyValue::is_primitive`]) belong here; the codec
157/// refuses to encode a `List` or `ApplicationData`.
158#[derive(Debug, Clone, PartialEq)]
159pub struct BACnetTimeValue {
160 /// The time at which the value applies.
161 pub time: Time,
162 /// The value: NULL, BOOLEAN, Unsigned, INTEGER, REAL, Double, OCTET
163 /// STRING, CharacterString, BIT STRING, ENUMERATED, Date, Time or
164 /// BACnetObjectIdentifier.
165 pub value: PropertyValue,
166}
167
168// ---------------------------------------------------------------------------
169// SpecialEventPeriod (Clause 12.24 -- Schedule Exception_Schedule; Clause 21.6)
170// ---------------------------------------------------------------------------
171
172/// The period portion of a BACnetSpecialEvent: either an inline
173/// CalendarEntry or a reference to an existing Calendar object.
174///
175/// Context tags per spec:
176/// - `[0]` CalendarEntry (constructed)
177/// - `[1]` CalendarReference (ObjectIdentifier)
178#[derive(Debug, Clone, PartialEq, Eq)]
179pub enum SpecialEventPeriod {
180 /// An inline calendar entry (context tag 0).
181 CalendarEntry(BACnetCalendarEntry),
182 /// A reference to a Calendar object (context tag 1).
183 CalendarReference(ObjectIdentifier),
184}
185
186// ---------------------------------------------------------------------------
187// BACnetSpecialEvent (Clause 12.24 -- Schedule Exception_Schedule; Clause 21.6)
188// ---------------------------------------------------------------------------
189
190/// BACnet special event: an exception schedule entry combining a period
191/// definition, a list of time-value pairs, and a priority.
192#[derive(Debug, Clone, PartialEq)]
193pub struct BACnetSpecialEvent {
194 /// The period this special event applies to.
195 pub period: SpecialEventPeriod,
196 /// Ordered list of time-value pairs to apply during this period.
197 pub list_of_time_values: Vec<BACnetTimeValue>,
198 /// Priority for conflict resolution, 1 (highest) to 16 (lowest).
199 ///
200 /// Held as the full Unsigned the wire can carry, so a decoded event keeps
201 /// a priority outside that range intact for its consumer to refuse; the
202 /// Schedule object answers such a value with VALUE_OUT_OF_RANGE.
203 pub event_priority: u64,
204}
205
206// ---------------------------------------------------------------------------
207// BACnetObjectPropertyReference (Clause 21 -- used by Loop and others)
208// ---------------------------------------------------------------------------
209
210/// A reference to a specific property (and optionally an array index) on
211/// a specific object within the same device.
212#[derive(Debug, Clone, PartialEq, Eq)]
213pub struct BACnetObjectPropertyReference {
214 /// The object being referenced.
215 pub object_identifier: ObjectIdentifier,
216 /// The property being referenced (PropertyIdentifier raw value).
217 pub property_identifier: u32,
218 /// Optional array index within the property.
219 pub property_array_index: Option<u32>,
220}
221
222impl BACnetObjectPropertyReference {
223 /// Create a reference without an array index.
224 pub fn new(object_identifier: ObjectIdentifier, property_identifier: u32) -> Self {
225 Self {
226 object_identifier,
227 property_identifier,
228 property_array_index: None,
229 }
230 }
231
232 /// Create a reference with an array index.
233 pub fn new_indexed(
234 object_identifier: ObjectIdentifier,
235 property_identifier: u32,
236 array_index: u32,
237 ) -> Self {
238 Self {
239 object_identifier,
240 property_identifier,
241 property_array_index: Some(array_index),
242 }
243 }
244
245 /// Whether the reference names nothing: its object is at the reserved
246 /// instance 4194303, which Clause 12.1 lets an identifier hold to mean
247 /// uninitialized or unused. A property of this type has no empty
248 /// encoding, so the stack serves an unset one in this form (#1417).
249 pub fn is_unset(&self) -> bool {
250 self.object_identifier.instance_number() == ObjectIdentifier::WILDCARD_INSTANCE
251 }
252}
253
254// ---------------------------------------------------------------------------
255// BACnetDeviceObjectPropertyReference (Clause 21 -- used by several objects)
256// ---------------------------------------------------------------------------
257
258/// Like `BACnetObjectPropertyReference` but may also specify a remote device.
259///
260/// When `device_identifier` is `None`, the reference is to an object in the
261/// local device.
262#[derive(Debug, Clone, PartialEq, Eq)]
263pub struct BACnetDeviceObjectPropertyReference {
264 /// The object being referenced.
265 pub object_identifier: ObjectIdentifier,
266 /// The property being referenced (PropertyIdentifier raw value).
267 pub property_identifier: u32,
268 /// Optional array index within the property.
269 pub property_array_index: Option<u32>,
270 /// Optional device identifier (None = local device).
271 pub device_identifier: Option<ObjectIdentifier>,
272}
273
274impl BACnetDeviceObjectPropertyReference {
275 /// Create a local-device reference without an array index.
276 pub fn new_local(object_identifier: ObjectIdentifier, property_identifier: u32) -> Self {
277 Self {
278 object_identifier,
279 property_identifier,
280 property_array_index: None,
281 device_identifier: None,
282 }
283 }
284
285 /// Create a remote-device reference without an array index.
286 pub fn new_remote(
287 object_identifier: ObjectIdentifier,
288 property_identifier: u32,
289 device_identifier: ObjectIdentifier,
290 ) -> Self {
291 Self {
292 object_identifier,
293 property_identifier,
294 property_array_index: None,
295 device_identifier: Some(device_identifier),
296 }
297 }
298
299 /// Create a reference with an array index (may be local or remote).
300 pub fn with_index(mut self, array_index: u32) -> Self {
301 self.property_array_index = Some(array_index);
302 self
303 }
304
305 /// Whether the device identifier is absent or names a Device object,
306 /// the rule [`BACnetDeviceObjectReference::device_identifier_is_device`]
307 /// applies to that type's device member.
308 pub fn device_identifier_is_device(&self) -> bool {
309 device_identifier_is_device(self.device_identifier)
310 }
311
312 /// Whether the reference names nothing: its object, or its Device when
313 /// it has one, is at the reserved instance 4194303 (Clause 12.1), the
314 /// rule Clause 12.30.11 gives an empty Trend Log Multiple element. A
315 /// property of this type has no empty encoding, so the stack serves an
316 /// unset one in this form (#1417).
317 pub fn is_unset(&self) -> bool {
318 let reserved =
319 |oid: ObjectIdentifier| oid.instance_number() == ObjectIdentifier::WILDCARD_INSTANCE;
320 reserved(self.object_identifier) || self.device_identifier.is_some_and(reserved)
321 }
322}
323
324/// Whether an optional device member is absent or a Device object
325/// identifier, the only object type the member of a device-qualified
326/// reference can hold (Clause 21). The `device_identifier_is_device` methods
327/// of both reference types apply it, and so does every check the objects
328/// and the Python bindings run on a device member.
329pub fn device_identifier_is_device(device: Option<ObjectIdentifier>) -> bool {
330 device.is_none_or(|device| device.object_type() == crate::enums::ObjectType::DEVICE)
331}
332
333// ---------------------------------------------------------------------------
334// BACnetAddress (Clause 21 -- network address used by BACnetRecipient)
335// ---------------------------------------------------------------------------
336
337/// A BACnet network address: network number (0 = local) plus MAC address.
338#[derive(Debug, Clone, PartialEq, Eq)]
339pub struct BACnetAddress {
340 /// Network number (0 = local network, 1-65534 = remote, 65535 = broadcast).
341 pub network_number: u16,
342 /// MAC-layer address (variable length, at most [`Self::MAX_MAC_LEN`] on the
343 /// wire; empty = broadcast).
344 pub mac_address: MacAddr,
345}
346
347impl BACnetAddress {
348 /// The longest `mac_address`, in octets, that this stack encodes or decodes
349 /// in any `BACnetAddress` (#1124, #1156), and of any DADR or SADR the
350 /// network layer encodes or decodes (#1141).
351 ///
352 /// Clause 21 puts no length on the OCTET STRING, but a MAC is only useful
353 /// if it names a node on some data link. Table 6-2 gives the network-layer
354 /// address length of each standard data link, and the longest there is 7
355 /// octets (a LonTalk Neuron_ID destination). The data links this stack
356 /// serves address a node in at most 6 octets: 1 for MS/TP, 3 for B/IPv6
357 /// (its VMAC, U.1.3) and 6 for B/IP, Ethernet and B/SC. The bound stays at
358 /// 18, the length of a B/IPv6 address (16-octet IPv6 address and 2-octet
359 /// UDP port), which the stack once used as the B/IPv6 MAC: it is a
360 /// defensive ceiling, and lowering it is a decision of its own. A longer
361 /// MAC names no node on any data link the stack serves, nor on a standard
362 /// one behind a router.
363 ///
364 /// Every codec for this type in bacnet-encoding holds to the bound: the
365 /// recipient, ValueSource and AuditLogQuery decoders refuse a longer MAC
366 /// and their encoders refuse to write one. The local setters that store a
367 /// configured recipient (a Recipient_List destination or the
368 /// Audit_Notification_Recipient) refuse one too, so a stored recipient
369 /// always decodes again. The NPDU codec refuses a longer DLEN or SLEN
370 /// (`NpduAddress::MAX_MAC_LEN` in bacnet-encoding), and bacnet-network's
371 /// `NetworkLayer` and `BACnetRouter` drop a frame whose link-layer source
372 /// MAC is longer (#1198), so the source addresses the stack learns off the
373 /// network, which COV subscription lists and audit records report, fit the
374 /// bound as well. So does the device MAC a You-Are request assigns (#1200).
375 pub const MAX_MAC_LEN: usize = 18;
376
377 /// Create a local-broadcast address.
378 pub fn local_broadcast() -> Self {
379 Self {
380 network_number: 0,
381 mac_address: MacAddr::new(),
382 }
383 }
384
385 /// Create a BACnet/IP address from a 6-byte octet-string (4-byte IPv4 + 2-byte port).
386 pub fn from_ip(ip_port_bytes: [u8; 6]) -> Self {
387 Self {
388 network_number: 0,
389 mac_address: MacAddr::from_slice(&ip_port_bytes),
390 }
391 }
392}
393
394// ---------------------------------------------------------------------------
395// BACnetRecipient (Clause 21 -- used by BACnetDestination / NotificationClass)
396// ---------------------------------------------------------------------------
397
398/// A BACnet notification recipient: either a Device object reference or a
399/// network address.
400///
401/// Context tags per spec:
402/// - `[0]` Device (ObjectIdentifier)
403/// - `[1]` Address (BACnetAddress)
404#[derive(Debug, Clone, PartialEq, Eq)]
405pub enum BACnetRecipient {
406 /// A device identified by its Object Identifier (context tag 0).
407 Device(ObjectIdentifier),
408 /// A device identified by its network address (context tag 1).
409 Address(BACnetAddress),
410}
411
412// ---------------------------------------------------------------------------
413// BACnetDestination -- notification recipient, schedule, and delivery options
414// ---------------------------------------------------------------------------
415
416/// A single entry in a NotificationClass recipient list.
417///
418/// Specifies *who* receives the notification, *when* (days/times), and *how*
419/// (confirmed vs. unconfirmed, which transition types).
420#[derive(Debug, Clone, PartialEq, Eq)]
421pub struct BACnetDestination {
422 /// Weekdays when this destination accepts notifications.
423 pub valid_days: DaysOfWeek,
424 /// Start of the daily time window during which this destination is active.
425 pub from_time: Time,
426 /// End of the daily time window.
427 pub to_time: Time,
428 /// The notification recipient.
429 pub recipient: BACnetRecipient,
430 /// Process identifier on the receiving device.
431 pub process_identifier: u32,
432 /// If true, use ConfirmedEventNotification; otherwise unconfirmed.
433 pub issue_confirmed_notifications: bool,
434 /// Event transitions whose notifications this destination receives.
435 pub transitions: EventTransitionBits,
436}
437
438// ---------------------------------------------------------------------------
439// BACnetEventNotificationSubscription (Clause 21)
440// ---------------------------------------------------------------------------
441
442/// One entry of a Notification Forwarder's Subscribed_Recipients (Clause
443/// 12.51.9): a recipient that asked for forwarded notifications for a limited
444/// time.
445///
446/// The recipient and process identifier identify the entry: the list services
447/// match on those two members alone.
448#[derive(Debug, Clone, PartialEq, Eq)]
449pub struct BACnetEventNotificationSubscription {
450 /// Device or address that receives the notifications (`[0]`).
451 pub recipient: BACnetRecipient,
452 /// Process on the recipient that receives them (`[1]`).
453 pub process_identifier: u32,
454 /// `true` for confirmed notifications, `false` for unconfirmed ones (`[2]`).
455 pub issue_confirmed_notifications: bool,
456 /// Minutes left before the entry lapses (`[3]`). Unlike COV subscription
457 /// lifetimes this counts minutes, not seconds.
458 pub time_remaining: u32,
459}
460
461// ---------------------------------------------------------------------------
462// BACnetPortPermission (Clause 21)
463// ---------------------------------------------------------------------------
464
465/// One element of a Notification Forwarder's Port_Filter (Clause 12.51.11):
466/// whether notifications that arrive through one network port are forwarded.
467#[derive(Debug, Clone, Copy, PartialEq, Eq)]
468pub struct BACnetPortPermission {
469 /// The port, by its Clause 6 port ID; 0 on a node that does not route
470 /// (`[0]`).
471 pub port_id: u8,
472 /// `true` when notifications received through the port are forwarded
473 /// (`[1]`).
474 pub enabled: bool,
475}
476
477// ---------------------------------------------------------------------------
478// BACnetScale (Clause 21)
479// ---------------------------------------------------------------------------
480
481/// BACnet Scale: CHOICE { float-scale \[0\] Real, integer-scale \[1\] Integer }.
482#[derive(Debug, Clone, PartialEq)]
483pub enum BACnetScale {
484 /// Present_Value is multiplied by this factor to get engineering units.
485 FloatScale(f32),
486 /// Present_Value is multiplied by ten raised to this power to get engineering units.
487 IntegerScale(i32),
488}
489
490// ---------------------------------------------------------------------------
491// BACnetPrescale (Clause 21)
492// ---------------------------------------------------------------------------
493
494/// BACnet Prescale: SEQUENCE { multiplier Unsigned, modulo-divide Unsigned }.
495#[derive(Debug, Clone, PartialEq, Eq)]
496pub struct BACnetPrescale {
497 /// Numerator of the pulse-to-value conversion ratio, added to the accumulator per input pulse.
498 pub multiplier: u32,
499 /// Denominator of the conversion ratio; each time the accumulator reaches it, the value steps
500 /// by one.
501 pub modulo_divide: u32,
502}
503
504// ---------------------------------------------------------------------------
505// BACnetShedLevel (Clause 21 — used by LoadControl)
506// ---------------------------------------------------------------------------
507
508/// A Load Control shed level (`BACnetShedLevel`, Clause 21): the datatype of
509/// Requested_Shed_Level, Expected_Shed_Level and Actual_Shed_Level
510/// (Clause 12.28).
511///
512/// On the wire each alternative is one primitive context tag: percent `[0]`,
513/// level `[1]`, amount `[2]`. The codec is
514/// `bacnet_encoding::constructed::{encode_shed_level, decode_shed_level}`.
515#[derive(Debug, Clone, PartialEq)]
516pub enum BACnetShedLevel {
517 /// The load to run at, as a percentage of the baseline (Unsigned).
518 Percent(u64),
519 /// A preconfigured shed level (Unsigned). Level 0 means no shed.
520 Level(u64),
521 /// Kilowatts to take off the baseline (REAL).
522 Amount(f32),
523}
524
525// ---------------------------------------------------------------------------
526// BACnetDeviceObjectReference (Clause 21 -- used by Access Control objects)
527// ---------------------------------------------------------------------------
528
529/// BACnet Device Object Reference.
530///
531/// Clause 21 encodes the optional device identifier as context `[0]` followed
532/// by the required object identifier as context `[1]`. `None` identifies a
533/// local object, including Staging target references.
534#[derive(Debug, Clone, PartialEq, Eq)]
535pub struct BACnetDeviceObjectReference {
536 /// Optional device identifier (None = local device).
537 pub device_identifier: Option<ObjectIdentifier>,
538 /// The object being referenced.
539 pub object_identifier: ObjectIdentifier,
540}
541
542impl BACnetDeviceObjectReference {
543 /// Whether the device identifier is absent or names a Device object.
544 ///
545 /// The Clause 21 production gives the first member to the Device that
546 /// holds the object, so an identifier of any other object type can't make
547 /// a valid reference. Setters and write paths that take these references
548 /// refuse one that fails this check.
549 pub fn device_identifier_is_device(&self) -> bool {
550 device_identifier_is_device(self.device_identifier)
551 }
552}
553
554impl From<ObjectIdentifier> for BACnetDeviceObjectReference {
555 /// A reference to an object in this device: no device identifier.
556 fn from(object_identifier: ObjectIdentifier) -> Self {
557 Self {
558 device_identifier: None,
559 object_identifier,
560 }
561 }
562}
563
564// ---------------------------------------------------------------------------
565// FaultParameters (Clause 12.12 -- Fault_Parameters of Event Enrollment)
566// ---------------------------------------------------------------------------
567
568/// Fault parameter variants for configuring fault detection algorithms.
569#[derive(Debug, Clone, PartialEq)]
570pub enum FaultParameters {
571 /// No fault detection.
572 FaultNone,
573 /// Fault on characterstring match.
574 FaultCharacterString {
575 /// Strings that, when the monitored value matches one, indicate a fault.
576 fault_values: Vec<String>,
577 },
578 /// Vendor-defined fault algorithm.
579 FaultExtended {
580 /// Vendor identifier that owns the extended algorithm.
581 vendor_id: u16,
582 /// Vendor-defined identifier of the fault algorithm.
583 extended_fault_type: u32,
584 /// Pre-encoded, vendor-specific parameter bytes carried opaquely.
585 parameters: Vec<u8>,
586 },
587 /// Fault on life safety state match.
588 FaultLifeSafety {
589 /// Life safety states that indicate a fault.
590 fault_values: Vec<LifeSafetyState>,
591 /// Reference to the mode property consulted when evaluating these states.
592 mode_property_reference: BACnetDeviceObjectPropertyReference,
593 },
594 /// Fault on property state match.
595 FaultState {
596 /// Property states that indicate a fault when the monitored property takes one of them.
597 fault_values: Vec<BACnetPropertyStates>,
598 },
599 /// Fault on status flags change.
600 FaultStatusFlags {
601 /// Reference to the StatusFlags property whose FAULT bit is monitored.
602 reference: BACnetDeviceObjectPropertyReference,
603 },
604 /// Fault when value exceeds range.
605 FaultOutOfRange {
606 /// Lower bound of the normal range; values below it are a fault.
607 min_normal: f64,
608 /// Upper bound of the normal range; values above it are a fault.
609 max_normal: f64,
610 },
611 /// Fault from listed reference.
612 FaultListed {
613 /// BACnetLIST property whose entries carry the fault indications to watch.
614 reference: BACnetDeviceObjectPropertyReference,
615 },
616}
617
618// ---------------------------------------------------------------------------
619// BACnetRecipientProcess (Clause 21)
620// ---------------------------------------------------------------------------
621
622/// BACnet Recipient Process — a recipient with an associated process identifier.
623#[derive(Debug, Clone, PartialEq)]
624pub struct BACnetRecipientProcess {
625 /// Device or address that receives the notifications.
626 pub recipient: BACnetRecipient,
627 /// Process on the recipient that asked for the notifications; echoed back in each one.
628 pub process_identifier: u32,
629}
630
631// ---------------------------------------------------------------------------
632// BACnetCOVSubscription (Clause 21)
633// ---------------------------------------------------------------------------
634
635/// BACnet COV Subscription — represents an active COV subscription.
636///
637/// The `monitored_property_reference` is a `BACnetObjectPropertyReference`
638/// (object + property + optional index).
639#[derive(Debug, Clone, PartialEq)]
640pub struct BACnetCOVSubscription {
641 /// Subscriber (device or address plus process identifier) that receives the notifications.
642 pub recipient: BACnetRecipientProcess,
643 /// Object and property being watched.
644 pub monitored_property_reference: BACnetObjectPropertyReference,
645 /// `true` for confirmed notifications, `false` for unconfirmed ones.
646 pub issue_confirmed_notifications: bool,
647 /// Seconds left before the subscription lapses; 0 means it never lapses.
648 pub time_remaining: u32,
649 /// COV increment in use for a numeric monitored property: the requested one, else the
650 /// object's COV_Increment. `None` when the monitored property isn't numeric.
651 pub cov_increment: Option<f32>,
652}
653
654// ---------------------------------------------------------------------------
655// BACnetCOVMultipleSubscription (Clause 21)
656// ---------------------------------------------------------------------------
657
658/// One COV-multiple context of Device `Active_COV_Multiple_Subscriptions`:
659/// a recipient and notification form with one remaining lifetime and maximum
660/// notification delay shared by every nested COV reference.
661#[derive(Debug, Clone, PartialEq)]
662pub struct BACnetCOVMultipleSubscription {
663 /// COV-client address and subscriber process identifier (`[0]`).
664 pub recipient: BACnetRecipientProcess,
665 /// Notification form of this context (`[1]`).
666 pub issue_confirmed_notifications: bool,
667 /// Remaining context lifetime in seconds (`[2]`).
668 pub time_remaining: u32,
669 /// Maximum notification delay in seconds (`[3]`).
670 pub max_notification_delay: u32,
671 /// Monitored objects and their COV references (`[4]`).
672 pub list_of_cov_subscription_specifications: Vec<BACnetCOVSubscriptionSpecification>,
673}
674
675/// The COV references a COV-multiple context holds for one monitored object.
676#[derive(Debug, Clone, PartialEq)]
677pub struct BACnetCOVSubscriptionSpecification {
678 /// Monitored object (`[0]`).
679 pub monitored_object_identifier: ObjectIdentifier,
680 /// COV references on that object (`[1]`).
681 pub list_of_cov_references: Vec<BACnetCOVReference>,
682}
683
684/// One monitored property (a `BACnetPropertyReference`) of a COV-multiple
685/// subscription specification.
686#[derive(Debug, Clone, PartialEq)]
687pub struct BACnetCOVReference {
688 /// Monitored property (`[0]` property identifier).
689 pub property_identifier: crate::enums::PropertyIdentifier,
690 /// Optional array index; absence and zero are distinct.
691 pub property_array_index: Option<u32>,
692 /// COV increment in use (`[1]`), present for numeric monitored values.
693 pub cov_increment: Option<f32>,
694 /// Whether notifications carry the time of change (`[2]`).
695 pub timestamped: bool,
696}
697
698// ---------------------------------------------------------------------------
699// BACnetValueSource (Clause 21)
700// ---------------------------------------------------------------------------
701
702/// BACnet Value Source — identifies the source of a property value write.
703#[derive(Debug, Clone, PartialEq)]
704pub enum BACnetValueSource {
705 /// No identified source: context \[0\] NULL.
706 None,
707 /// Source object, optionally qualified by a device: constructed context \[1\].
708 Object(BACnetDeviceObjectReference),
709 /// Source network/MAC address: constructed context \[2\].
710 Address(BACnetAddress),
711}
712
713// ---------------------------------------------------------------------------
714// BACnetEventParameter -- Event_Parameters algorithm alternatives
715// ---------------------------------------------------------------------------
716
717mod event_parameter;
718pub use event_parameter::{event_parameter_tag, BACnetEventParameter, ChangeOfValueCriteria};
719
720mod property_states;
721pub use property_states::{
722 BACnetExtendedPropertyState, BACnetPropertyStates, BACnetProprietaryPropertyState,
723};
724
725// ---------------------------------------------------------------------------
726// Tests
727// ---------------------------------------------------------------------------
728
729#[cfg(test)]
730mod tests;