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;
12use crate::log_persistence::LogAcquisitionSettings;
13
14pub const DEFAULT_LOG_INTERVAL: u32 = 6_000;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub(super) enum Rules {
23 TrendLog,
31 TrendLogMultiple,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub(super) struct Acquisition {
40 rules: Rules,
41 logging_type: LoggingType,
42 log_interval: u32,
44 align_intervals: bool,
45 interval_offset: u32,
46 trigger: bool,
47}
48
49impl Acquisition {
50 pub(super) fn new(rules: Rules) -> Self {
52 Self {
53 rules,
54 logging_type: LoggingType::POLLED,
55 log_interval: 0,
56 align_intervals: false,
57 interval_offset: 0,
58 trigger: false,
59 }
60 }
61
62 pub(super) fn logging_type(&self) -> LoggingType {
63 self.logging_type
64 }
65
66 pub(super) fn settings(&self) -> LogAcquisitionSettings {
69 LogAcquisitionSettings {
70 logging_type: self.logging_type,
71 log_interval: self.log_interval,
72 align_intervals: self.align_intervals,
73 interval_offset: self.interval_offset,
74 }
75 }
76
77 pub(super) fn restore(&mut self, settings: LogAcquisitionSettings) {
80 self.logging_type = settings.logging_type;
81 self.log_interval = settings.log_interval;
82 self.align_intervals = settings.align_intervals;
83 self.interval_offset = settings.interval_offset;
84 self.trigger = false;
85 }
86
87 pub(super) fn log_interval_writable(&self) -> bool {
90 self.logging_type != LoggingType::TRIGGERED
91 }
92
93 pub(super) fn read(&self, property: P) -> Option<PropertyValue> {
95 Some(match property {
96 P::LOGGING_TYPE => PropertyValue::Enumerated(self.logging_type.to_raw()),
97 P::LOG_INTERVAL => PropertyValue::Unsigned(self.log_interval.into()),
98 P::ALIGN_INTERVALS => PropertyValue::Boolean(self.align_intervals),
99 P::INTERVAL_OFFSET => PropertyValue::Unsigned(self.interval_offset.into()),
100 P::TRIGGER => PropertyValue::Boolean(self.trigger),
101 _ => return None,
102 })
103 }
104
105 pub(super) fn write(
109 &mut self,
110 property: P,
111 value: &PropertyValue,
112 ) -> Option<Result<(), Error>> {
113 Some(match (property, value) {
114 (P::LOGGING_TYPE, PropertyValue::Enumerated(raw)) => {
115 self.set_logging_type(LoggingType::from_raw(*raw))
116 }
117 (P::LOG_INTERVAL, _) if !self.log_interval_writable() => return None,
118 (P::LOG_INTERVAL, PropertyValue::Unsigned(raw)) => {
119 common::u64_to_u32(*raw).and_then(|hundredths| self.store_log_interval(hundredths))
120 }
121 (P::INTERVAL_OFFSET, PropertyValue::Unsigned(raw)) => {
122 common::u64_to_u32(*raw).map(|hundredths| self.interval_offset = hundredths)
123 }
124 (P::ALIGN_INTERVALS, PropertyValue::Boolean(align)) => {
125 self.align_intervals = *align;
126 Ok(())
127 }
128 (P::TRIGGER, PropertyValue::Boolean(trigger)) => self.write_trigger(*trigger),
129 (
130 P::LOGGING_TYPE
131 | P::LOG_INTERVAL
132 | P::INTERVAL_OFFSET
133 | P::ALIGN_INTERVALS
134 | P::TRIGGER,
135 _,
136 ) => Err(common::invalid_data_type_error()),
137 _ => return None,
138 })
139 }
140
141 pub(super) fn set_logging_type(&mut self, logging_type: LoggingType) -> Result<(), Error> {
148 match logging_type {
149 LoggingType::POLLED => {
150 if self.log_interval == 0 {
151 self.log_interval = DEFAULT_LOG_INTERVAL;
152 }
153 self.trigger = false;
154 }
155 LoggingType::TRIGGERED => self.log_interval = 0,
156 LoggingType::COV if self.rules == Rules::TrendLog => {
157 self.log_interval = 0;
158 self.trigger = false;
159 }
160 _ => return Err(self.unsupported_logging_type()),
161 }
162 self.logging_type = logging_type;
163 Ok(())
164 }
165
166 pub(super) fn set_log_interval(&mut self, hundredths: u32) -> Result<(), Error> {
171 if !self.log_interval_writable() {
172 return Err(common::write_access_denied_error());
173 }
174 self.store_log_interval(hundredths)
175 }
176
177 fn store_log_interval(&mut self, hundredths: u32) -> Result<(), Error> {
183 if self.rules == Rules::TrendLog {
184 match self.logging_type {
185 LoggingType::POLLED if self.log_interval != 0 && hundredths == 0 => {
186 self.logging_type = LoggingType::COV;
187 }
188 LoggingType::COV if hundredths != 0 => {
189 self.logging_type = LoggingType::POLLED;
190 }
191 _ => {}
192 }
193 }
194 self.log_interval = hundredths;
195 Ok(())
196 }
197
198 fn unsupported_logging_type(&self) -> Error {
201 match self.rules {
202 Rules::TrendLog => common::protocol_error(
203 ErrorClass::PROPERTY,
204 ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
205 ),
206 Rules::TrendLogMultiple => common::value_out_of_range_error(),
207 }
208 }
209
210 pub(super) fn set_align_intervals(&mut self, align: bool) {
211 self.align_intervals = align;
212 }
213
214 pub(super) fn set_interval_offset(&mut self, hundredths: u32) {
215 self.interval_offset = hundredths;
216 }
217
218 fn write_trigger(&mut self, trigger: bool) -> Result<(), Error> {
224 if !trigger {
225 return Ok(());
226 }
227 if self.logging_type != LoggingType::TRIGGERED {
228 return Err(common::protocol_error(
229 ErrorClass::PROPERTY,
230 ErrorCode::NOT_CONFIGURED_FOR_TRIGGERED_LOGGING,
231 ));
232 }
233 self.trigger = true;
234 Ok(())
235 }
236
237 pub(super) fn trigger(&mut self) -> Result<(), Error> {
239 self.write_trigger(true)
240 }
241
242 pub(super) fn acquired(&mut self) {
244 self.trigger = false;
245 }
246}