bacnet_objects/database/event_algorithm_inhibit.rs
1//! Following an Event_Algorithm_Inhibit_Ref (#1329).
2//!
3//! An intrinsic reporter can't see the object its reference names, so the
4//! database reads the property for it. The reference is a
5//! BACnetObjectPropertyReference, which has no device member, so it only
6//! ever names a property of this device; there is nothing remote to read.
7//! The clauses that define the pair (Clause 12.2.31 and its counterparts)
8//! expect a Boolean or a BinaryPV there, map ACTIVE to TRUE and INACTIVE to
9//! FALSE, and take a property that doesn't exist as FALSE.
10//!
11//! A read only says Enumerated, not which enumeration, and an Event_State
12//! of FAULT or a Reliability of NO_SENSOR is Enumerated 1 as well. So the
13//! inhibit is TRUE only for a Boolean TRUE, or for an ACTIVE read from a
14//! property [`holds_binary_pv`] knows as a BinaryPV: Present_Value,
15//! Relinquish_Default, an element of Priority_Array, Alarm_Value and
16//! Feedback_Value on the binary types that have them, and an Access
17//! Credential's Credential_Status. Anything else, a property this device
18//! can't read included, is FALSE.
19//!
20//! The bundled server follows the reference every time it evaluates the
21//! object: the evaluation a write of the object runs, and the one-second
22//! tick every intrinsic reporter gets. A change to the referenced property
23//! therefore reaches Event_Algorithm_Inhibit, and the algorithm, within a
24//! second, and a read of Event_Algorithm_Inhibit returns the value as of
25//! the last evaluation. Both reads happen under the database guard the
26//! evaluation already holds, so following takes no lock of its own.
27
28use bacnet_types::constructed::BACnetObjectPropertyReference;
29use bacnet_types::enums::{BinaryPV, ObjectType, PropertyIdentifier};
30use bacnet_types::primitives::{ObjectIdentifier, PropertyValue};
31
32use super::ObjectDatabase;
33
34impl ObjectDatabase {
35 /// Read the property the object `oid`'s Event_Algorithm_Inhibit_Ref
36 /// names and hand the value over as its Event_Algorithm_Inhibit; returns
37 /// whether that changed. An object with no reference, one that doesn't
38 /// report intrinsically, or one this database doesn't hold is left
39 /// alone.
40 pub fn follow_event_algorithm_inhibit(&mut self, oid: &ObjectIdentifier) -> bool {
41 let Some(reference) = self
42 .objects
43 .get(oid)
44 .and_then(|object| object.event_algorithm_inhibit_reference_internal())
45 else {
46 return false;
47 };
48 let inhibit = self.inhibit_value(&reference);
49 self.objects
50 .get_mut(oid)
51 .is_some_and(|object| object.follow_event_algorithm_inhibit_internal(inhibit))
52 }
53
54 /// The Boolean the referenced property stands for; see the module
55 /// documentation.
56 fn inhibit_value(&self, reference: &BACnetObjectPropertyReference) -> bool {
57 let property = PropertyIdentifier::from_raw(reference.property_identifier);
58 let index = reference.property_array_index;
59 let target = reference.object_identifier;
60 let value = self
61 .get(&target)
62 .and_then(|object| object.read_property(property, index).ok());
63 match value {
64 Some(PropertyValue::Boolean(inhibit)) => inhibit,
65 Some(PropertyValue::Enumerated(raw)) => {
66 raw == BinaryPV::ACTIVE.to_raw()
67 && holds_binary_pv(target.object_type(), property, index)
68 }
69 _ => false,
70 }
71 }
72}
73
74/// Whether `property`, read at `index`, holds a BACnetBinaryPV on an object
75/// of `object_type`: Tables 12-6, 12-8 and 12-10 for the binary types, and
76/// Table 12-40 for an Access Credential's Credential_Status.
77fn holds_binary_pv(
78 object_type: ObjectType,
79 property: PropertyIdentifier,
80 index: Option<u32>,
81) -> bool {
82 use PropertyIdentifier as P;
83 let commanded = property == P::RELINQUISH_DEFAULT
84 || (property == P::PRIORITY_ARRAY && index.is_some_and(|slot| slot > 0));
85 match object_type {
86 ObjectType::BINARY_INPUT => matches!(property, P::PRESENT_VALUE | P::ALARM_VALUE),
87 ObjectType::BINARY_OUTPUT => {
88 matches!(property, P::PRESENT_VALUE | P::FEEDBACK_VALUE) || commanded
89 }
90 ObjectType::BINARY_VALUE => {
91 matches!(property, P::PRESENT_VALUE | P::ALARM_VALUE) || commanded
92 }
93 ObjectType::ACCESS_CREDENTIAL => property == P::CREDENTIAL_STATUS,
94 _ => false,
95 }
96}
97
98#[cfg(test)]
99#[path = "event_algorithm_inhibit_tests.rs"]
100mod tests;