use bacnet_types::constructed::BACnetObjectPropertyReference;
use bacnet_types::enums::{ObjectType, PropertyIdentifier as P};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use super::EventTransition;
use crate::common;
use crate::property_metadata::{
PropertyConformance::Optional, PropertyMetadata,
PropertyPresenceCondition::IntrinsicReportingOptional, PropertyWriteCapability::Always,
};
use crate::reference::{self, ReferenceFrame};
pub(crate) const REPORTING_OPTION_METADATA: [PropertyMetadata; 3] = [
PropertyMetadata::new(
P::EVENT_MESSAGE_TEXTS_CONFIG,
Optional,
Some(IntrinsicReportingOptional),
Always,
),
PropertyMetadata::new(
P::EVENT_ALGORITHM_INHIBIT_REF,
Optional,
Some(IntrinsicReportingOptional),
Always,
),
PropertyMetadata::new(
P::EVENT_ALGORITHM_INHIBIT,
Optional,
Some(IntrinsicReportingOptional),
Always,
),
];
const UNSET_REFERENCE_TYPE: ObjectType = ObjectType::BINARY_VALUE;
#[derive(Debug, Clone, Default, PartialEq)]
pub(crate) struct ReportingOptions {
message_texts_config: [String; 3],
inhibit_reference: Option<BACnetObjectPropertyReference>,
inhibit: bool,
}
impl ReportingOptions {
pub(crate) fn inhibited(&self) -> bool {
self.inhibit
}
pub(crate) fn inhibit_reference(&self) -> Option<&BACnetObjectPropertyReference> {
self.inhibit_reference.as_ref()
}
pub(crate) fn follow(&mut self, inhibit: bool) -> bool {
if self.inhibit_reference.is_none() || self.inhibit == inhibit {
return false;
}
self.inhibit = inhibit;
true
}
pub(crate) fn message_text(&self, coordinate: EventTransition) -> Option<&str> {
let text = &self.message_texts_config[coordinate.index()];
(!text.is_empty()).then_some(text.as_str())
}
pub(crate) fn read(
&self,
property: P,
array_index: Option<u32>,
) -> Option<Result<PropertyValue, Error>> {
Some(match property {
P::EVENT_MESSAGE_TEXTS_CONFIG => {
let text = |text: &String| PropertyValue::CharacterString(text.clone());
match array_index {
None => Ok(PropertyValue::List(
self.message_texts_config.iter().map(text).collect(),
)),
Some(0) => Ok(PropertyValue::Unsigned(3)),
Some(index @ 1..=3) => Ok(text(&self.message_texts_config[index as usize - 1])),
Some(_) => Err(common::invalid_array_index_error()),
}
}
P::EVENT_ALGORITHM_INHIBIT_REF | P::EVENT_ALGORITHM_INHIBIT
if array_index.is_some() =>
{
Err(common::property_is_not_an_array_error())
}
P::EVENT_ALGORITHM_INHIBIT_REF => Ok(reference::object_property_reference_value(
self.inhibit_reference.as_ref(),
UNSET_REFERENCE_TYPE,
)),
P::EVENT_ALGORITHM_INHIBIT => Ok(PropertyValue::Boolean(self.inhibit)),
_ => return None,
})
}
pub(crate) fn write(
&mut self,
property: P,
array_index: Option<u32>,
value: &PropertyValue,
detection_enabled: bool,
) -> Option<Result<(), Error>> {
Some(match property {
P::EVENT_MESSAGE_TEXTS_CONFIG => self.write_texts(array_index, value),
P::EVENT_ALGORITHM_INHIBIT_REF | P::EVENT_ALGORITHM_INHIBIT
if array_index.is_some() =>
{
Err(common::property_is_not_an_array_error())
}
P::EVENT_ALGORITHM_INHIBIT_REF => {
reference::decode_reference_write(value, ReferenceFrame::Bare).map(|reference| {
if reference.is_none() && self.inhibit_reference.is_some() {
self.inhibit = false;
}
self.inhibit_reference = reference;
})
}
P::EVENT_ALGORITHM_INHIBIT => {
let PropertyValue::Boolean(inhibit) = *value else {
return Some(Err(common::invalid_data_type_error()));
};
if self.inhibit_reference.is_some() || !detection_enabled {
Err(common::write_access_denied_error())
} else {
self.inhibit = inhibit;
Ok(())
}
}
_ => return None,
})
}
fn write_texts(
&mut self,
array_index: Option<u32>,
value: &PropertyValue,
) -> Result<(), Error> {
let label = |value: &PropertyValue| match value {
PropertyValue::CharacterString(text) => Ok(text.clone()),
_ => Err(common::invalid_data_type_error()),
};
match array_index {
None => {
let values = match value {
PropertyValue::List(values) => values.as_slice(),
single => std::slice::from_ref(single),
};
let texts = values.iter().map(label).collect::<Result<Vec<_>, _>>()?;
self.message_texts_config = texts
.try_into()
.map_err(|_| common::value_out_of_range_error())?;
Ok(())
}
Some(0) => Err(common::write_access_denied_error()),
Some(index @ 1..=3) => {
self.message_texts_config[index as usize - 1] = label(value)?;
Ok(())
}
Some(_) => Err(common::invalid_array_index_error()),
}
}
}
#[cfg(test)]
#[path = "options_tests.rs"]
mod tests;