bacnet_server/server/local_access_inputs.rs
1//! The application's route to the inputs of the access-control objects the
2//! server holds (#1132): an Access Point's access events, a Credential Data
3//! Input's reads and an Access Door's hardware state.
4//!
5//! Each takes a typed record whose values change together, as one local
6//! write through `write_local_as`: the object takes the record under the
7//! database guard, then the event pass and the COV fanout run as they do
8//! after any other local write, and the Tokio-runtime rule of #1367 holds.
9//! The records and their rules are in `bacnet_objects::access_control`.
10
11use super::*;
12use bacnet_objects::access_control::{
13 AccessControlInput, AccessEventReport, CredentialReadReport, DoorStateReport,
14};
15
16impl<T: TransportPort + 'static> BACnetServer<T> {
17 /// Report an access event at an Access Point: Access_Event,
18 /// Access_Event_Tag, Access_Event_Time, Access_Event_Credential and
19 /// Access_Event_Authentication_Factor change together (Clause
20 /// 12.31.27.1), checked as `AccessPointObject::set_access_event` checks
21 /// them (VALUE_OUT_OF_RANGE for a credential reference, factor or event
22 /// it refuses; an event is a named BACnetAccessEvent or a proprietary
23 /// one from 512 to 65535). A report without a time is stamped from the
24 /// Device clock.
25 ///
26 /// Access_Event_Time is the point's Table 13-1 trigger, so each event
27 /// with a new time sends the SubscribeCOV report, carrying all five
28 /// values. A time the point stamps itself is strictly after the one it
29 /// served, so several events of one transaction each move the time and
30 /// each report. While Out_Of_Service is TRUE (Clause 12.31.8), or
31 /// Reliability isn't NO_FAULT_DETECTED (Clause 12.31.7), the point
32 /// performs no authentication, so an event is refused with PROPERTY /
33 /// WRITE_ACCESS_DENIED and nothing changes. An unknown object fails
34 /// with OBJECT / UNKNOWN_OBJECT and any object other than an Access
35 /// Point with OBJECT / OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED. The Python
36 /// binding exposes this as `BACnetServer.report_access_event_local`.
37 ///
38 /// Like [`write_local`](Self::write_local), it must be awaited inside a
39 /// Tokio runtime, failing before anything is written outside one, and a
40 /// caller dropped once the event has committed skips none of the work
41 /// it owes (#1367).
42 pub async fn report_access_event_local(
43 &self,
44 oid: &ObjectIdentifier,
45 report: AccessEventReport,
46 ) -> Result<(), Error> {
47 self.report_access_input(oid, AccessControlInput::AccessEvent(report))
48 .await
49 }
50
51 /// Report a factor a Credential Data Input's reader has read:
52 /// Present_Value and Update_Time change together (Clause 12.36.11). The
53 /// factor must name one of the reader's declared formats with its class,
54 /// or be the UNDEFINED or ERROR factor with class 0, else
55 /// VALUE_OUT_OF_RANGE. A report without a time is stamped from the
56 /// Device clock, or the object's next sequence number without a usable
57 /// clock, so reading the same card again still moves Update_Time.
58 ///
59 /// Update_Time is the reader's Table 13-1 trigger, so each read with a
60 /// new time sends the SubscribeCOV report. While Out_Of_Service is TRUE
61 /// a client may be simulating the reader, so the read replaces the
62 /// reader's values put aside, as `set_present_value` does (#1168): the
63 /// served values, and with them the COV report, stay as they are, and
64 /// the return to service serves the latest read. The other errors are
65 /// those of [`Self::report_access_event_local`], for a Credential Data
66 /// Input. The Python binding exposes this as
67 /// `BACnetServer.report_credential_read_local`.
68 ///
69 /// The Tokio-runtime rule of [`write_local`](Self::write_local) applies.
70 pub async fn report_credential_read_local(
71 &self,
72 oid: &ObjectIdentifier,
73 report: CredentialReadReport,
74 ) -> Result<(), Error> {
75 self.report_access_input(oid, AccessControlInput::CredentialRead(report))
76 .await
77 }
78
79 /// Report an Access Door's hardware state: whichever of Door_Status,
80 /// Lock_Status and Door_Alarm_State the report gives, together. Each
81 /// must fall in its production, and the alarm state be NORMAL or a
82 /// member of Alarm_Values or Fault_Values and no member of
83 /// Masked_Alarm_Values, else VALUE_OUT_OF_RANGE and nothing changes.
84 ///
85 /// Door_Alarm_State is the door's Table 13-1 trigger, so a change of it
86 /// sends the SubscribeCOV report, and the event pass after the write
87 /// runs the door's CHANGE_OF_STATE and FAULT_STATE algorithms on it at
88 /// once. Secured_Status follows the new values on its next read. While
89 /// Out_Of_Service is TRUE a client may be simulating the door, so the
90 /// report replaces the device values put aside, as the door's setters do
91 /// (#1131): the served values stay, so no COV report or event follows,
92 /// and the return to service serves the latest values reported. The
93 /// other errors are those of
94 /// [`Self::report_access_event_local`], for an Access Door. The Python
95 /// binding exposes this as `BACnetServer.report_door_state_local`.
96 ///
97 /// The Tokio-runtime rule of [`write_local`](Self::write_local) applies.
98 pub async fn report_door_state_local(
99 &self,
100 oid: &ObjectIdentifier,
101 report: DoorStateReport,
102 ) -> Result<(), Error> {
103 self.report_access_input(oid, AccessControlInput::DoorState(report))
104 .await
105 }
106
107 /// One access-control input, as a local write.
108 async fn report_access_input(
109 &self,
110 oid: &ObjectIdentifier,
111 input: AccessControlInput,
112 ) -> Result<(), Error> {
113 self.write_local_as(
114 oid,
115 LocalWrite::ApplicationAccessInput(&input),
116 PropertyValue::Null,
117 None,
118 )
119 .await
120 }
121}