1use std::borrow::Cow;
4use std::collections::VecDeque;
5use std::sync::Arc;
6
7use bacnet_types::bitstring::EventTransitionBits;
8use bacnet_types::constructed::{BACnetDeviceObjectPropertyReference, BACnetLogRecord};
9use bacnet_types::enums::{
10 ErrorClass, ErrorCode, LoggingType, NotifyType, ObjectType, PropertyIdentifier, Reliability,
11};
12use bacnet_types::error::Error;
13use bacnet_types::primitives::{Date, ObjectIdentifier, PropertyValue, StatusFlags, Time};
14
15use crate::clock::ClockReader;
16use crate::common::{self, read_property_list_property};
17use crate::log_buffer::{
18 log_buffer_read_denied, LogBufferRecords, LogRecordBuffer, LogRecordIdentity,
19};
20use crate::log_lifecycle::LogLifecycle;
21use crate::log_reporting::{impl_buffer_ready_reporting, BufferReadyReporting};
22use crate::log_window::LogWindow;
23use crate::traits::BACnetObject;
24use acquisition::{Acquisition, Rules};
25
26mod acquisition;
27mod metadata;
28mod multiple;
29mod multiple_metadata;
30mod references;
31
32pub use acquisition::DEFAULT_LOG_INTERVAL;
33pub use multiple::TrendLogMultipleObject;
34pub use references::MAX_LOG_DEVICE_OBJECT_PROPERTIES;
35
36pub struct TrendLogObject {
46 oid: ObjectIdentifier,
47 name: String,
48 description: String,
49 log_enable: bool,
50 stop_when_full: bool,
51 buffer_size: u32,
52 log_buffer: LogRecordBuffer,
53 reliability: Reliability,
54 log_device_object_property: Option<BACnetDeviceObjectPropertyReference>,
55 acquisition: Acquisition,
56 window: LogWindow,
57 reporting: BufferReadyReporting,
58 clock: Option<Arc<dyn ClockReader>>,
59}
60
61impl TrendLogObject {
62 pub fn new(instance: u32, name: impl Into<String>, buffer_size: u32) -> Result<Self, Error> {
64 let oid = ObjectIdentifier::new(ObjectType::TREND_LOG, instance)?;
65 Ok(Self {
66 oid,
67 name: name.into(),
68 description: String::new(),
69 log_enable: true,
70 stop_when_full: false,
71 buffer_size,
72 log_buffer: LogRecordBuffer::new(buffer_size),
73 reliability: Reliability::NO_FAULT_DETECTED,
74 log_device_object_property: None,
75 acquisition: Acquisition::new(Rules::TrendLog),
76 window: LogWindow::default(),
77 reporting: BufferReadyReporting::default(),
78 clock: None,
79 })
80 }
81
82 pub fn add_record(&mut self, record: BACnetLogRecord) -> Result<(), Error> {
93 self.lifecycle().try_add_ordinary(record)?;
94 self.acquisition.acquired();
95 Ok(())
96 }
97
98 pub fn records(&self) -> &VecDeque<BACnetLogRecord> {
100 self.log_buffer.records()
101 }
102
103 pub fn clear(&mut self) {
105 self.log_buffer.clear();
106 }
107
108 pub fn set_description(&mut self, desc: impl Into<String>) {
110 self.description = desc.into();
111 }
112
113 pub fn set_log_device_object_property(
122 &mut self,
123 reference: Option<BACnetDeviceObjectPropertyReference>,
124 ) -> Result<(), Error> {
125 if let Some(reference) = &reference {
126 crate::device_reference::check_device_member(reference.device_identifier)?;
127 }
128 self.log_device_object_property = reference.and_then(crate::device_reference::set_or_unset);
129 Ok(())
130 }
131
132 fn write_log_device_object_property(&mut self, value: PropertyValue) -> Result<(), Error> {
140 let reference = crate::device_reference::decode_reference(&value)?;
141 references::check_written(&reference)?;
142 let reference = crate::device_reference::set_or_unset(reference);
143 if reference != self.log_device_object_property {
144 self.lifecycle().purge()?;
145 self.log_device_object_property = reference;
146 }
147 Ok(())
148 }
149
150 pub fn set_logging_type(&mut self, logging_type: LoggingType) -> Result<(), Error> {
156 self.acquisition.set_logging_type(logging_type)
157 }
158
159 pub fn set_log_interval(&mut self, hundredths: u32) -> Result<(), Error> {
165 self.acquisition.set_log_interval(hundredths)
166 }
167
168 pub fn set_start_time(&mut self, date: Date, time: Time) -> Result<(), Error> {
177 self.lifecycle()
178 .configure_window(PropertyIdentifier::START_TIME, (date, time))
179 }
180
181 pub fn set_stop_time(&mut self, date: Date, time: Time) -> Result<(), Error> {
185 self.lifecycle()
186 .configure_window(PropertyIdentifier::STOP_TIME, (date, time))
187 }
188
189 pub fn set_align_intervals(&mut self, align: bool) {
192 self.acquisition.set_align_intervals(align);
193 }
194
195 pub fn set_interval_offset(&mut self, hundredths: u32) {
198 self.acquisition.set_interval_offset(hundredths);
199 }
200
201 pub fn trigger(&mut self) -> Result<(), Error> {
205 self.acquisition.trigger()
206 }
207
208 pub fn set_notification_threshold(&mut self, threshold: u32) {
211 self.reporting.set_notification_threshold(threshold);
212 }
213
214 pub fn set_notification_class(&mut self, class: u32) {
217 self.reporting.set_notification_class(class);
218 }
219
220 pub fn set_event_enable(&mut self, enable: EventTransitionBits) {
223 self.reporting.set_event_enable(enable);
224 }
225
226 pub fn set_notify_type(&mut self, notify_type: NotifyType) {
228 self.reporting.set_notify_type(notify_type);
229 }
230
231 fn lifecycle(&mut self) -> LogLifecycle<'_, BACnetLogRecord> {
232 LogLifecycle::new(
233 &mut self.log_buffer,
234 &mut self.log_enable,
235 &mut self.stop_when_full,
236 self.clock.as_ref(),
237 &mut self.window,
238 &mut self.reporting,
239 )
240 }
241}
242
243impl BACnetObject for TrendLogObject {
244 fn object_identifier(&self) -> ObjectIdentifier {
245 self.oid
246 }
247
248 fn object_name(&self) -> &str {
249 &self.name
250 }
251
252 fn read_property(
253 &self,
254 property: PropertyIdentifier,
255 array_index: Option<u32>,
256 ) -> Result<PropertyValue, Error> {
257 if let Some(value) = self
260 .acquisition
261 .read(property)
262 .or_else(|| self.window.read(property))
263 {
264 return Ok(value);
265 }
266 let total = self.log_buffer.total_record_count();
267 if let Some(result) = self.reporting.read(property, array_index, total) {
268 return result;
269 }
270 match property {
271 p if p == PropertyIdentifier::OBJECT_IDENTIFIER => {
272 Ok(PropertyValue::ObjectIdentifier(self.oid))
273 }
274 p if p == PropertyIdentifier::OBJECT_NAME => {
275 Ok(PropertyValue::CharacterString(self.name.clone()))
276 }
277 p if p == PropertyIdentifier::DESCRIPTION => {
278 Ok(PropertyValue::CharacterString(self.description.clone()))
279 }
280 p if p == PropertyIdentifier::OBJECT_TYPE => {
281 Ok(PropertyValue::Enumerated(ObjectType::TREND_LOG.to_raw()))
282 }
283 p if p == PropertyIdentifier::LOG_ENABLE => Ok(PropertyValue::Boolean(self.log_enable)),
284 p if p == PropertyIdentifier::STOP_WHEN_FULL => {
285 Ok(PropertyValue::Boolean(self.stop_when_full))
286 }
287 p if p == PropertyIdentifier::BUFFER_SIZE => {
288 Ok(PropertyValue::Unsigned(self.buffer_size as u64))
289 }
290 p if p == PropertyIdentifier::RECORD_COUNT => {
291 Ok(PropertyValue::Unsigned(self.records().len() as u64))
292 }
293 p if p == PropertyIdentifier::TOTAL_RECORD_COUNT => {
294 Ok(PropertyValue::Unsigned(u64::from(total)))
295 }
296 p if p == PropertyIdentifier::STATUS_FLAGS => Ok(common::compute_status_flags(
301 StatusFlags::empty(),
302 self.reliability,
303 false,
304 self.reporting.event_state(),
305 )),
306 p if p == PropertyIdentifier::RELIABILITY => {
307 Ok(PropertyValue::Enumerated(self.reliability.to_raw()))
308 }
309 p if p == PropertyIdentifier::LOG_BUFFER => Err(log_buffer_read_denied()),
311 p if p == PropertyIdentifier::LOG_DEVICE_OBJECT_PROPERTY => {
314 Ok(crate::device_reference::optional_reference_value(
315 self.log_device_object_property.as_ref(),
316 ObjectType::ANALOG_INPUT,
317 ))
318 }
319 p if p == PropertyIdentifier::PROPERTY_LIST => {
320 read_property_list_property(&self.property_list(), array_index)
321 }
322 _ => Err(Error::Protocol {
323 class: ErrorClass::PROPERTY.to_raw() as u32,
324 code: ErrorCode::UNKNOWN_PROPERTY.to_raw() as u32,
325 }),
326 }
327 }
328
329 fn write_property(
330 &mut self,
331 property: PropertyIdentifier,
332 array_index: Option<u32>,
333 value: PropertyValue,
334 _priority: Option<u8>,
335 ) -> Result<(), Error> {
336 if property == PropertyIdentifier::LOG_ENABLE {
337 if let PropertyValue::Boolean(v) = value {
338 return self.lifecycle().write_enable(v);
339 }
340 return Err(Error::Protocol {
341 class: ErrorClass::PROPERTY.to_raw() as u32,
342 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
343 });
344 }
345 if let Some(result) = self.acquisition.write(property, &value) {
346 return result;
347 }
348 if let Some(result) = self.window.write(property, &value) {
349 result?;
351 self.lifecycle().refresh_window();
352 return Ok(());
353 }
354 if property == PropertyIdentifier::STOP_WHEN_FULL {
355 if let PropertyValue::Boolean(v) = value {
356 return self.lifecycle().write_stop_when_full(v);
357 }
358 return Err(Error::Protocol {
359 class: ErrorClass::PROPERTY.to_raw() as u32,
360 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
361 });
362 }
363 if property == PropertyIdentifier::RECORD_COUNT {
364 if let PropertyValue::Unsigned(0) = value {
365 return self.lifecycle().purge();
366 }
367 return Err(Error::Protocol {
368 class: ErrorClass::PROPERTY.to_raw() as u32,
369 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
370 });
371 }
372 if property == PropertyIdentifier::RELIABILITY {
381 return Err(Error::Protocol {
382 class: ErrorClass::PROPERTY.to_raw() as u32,
383 code: ErrorCode::WRITE_ACCESS_DENIED.to_raw() as u32,
384 });
385 }
386 if property == PropertyIdentifier::LOG_DEVICE_OBJECT_PROPERTY {
387 return self.write_log_device_object_property(value);
388 }
389 let total = self.log_buffer.total_record_count();
390 if let Some(result) = self.reporting.write(property, array_index, &value, total) {
391 return result;
392 }
393 if let Some(result) = common::write_description(&mut self.description, property, &value) {
394 return result;
395 }
396 Err(crate::common::unhandled_write_error(
397 self.property_metadata().as_ref(),
398 property,
399 array_index,
400 ))
401 }
402
403 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
404 metadata::for_object(self)
405 }
406
407 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
408 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
409 }
410
411 fn bind_clock_internal(&mut self, clock: Option<Arc<dyn ClockReader>>) {
412 self.clock = clock;
413 }
414
415 fn log_record_identities_internal(&self) -> Option<Vec<LogRecordIdentity>> {
416 Some(self.log_buffer.identities())
417 }
418
419 fn log_buffer_internal(&self) -> Option<&dyn LogBufferRecords> {
420 Some(&self.log_buffer)
421 }
422
423 fn add_trend_record(&mut self, record: BACnetLogRecord) -> Result<(), Error> {
424 self.add_record(record)
425 }
426
427 fn refresh_log_window_internal(&mut self) -> bool {
428 self.lifecycle().refresh_window()
429 }
430
431 impl_buffer_ready_reporting!(reporting, log_buffer);
432}
433
434#[cfg(test)]
435mod log_record_tests;
436
437#[cfg(test)]
438mod multiple_options_tests;
439
440#[cfg(test)]
441mod options_tests;
442
443#[cfg(test)]
444mod reference_tests;
445
446#[cfg(test)]
447mod tests;