bacnet_objects/trend/
acquisition.rs1use bacnet_types::enums::{ErrorClass, ErrorCode, LoggingType, PropertyIdentifier as P};
8use bacnet_types::error::Error;
9use bacnet_types::primitives::PropertyValue;
10
11use crate::common;
12
13pub const DEFAULT_LOG_INTERVAL: u32 = 6_000;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub(super) enum Rules {
22 TrendLog,
30 TrendLogMultiple,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub(super) struct Acquisition {
39 rules: Rules,
40 logging_type: LoggingType,
41 log_interval: u32,
43 align_intervals: bool,
44 interval_offset: u32,
45 trigger: bool,
46}
47
48impl Acquisition {
49 pub(super) fn new(rules: Rules) -> Self {
51 Self {
52 rules,
53 logging_type: LoggingType::POLLED,
54 log_interval: 0,
55 align_intervals: false,
56 interval_offset: 0,
57 trigger: false,
58 }
59 }
60
61 pub(super) fn logging_type(&self) -> LoggingType {
62 self.logging_type
63 }
64
65 pub(super) fn log_interval_writable(&self) -> bool {
68 self.logging_type != LoggingType::TRIGGERED
69 }
70
71 pub(super) fn read(&self, property: P) -> Option<PropertyValue> {
73 Some(match property {
74 P::LOGGING_TYPE => PropertyValue::Enumerated(self.logging_type.to_raw()),
75 P::LOG_INTERVAL => PropertyValue::Unsigned(self.log_interval.into()),
76 P::ALIGN_INTERVALS => PropertyValue::Boolean(self.align_intervals),
77 P::INTERVAL_OFFSET => PropertyValue::Unsigned(self.interval_offset.into()),
78 P::TRIGGER => PropertyValue::Boolean(self.trigger),
79 _ => return None,
80 })
81 }
82
83 pub(super) fn write(
87 &mut self,
88 property: P,
89 value: &PropertyValue,
90 ) -> Option<Result<(), Error>> {
91 Some(match (property, value) {
92 (P::LOGGING_TYPE, PropertyValue::Enumerated(raw)) => {
93 self.set_logging_type(LoggingType::from_raw(*raw))
94 }
95 (P::LOG_INTERVAL, _) if !self.log_interval_writable() => return None,
96 (P::LOG_INTERVAL, PropertyValue::Unsigned(raw)) => {
97 common::u64_to_u32(*raw).and_then(|hundredths| self.store_log_interval(hundredths))
98 }
99 (P::INTERVAL_OFFSET, PropertyValue::Unsigned(raw)) => {
100 common::u64_to_u32(*raw).map(|hundredths| self.interval_offset = hundredths)
101 }
102 (P::ALIGN_INTERVALS, PropertyValue::Boolean(align)) => {
103 self.align_intervals = *align;
104 Ok(())
105 }
106 (P::TRIGGER, PropertyValue::Boolean(trigger)) => self.write_trigger(*trigger),
107 (
108 P::LOGGING_TYPE
109 | P::LOG_INTERVAL
110 | P::INTERVAL_OFFSET
111 | P::ALIGN_INTERVALS
112 | P::TRIGGER,
113 _,
114 ) => Err(common::invalid_data_type_error()),
115 _ => return None,
116 })
117 }
118
119 pub(super) fn set_logging_type(&mut self, logging_type: LoggingType) -> Result<(), Error> {
126 match logging_type {
127 LoggingType::POLLED => {
128 if self.log_interval == 0 {
129 self.log_interval = DEFAULT_LOG_INTERVAL;
130 }
131 self.trigger = false;
132 }
133 LoggingType::TRIGGERED => self.log_interval = 0,
134 _ => return Err(self.unsupported_logging_type()),
135 }
136 self.logging_type = logging_type;
137 Ok(())
138 }
139
140 pub(super) fn set_log_interval(&mut self, hundredths: u32) -> Result<(), Error> {
143 if !self.log_interval_writable() {
144 return Err(common::write_access_denied_error());
145 }
146 self.store_log_interval(hundredths)
147 }
148
149 fn store_log_interval(&mut self, hundredths: u32) -> Result<(), Error> {
154 if self.rules == Rules::TrendLog
155 && self.logging_type == LoggingType::POLLED
156 && self.log_interval != 0
157 && hundredths == 0
158 {
159 return Err(self.unsupported_logging_type());
160 }
161 self.log_interval = hundredths;
162 Ok(())
163 }
164
165 fn unsupported_logging_type(&self) -> Error {
168 match self.rules {
169 Rules::TrendLog => common::protocol_error(
170 ErrorClass::PROPERTY,
171 ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
172 ),
173 Rules::TrendLogMultiple => common::value_out_of_range_error(),
174 }
175 }
176
177 pub(super) fn set_align_intervals(&mut self, align: bool) {
178 self.align_intervals = align;
179 }
180
181 pub(super) fn set_interval_offset(&mut self, hundredths: u32) {
182 self.interval_offset = hundredths;
183 }
184
185 fn write_trigger(&mut self, trigger: bool) -> Result<(), Error> {
191 if !trigger {
192 return Ok(());
193 }
194 if self.logging_type != LoggingType::TRIGGERED {
195 return Err(common::protocol_error(
196 ErrorClass::PROPERTY,
197 ErrorCode::NOT_CONFIGURED_FOR_TRIGGERED_LOGGING,
198 ));
199 }
200 self.trigger = true;
201 Ok(())
202 }
203
204 pub(super) fn trigger(&mut self) -> Result<(), Error> {
206 self.write_trigger(true)
207 }
208
209 pub(super) fn acquired(&mut self) {
211 self.trigger = false;
212 }
213}