use bacnet_types::enums::{
ErrorClass, ErrorCode, EventState, ObjectType, PropertyIdentifier, Reliability,
};
use bacnet_types::error::Error;
use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, StatusFlags};
use std::borrow::Cow;
use crate::common::{self, read_common_properties};
use crate::event::{history::EventHistory, OutOfRangeDetector};
use crate::traits::{BACnetObject, ReliabilityEvaluation};
#[derive(Clone, Copy)]
struct FaultOutOfRangeLimits {
low: f32,
high: f32,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum OwnedRangeFault {
UnderRange,
OverRange,
}
impl OwnedRangeFault {
fn reliability(self) -> Reliability {
match self {
Self::UnderRange => Reliability::UNDER_RANGE,
Self::OverRange => Reliability::OVER_RANGE,
}
}
}
#[derive(Default)]
struct FaultOutOfRangeState {
limits: Option<FaultOutOfRangeLimits>,
owned_fault: Option<OwnedRangeFault>,
}
impl FaultOutOfRangeState {
fn configure(&mut self, low: f32, high: f32) -> Result<(), Error> {
if !low.is_finite() || !high.is_finite() || low > high {
return Err(common::value_out_of_range_error());
}
self.limits = Some(FaultOutOfRangeLimits { low, high });
Ok(())
}
fn read_limit(&self, property: PropertyIdentifier) -> Option<PropertyValue> {
let limits = self.limits?;
match property {
PropertyIdentifier::FAULT_LOW_LIMIT => Some(PropertyValue::Real(limits.low)),
PropertyIdentifier::FAULT_HIGH_LIMIT => Some(PropertyValue::Real(limits.high)),
_ => None,
}
}
fn clear_ownership(&mut self) {
self.owned_fault = None;
}
fn evaluate(
&mut self,
monitored_value: f32,
reliability: &mut Reliability,
) -> Result<ReliabilityEvaluation, Error> {
let Some(limits) = self.limits else {
return Ok(ReliabilityEvaluation::Unchanged);
};
if !monitored_value.is_finite() {
return Err(common::value_out_of_range_error());
}
let observed_fault = if monitored_value < limits.low {
Some(OwnedRangeFault::UnderRange)
} else if monitored_value > limits.high {
Some(OwnedRangeFault::OverRange)
} else {
None
};
let (new_reliability, new_owner) = if self.owned_fault.is_some() {
(
observed_fault
.map(OwnedRangeFault::reliability)
.unwrap_or(Reliability::NO_FAULT_DETECTED),
observed_fault,
)
} else if *reliability == Reliability::NO_FAULT_DETECTED {
let Some(fault) = observed_fault else {
return Ok(ReliabilityEvaluation::Unchanged);
};
(fault.reliability(), Some(fault))
} else {
return Ok(ReliabilityEvaluation::Unchanged);
};
if new_reliability == *reliability {
return Ok(ReliabilityEvaluation::Unchanged);
}
let old_reliability = *reliability;
*reliability = new_reliability;
self.owned_fault = new_owner;
Ok(ReliabilityEvaluation::Changed {
old_reliability,
new_reliability,
})
}
}
const CREATION_ONLY: &[PropertyIdentifier] = &[PropertyIdentifier::UNITS];
fn initialize_units(
units: &mut u32,
property: PropertyIdentifier,
value: PropertyValue,
) -> Result<(), Error> {
if property != PropertyIdentifier::UNITS {
return Err(common::write_access_denied_error());
}
let PropertyValue::Enumerated(raw) = value else {
return Err(common::invalid_data_type_error());
};
if raw > 65_535 {
return Err(common::value_out_of_range_error());
}
*units = raw;
Ok(())
}
mod input;
mod output;
mod value;
pub use input::*;
pub use output::*;
pub use value::*;
#[cfg(test)]
mod tests;