use bacnet_types::enums::{PropertyIdentifier, Reliability};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use crate::common::{
invalid_data_type_error, is_reliability_value_valid, value_out_of_range_error,
write_access_denied_error,
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(crate) struct ReliabilityInhibitState {
enabled: bool,
oos_client_reliability_override: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum OutOfServiceWrite {
Applied,
Relinquished,
}
impl ReliabilityInhibitState {
#[inline]
pub(crate) fn enabled(self) -> bool {
self.enabled
}
#[inline]
pub(crate) fn read(self, property: PropertyIdentifier) -> Option<PropertyValue> {
(property == PropertyIdentifier::RELIABILITY_EVALUATION_INHIBIT)
.then_some(PropertyValue::Boolean(self.enabled))
}
#[inline]
pub(crate) fn write_inhibit(
&mut self,
reliability: &mut Reliability,
out_of_service: bool,
property: PropertyIdentifier,
value: &PropertyValue,
) -> Option<Result<(), Error>> {
if property != PropertyIdentifier::RELIABILITY_EVALUATION_INHIBIT {
return None;
}
let PropertyValue::Boolean(enabled) = value else {
return Some(Err(invalid_data_type_error()));
};
self.enabled = *enabled;
if self.enabled && !(out_of_service && self.oos_client_reliability_override) {
*reliability = Reliability::NO_FAULT_DETECTED;
}
Some(Ok(()))
}
#[inline]
pub(crate) fn write_out_of_service(
&mut self,
out_of_service: &mut bool,
reliability: &mut Reliability,
saved_reliability: &mut Option<Reliability>,
property: PropertyIdentifier,
value: &PropertyValue,
) -> Option<Result<OutOfServiceWrite, Error>> {
if property != PropertyIdentifier::OUT_OF_SERVICE {
return None;
}
if *value == PropertyValue::Null {
return Some(Ok(OutOfServiceWrite::Relinquished));
}
let PropertyValue::Boolean(enabled) = value else {
return Some(Err(invalid_data_type_error()));
};
if !*out_of_service && *enabled {
*saved_reliability = Some(*reliability);
self.oos_client_reliability_override = false;
if self.enabled {
*reliability = Reliability::NO_FAULT_DETECTED;
}
} else if *out_of_service && !*enabled {
self.oos_client_reliability_override = false;
let saved = saved_reliability.take();
*reliability = if self.enabled {
Reliability::NO_FAULT_DETECTED
} else {
saved.unwrap_or(Reliability::NO_FAULT_DETECTED)
};
}
*out_of_service = *enabled;
Some(Ok(OutOfServiceWrite::Applied))
}
#[inline]
pub(crate) fn write_client_reliability(
&mut self,
out_of_service: bool,
reliability: &mut Reliability,
property: PropertyIdentifier,
value: &PropertyValue,
) -> Option<Result<(), Error>> {
if property != PropertyIdentifier::RELIABILITY {
return None;
}
if !out_of_service {
return Some(Err(write_access_denied_error()));
}
let PropertyValue::Enumerated(raw) = value else {
return Some(Err(invalid_data_type_error()));
};
let new_reliability = Reliability::from_raw(*raw);
if !is_reliability_value_valid(new_reliability) {
return Some(Err(value_out_of_range_error()));
}
*reliability = new_reliability;
self.oos_client_reliability_override = true;
Some(Ok(()))
}
}