Skip to main content

bacnet_objects/analog/
mod.rs

1//! Analog Input (type 0), Analog Output (type 1), and Analog Value (type 2) objects.
2//!
3//! Per ASHRAE 135-2020 Clauses 12.2 (AI), 12.3 (AO), and 12.4 (AV).
4
5use bacnet_types::enums::{
6    ErrorClass, ErrorCode, EventState, ObjectType, PropertyIdentifier, Reliability,
7};
8use bacnet_types::error::Error;
9use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, StatusFlags};
10use std::borrow::Cow;
11
12use crate::common::{self, read_common_properties};
13use crate::event::{history::EventHistory, OutOfRangeDetector};
14use crate::rollback::impl_intrinsic_write_rollback;
15use crate::traits::{BACnetObject, ReliabilityEvaluation};
16
17#[derive(Clone, Copy)]
18struct FaultOutOfRangeLimits {
19    low: f32,
20    high: f32,
21}
22
23#[derive(Clone, Copy, PartialEq, Eq)]
24pub(crate) enum OwnedRangeFault {
25    UnderRange,
26    OverRange,
27}
28
29impl OwnedRangeFault {
30    fn reliability(self) -> u32 {
31        match self {
32            Self::UnderRange => Reliability::UNDER_RANGE.to_raw(),
33            Self::OverRange => Reliability::OVER_RANGE.to_raw(),
34        }
35    }
36}
37
38#[derive(Default)]
39struct FaultOutOfRangeState {
40    // Deliberately separate from the intrinsic OutOfRangeDetector: reliability
41    // has strict boundaries, no deadband, and first-stage ownership precedence.
42    limits: Option<FaultOutOfRangeLimits>,
43    owned_fault: Option<OwnedRangeFault>,
44}
45
46impl FaultOutOfRangeState {
47    fn configure(&mut self, low: f32, high: f32) -> Result<(), Error> {
48        if !low.is_finite() || !high.is_finite() || low > high {
49            return Err(common::value_out_of_range_error());
50        }
51        self.limits = Some(FaultOutOfRangeLimits { low, high });
52        Ok(())
53    }
54
55    fn read_limit(&self, property: PropertyIdentifier) -> Option<PropertyValue> {
56        let limits = self.limits?;
57        match property {
58            PropertyIdentifier::FAULT_LOW_LIMIT => Some(PropertyValue::Real(limits.low)),
59            PropertyIdentifier::FAULT_HIGH_LIMIT => Some(PropertyValue::Real(limits.high)),
60            _ => None,
61        }
62    }
63
64    fn property_list(
65        &self,
66        base: &'static [PropertyIdentifier],
67    ) -> Cow<'static, [PropertyIdentifier]> {
68        if self.limits.is_none() {
69            return Cow::Borrowed(base);
70        }
71        let mut properties = Vec::with_capacity(base.len() + 2);
72        properties.extend_from_slice(base);
73        properties.extend([
74            PropertyIdentifier::FAULT_HIGH_LIMIT,
75            PropertyIdentifier::FAULT_LOW_LIMIT,
76        ]);
77        Cow::Owned(properties)
78    }
79
80    fn clear_ownership(&mut self) {
81        self.owned_fault = None;
82    }
83
84    fn evaluate(
85        &mut self,
86        monitored_value: f32,
87        reliability: &mut u32,
88    ) -> Result<ReliabilityEvaluation, Error> {
89        let Some(limits) = self.limits else {
90            return Ok(ReliabilityEvaluation::Unchanged);
91        };
92        if !monitored_value.is_finite() {
93            return Err(common::value_out_of_range_error());
94        }
95        let observed_fault = if monitored_value < limits.low {
96            Some(OwnedRangeFault::UnderRange)
97        } else if monitored_value > limits.high {
98            Some(OwnedRangeFault::OverRange)
99        } else {
100            None
101        };
102
103        let (new_reliability, new_owner) = if self.owned_fault.is_some() {
104            (
105                observed_fault
106                    .map(OwnedRangeFault::reliability)
107                    .unwrap_or_else(|| Reliability::NO_FAULT_DETECTED.to_raw()),
108                observed_fault,
109            )
110        } else if *reliability == Reliability::NO_FAULT_DETECTED.to_raw() {
111            let Some(fault) = observed_fault else {
112                return Ok(ReliabilityEvaluation::Unchanged);
113            };
114            (fault.reliability(), Some(fault))
115        } else {
116            return Ok(ReliabilityEvaluation::Unchanged);
117        };
118
119        if new_reliability == *reliability {
120            return Ok(ReliabilityEvaluation::Unchanged);
121        }
122        let old_reliability = *reliability;
123        *reliability = new_reliability;
124        self.owned_fault = new_owner;
125        Ok(ReliabilityEvaluation::Changed {
126            old_reliability,
127            new_reliability,
128        })
129    }
130}
131
132mod input;
133mod output;
134mod value;
135pub use input::*;
136pub use output::*;
137pub use value::*;
138
139// ---------------------------------------------------------------------------
140// Tests
141// ---------------------------------------------------------------------------
142
143#[cfg(test)]
144mod tests;