1use std::borrow::Cow;
4use std::collections::VecDeque;
5
6use bacnet_types::constructed::{BACnetDeviceObjectPropertyReference, BACnetLogRecord, LogDatum};
7use bacnet_types::enums::{ErrorClass, ErrorCode, ObjectType, PropertyIdentifier};
8use bacnet_types::error::Error;
9use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
10
11use crate::common::{self, read_property_list_property};
12use crate::traits::BACnetObject;
13
14pub struct TrendLogObject {
19 oid: ObjectIdentifier,
20 name: String,
21 description: String,
22 log_enable: bool,
23 log_interval: u32,
24 stop_when_full: bool,
25 buffer_size: u32,
26 buffer: VecDeque<BACnetLogRecord>,
27 total_record_count: u64,
28 out_of_service: bool,
29 reliability: u32,
30 status_flags: StatusFlags,
31 log_device_object_property: Option<BACnetDeviceObjectPropertyReference>,
32 logging_type: u32, }
34
35impl TrendLogObject {
36 pub fn new(instance: u32, name: impl Into<String>, buffer_size: u32) -> Result<Self, Error> {
37 let oid = ObjectIdentifier::new(ObjectType::TREND_LOG, instance)?;
38 Ok(Self {
39 oid,
40 name: name.into(),
41 description: String::new(),
42 log_enable: true,
43 log_interval: 0,
44 stop_when_full: false,
45 buffer_size,
46 buffer: VecDeque::new(),
47 total_record_count: 0,
48 out_of_service: false,
49 reliability: 0,
50 status_flags: StatusFlags::empty(),
51 log_device_object_property: None,
52 logging_type: 0,
53 })
54 }
55
56 pub fn add_record(&mut self, record: BACnetLogRecord) {
58 if !self.log_enable {
59 return;
60 }
61 if self.buffer.len() >= self.buffer_size as usize {
62 if self.stop_when_full {
63 return;
64 }
65 self.buffer.pop_front();
66 }
67 self.buffer.push_back(record);
68 self.total_record_count += 1;
69 }
70
71 pub fn records(&self) -> &VecDeque<BACnetLogRecord> {
73 &self.buffer
74 }
75
76 pub fn clear(&mut self) {
78 self.buffer.clear();
79 }
80
81 pub fn set_description(&mut self, desc: impl Into<String>) {
83 self.description = desc.into();
84 }
85
86 pub fn set_log_device_object_property(
88 &mut self,
89 reference: Option<BACnetDeviceObjectPropertyReference>,
90 ) {
91 self.log_device_object_property = reference;
92 }
93
94 pub fn set_logging_type(&mut self, logging_type: u32) {
96 self.logging_type = logging_type;
97 }
98}
99
100impl BACnetObject for TrendLogObject {
101 fn object_identifier(&self) -> ObjectIdentifier {
102 self.oid
103 }
104
105 fn object_name(&self) -> &str {
106 &self.name
107 }
108
109 fn read_property(
110 &self,
111 property: PropertyIdentifier,
112 array_index: Option<u32>,
113 ) -> Result<PropertyValue, Error> {
114 match property {
115 p if p == PropertyIdentifier::OBJECT_IDENTIFIER => {
116 Ok(PropertyValue::ObjectIdentifier(self.oid))
117 }
118 p if p == PropertyIdentifier::OBJECT_NAME => {
119 Ok(PropertyValue::CharacterString(self.name.clone()))
120 }
121 p if p == PropertyIdentifier::DESCRIPTION => {
122 Ok(PropertyValue::CharacterString(self.description.clone()))
123 }
124 p if p == PropertyIdentifier::OBJECT_TYPE => {
125 Ok(PropertyValue::Enumerated(ObjectType::TREND_LOG.to_raw()))
126 }
127 p if p == PropertyIdentifier::LOG_ENABLE => Ok(PropertyValue::Boolean(self.log_enable)),
128 p if p == PropertyIdentifier::LOG_INTERVAL => {
129 Ok(PropertyValue::Unsigned(self.log_interval as u64))
130 }
131 p if p == PropertyIdentifier::STOP_WHEN_FULL => {
132 Ok(PropertyValue::Boolean(self.stop_when_full))
133 }
134 p if p == PropertyIdentifier::BUFFER_SIZE => {
135 Ok(PropertyValue::Unsigned(self.buffer_size as u64))
136 }
137 p if p == PropertyIdentifier::RECORD_COUNT => {
138 Ok(PropertyValue::Unsigned(self.buffer.len() as u64))
139 }
140 p if p == PropertyIdentifier::TOTAL_RECORD_COUNT => {
141 Ok(PropertyValue::Unsigned(self.total_record_count))
142 }
143 p if p == PropertyIdentifier::STATUS_FLAGS => Ok(PropertyValue::BitString {
144 unused_bits: 4,
145 data: vec![self.status_flags.bits() << 4],
146 }),
147 p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(0)),
148 p if p == PropertyIdentifier::RELIABILITY => {
149 Ok(PropertyValue::Enumerated(self.reliability))
150 }
151 p if p == PropertyIdentifier::OUT_OF_SERVICE => {
152 Ok(PropertyValue::Boolean(self.out_of_service))
153 }
154 p if p == PropertyIdentifier::LOG_BUFFER => {
155 let records = self
156 .buffer
157 .iter()
158 .map(|record| {
159 let datum_value = match &record.log_datum {
160 LogDatum::LogStatus(v) => PropertyValue::Unsigned(*v as u64),
161 LogDatum::BooleanValue(v) => PropertyValue::Boolean(*v),
162 LogDatum::RealValue(v) => PropertyValue::Real(*v),
163 LogDatum::EnumValue(v) => PropertyValue::Enumerated(*v),
164 LogDatum::UnsignedValue(v) => PropertyValue::Unsigned(*v),
165 LogDatum::SignedValue(v) => PropertyValue::Signed(*v as i32),
166 LogDatum::BitstringValue { unused_bits, data } => {
167 PropertyValue::BitString {
168 unused_bits: *unused_bits,
169 data: data.clone(),
170 }
171 }
172 LogDatum::NullValue => PropertyValue::Null,
173 LogDatum::Failure {
174 error_class,
175 error_code,
176 } => PropertyValue::List(vec![
177 PropertyValue::Unsigned(*error_class as u64),
178 PropertyValue::Unsigned(*error_code as u64),
179 ]),
180 LogDatum::TimeChange(v) => PropertyValue::Real(*v),
181 LogDatum::AnyValue(bytes) => PropertyValue::OctetString(bytes.clone()),
182 };
183 PropertyValue::List(vec![
184 PropertyValue::Date(record.date),
185 PropertyValue::Time(record.time),
186 datum_value,
187 ])
188 })
189 .collect();
190 Ok(PropertyValue::List(records))
191 }
192 p if p == PropertyIdentifier::LOGGING_TYPE => {
193 Ok(PropertyValue::Enumerated(self.logging_type))
194 }
195 p if p == PropertyIdentifier::LOG_DEVICE_OBJECT_PROPERTY => {
196 match &self.log_device_object_property {
197 None => Ok(PropertyValue::Null),
198 Some(r) => Ok(PropertyValue::List(vec![
199 PropertyValue::ObjectIdentifier(r.object_identifier),
200 PropertyValue::Unsigned(r.property_identifier as u64),
201 match r.property_array_index {
202 Some(idx) => PropertyValue::Unsigned(idx as u64),
203 None => PropertyValue::Null,
204 },
205 match r.device_identifier {
206 Some(dev) => PropertyValue::ObjectIdentifier(dev),
207 None => PropertyValue::Null,
208 },
209 ])),
210 }
211 }
212 p if p == PropertyIdentifier::PROPERTY_LIST => {
213 read_property_list_property(&self.property_list(), array_index)
214 }
215 _ => Err(Error::Protocol {
216 class: ErrorClass::PROPERTY.to_raw() as u32,
217 code: ErrorCode::UNKNOWN_PROPERTY.to_raw() as u32,
218 }),
219 }
220 }
221
222 fn write_property(
223 &mut self,
224 property: PropertyIdentifier,
225 _array_index: Option<u32>,
226 value: PropertyValue,
227 _priority: Option<u8>,
228 ) -> Result<(), Error> {
229 if property == PropertyIdentifier::LOG_ENABLE {
230 if let PropertyValue::Boolean(v) = value {
231 self.log_enable = v;
232 return Ok(());
233 }
234 return Err(Error::Protocol {
235 class: ErrorClass::PROPERTY.to_raw() as u32,
236 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
237 });
238 }
239 if property == PropertyIdentifier::LOG_INTERVAL {
240 if let PropertyValue::Unsigned(v) = value {
241 self.log_interval = common::u64_to_u32(v)?;
242 return Ok(());
243 }
244 return Err(Error::Protocol {
245 class: ErrorClass::PROPERTY.to_raw() as u32,
246 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
247 });
248 }
249 if property == PropertyIdentifier::STOP_WHEN_FULL {
250 if let PropertyValue::Boolean(v) = value {
251 self.stop_when_full = v;
252 return Ok(());
253 }
254 return Err(Error::Protocol {
255 class: ErrorClass::PROPERTY.to_raw() as u32,
256 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
257 });
258 }
259 if property == PropertyIdentifier::RECORD_COUNT {
260 if let PropertyValue::Unsigned(0) = value {
262 self.buffer.clear();
263 return Ok(());
264 }
265 return Err(Error::Protocol {
266 class: ErrorClass::PROPERTY.to_raw() as u32,
267 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
268 });
269 }
270 if property == PropertyIdentifier::RELIABILITY {
271 if let PropertyValue::Enumerated(v) = value {
272 self.reliability = v;
273 return Ok(());
274 }
275 return Err(Error::Protocol {
276 class: ErrorClass::PROPERTY.to_raw() as u32,
277 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
278 });
279 }
280 if property == PropertyIdentifier::OUT_OF_SERVICE {
281 if let PropertyValue::Boolean(v) = value {
282 self.out_of_service = v;
283 return Ok(());
284 }
285 return Err(Error::Protocol {
286 class: ErrorClass::PROPERTY.to_raw() as u32,
287 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
288 });
289 }
290 if property == PropertyIdentifier::DESCRIPTION {
291 if let PropertyValue::CharacterString(s) = value {
292 self.description = s;
293 return Ok(());
294 }
295 return Err(Error::Protocol {
296 class: ErrorClass::PROPERTY.to_raw() as u32,
297 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
298 });
299 }
300 Err(Error::Protocol {
301 class: ErrorClass::PROPERTY.to_raw() as u32,
302 code: ErrorCode::WRITE_ACCESS_DENIED.to_raw() as u32,
303 })
304 }
305
306 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
307 static PROPS: &[PropertyIdentifier] = &[
308 PropertyIdentifier::OBJECT_IDENTIFIER,
309 PropertyIdentifier::OBJECT_NAME,
310 PropertyIdentifier::DESCRIPTION,
311 PropertyIdentifier::OBJECT_TYPE,
312 PropertyIdentifier::LOG_ENABLE,
313 PropertyIdentifier::LOG_INTERVAL,
314 PropertyIdentifier::STOP_WHEN_FULL,
315 PropertyIdentifier::BUFFER_SIZE,
316 PropertyIdentifier::LOG_BUFFER,
317 PropertyIdentifier::RECORD_COUNT,
318 PropertyIdentifier::TOTAL_RECORD_COUNT,
319 PropertyIdentifier::STATUS_FLAGS,
320 PropertyIdentifier::EVENT_STATE,
321 PropertyIdentifier::RELIABILITY,
322 PropertyIdentifier::OUT_OF_SERVICE,
323 PropertyIdentifier::LOGGING_TYPE,
324 PropertyIdentifier::LOG_DEVICE_OBJECT_PROPERTY,
325 ];
326 Cow::Borrowed(PROPS)
327 }
328
329 fn add_trend_record(&mut self, record: BACnetLogRecord) {
330 self.add_record(record);
331 }
332}
333
334pub struct TrendLogMultipleObject {
344 oid: ObjectIdentifier,
345 name: String,
346 description: String,
347 log_enable: bool,
348 log_interval: u32,
349 stop_when_full: bool,
350 buffer_size: u32,
351 buffer: VecDeque<BACnetLogRecord>,
352 total_record_count: u64,
353 status_flags: StatusFlags,
354 log_device_object_property: Vec<BACnetDeviceObjectPropertyReference>,
355 logging_type: u32, out_of_service: bool,
357 reliability: u32,
358}
359
360impl TrendLogMultipleObject {
361 pub fn new(instance: u32, name: impl Into<String>, buffer_size: u32) -> Result<Self, Error> {
362 let oid = ObjectIdentifier::new(ObjectType::TREND_LOG_MULTIPLE, instance)?;
363 Ok(Self {
364 oid,
365 name: name.into(),
366 description: String::new(),
367 log_enable: true,
368 log_interval: 0,
369 stop_when_full: false,
370 buffer_size,
371 buffer: VecDeque::new(),
372 total_record_count: 0,
373 status_flags: StatusFlags::empty(),
374 log_device_object_property: Vec::new(),
375 logging_type: 0,
376 out_of_service: false,
377 reliability: 0,
378 })
379 }
380
381 pub fn add_record(&mut self, record: BACnetLogRecord) {
383 if !self.log_enable {
384 return;
385 }
386 if self.buffer.len() >= self.buffer_size as usize {
387 if self.stop_when_full {
388 return;
389 }
390 self.buffer.pop_front();
391 }
392 self.buffer.push_back(record);
393 self.total_record_count += 1;
394 }
395
396 pub fn add_property_reference(&mut self, reference: BACnetDeviceObjectPropertyReference) {
398 self.log_device_object_property.push(reference);
399 }
400
401 pub fn records(&self) -> &VecDeque<BACnetLogRecord> {
403 &self.buffer
404 }
405
406 pub fn clear(&mut self) {
408 self.buffer.clear();
409 }
410
411 pub fn set_description(&mut self, desc: impl Into<String>) {
413 self.description = desc.into();
414 }
415
416 pub fn set_logging_type(&mut self, logging_type: u32) {
418 self.logging_type = logging_type;
419 }
420}
421
422impl BACnetObject for TrendLogMultipleObject {
423 fn object_identifier(&self) -> ObjectIdentifier {
424 self.oid
425 }
426
427 fn object_name(&self) -> &str {
428 &self.name
429 }
430
431 fn read_property(
432 &self,
433 property: PropertyIdentifier,
434 array_index: Option<u32>,
435 ) -> Result<PropertyValue, Error> {
436 match property {
437 p if p == PropertyIdentifier::OBJECT_IDENTIFIER => {
438 Ok(PropertyValue::ObjectIdentifier(self.oid))
439 }
440 p if p == PropertyIdentifier::OBJECT_NAME => {
441 Ok(PropertyValue::CharacterString(self.name.clone()))
442 }
443 p if p == PropertyIdentifier::DESCRIPTION => {
444 Ok(PropertyValue::CharacterString(self.description.clone()))
445 }
446 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
447 ObjectType::TREND_LOG_MULTIPLE.to_raw(),
448 )),
449 p if p == PropertyIdentifier::LOG_ENABLE => Ok(PropertyValue::Boolean(self.log_enable)),
450 p if p == PropertyIdentifier::LOG_INTERVAL => {
451 Ok(PropertyValue::Unsigned(self.log_interval as u64))
452 }
453 p if p == PropertyIdentifier::STOP_WHEN_FULL => {
454 Ok(PropertyValue::Boolean(self.stop_when_full))
455 }
456 p if p == PropertyIdentifier::BUFFER_SIZE => {
457 Ok(PropertyValue::Unsigned(self.buffer_size as u64))
458 }
459 p if p == PropertyIdentifier::RECORD_COUNT => {
460 Ok(PropertyValue::Unsigned(self.buffer.len() as u64))
461 }
462 p if p == PropertyIdentifier::TOTAL_RECORD_COUNT => {
463 Ok(PropertyValue::Unsigned(self.total_record_count))
464 }
465 p if p == PropertyIdentifier::STATUS_FLAGS => Ok(PropertyValue::BitString {
466 unused_bits: 4,
467 data: vec![self.status_flags.bits() << 4],
468 }),
469 p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(0)),
470 p if p == PropertyIdentifier::OUT_OF_SERVICE => {
471 Ok(PropertyValue::Boolean(self.out_of_service))
472 }
473 p if p == PropertyIdentifier::RELIABILITY => {
474 Ok(PropertyValue::Enumerated(self.reliability))
475 }
476 p if p == PropertyIdentifier::LOG_BUFFER => {
477 let records = self
478 .buffer
479 .iter()
480 .map(|record| {
481 let datum_value = match &record.log_datum {
482 LogDatum::LogStatus(v) => PropertyValue::Unsigned(*v as u64),
483 LogDatum::BooleanValue(v) => PropertyValue::Boolean(*v),
484 LogDatum::RealValue(v) => PropertyValue::Real(*v),
485 LogDatum::EnumValue(v) => PropertyValue::Enumerated(*v),
486 LogDatum::UnsignedValue(v) => PropertyValue::Unsigned(*v),
487 LogDatum::SignedValue(v) => PropertyValue::Signed(*v as i32),
488 LogDatum::BitstringValue { unused_bits, data } => {
489 PropertyValue::BitString {
490 unused_bits: *unused_bits,
491 data: data.clone(),
492 }
493 }
494 LogDatum::NullValue => PropertyValue::Null,
495 LogDatum::Failure {
496 error_class,
497 error_code,
498 } => PropertyValue::List(vec![
499 PropertyValue::Unsigned(*error_class as u64),
500 PropertyValue::Unsigned(*error_code as u64),
501 ]),
502 LogDatum::TimeChange(v) => PropertyValue::Real(*v),
503 LogDatum::AnyValue(bytes) => PropertyValue::OctetString(bytes.clone()),
504 };
505 PropertyValue::List(vec![
506 PropertyValue::Date(record.date),
507 PropertyValue::Time(record.time),
508 datum_value,
509 ])
510 })
511 .collect();
512 Ok(PropertyValue::List(records))
513 }
514 p if p == PropertyIdentifier::LOGGING_TYPE => {
515 Ok(PropertyValue::Enumerated(self.logging_type))
516 }
517 p if p == PropertyIdentifier::LOG_DEVICE_OBJECT_PROPERTY => {
518 let refs: Vec<PropertyValue> = self
519 .log_device_object_property
520 .iter()
521 .map(|r| {
522 PropertyValue::List(vec![
523 PropertyValue::ObjectIdentifier(r.object_identifier),
524 PropertyValue::Unsigned(r.property_identifier as u64),
525 match r.property_array_index {
526 Some(idx) => PropertyValue::Unsigned(idx as u64),
527 None => PropertyValue::Null,
528 },
529 match r.device_identifier {
530 Some(dev) => PropertyValue::ObjectIdentifier(dev),
531 None => PropertyValue::Null,
532 },
533 ])
534 })
535 .collect();
536 Ok(PropertyValue::List(refs))
537 }
538 p if p == PropertyIdentifier::PROPERTY_LIST => {
539 read_property_list_property(&self.property_list(), array_index)
540 }
541 _ => Err(Error::Protocol {
542 class: ErrorClass::PROPERTY.to_raw() as u32,
543 code: ErrorCode::UNKNOWN_PROPERTY.to_raw() as u32,
544 }),
545 }
546 }
547
548 fn write_property(
549 &mut self,
550 property: PropertyIdentifier,
551 _array_index: Option<u32>,
552 value: PropertyValue,
553 _priority: Option<u8>,
554 ) -> Result<(), Error> {
555 if property == PropertyIdentifier::LOG_ENABLE {
556 if let PropertyValue::Boolean(v) = value {
557 self.log_enable = v;
558 return Ok(());
559 }
560 return Err(Error::Protocol {
561 class: ErrorClass::PROPERTY.to_raw() as u32,
562 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
563 });
564 }
565 if property == PropertyIdentifier::LOG_INTERVAL {
566 if let PropertyValue::Unsigned(v) = value {
567 self.log_interval = common::u64_to_u32(v)?;
568 return Ok(());
569 }
570 return Err(Error::Protocol {
571 class: ErrorClass::PROPERTY.to_raw() as u32,
572 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
573 });
574 }
575 if property == PropertyIdentifier::STOP_WHEN_FULL {
576 if let PropertyValue::Boolean(v) = value {
577 self.stop_when_full = v;
578 return Ok(());
579 }
580 return Err(Error::Protocol {
581 class: ErrorClass::PROPERTY.to_raw() as u32,
582 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
583 });
584 }
585 if property == PropertyIdentifier::RECORD_COUNT {
586 if let PropertyValue::Unsigned(0) = value {
588 self.buffer.clear();
589 return Ok(());
590 }
591 return Err(Error::Protocol {
592 class: ErrorClass::PROPERTY.to_raw() as u32,
593 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
594 });
595 }
596 if property == PropertyIdentifier::DESCRIPTION {
597 if let PropertyValue::CharacterString(s) = value {
598 self.description = s;
599 return Ok(());
600 }
601 return Err(Error::Protocol {
602 class: ErrorClass::PROPERTY.to_raw() as u32,
603 code: ErrorCode::INVALID_DATA_TYPE.to_raw() as u32,
604 });
605 }
606 Err(Error::Protocol {
607 class: ErrorClass::PROPERTY.to_raw() as u32,
608 code: ErrorCode::WRITE_ACCESS_DENIED.to_raw() as u32,
609 })
610 }
611
612 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
613 static PROPS: &[PropertyIdentifier] = &[
614 PropertyIdentifier::OBJECT_IDENTIFIER,
615 PropertyIdentifier::OBJECT_NAME,
616 PropertyIdentifier::DESCRIPTION,
617 PropertyIdentifier::OBJECT_TYPE,
618 PropertyIdentifier::LOG_ENABLE,
619 PropertyIdentifier::LOG_INTERVAL,
620 PropertyIdentifier::STOP_WHEN_FULL,
621 PropertyIdentifier::BUFFER_SIZE,
622 PropertyIdentifier::LOG_BUFFER,
623 PropertyIdentifier::RECORD_COUNT,
624 PropertyIdentifier::TOTAL_RECORD_COUNT,
625 PropertyIdentifier::STATUS_FLAGS,
626 PropertyIdentifier::EVENT_STATE,
627 PropertyIdentifier::OUT_OF_SERVICE,
628 PropertyIdentifier::RELIABILITY,
629 PropertyIdentifier::LOGGING_TYPE,
630 PropertyIdentifier::LOG_DEVICE_OBJECT_PROPERTY,
631 ];
632 Cow::Borrowed(PROPS)
633 }
634
635 fn add_trend_record(&mut self, record: BACnetLogRecord) {
636 self.add_record(record);
637 }
638}
639
640#[cfg(test)]
641mod tests;