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_reporting::{impl_buffer_ready_reporting, BufferReadyReporting};
26use crate::log_window::LogWindow;
27use crate::traits::BACnetObject;
28
29pub struct TrendLogMultipleObject {
39 oid: ObjectIdentifier,
40 name: String,
41 description: String,
42 log_enable: bool,
43 stop_when_full: bool,
44 buffer_size: u32,
45 log_buffer: LogRecordBuffer<BACnetLogMultipleRecord>,
46 log_device_object_property: Vec<BACnetDeviceObjectPropertyReference>,
47 acquisition: Acquisition,
48 window: LogWindow,
49 reporting: BufferReadyReporting,
50 reliability: Reliability,
51 clock: Option<Arc<dyn ClockReader>>,
52}
53
54impl TrendLogMultipleObject {
55 pub fn new(instance: u32, name: impl Into<String>, buffer_size: u32) -> Result<Self, Error> {
58 let oid = ObjectIdentifier::new(ObjectType::TREND_LOG_MULTIPLE, instance)?;
59 Ok(Self {
60 oid,
61 name: name.into(),
62 description: String::new(),
63 log_enable: true,
64 stop_when_full: false,
65 buffer_size,
66 log_buffer: LogRecordBuffer::new(buffer_size),
67 log_device_object_property: Vec::new(),
68 acquisition: Acquisition::new(Rules::TrendLogMultiple),
69 window: LogWindow::default(),
70 reporting: BufferReadyReporting::default(),
71 reliability: Reliability::NO_FAULT_DETECTED,
72 clock: None,
73 })
74 }
75
76 pub fn add_record(&mut self, record: BACnetLogMultipleRecord) -> Result<(), Error> {
87 self.lifecycle().try_add_ordinary(record)?;
88 self.acquisition.acquired();
89 Ok(())
90 }
91
92 pub fn add_property_reference(
101 &mut self,
102 reference: BACnetDeviceObjectPropertyReference,
103 ) -> Result<(), Error> {
104 device_reference::check_device_member(reference.device_identifier)?;
105 if self.log_device_object_property.len() >= MAX_LOG_DEVICE_OBJECT_PROPERTIES {
106 return Err(references::no_space_error());
107 }
108 self.log_device_object_property.push(reference);
109 Ok(())
110 }
111
112 fn write_log_device_object_property(
127 &mut self,
128 array_index: Option<u32>,
129 value: PropertyValue,
130 ) -> Result<(), Error> {
131 let mut candidate = self.log_device_object_property.clone();
132 match array_index {
133 None => {
134 let written = device_reference::decode_references(&value)?;
135 if written.len() > MAX_LOG_DEVICE_OBJECT_PROPERTIES {
136 return Err(references::no_space_error());
137 }
138 for reference in &written {
139 references::check_written(reference)?;
140 }
141 candidate = written;
142 }
143 Some(0) => {
144 let PropertyValue::Unsigned(size) = value else {
145 return Err(common::invalid_data_type_error());
146 };
147 let size = usize::try_from(size)
148 .ok()
149 .filter(|size| *size <= MAX_LOG_DEVICE_OBJECT_PROPERTIES)
150 .ok_or_else(references::no_space_error)?;
151 candidate.resize_with(size, references::empty_element);
152 }
153 Some(index) => {
154 let slot = usize::try_from(index - 1)
155 .ok()
156 .and_then(|slot| candidate.get_mut(slot))
157 .ok_or_else(common::invalid_array_index_error)?;
158 let reference = device_reference::decode_reference(&value)?;
159 references::check_written(&reference)?;
160 *slot = reference;
161 }
162 }
163 if candidate != self.log_device_object_property {
164 self.lifecycle().purge()?;
165 self.log_device_object_property = candidate;
166 }
167 Ok(())
168 }
169
170 pub fn records(&self) -> &VecDeque<BACnetLogMultipleRecord> {
172 self.log_buffer.records()
173 }
174
175 pub fn total_record_count(&self) -> u32 {
179 self.log_buffer.total_record_count()
180 }
181
182 pub fn restore_log_buffer(
190 &mut self,
191 total_record_count: u32,
192 records: impl IntoIterator<Item = BACnetLogMultipleRecord>,
193 ) -> Result<(), Error> {
194 self.lifecycle()
195 .restore(total_record_count, records.into_iter().collect())
196 }
197
198 pub fn record_interruption(&mut self, date: Date, time: Time) -> Result<(), Error> {
202 self.lifecycle().record_interruption((date, time))
203 }
204
205 pub fn clear(&mut self) {
207 self.log_buffer.clear();
208 }
209
210 pub fn set_description(&mut self, desc: impl Into<String>) {
212 self.description = desc.into();
213 }
214
215 pub fn set_logging_type(&mut self, logging_type: LoggingType) -> Result<(), Error> {
222 self.acquisition.set_logging_type(logging_type)
223 }
224
225 pub fn set_log_interval(&mut self, hundredths: u32) -> Result<(), Error> {
228 self.acquisition.set_log_interval(hundredths)
229 }
230
231 pub fn set_start_time(&mut self, date: Date, time: Time) -> Result<(), Error> {
240 self.lifecycle()
241 .configure_window(PropertyIdentifier::START_TIME, (date, time))
242 }
243
244 pub fn set_stop_time(&mut self, date: Date, time: Time) -> Result<(), Error> {
248 self.lifecycle()
249 .configure_window(PropertyIdentifier::STOP_TIME, (date, time))
250 }
251
252 pub fn set_align_intervals(&mut self, align: bool) {
255 self.acquisition.set_align_intervals(align);
256 }
257
258 pub fn set_interval_offset(&mut self, hundredths: u32) {
261 self.acquisition.set_interval_offset(hundredths);
262 }
263
264 pub fn trigger(&mut self) -> Result<(), Error> {
268 self.acquisition.trigger()
269 }
270
271 pub(super) fn logging_type(&self) -> LoggingType {
272 self.acquisition.logging_type()
273 }
274
275 pub fn set_notification_threshold(&mut self, threshold: u32) {
278 self.reporting.set_notification_threshold(threshold);
279 }
280
281 pub fn set_notification_class(&mut self, class: u32) {
284 self.reporting.set_notification_class(class);
285 }
286
287 pub fn set_event_enable(&mut self, enable: EventTransitionBits) {
290 self.reporting.set_event_enable(enable);
291 }
292
293 pub fn set_notify_type(&mut self, notify_type: NotifyType) {
295 self.reporting.set_notify_type(notify_type);
296 }
297
298 fn lifecycle(&mut self) -> LogLifecycle<'_, BACnetLogMultipleRecord> {
299 LogLifecycle::new(
300 &mut self.log_buffer,
301 &mut self.log_enable,
302 &mut self.stop_when_full,
303 self.clock.as_ref(),
304 &mut self.window,
305 &mut self.reporting,
306 )
307 }
308}
309
310impl BACnetObject for TrendLogMultipleObject {
311 fn object_identifier(&self) -> ObjectIdentifier {
312 self.oid
313 }
314
315 fn object_name(&self) -> &str {
316 &self.name
317 }
318
319 fn read_property(
320 &self,
321 property: PropertyIdentifier,
322 array_index: Option<u32>,
323 ) -> Result<PropertyValue, Error> {
324 if let Some(value) = self
327 .acquisition
328 .read(property)
329 .or_else(|| self.window.read(property))
330 {
331 return Ok(value);
332 }
333 let total = self.log_buffer.total_record_count();
334 if let Some(result) = self.reporting.read(property, array_index, total) {
335 return result;
336 }
337 match property {
338 p if p == PropertyIdentifier::OBJECT_IDENTIFIER => {
339 Ok(PropertyValue::ObjectIdentifier(self.oid))
340 }
341 p if p == PropertyIdentifier::OBJECT_NAME => {
342 Ok(PropertyValue::CharacterString(self.name.clone()))
343 }
344 p if p == PropertyIdentifier::DESCRIPTION => {
345 Ok(PropertyValue::CharacterString(self.description.clone()))
346 }
347 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
348 ObjectType::TREND_LOG_MULTIPLE.to_raw(),
349 )),
350 p if p == PropertyIdentifier::LOG_ENABLE => Ok(PropertyValue::Boolean(self.log_enable)),
351 p if p == PropertyIdentifier::STOP_WHEN_FULL => {
352 Ok(PropertyValue::Boolean(self.stop_when_full))
353 }
354 p if p == PropertyIdentifier::BUFFER_SIZE => {
355 Ok(PropertyValue::Unsigned(self.buffer_size as u64))
356 }
357 p if p == PropertyIdentifier::RECORD_COUNT => {
358 Ok(PropertyValue::Unsigned(self.records().len() as u64))
359 }
360 p if p == PropertyIdentifier::TOTAL_RECORD_COUNT => {
361 Ok(PropertyValue::Unsigned(u64::from(total)))
362 }
363 p if p == PropertyIdentifier::STATUS_FLAGS => Ok(common::compute_status_flags(
368 StatusFlags::empty(),
369 self.reliability,
370 false,
371 self.reporting.event_state(),
372 )),
373 p if p == PropertyIdentifier::RELIABILITY => {
374 Ok(PropertyValue::Enumerated(self.reliability.to_raw()))
375 }
376 p if p == PropertyIdentifier::LOG_BUFFER => Err(log_buffer_read_denied()),
378 p if p == PropertyIdentifier::LOG_DEVICE_OBJECT_PROPERTY => common::read_array(
380 device_reference::reference_elements(&self.log_device_object_property),
381 array_index,
382 ),
383 p if p == PropertyIdentifier::PROPERTY_LIST => {
384 read_property_list_property(&self.property_list(), array_index)
385 }
386 _ => Err(Error::Protocol {
387 class: ErrorClass::PROPERTY.to_raw() as u32,
388 code: ErrorCode::UNKNOWN_PROPERTY.to_raw() as u32,
389 }),
390 }
391 }
392
393 fn write_property(
394 &mut self,
395 property: PropertyIdentifier,
396 array_index: Option<u32>,
397 value: PropertyValue,
398 _priority: Option<u8>,
399 ) -> Result<(), Error> {
400 if property == PropertyIdentifier::LOG_ENABLE {
401 if let PropertyValue::Boolean(v) = value {
402 return self.lifecycle().write_enable(v);
403 }
404 return Err(Error::Protocol {
405 class: ErrorClass::PROPERTY.to_raw() as u32,
406 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
407 });
408 }
409 if let Some(result) = self.acquisition.write(property, &value) {
410 return result;
411 }
412 if let Some(result) = self.window.write(property, &value) {
413 result?;
415 self.lifecycle().refresh_window();
416 return Ok(());
417 }
418 if property == PropertyIdentifier::STOP_WHEN_FULL {
419 if let PropertyValue::Boolean(v) = value {
420 return self.lifecycle().write_stop_when_full(v);
421 }
422 return Err(Error::Protocol {
423 class: ErrorClass::PROPERTY.to_raw() as u32,
424 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
425 });
426 }
427 if property == PropertyIdentifier::RECORD_COUNT {
428 if let PropertyValue::Unsigned(0) = value {
429 return self.lifecycle().purge();
430 }
431 return Err(Error::Protocol {
432 class: ErrorClass::PROPERTY.to_raw() as u32,
433 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
434 });
435 }
436 if property == PropertyIdentifier::LOG_DEVICE_OBJECT_PROPERTY {
437 return self.write_log_device_object_property(array_index, value);
438 }
439 let total = self.log_buffer.total_record_count();
440 if let Some(result) = self.reporting.write(property, array_index, &value, total) {
441 return result;
442 }
443 if let Some(result) = common::write_description(&mut self.description, property, &value) {
444 return result;
445 }
446 Err(crate::common::unhandled_write_error(
447 self.property_metadata().as_ref(),
448 property,
449 array_index,
450 ))
451 }
452
453 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
454 multiple_metadata::for_object(self)
455 }
456
457 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
458 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
459 }
460
461 fn bind_clock_internal(&mut self, clock: Option<Arc<dyn ClockReader>>) {
462 self.clock = clock;
463 }
464
465 fn log_record_identities_internal(&self) -> Option<Vec<LogRecordIdentity>> {
466 Some(self.log_buffer.identities())
467 }
468
469 fn log_buffer_internal(&self) -> Option<&dyn LogBufferRecords> {
470 Some(&self.log_buffer)
471 }
472
473 fn add_trend_multiple_record(&mut self, record: BACnetLogMultipleRecord) -> Result<(), Error> {
474 self.add_record(record)
475 }
476
477 fn refresh_log_window_internal(&mut self) -> bool {
478 self.lifecycle().refresh_window()
479 }
480
481 impl_buffer_ready_reporting!(reporting, log_buffer);
482}