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