Skip to main content

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;