use bacnet_types::enums::{ErrorClass, ErrorCode, LoggingType, PropertyIdentifier as P};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use crate::common;
pub const DEFAULT_LOG_INTERVAL: u32 = 6_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Rules {
TrendLog,
TrendLogMultiple,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct Acquisition {
rules: Rules,
logging_type: LoggingType,
log_interval: u32,
align_intervals: bool,
interval_offset: u32,
trigger: bool,
}
impl Acquisition {
pub(super) fn new(rules: Rules) -> Self {
Self {
rules,
logging_type: LoggingType::POLLED,
log_interval: 0,
align_intervals: false,
interval_offset: 0,
trigger: false,
}
}
pub(super) fn logging_type(&self) -> LoggingType {
self.logging_type
}
pub(super) fn log_interval_writable(&self) -> bool {
self.logging_type != LoggingType::TRIGGERED
}
pub(super) fn read(&self, property: P) -> Option<PropertyValue> {
Some(match property {
P::LOGGING_TYPE => PropertyValue::Enumerated(self.logging_type.to_raw()),
P::LOG_INTERVAL => PropertyValue::Unsigned(self.log_interval.into()),
P::ALIGN_INTERVALS => PropertyValue::Boolean(self.align_intervals),
P::INTERVAL_OFFSET => PropertyValue::Unsigned(self.interval_offset.into()),
P::TRIGGER => PropertyValue::Boolean(self.trigger),
_ => return None,
})
}
pub(super) fn write(
&mut self,
property: P,
value: &PropertyValue,
) -> Option<Result<(), Error>> {
Some(match (property, value) {
(P::LOGGING_TYPE, PropertyValue::Enumerated(raw)) => {
self.set_logging_type(LoggingType::from_raw(*raw))
}
(P::LOG_INTERVAL, _) if !self.log_interval_writable() => return None,
(P::LOG_INTERVAL, PropertyValue::Unsigned(raw)) => {
common::u64_to_u32(*raw).and_then(|hundredths| self.store_log_interval(hundredths))
}
(P::INTERVAL_OFFSET, PropertyValue::Unsigned(raw)) => {
common::u64_to_u32(*raw).map(|hundredths| self.interval_offset = hundredths)
}
(P::ALIGN_INTERVALS, PropertyValue::Boolean(align)) => {
self.align_intervals = *align;
Ok(())
}
(P::TRIGGER, PropertyValue::Boolean(trigger)) => self.write_trigger(*trigger),
(
P::LOGGING_TYPE
| P::LOG_INTERVAL
| P::INTERVAL_OFFSET
| P::ALIGN_INTERVALS
| P::TRIGGER,
_,
) => Err(common::invalid_data_type_error()),
_ => return None,
})
}
pub(super) fn set_logging_type(&mut self, logging_type: LoggingType) -> Result<(), Error> {
match logging_type {
LoggingType::POLLED => {
if self.log_interval == 0 {
self.log_interval = DEFAULT_LOG_INTERVAL;
}
self.trigger = false;
}
LoggingType::TRIGGERED => self.log_interval = 0,
_ => return Err(self.unsupported_logging_type()),
}
self.logging_type = logging_type;
Ok(())
}
pub(super) fn set_log_interval(&mut self, hundredths: u32) -> Result<(), Error> {
if !self.log_interval_writable() {
return Err(common::write_access_denied_error());
}
self.store_log_interval(hundredths)
}
fn store_log_interval(&mut self, hundredths: u32) -> Result<(), Error> {
if self.rules == Rules::TrendLog
&& self.logging_type == LoggingType::POLLED
&& self.log_interval != 0
&& hundredths == 0
{
return Err(self.unsupported_logging_type());
}
self.log_interval = hundredths;
Ok(())
}
fn unsupported_logging_type(&self) -> Error {
match self.rules {
Rules::TrendLog => common::protocol_error(
ErrorClass::PROPERTY,
ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
),
Rules::TrendLogMultiple => common::value_out_of_range_error(),
}
}
pub(super) fn set_align_intervals(&mut self, align: bool) {
self.align_intervals = align;
}
pub(super) fn set_interval_offset(&mut self, hundredths: u32) {
self.interval_offset = hundredths;
}
fn write_trigger(&mut self, trigger: bool) -> Result<(), Error> {
if !trigger {
return Ok(());
}
if self.logging_type != LoggingType::TRIGGERED {
return Err(common::protocol_error(
ErrorClass::PROPERTY,
ErrorCode::NOT_CONFIGURED_FOR_TRIGGERED_LOGGING,
));
}
self.trigger = true;
Ok(())
}
pub(super) fn trigger(&mut self) -> Result<(), Error> {
self.write_trigger(true)
}
pub(super) fn acquired(&mut self) {
self.trigger = false;
}
}