1use std::borrow::Cow;
4use std::collections::VecDeque;
5use std::sync::Arc;
6
7use bacnet_types::bitstring::EventTransitionBits;
8use bacnet_types::constructed::{BACnetDeviceObjectPropertyReference, BACnetLogMultipleRecord};
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 super::acquisition::{Acquisition, Rules};
16use super::multiple_metadata;
17use super::references::{self, MAX_LOG_DEVICE_OBJECT_PROPERTIES};
18use crate::clock::ClockReader;
19use crate::common::{self, read_property_list_property};
20use crate::device_reference;
21use crate::log_buffer::{
22 log_buffer_read_denied, LogBufferRecords, LogRecordBuffer, LogRecordIdentity,
23};
24use crate::log_lifecycle::LogLifecycle;
25use crate::log_persistence::store::{DurableLog, LogStore};
26use crate::log_reporting::{impl_buffer_ready_reporting, BufferReadyReporting};
27use crate::log_window::LogWindow;
28use crate::traits::BACnetObject;
29
30pub struct TrendLogMultipleObject {
40 oid: ObjectIdentifier,
41 name: String,
42 description: String,
43 log_enable: bool,
44 stop_when_full: bool,
45 buffer_size: u32,
46 log_buffer: LogRecordBuffer<BACnetLogMultipleRecord>,
47 log_device_object_property: Vec<BACnetDeviceObjectPropertyReference>,
48 acquisition: Acquisition,
49 window: LogWindow,
50 reporting: BufferReadyReporting,
51 reliability: Reliability,
52 clock: Option<Arc<dyn ClockReader>>,
53 store: Option<LogStore<BACnetLogMultipleRecord>>,
55}
56
57impl TrendLogMultipleObject {
58 pub fn new(instance: u32, name: impl Into<String>, buffer_size: u32) -> Result<Self, Error> {
61 let oid = ObjectIdentifier::new(ObjectType::TREND_LOG_MULTIPLE, instance)?;
62 Ok(Self {
63 oid,
64 name: name.into(),
65 description: String::new(),
66 log_enable: true,
67 stop_when_full: false,
68 buffer_size,
69 log_buffer: LogRecordBuffer::new(buffer_size),
70 log_device_object_property: Vec::new(),
71 acquisition: Acquisition::new(Rules::TrendLogMultiple),
72 window: LogWindow::default(),
73 reporting: BufferReadyReporting::default(),
74 reliability: Reliability::NO_FAULT_DETECTED,
75 clock: None,
76 store: None,
77 })
78 }
79
80 pub fn add_record(&mut self, record: BACnetLogMultipleRecord) -> Result<(), Error> {
91 self.settle_interruption(Some((record.date, record.time)));
92 self.lifecycle().try_add_ordinary(record)?;
93 self.acquisition.acquired();
94 self.commit_if_counted();
95 Ok(())
96 }
97
98 pub fn add_property_reference(
107 &mut self,
108 reference: BACnetDeviceObjectPropertyReference,
109 ) -> Result<(), Error> {
110 device_reference::check_device_member(reference.device_identifier)?;
111 if self.log_device_object_property.len() >= MAX_LOG_DEVICE_OBJECT_PROPERTIES {
112 return Err(references::no_space_error());
113 }
114 self.log_device_object_property.push(reference);
115 Ok(())
116 }
117
118 fn write_log_device_object_property(
133 &mut self,
134 array_index: Option<u32>,
135 value: PropertyValue,
136 ) -> Result<(), Error> {
137 let mut candidate = self.log_device_object_property.clone();
138 match array_index {
139 None => {
140 let written = device_reference::decode_references(&value)?;
141 if written.len() > MAX_LOG_DEVICE_OBJECT_PROPERTIES {
142 return Err(references::no_space_error());
143 }
144 for reference in &written {
145 references::check_written(reference)?;
146 }
147 candidate = written;
148 }
149 Some(0) => {
150 let PropertyValue::Unsigned(size) = value else {
151 return Err(common::invalid_data_type_error());
152 };
153 let size = usize::try_from(size)
154 .ok()
155 .filter(|size| *size <= MAX_LOG_DEVICE_OBJECT_PROPERTIES)
156 .ok_or_else(references::no_space_error)?;
157 candidate.resize_with(size, references::empty_element);
158 }
159 Some(index) => {
160 let slot = usize::try_from(index - 1)
161 .ok()
162 .and_then(|slot| candidate.get_mut(slot))
163 .ok_or_else(common::invalid_array_index_error)?;
164 let reference = device_reference::decode_reference(&value)?;
165 references::check_written(&reference)?;
166 *slot = reference;
167 }
168 }
169 if candidate != self.log_device_object_property {
170 self.lifecycle().purge()?;
171 self.log_device_object_property = candidate;
172 }
173 Ok(())
174 }
175
176 pub fn records(&self) -> &VecDeque<BACnetLogMultipleRecord> {
178 self.log_buffer.records()
179 }
180
181 pub fn total_record_count(&self) -> u32 {
185 self.log_buffer.total_record_count()
186 }
187
188 pub fn restore_log_buffer(
196 &mut self,
197 total_record_count: u32,
198 records: impl IntoIterator<Item = BACnetLogMultipleRecord>,
199 ) -> Result<(), Error> {
200 self.lifecycle()
201 .restore(total_record_count, records.into_iter().collect())
202 }
203
204 pub fn record_interruption(&mut self, date: Date, time: Time) -> Result<(), Error> {
210 self.lifecycle().record_interruption((date, time))?;
211 if let Some(store) = &mut self.store {
212 store.interruption_recorded();
213 }
214 self.commit_if_counted();
215 Ok(())
216 }
217
218 pub fn clear(&mut self) {
220 self.log_buffer.clear();
221 }
222
223 pub fn set_description(&mut self, desc: impl Into<String>) {
225 self.description = desc.into();
226 }
227
228 pub fn set_logging_type(&mut self, logging_type: LoggingType) -> Result<(), Error> {
235 self.acquisition.set_logging_type(logging_type)
236 }
237
238 pub fn set_log_interval(&mut self, hundredths: u32) -> Result<(), Error> {
241 self.acquisition.set_log_interval(hundredths)
242 }
243
244 pub fn set_start_time(&mut self, date: Date, time: Time) -> Result<(), Error> {
253 self.lifecycle()
254 .configure_window(PropertyIdentifier::START_TIME, (date, time))
255 }
256
257 pub fn set_stop_time(&mut self, date: Date, time: Time) -> Result<(), Error> {
261 self.lifecycle()
262 .configure_window(PropertyIdentifier::STOP_TIME, (date, time))
263 }
264
265 pub fn set_align_intervals(&mut self, align: bool) {
268 self.acquisition.set_align_intervals(align);
269 }
270
271 pub fn set_interval_offset(&mut self, hundredths: u32) {
274 self.acquisition.set_interval_offset(hundredths);
275 }
276
277 pub fn trigger(&mut self) -> Result<(), Error> {
281 self.acquisition.trigger()
282 }
283
284 pub(super) fn logging_type(&self) -> LoggingType {
285 self.acquisition.logging_type()
286 }
287
288 pub fn set_notification_threshold(&mut self, threshold: u32) {
291 self.reporting.set_notification_threshold(threshold);
292 }
293
294 pub fn set_notification_class(&mut self, class: u32) {
297 self.reporting.set_notification_class(class);
298 }
299
300 pub fn set_event_enable(&mut self, enable: EventTransitionBits) {
303 self.reporting.set_event_enable(enable);
304 }
305
306 pub fn set_notify_type(&mut self, notify_type: NotifyType) {
308 self.reporting.set_notify_type(notify_type);
309 }
310
311 fn lifecycle(&mut self) -> LogLifecycle<'_, BACnetLogMultipleRecord> {
312 LogLifecycle::new(
313 &mut self.log_buffer,
314 &mut self.log_enable,
315 &mut self.stop_when_full,
316 self.clock.as_ref(),
317 &mut self.window,
318 &mut self.reporting,
319 )
320 }
321}
322
323impl BACnetObject for TrendLogMultipleObject {
324 fn object_identifier(&self) -> ObjectIdentifier {
325 self.oid
326 }
327
328 fn object_name(&self) -> &str {
329 &self.name
330 }
331
332 fn read_property(
333 &self,
334 property: PropertyIdentifier,
335 array_index: Option<u32>,
336 ) -> Result<PropertyValue, Error> {
337 if let Some(value) = self
340 .acquisition
341 .read(property)
342 .or_else(|| self.window.read(property))
343 {
344 return Ok(value);
345 }
346 let total = self.log_buffer.total_record_count();
347 if let Some(result) = self.reporting.read(property, array_index, total) {
348 return result;
349 }
350 match property {
351 p if p == PropertyIdentifier::OBJECT_IDENTIFIER => {
352 Ok(PropertyValue::ObjectIdentifier(self.oid))
353 }
354 p if p == PropertyIdentifier::OBJECT_NAME => {
355 Ok(PropertyValue::CharacterString(self.name.clone()))
356 }
357 p if p == PropertyIdentifier::DESCRIPTION => {
358 Ok(PropertyValue::CharacterString(self.description.clone()))
359 }
360 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
361 ObjectType::TREND_LOG_MULTIPLE.to_raw(),
362 )),
363 p if p == PropertyIdentifier::LOG_ENABLE => Ok(PropertyValue::Boolean(self.log_enable)),
364 p if p == PropertyIdentifier::STOP_WHEN_FULL => {
365 Ok(PropertyValue::Boolean(self.stop_when_full))
366 }
367 p if p == PropertyIdentifier::BUFFER_SIZE => {
368 Ok(PropertyValue::Unsigned(self.buffer_size as u64))
369 }
370 p if p == PropertyIdentifier::RECORD_COUNT => {
371 Ok(PropertyValue::Unsigned(self.records().len() as u64))
372 }
373 p if p == PropertyIdentifier::TOTAL_RECORD_COUNT => {
374 Ok(PropertyValue::Unsigned(u64::from(total)))
375 }
376 p if p == PropertyIdentifier::STATUS_FLAGS => Ok(common::compute_status_flags(
381 StatusFlags::empty(),
382 self.reliability,
383 false,
384 self.reporting.event_state(),
385 )),
386 p if p == PropertyIdentifier::RELIABILITY => {
387 Ok(PropertyValue::Enumerated(self.reliability.to_raw()))
388 }
389 p if p == PropertyIdentifier::LOG_BUFFER => Err(log_buffer_read_denied()),
391 p if p == PropertyIdentifier::LOG_DEVICE_OBJECT_PROPERTY => common::read_array(
393 device_reference::reference_elements(&self.log_device_object_property),
394 array_index,
395 ),
396 p if p == PropertyIdentifier::PROPERTY_LIST => {
397 read_property_list_property(&self.property_list(), array_index)
398 }
399 _ => Err(Error::Protocol {
400 class: ErrorClass::PROPERTY,
401 code: ErrorCode::UNKNOWN_PROPERTY,
402 }),
403 }
404 }
405
406 fn write_property(
407 &mut self,
408 property: PropertyIdentifier,
409 array_index: Option<u32>,
410 value: PropertyValue,
411 _priority: Option<u8>,
412 ) -> Result<(), Error> {
413 if property == PropertyIdentifier::LOG_ENABLE {
414 if let PropertyValue::Boolean(v) = value {
415 return self.lifecycle().write_enable(v);
416 }
417 return Err(Error::Protocol {
418 class: ErrorClass::PROPERTY,
419 code: ErrorCode::INVALID_DATA_TYPE,
420 });
421 }
422 if let Some(result) = self.acquisition.write(property, &value) {
423 return result;
424 }
425 if let Some(result) = self.window.write(property, &value) {
426 result?;
428 self.lifecycle().refresh_window();
429 return Ok(());
430 }
431 if property == PropertyIdentifier::STOP_WHEN_FULL {
432 if let PropertyValue::Boolean(v) = value {
433 return self.lifecycle().write_stop_when_full(v);
434 }
435 return Err(Error::Protocol {
436 class: ErrorClass::PROPERTY,
437 code: ErrorCode::INVALID_DATA_TYPE,
438 });
439 }
440 if property == PropertyIdentifier::RECORD_COUNT {
441 if let PropertyValue::Unsigned(0) = value {
442 return self.lifecycle().purge();
443 }
444 return Err(Error::Protocol {
445 class: ErrorClass::PROPERTY,
446 code: ErrorCode::INVALID_DATA_TYPE,
447 });
448 }
449 if property == PropertyIdentifier::LOG_DEVICE_OBJECT_PROPERTY {
450 return self.write_log_device_object_property(array_index, value);
451 }
452 let total = self.log_buffer.total_record_count();
453 if let Some(result) = self.reporting.write(property, array_index, &value, total) {
454 return result;
455 }
456 if let Some(result) = common::write_description(&mut self.description, property, &value) {
457 return result;
458 }
459 Err(crate::common::unhandled_write_error(
460 self.property_metadata().as_ref(),
461 property,
462 array_index,
463 ))
464 }
465
466 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
467 multiple_metadata::for_object(self)
468 }
469
470 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
471 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
472 }
473
474 fn bind_clock_internal(&mut self, clock: Option<Arc<dyn ClockReader>>) {
475 self.clock = clock;
476 self.settle_interruption(None);
477 }
478
479 fn durable_writes_internal(&mut self) -> Option<&mut dyn crate::durable::DurableWrites> {
480 self.store.is_some().then_some(self as _)
481 }
482
483 fn advance_monotonic_time_internal(&mut self, now: std::time::Duration) -> bool {
484 self.commit_if_interval(now);
485 false
486 }
487
488 fn log_record_identities_internal(&self) -> Option<Vec<LogRecordIdentity>> {
489 Some(self.log_buffer.identities())
490 }
491
492 fn log_buffer_internal(&self) -> Option<&dyn LogBufferRecords> {
493 Some(&self.log_buffer)
494 }
495
496 fn add_trend_multiple_record(&mut self, record: BACnetLogMultipleRecord) -> Result<(), Error> {
497 self.add_record(record)
498 }
499
500 fn refresh_log_window_internal(&mut self) -> bool {
501 self.settle_interruption(None);
502 let recorded = self.lifecycle().refresh_window();
503 self.commit_if_counted();
504 recorded
505 }
506
507 impl_buffer_ready_reporting!(reporting, log_buffer);
508}
509
510#[path = "multiple_durable.rs"]
511mod durable;