1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! The application's route to the inputs of the access-control objects the
//! server holds (#1132): an Access Point's access events, a Credential Data
//! Input's reads and an Access Door's hardware state.
//!
//! Each takes a typed record whose values change together, as one local
//! write through `write_local_as`: the object takes the record under the
//! database guard, then the event pass and the COV fanout run as they do
//! after any other local write, and the Tokio-runtime rule of #1367 holds.
//! The records and their rules are in `bacnet_objects::access_control`.
use super::*;
use bacnet_objects::access_control::{
AccessControlInput, AccessEventReport, CredentialReadReport, DoorStateReport,
};
impl<T: TransportPort + 'static> BACnetServer<T> {
/// 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`](Self::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).
pub async fn report_access_event_local(
&self,
oid: &ObjectIdentifier,
report: AccessEventReport,
) -> Result<(), Error> {
self.report_access_input(oid, AccessControlInput::AccessEvent(report))
.await
}
/// 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`](Self::write_local) applies.
pub async fn report_credential_read_local(
&self,
oid: &ObjectIdentifier,
report: CredentialReadReport,
) -> Result<(), Error> {
self.report_access_input(oid, AccessControlInput::CredentialRead(report))
.await
}
/// 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`](Self::write_local) applies.
pub async fn report_door_state_local(
&self,
oid: &ObjectIdentifier,
report: DoorStateReport,
) -> Result<(), Error> {
self.report_access_input(oid, AccessControlInput::DoorState(report))
.await
}
/// One access-control input, as a local write.
async fn report_access_input(
&self,
oid: &ObjectIdentifier,
input: AccessControlInput,
) -> Result<(), Error> {
self.write_local_as(
oid,
LocalWrite::ApplicationAccessInput(&input),
PropertyValue::Null,
None,
)
.await
}
}