Skip to main content

bacnet_objects/access_control/
mod.rs

1//! Access Control objects (ASHRAE 135-2020 Clause 12).
2//!
3//! This module implements the seven BACnet access control object types:
4//! - AccessDoor (type 30)
5//! - AccessCredential (type 32)
6//! - AccessPoint (type 33)
7//! - AccessRights (type 34)
8//! - AccessUser (type 35)
9//! - AccessZone (type 36)
10//! - CredentialDataInput (type 37)
11
12use bacnet_encoding::primitives::encode_timestamp_choice;
13use bacnet_types::constructed::BACnetDeviceObjectReference;
14use bacnet_types::enums::{
15    AccessEvent, AccessUserType, AuthenticationStatus, BinaryPV, DoorAlarmState, DoorSecuredStatus,
16    DoorStatus, DoorValue, EventState, LockStatus, ObjectType, PropertyIdentifier, Reliability,
17};
18use bacnet_types::error::Error;
19use bacnet_types::primitives::{
20    BACnetTimeStamp, Date, ObjectIdentifier, PropertyValue, StatusFlags, Time,
21};
22use bytes::BytesMut;
23use std::borrow::Cow;
24use std::sync::Arc;
25
26use crate::clock::{current_datetime, ClockReader};
27use crate::common::{self, read_common_properties};
28use crate::traits::BACnetObject;
29
30/// The time stamp an Access Point's Access_Event_Time and a Credential Data
31/// Input's Update_Time hold before their first update: the date-and-time form
32/// with every octet unspecified (Clauses 12.31.29 and 12.36.11).
33fn never_updated() -> BACnetTimeStamp {
34    BACnetTimeStamp::DateTime {
35        date: Date {
36            year: Date::UNSPECIFIED,
37            month: Date::UNSPECIFIED,
38            day: Date::UNSPECIFIED,
39            day_of_week: Date::UNSPECIFIED,
40        },
41        time: Time {
42            hour: Time::UNSPECIFIED,
43            minute: Time::UNSPECIFIED,
44            second: Time::UNSPECIFIED,
45            hundredths: Time::UNSPECIFIED,
46        },
47    }
48}
49
50/// A `BACnetTimeStamp` property in its Clause 21 CHOICE form.
51fn timestamp_value(stamp: &BACnetTimeStamp) -> Result<PropertyValue, Error> {
52    let mut buf = BytesMut::new();
53    encode_timestamp_choice(&mut buf, stamp)?;
54    Ok(PropertyValue::ApplicationData(buf.to_vec()))
55}
56
57/// The BACnetTimeStamp (Clause 21.6) for an update recorded now: the Device
58/// clock's date and time, or `sequence()` in the sequence-number form when
59/// there is no usable clock.
60///
61/// Without a clock every date-and-time stamp would be the unspecified one,
62/// so successive updates would carry the same time and a subscriber watching
63/// that time (Table 13-1) would hear none of them. A sequence number moves on
64/// with each update instead; Clauses 12.31.29 and 12.36.11 both allow an
65/// update time in that form.
66fn update_stamp(
67    clock: Option<&dyn ClockReader>,
68    sequence: impl FnOnce() -> u16,
69) -> BACnetTimeStamp {
70    match current_datetime(clock) {
71        Some((date, time)) => BACnetTimeStamp::DateTime { date, time },
72        None => BACnetTimeStamp::SequenceNumber(sequence()),
73    }
74}
75
76/// The sequence number after `previous`: it climbs from 1 to 65535, the top
77/// of the production's range, and starts again at 1. It never takes 0,
78/// which marks an update time with no update yet (Clauses 12.31.29 and
79/// 12.36.11).
80fn next_sequence(previous: u16) -> u16 {
81    if previous == u16::MAX {
82        1
83    } else {
84        previous + 1
85    }
86}
87
88/// The Device clock an object stamps its update times from, and the
89/// sequence numbers it hands out, in order, while that clock gives no usable
90/// reading (`update_stamp`).
91#[derive(Default)]
92struct UpdateClock {
93    clock: Option<Arc<dyn ClockReader>>,
94    /// The last sequence number handed out; 0 before the first.
95    sequence: u16,
96}
97
98impl UpdateClock {
99    /// Stamp an update made now.
100    fn stamp(&mut self) -> BACnetTimeStamp {
101        let sequence = &mut self.sequence;
102        update_stamp(self.clock.as_deref(), || {
103            *sequence = next_sequence(*sequence);
104            *sequence
105        })
106    }
107}
108
109/// A client's simulated Reliability, written while Out_Of_Service is TRUE:
110/// an Enumerated inside the BACnetReliability production. Another datatype is
111/// INVALID_DATA_TYPE and another number VALUE_OUT_OF_RANGE.
112fn simulated_reliability(value: &PropertyValue) -> Result<Reliability, Error> {
113    let PropertyValue::Enumerated(raw) = value else {
114        return Err(common::invalid_data_type_error());
115    };
116    checked_reliability(Reliability::from_raw(*raw))
117}
118
119/// `reliability`, or VALUE_OUT_OF_RANGE outside the BACnetReliability
120/// production.
121fn checked_reliability(reliability: Reliability) -> Result<Reliability, Error> {
122    if common::is_reliability_value_valid(reliability) {
123        Ok(reliability)
124    } else {
125        Err(common::value_out_of_range_error())
126    }
127}
128
129/// The references a setter of one of the reference lists or arrays stores,
130/// each naming an object of `object_type` in this device or in the Device its
131/// device identifier names. A reference to another object type, or one whose
132/// device identifier isn't a Device (the shared `check_device_member`), is
133/// VALUE_OUT_OF_RANGE, so the setter keeps what it held.
134fn references_to(
135    object_type: ObjectType,
136    references: impl IntoIterator<Item = impl Into<BACnetDeviceObjectReference>>,
137) -> Result<Vec<BACnetDeviceObjectReference>, Error> {
138    let references: Vec<BACnetDeviceObjectReference> =
139        references.into_iter().map(Into::into).collect();
140    for reference in &references {
141        crate::device_reference::check_device_member(reference.device_identifier)?;
142        if reference.object_identifier.object_type() != object_type {
143            return Err(common::value_out_of_range_error());
144        }
145    }
146    Ok(references)
147}
148
149// ---------------------------------------------------------------------------
150
151mod credential;
152mod credential_data_input;
153mod credential_data_input_formats;
154mod credential_data_input_out_of_service;
155mod credential_rules;
156mod door;
157mod door_alarm;
158mod door_out_of_service;
159mod metadata_identity;
160mod metadata_topology;
161mod point;
162mod point_authorization;
163mod rights;
164mod rights_writes;
165mod user;
166mod zone;
167mod zone_occupancy;
168mod zone_out_of_service;
169pub use credential::*;
170pub use credential_data_input::*;
171pub use door::*;
172pub use point::*;
173pub use rights::*;
174pub use user::*;
175pub use zone::*;
176
177#[cfg(test)]
178mod array_tests;
179#[cfg(test)]
180mod constructed_value_tests;
181#[cfg(test)]
182mod credential_data_input_format_tests;
183#[cfg(test)]
184mod credential_data_input_out_of_service_tests;
185#[cfg(test)]
186mod credential_tests;
187#[cfg(test)]
188mod device_reference_tests;
189#[cfg(test)]
190mod door_alarm_tests;
191#[cfg(test)]
192mod door_out_of_service_tests;
193#[cfg(test)]
194mod door_pulse_tests;
195#[cfg(test)]
196mod point_authorization_tests;
197#[cfg(test)]
198mod point_out_of_service_tests;
199#[cfg(test)]
200mod point_status_tests;
201#[cfg(test)]
202mod tests;
203#[cfg(test)]
204mod typed_value_tests;
205#[cfg(test)]
206mod user_references_tests;
207#[cfg(test)]
208mod zone_event_tests;
209#[cfg(test)]
210mod zone_occupancy_tests;
211#[cfg(test)]
212mod zone_out_of_service_tests;
213#[cfg(test)]
214mod zone_points_tests;