1use bacnet_types::enums::{ObjectType, PropertyIdentifier};
6use bacnet_types::error::Error;
7use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, StatusFlags};
8use std::borrow::Cow;
9
10use crate::common::{self, read_common_properties, read_event_properties, write_event_properties};
11use crate::event::{EventStateChange, OutOfRangeDetector};
12use crate::traits::BACnetObject;
13
14pub struct AnalogInputObject {
20 oid: ObjectIdentifier,
21 name: String,
22 description: String,
23 present_value: f32,
24 units: u32,
25 out_of_service: bool,
26 status_flags: StatusFlags,
27 cov_increment: f32,
31 event_detector: OutOfRangeDetector,
32 reliability: u32,
34 min_pres_value: Option<f32>,
36 max_pres_value: Option<f32>,
38 event_time_stamps: [BACnetTimeStamp; 3],
40 event_message_texts: [String; 3],
42}
43
44impl AnalogInputObject {
45 pub fn new(instance: u32, name: impl Into<String>, units: u32) -> Result<Self, Error> {
47 let _oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, instance)?;
48 Ok(Self {
49 oid: _oid,
50 name: name.into(),
51 description: String::new(),
52 present_value: 0.0,
53 units,
54 out_of_service: false,
55 status_flags: StatusFlags::empty(),
56 cov_increment: 0.0,
57 event_detector: OutOfRangeDetector::default(),
58 reliability: 0,
59 min_pres_value: None,
60 max_pres_value: None,
61 event_time_stamps: [
62 BACnetTimeStamp::SequenceNumber(0),
63 BACnetTimeStamp::SequenceNumber(0),
64 BACnetTimeStamp::SequenceNumber(0),
65 ],
66 event_message_texts: [String::new(), String::new(), String::new()],
67 })
68 }
69
70 pub fn set_present_value(&mut self, value: f32) {
72 debug_assert!(
73 value.is_finite(),
74 "set_present_value called with non-finite value"
75 );
76 self.present_value = value;
77 }
78
79 pub fn set_description(&mut self, desc: impl Into<String>) {
81 self.description = desc.into();
82 }
83
84 pub fn set_min_pres_value(&mut self, value: f32) {
86 self.min_pres_value = Some(value);
87 }
88
89 pub fn set_max_pres_value(&mut self, value: f32) {
91 self.max_pres_value = Some(value);
92 }
93}
94
95impl BACnetObject for AnalogInputObject {
96 fn object_identifier(&self) -> ObjectIdentifier {
97 self.oid
98 }
99
100 fn object_name(&self) -> &str {
101 &self.name
102 }
103
104 fn read_property(
105 &self,
106 property: PropertyIdentifier,
107 array_index: Option<u32>,
108 ) -> Result<PropertyValue, Error> {
109 if property == PropertyIdentifier::STATUS_FLAGS {
111 return Ok(common::compute_status_flags(
112 self.status_flags,
113 self.reliability,
114 self.out_of_service,
115 self.event_detector.event_state.to_raw(),
116 ));
117 }
118 if let Some(result) = read_common_properties!(self, property, array_index) {
119 return result;
120 }
121 if let Some(result) = read_event_properties!(self, property) {
122 return result;
123 }
124 match property {
125 p if p == PropertyIdentifier::OBJECT_TYPE => {
126 Ok(PropertyValue::Enumerated(ObjectType::ANALOG_INPUT.to_raw()))
127 }
128 p if p == PropertyIdentifier::PRESENT_VALUE => {
129 Ok(PropertyValue::Real(self.present_value))
130 }
131 p if p == PropertyIdentifier::UNITS => Ok(PropertyValue::Enumerated(self.units)),
132 p if p == PropertyIdentifier::COV_INCREMENT => {
133 Ok(PropertyValue::Real(self.cov_increment))
134 }
135 p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
136 Some(v) => Ok(PropertyValue::Real(v)),
137 None => Err(common::unknown_property_error()),
138 },
139 p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
140 Some(v) => Ok(PropertyValue::Real(v)),
141 None => Err(common::unknown_property_error()),
142 },
143 _ => Err(common::unknown_property_error()),
144 }
145 }
146
147 fn write_property(
148 &mut self,
149 property: PropertyIdentifier,
150 _array_index: Option<u32>,
151 value: PropertyValue,
152 _priority: Option<u8>,
153 ) -> Result<(), Error> {
154 if property == PropertyIdentifier::PRESENT_VALUE {
156 if !self.out_of_service {
157 return Err(common::write_access_denied_error());
158 }
159 if let PropertyValue::Real(v) = value {
160 if !v.is_finite() {
161 return Err(common::value_out_of_range_error());
162 }
163 self.present_value = v;
164 return Ok(());
165 }
166 return Err(common::invalid_data_type_error());
167 }
168 if let Some(result) =
169 common::write_out_of_service(&mut self.out_of_service, property, &value)
170 {
171 return result;
172 }
173 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
174 return result;
175 }
176 if let Some(result) = common::write_description(&mut self.description, property, &value) {
177 return result;
178 }
179 if property == PropertyIdentifier::RELIABILITY {
180 if let PropertyValue::Enumerated(v) = value {
181 self.reliability = v;
182 return Ok(());
183 }
184 return Err(common::invalid_data_type_error());
185 }
186 if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
187 {
188 return result;
189 }
190 if let Some(result) = write_event_properties!(self, property, value) {
191 return result;
192 }
193 Err(common::write_access_denied_error())
194 }
195
196 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
197 static PROPS: &[PropertyIdentifier] = &[
198 PropertyIdentifier::OBJECT_IDENTIFIER,
199 PropertyIdentifier::OBJECT_NAME,
200 PropertyIdentifier::DESCRIPTION,
201 PropertyIdentifier::OBJECT_TYPE,
202 PropertyIdentifier::PRESENT_VALUE,
203 PropertyIdentifier::STATUS_FLAGS,
204 PropertyIdentifier::EVENT_STATE,
205 PropertyIdentifier::OUT_OF_SERVICE,
206 PropertyIdentifier::UNITS,
207 PropertyIdentifier::COV_INCREMENT,
208 PropertyIdentifier::HIGH_LIMIT,
209 PropertyIdentifier::LOW_LIMIT,
210 PropertyIdentifier::DEADBAND,
211 PropertyIdentifier::LIMIT_ENABLE,
212 PropertyIdentifier::EVENT_ENABLE,
213 PropertyIdentifier::NOTIFY_TYPE,
214 PropertyIdentifier::NOTIFICATION_CLASS,
215 PropertyIdentifier::TIME_DELAY,
216 PropertyIdentifier::RELIABILITY,
217 PropertyIdentifier::ACKED_TRANSITIONS,
218 PropertyIdentifier::EVENT_TIME_STAMPS,
219 PropertyIdentifier::EVENT_MESSAGE_TEXTS,
220 ];
221 Cow::Borrowed(PROPS)
222 }
223
224 fn supports_cov(&self) -> bool {
225 true
226 }
227
228 fn cov_increment(&self) -> Option<f32> {
229 Some(self.cov_increment)
230 }
231
232 fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
233 self.event_detector.evaluate(self.present_value)
234 }
235
236 fn acknowledge_alarm(&mut self, transition_bit: u8) -> Result<(), bacnet_types::error::Error> {
237 self.event_detector.acked_transitions |= transition_bit & 0x07;
238 Ok(())
239 }
240}
241
242pub struct AnalogOutputObject {
248 oid: ObjectIdentifier,
249 name: String,
250 description: String,
251 present_value: f32,
252 units: u32,
253 out_of_service: bool,
254 status_flags: StatusFlags,
255 priority_array: [Option<f32>; 16],
256 relinquish_default: f32,
257 cov_increment: f32,
261 event_detector: OutOfRangeDetector,
262 reliability: u32,
263 min_pres_value: Option<f32>,
264 max_pres_value: Option<f32>,
265 event_time_stamps: [BACnetTimeStamp; 3],
266 event_message_texts: [String; 3],
267 value_source: common::ValueSourceTracking,
269}
270
271impl AnalogOutputObject {
272 pub fn new(instance: u32, name: impl Into<String>, units: u32) -> Result<Self, Error> {
274 let oid = ObjectIdentifier::new(ObjectType::ANALOG_OUTPUT, instance)?;
275 Ok(Self {
276 oid,
277 name: name.into(),
278 description: String::new(),
279 present_value: 0.0,
280 units,
281 out_of_service: false,
282 status_flags: StatusFlags::empty(),
283 priority_array: [None; 16],
284 relinquish_default: 0.0,
285 cov_increment: 0.0,
286 event_detector: OutOfRangeDetector::default(),
287 reliability: 0,
288 min_pres_value: None,
289 max_pres_value: None,
290 event_time_stamps: [
291 BACnetTimeStamp::SequenceNumber(0),
292 BACnetTimeStamp::SequenceNumber(0),
293 BACnetTimeStamp::SequenceNumber(0),
294 ],
295 event_message_texts: [String::new(), String::new(), String::new()],
296 value_source: common::ValueSourceTracking::default(),
297 })
298 }
299
300 pub fn set_description(&mut self, desc: impl Into<String>) {
302 self.description = desc.into();
303 }
304
305 pub fn set_min_pres_value(&mut self, value: f32) {
307 self.min_pres_value = Some(value);
308 }
309
310 pub fn set_max_pres_value(&mut self, value: f32) {
312 self.max_pres_value = Some(value);
313 }
314
315 fn recalculate_present_value(&mut self) {
317 self.present_value =
318 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
319 }
320}
321
322impl BACnetObject for AnalogOutputObject {
323 fn object_identifier(&self) -> ObjectIdentifier {
324 self.oid
325 }
326
327 fn object_name(&self) -> &str {
328 &self.name
329 }
330
331 fn read_property(
332 &self,
333 property: PropertyIdentifier,
334 array_index: Option<u32>,
335 ) -> Result<PropertyValue, Error> {
336 if property == PropertyIdentifier::STATUS_FLAGS {
337 return Ok(common::compute_status_flags(
338 self.status_flags,
339 self.reliability,
340 self.out_of_service,
341 self.event_detector.event_state.to_raw(),
342 ));
343 }
344 if let Some(result) = read_common_properties!(self, property, array_index) {
345 return result;
346 }
347 if let Some(result) = read_event_properties!(self, property) {
348 return result;
349 }
350 match property {
351 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
352 ObjectType::ANALOG_OUTPUT.to_raw(),
353 )),
354 p if p == PropertyIdentifier::PRESENT_VALUE => {
355 Ok(PropertyValue::Real(self.present_value))
356 }
357 p if p == PropertyIdentifier::UNITS => Ok(PropertyValue::Enumerated(self.units)),
358 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
359 common::read_priority_array!(self, array_index, PropertyValue::Real)
360 }
361 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
362 Ok(PropertyValue::Real(self.relinquish_default))
363 }
364 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
365 Ok(common::current_command_priority(&self.priority_array))
366 }
367 p if p == PropertyIdentifier::VALUE_SOURCE => {
368 Ok(self.value_source.value_source.clone())
369 }
370 p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
371 match self.value_source.last_command_time {
372 BACnetTimeStamp::SequenceNumber(n) => n,
373 _ => 0,
374 },
375 )),
376 p if p == PropertyIdentifier::COV_INCREMENT => {
377 Ok(PropertyValue::Real(self.cov_increment))
378 }
379 p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
380 Some(v) => Ok(PropertyValue::Real(v)),
381 None => Err(common::unknown_property_error()),
382 },
383 p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
384 Some(v) => Ok(PropertyValue::Real(v)),
385 None => Err(common::unknown_property_error()),
386 },
387 _ => Err(common::unknown_property_error()),
388 }
389 }
390
391 fn write_property(
392 &mut self,
393 property: PropertyIdentifier,
394 array_index: Option<u32>,
395 value: PropertyValue,
396 priority: Option<u8>,
397 ) -> Result<(), Error> {
398 common::write_priority_array_direct!(self, property, array_index, value, |v| {
399 if let PropertyValue::Real(f) = v {
400 if !f.is_finite() {
401 return Err(common::value_out_of_range_error());
402 }
403 Ok(f)
404 } else {
405 Err(common::invalid_data_type_error())
406 }
407 });
408 if property == PropertyIdentifier::PRESENT_VALUE {
409 return common::write_priority_array!(self, value, priority, |v| {
410 if let PropertyValue::Real(f) = v {
411 if !f.is_finite() {
412 return Err(common::value_out_of_range_error());
413 }
414 Ok(f)
415 } else {
416 Err(common::invalid_data_type_error())
417 }
418 });
419 }
420 if let Some(result) =
421 common::write_out_of_service(&mut self.out_of_service, property, &value)
422 {
423 return result;
424 }
425 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
426 return result;
427 }
428 if let Some(result) = common::write_description(&mut self.description, property, &value) {
429 return result;
430 }
431 if property == PropertyIdentifier::RELIABILITY {
432 if let PropertyValue::Enumerated(v) = value {
433 self.reliability = v;
434 return Ok(());
435 }
436 return Err(common::invalid_data_type_error());
437 }
438 if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
439 {
440 return result;
441 }
442 if let Some(result) = write_event_properties!(self, property, value) {
443 return result;
444 }
445 Err(common::write_access_denied_error())
446 }
447
448 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
449 static PROPS: &[PropertyIdentifier] = &[
450 PropertyIdentifier::OBJECT_IDENTIFIER,
451 PropertyIdentifier::OBJECT_NAME,
452 PropertyIdentifier::DESCRIPTION,
453 PropertyIdentifier::OBJECT_TYPE,
454 PropertyIdentifier::PRESENT_VALUE,
455 PropertyIdentifier::STATUS_FLAGS,
456 PropertyIdentifier::EVENT_STATE,
457 PropertyIdentifier::OUT_OF_SERVICE,
458 PropertyIdentifier::UNITS,
459 PropertyIdentifier::PRIORITY_ARRAY,
460 PropertyIdentifier::RELINQUISH_DEFAULT,
461 PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
462 PropertyIdentifier::COV_INCREMENT,
463 PropertyIdentifier::HIGH_LIMIT,
464 PropertyIdentifier::LOW_LIMIT,
465 PropertyIdentifier::DEADBAND,
466 PropertyIdentifier::LIMIT_ENABLE,
467 PropertyIdentifier::EVENT_ENABLE,
468 PropertyIdentifier::NOTIFY_TYPE,
469 PropertyIdentifier::NOTIFICATION_CLASS,
470 PropertyIdentifier::TIME_DELAY,
471 PropertyIdentifier::RELIABILITY,
472 PropertyIdentifier::ACKED_TRANSITIONS,
473 PropertyIdentifier::EVENT_TIME_STAMPS,
474 PropertyIdentifier::EVENT_MESSAGE_TEXTS,
475 ];
476 Cow::Borrowed(PROPS)
477 }
478
479 fn supports_cov(&self) -> bool {
480 true
481 }
482
483 fn cov_increment(&self) -> Option<f32> {
484 Some(self.cov_increment)
485 }
486
487 fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
488 self.event_detector.evaluate(self.present_value)
489 }
490
491 fn acknowledge_alarm(&mut self, transition_bit: u8) -> Result<(), bacnet_types::error::Error> {
492 self.event_detector.acked_transitions |= transition_bit & 0x07;
493 Ok(())
494 }
495}
496
497pub struct AnalogValueObject {
503 oid: ObjectIdentifier,
504 name: String,
505 description: String,
506 present_value: f32,
507 units: u32,
508 out_of_service: bool,
509 status_flags: StatusFlags,
510 priority_array: [Option<f32>; 16],
512 relinquish_default: f32,
513 cov_increment: f32,
517 event_detector: OutOfRangeDetector,
518 reliability: u32,
520 min_pres_value: Option<f32>,
521 max_pres_value: Option<f32>,
522 event_time_stamps: [BACnetTimeStamp; 3],
523 event_message_texts: [String; 3],
524 value_source: common::ValueSourceTracking,
526}
527
528impl AnalogValueObject {
529 pub fn new(instance: u32, name: impl Into<String>, units: u32) -> Result<Self, Error> {
531 let oid = ObjectIdentifier::new(ObjectType::ANALOG_VALUE, instance)?;
532 Ok(Self {
533 oid,
534 name: name.into(),
535 description: String::new(),
536 present_value: 0.0,
537 units,
538 out_of_service: false,
539 status_flags: StatusFlags::empty(),
540 priority_array: [None; 16],
541 relinquish_default: 0.0,
542 cov_increment: 0.0,
543 event_detector: OutOfRangeDetector::default(),
544 reliability: 0,
545 min_pres_value: None,
546 max_pres_value: None,
547 event_time_stamps: [
548 BACnetTimeStamp::SequenceNumber(0),
549 BACnetTimeStamp::SequenceNumber(0),
550 BACnetTimeStamp::SequenceNumber(0),
551 ],
552 event_message_texts: [String::new(), String::new(), String::new()],
553 value_source: common::ValueSourceTracking::default(),
554 })
555 }
556
557 pub fn set_present_value(&mut self, value: f32) {
560 debug_assert!(
561 value.is_finite(),
562 "set_present_value called with non-finite value"
563 );
564 self.present_value = value;
565 }
566
567 pub fn set_description(&mut self, desc: impl Into<String>) {
569 self.description = desc.into();
570 }
571
572 pub fn set_min_pres_value(&mut self, value: f32) {
574 self.min_pres_value = Some(value);
575 }
576
577 pub fn set_max_pres_value(&mut self, value: f32) {
579 self.max_pres_value = Some(value);
580 }
581
582 fn recalculate_present_value(&mut self) {
584 self.present_value =
585 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
586 }
587}
588
589impl BACnetObject for AnalogValueObject {
590 fn object_identifier(&self) -> ObjectIdentifier {
591 self.oid
592 }
593
594 fn object_name(&self) -> &str {
595 &self.name
596 }
597
598 fn read_property(
599 &self,
600 property: PropertyIdentifier,
601 array_index: Option<u32>,
602 ) -> Result<PropertyValue, Error> {
603 if property == PropertyIdentifier::STATUS_FLAGS {
604 return Ok(common::compute_status_flags(
605 self.status_flags,
606 self.reliability,
607 self.out_of_service,
608 self.event_detector.event_state.to_raw(),
609 ));
610 }
611 if let Some(result) = read_common_properties!(self, property, array_index) {
612 return result;
613 }
614 if let Some(result) = read_event_properties!(self, property) {
615 return result;
616 }
617 match property {
618 p if p == PropertyIdentifier::OBJECT_TYPE => {
619 Ok(PropertyValue::Enumerated(ObjectType::ANALOG_VALUE.to_raw()))
620 }
621 p if p == PropertyIdentifier::PRESENT_VALUE => {
622 Ok(PropertyValue::Real(self.present_value))
623 }
624 p if p == PropertyIdentifier::UNITS => Ok(PropertyValue::Enumerated(self.units)),
625 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
626 common::read_priority_array!(self, array_index, PropertyValue::Real)
627 }
628 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
629 Ok(PropertyValue::Real(self.relinquish_default))
630 }
631 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
632 Ok(common::current_command_priority(&self.priority_array))
633 }
634 p if p == PropertyIdentifier::VALUE_SOURCE => {
635 Ok(self.value_source.value_source.clone())
636 }
637 p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
638 match self.value_source.last_command_time {
639 BACnetTimeStamp::SequenceNumber(n) => n,
640 _ => 0,
641 },
642 )),
643 p if p == PropertyIdentifier::COV_INCREMENT => {
644 Ok(PropertyValue::Real(self.cov_increment))
645 }
646 p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
647 Some(v) => Ok(PropertyValue::Real(v)),
648 None => Err(common::unknown_property_error()),
649 },
650 p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
651 Some(v) => Ok(PropertyValue::Real(v)),
652 None => Err(common::unknown_property_error()),
653 },
654 _ => Err(common::unknown_property_error()),
655 }
656 }
657
658 fn write_property(
659 &mut self,
660 property: PropertyIdentifier,
661 array_index: Option<u32>,
662 value: PropertyValue,
663 priority: Option<u8>,
664 ) -> Result<(), Error> {
665 common::write_priority_array_direct!(self, property, array_index, value, |v| {
666 if let PropertyValue::Real(f) = v {
667 if !f.is_finite() {
668 return Err(common::value_out_of_range_error());
669 }
670 Ok(f)
671 } else {
672 Err(common::invalid_data_type_error())
673 }
674 });
675 if property == PropertyIdentifier::PRESENT_VALUE {
676 return common::write_priority_array!(self, value, priority, |v| {
677 if let PropertyValue::Real(f) = v {
678 if !f.is_finite() {
679 return Err(common::value_out_of_range_error());
680 }
681 Ok(f)
682 } else {
683 Err(common::invalid_data_type_error())
684 }
685 });
686 }
687 if let Some(result) =
688 common::write_out_of_service(&mut self.out_of_service, property, &value)
689 {
690 return result;
691 }
692 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
693 return result;
694 }
695 if let Some(result) = common::write_description(&mut self.description, property, &value) {
696 return result;
697 }
698 if property == PropertyIdentifier::RELIABILITY {
699 if let PropertyValue::Enumerated(v) = value {
700 self.reliability = v;
701 return Ok(());
702 }
703 return Err(common::invalid_data_type_error());
704 }
705 if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
706 {
707 return result;
708 }
709 if let Some(result) = write_event_properties!(self, property, value) {
710 return result;
711 }
712 Err(common::write_access_denied_error())
713 }
714
715 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
716 static PROPS: &[PropertyIdentifier] = &[
717 PropertyIdentifier::OBJECT_IDENTIFIER,
718 PropertyIdentifier::OBJECT_NAME,
719 PropertyIdentifier::DESCRIPTION,
720 PropertyIdentifier::OBJECT_TYPE,
721 PropertyIdentifier::PRESENT_VALUE,
722 PropertyIdentifier::STATUS_FLAGS,
723 PropertyIdentifier::EVENT_STATE,
724 PropertyIdentifier::OUT_OF_SERVICE,
725 PropertyIdentifier::UNITS,
726 PropertyIdentifier::PRIORITY_ARRAY,
727 PropertyIdentifier::RELINQUISH_DEFAULT,
728 PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
729 PropertyIdentifier::COV_INCREMENT,
730 PropertyIdentifier::HIGH_LIMIT,
731 PropertyIdentifier::LOW_LIMIT,
732 PropertyIdentifier::DEADBAND,
733 PropertyIdentifier::LIMIT_ENABLE,
734 PropertyIdentifier::EVENT_ENABLE,
735 PropertyIdentifier::NOTIFY_TYPE,
736 PropertyIdentifier::NOTIFICATION_CLASS,
737 PropertyIdentifier::TIME_DELAY,
738 PropertyIdentifier::RELIABILITY,
739 PropertyIdentifier::ACKED_TRANSITIONS,
740 PropertyIdentifier::EVENT_TIME_STAMPS,
741 PropertyIdentifier::EVENT_MESSAGE_TEXTS,
742 ];
743 Cow::Borrowed(PROPS)
744 }
745
746 fn supports_cov(&self) -> bool {
747 true
748 }
749
750 fn cov_increment(&self) -> Option<f32> {
751 Some(self.cov_increment)
752 }
753
754 fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
755 self.event_detector.evaluate(self.present_value)
756 }
757
758 fn acknowledge_alarm(&mut self, transition_bit: u8) -> Result<(), bacnet_types::error::Error> {
759 self.event_detector.acked_transitions |= transition_bit & 0x07;
760 Ok(())
761 }
762}
763
764#[cfg(test)]
769mod tests {
770 use super::*;
771 use crate::event::LimitEnable;
772 use bacnet_types::enums::EventState;
773
774 #[test]
777 fn ai_read_present_value() {
778 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap(); ai.set_present_value(72.5);
780 let val = ai
781 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
782 .unwrap();
783 assert_eq!(val, PropertyValue::Real(72.5));
784 }
785
786 #[test]
787 fn ai_read_units() {
788 let ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
789 let val = ai.read_property(PropertyIdentifier::UNITS, None).unwrap();
790 assert_eq!(val, PropertyValue::Enumerated(62));
791 }
792
793 #[test]
794 fn ai_write_present_value_denied_when_in_service() {
795 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
796 let result = ai.write_property(
797 PropertyIdentifier::PRESENT_VALUE,
798 None,
799 PropertyValue::Real(99.0),
800 None,
801 );
802 assert!(result.is_err());
803 }
804
805 #[test]
806 fn ai_write_present_value_allowed_when_out_of_service() {
807 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
808 ai.write_property(
809 PropertyIdentifier::OUT_OF_SERVICE,
810 None,
811 PropertyValue::Boolean(true),
812 None,
813 )
814 .unwrap();
815 ai.write_property(
816 PropertyIdentifier::PRESENT_VALUE,
817 None,
818 PropertyValue::Real(99.0),
819 None,
820 )
821 .unwrap();
822 let val = ai
823 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
824 .unwrap();
825 assert_eq!(val, PropertyValue::Real(99.0));
826 }
827
828 #[test]
829 fn ai_read_unknown_property() {
830 let ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
831 let result = ai.read_property(PropertyIdentifier::PRIORITY_ARRAY, None);
832 assert!(result.is_err());
833 }
834
835 #[test]
838 fn ao_write_with_priority() {
839 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
840
841 ao.write_property(
843 PropertyIdentifier::PRESENT_VALUE,
844 None,
845 PropertyValue::Real(50.0),
846 Some(8),
847 )
848 .unwrap();
849
850 let val = ao
851 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
852 .unwrap();
853 assert_eq!(val, PropertyValue::Real(50.0));
854
855 let slot = ao
857 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(8))
858 .unwrap();
859 assert_eq!(slot, PropertyValue::Real(50.0));
860
861 let slot = ao
863 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(1))
864 .unwrap();
865 assert_eq!(slot, PropertyValue::Null);
866 }
867
868 #[test]
869 fn ao_relinquish_falls_to_default() {
870 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
871
872 ao.write_property(
874 PropertyIdentifier::PRESENT_VALUE,
875 None,
876 PropertyValue::Real(75.0),
877 Some(16),
878 )
879 .unwrap();
880 assert_eq!(
881 ao.read_property(PropertyIdentifier::PRESENT_VALUE, None)
882 .unwrap(),
883 PropertyValue::Real(75.0)
884 );
885
886 ao.write_property(
888 PropertyIdentifier::PRESENT_VALUE,
889 None,
890 PropertyValue::Null,
891 Some(16),
892 )
893 .unwrap();
894
895 assert_eq!(
897 ao.read_property(PropertyIdentifier::PRESENT_VALUE, None)
898 .unwrap(),
899 PropertyValue::Real(0.0)
900 );
901 }
902
903 #[test]
904 fn ao_higher_priority_wins() {
905 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
906
907 ao.write_property(
908 PropertyIdentifier::PRESENT_VALUE,
909 None,
910 PropertyValue::Real(10.0),
911 Some(16),
912 )
913 .unwrap();
914 ao.write_property(
915 PropertyIdentifier::PRESENT_VALUE,
916 None,
917 PropertyValue::Real(90.0),
918 Some(8),
919 )
920 .unwrap();
921
922 assert_eq!(
924 ao.read_property(PropertyIdentifier::PRESENT_VALUE, None)
925 .unwrap(),
926 PropertyValue::Real(90.0)
927 );
928 }
929
930 #[test]
933 fn ai_read_event_state_default_normal() {
934 let ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
935 let val = ai
936 .read_property(PropertyIdentifier::EVENT_STATE, None)
937 .unwrap();
938 assert_eq!(val, PropertyValue::Enumerated(EventState::NORMAL.to_raw()));
939 }
940
941 #[test]
942 fn ai_read_write_high_limit() {
943 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
944 ai.write_property(
945 PropertyIdentifier::HIGH_LIMIT,
946 None,
947 PropertyValue::Real(85.0),
948 None,
949 )
950 .unwrap();
951 assert_eq!(
952 ai.read_property(PropertyIdentifier::HIGH_LIMIT, None)
953 .unwrap(),
954 PropertyValue::Real(85.0)
955 );
956 }
957
958 #[test]
959 fn ai_read_write_low_limit() {
960 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
961 ai.write_property(
962 PropertyIdentifier::LOW_LIMIT,
963 None,
964 PropertyValue::Real(15.0),
965 None,
966 )
967 .unwrap();
968 assert_eq!(
969 ai.read_property(PropertyIdentifier::LOW_LIMIT, None)
970 .unwrap(),
971 PropertyValue::Real(15.0)
972 );
973 }
974
975 #[test]
976 fn ai_read_write_deadband() {
977 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
978 ai.write_property(
979 PropertyIdentifier::DEADBAND,
980 None,
981 PropertyValue::Real(2.5),
982 None,
983 )
984 .unwrap();
985 assert_eq!(
986 ai.read_property(PropertyIdentifier::DEADBAND, None)
987 .unwrap(),
988 PropertyValue::Real(2.5)
989 );
990 }
991
992 #[test]
993 fn ai_deadband_reject_negative() {
994 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
995 let result = ai.write_property(
996 PropertyIdentifier::DEADBAND,
997 None,
998 PropertyValue::Real(-1.0),
999 None,
1000 );
1001 assert!(result.is_err());
1002 }
1003
1004 #[test]
1005 fn ai_read_write_limit_enable() {
1006 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
1007 let enable_both = LimitEnable::BOTH.to_bits();
1008 ai.write_property(
1009 PropertyIdentifier::LIMIT_ENABLE,
1010 None,
1011 PropertyValue::BitString {
1012 unused_bits: 6,
1013 data: vec![enable_both],
1014 },
1015 None,
1016 )
1017 .unwrap();
1018 let val = ai
1019 .read_property(PropertyIdentifier::LIMIT_ENABLE, None)
1020 .unwrap();
1021 if let PropertyValue::BitString { data, .. } = val {
1022 let le = LimitEnable::from_bits(data[0]);
1023 assert!(le.low_limit_enable);
1024 assert!(le.high_limit_enable);
1025 } else {
1026 panic!("Expected BitString");
1027 }
1028 }
1029
1030 #[test]
1031 fn ai_intrinsic_reporting_triggers_on_present_value_change() {
1032 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
1033 ai.write_property(
1035 PropertyIdentifier::HIGH_LIMIT,
1036 None,
1037 PropertyValue::Real(80.0),
1038 None,
1039 )
1040 .unwrap();
1041 ai.write_property(
1042 PropertyIdentifier::LOW_LIMIT,
1043 None,
1044 PropertyValue::Real(20.0),
1045 None,
1046 )
1047 .unwrap();
1048 ai.write_property(
1049 PropertyIdentifier::DEADBAND,
1050 None,
1051 PropertyValue::Real(2.0),
1052 None,
1053 )
1054 .unwrap();
1055 ai.write_property(
1056 PropertyIdentifier::LIMIT_ENABLE,
1057 None,
1058 PropertyValue::BitString {
1059 unused_bits: 6,
1060 data: vec![LimitEnable::BOTH.to_bits()],
1061 },
1062 None,
1063 )
1064 .unwrap();
1065 ai.write_property(
1066 PropertyIdentifier::EVENT_ENABLE,
1067 None,
1068 PropertyValue::BitString {
1069 unused_bits: 5,
1070 data: vec![0x07 << 5], },
1072 None,
1073 )
1074 .unwrap();
1075
1076 ai.set_present_value(50.0);
1078 assert!(ai.evaluate_intrinsic_reporting().is_none());
1079
1080 ai.set_present_value(81.0);
1082 let change = ai.evaluate_intrinsic_reporting().unwrap();
1083 assert_eq!(change.from, EventState::NORMAL);
1084 assert_eq!(change.to, EventState::HIGH_LIMIT);
1085
1086 assert_eq!(
1088 ai.read_property(PropertyIdentifier::EVENT_STATE, None)
1089 .unwrap(),
1090 PropertyValue::Enumerated(EventState::HIGH_LIMIT.to_raw())
1091 );
1092
1093 ai.set_present_value(77.0);
1095 let change = ai.evaluate_intrinsic_reporting().unwrap();
1096 assert_eq!(change.to, EventState::NORMAL);
1097 }
1098
1099 #[test]
1100 fn ao_intrinsic_reporting_after_priority_write() {
1101 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1102 ao.write_property(
1103 PropertyIdentifier::HIGH_LIMIT,
1104 None,
1105 PropertyValue::Real(80.0),
1106 None,
1107 )
1108 .unwrap();
1109 ao.write_property(
1110 PropertyIdentifier::LOW_LIMIT,
1111 None,
1112 PropertyValue::Real(20.0),
1113 None,
1114 )
1115 .unwrap();
1116 ao.write_property(
1117 PropertyIdentifier::DEADBAND,
1118 None,
1119 PropertyValue::Real(2.0),
1120 None,
1121 )
1122 .unwrap();
1123 ao.write_property(
1124 PropertyIdentifier::LIMIT_ENABLE,
1125 None,
1126 PropertyValue::BitString {
1127 unused_bits: 6,
1128 data: vec![LimitEnable::BOTH.to_bits()],
1129 },
1130 None,
1131 )
1132 .unwrap();
1133 ao.write_property(
1134 PropertyIdentifier::EVENT_ENABLE,
1135 None,
1136 PropertyValue::BitString {
1137 unused_bits: 5,
1138 data: vec![0x07 << 5], },
1140 None,
1141 )
1142 .unwrap();
1143
1144 ao.write_property(
1146 PropertyIdentifier::PRESENT_VALUE,
1147 None,
1148 PropertyValue::Real(85.0),
1149 Some(8),
1150 )
1151 .unwrap();
1152 let change = ao.evaluate_intrinsic_reporting().unwrap();
1153 assert_eq!(change.to, EventState::HIGH_LIMIT);
1154 }
1155
1156 #[test]
1157 fn ai_read_reliability_default() {
1158 let ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
1159 let val = ai
1160 .read_property(PropertyIdentifier::RELIABILITY, None)
1161 .unwrap();
1162 assert_eq!(val, PropertyValue::Enumerated(0)); }
1164
1165 #[test]
1166 fn ai_description_read_write() {
1167 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
1168 let val = ai
1170 .read_property(PropertyIdentifier::DESCRIPTION, None)
1171 .unwrap();
1172 assert_eq!(val, PropertyValue::CharacterString(String::new()));
1173 ai.write_property(
1175 PropertyIdentifier::DESCRIPTION,
1176 None,
1177 PropertyValue::CharacterString("Zone temperature sensor".into()),
1178 None,
1179 )
1180 .unwrap();
1181 let val = ai
1182 .read_property(PropertyIdentifier::DESCRIPTION, None)
1183 .unwrap();
1184 assert_eq!(
1185 val,
1186 PropertyValue::CharacterString("Zone temperature sensor".into())
1187 );
1188 }
1189
1190 #[test]
1191 fn ai_set_description_convenience() {
1192 let mut ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
1193 ai.set_description("Supply air temperature");
1194 assert_eq!(
1195 ai.read_property(PropertyIdentifier::DESCRIPTION, None)
1196 .unwrap(),
1197 PropertyValue::CharacterString("Supply air temperature".into())
1198 );
1199 }
1200
1201 #[test]
1202 fn ai_description_in_property_list() {
1203 let ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
1204 assert!(ai
1205 .property_list()
1206 .contains(&PropertyIdentifier::DESCRIPTION));
1207 }
1208
1209 #[test]
1210 fn ao_description_read_write() {
1211 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1212 ao.write_property(
1213 PropertyIdentifier::DESCRIPTION,
1214 None,
1215 PropertyValue::CharacterString("Chilled water valve".into()),
1216 None,
1217 )
1218 .unwrap();
1219 assert_eq!(
1220 ao.read_property(PropertyIdentifier::DESCRIPTION, None)
1221 .unwrap(),
1222 PropertyValue::CharacterString("Chilled water valve".into())
1223 );
1224 }
1225
1226 #[test]
1227 fn ao_description_in_property_list() {
1228 let ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1229 assert!(ao
1230 .property_list()
1231 .contains(&PropertyIdentifier::DESCRIPTION));
1232 }
1233
1234 #[test]
1235 fn ao_read_reliability_default() {
1236 let ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1237 let val = ao
1238 .read_property(PropertyIdentifier::RELIABILITY, None)
1239 .unwrap();
1240 assert_eq!(val, PropertyValue::Enumerated(0)); }
1242
1243 #[test]
1246 fn ao_priority_array_index_zero_returns_size() {
1247 let ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1248 let val = ao
1249 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(0))
1250 .unwrap();
1251 assert_eq!(val, PropertyValue::Unsigned(16));
1252 }
1253
1254 #[test]
1255 fn ao_priority_array_index_out_of_bounds() {
1256 let ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1257 let result = ao.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(17));
1259 assert!(result.is_err());
1260 }
1261
1262 #[test]
1263 fn ao_priority_array_index_far_out_of_bounds() {
1264 let ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1265 let result = ao.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(100));
1267 assert!(result.is_err());
1268 }
1269
1270 #[test]
1271 fn ao_priority_array_index_u32_max_out_of_bounds() {
1272 let ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1273 let result = ao.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(u32::MAX));
1274 assert!(result.is_err());
1275 }
1276
1277 #[test]
1280 fn ao_write_with_priority_zero_rejected() {
1281 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1282 let result = ao.write_property(
1284 PropertyIdentifier::PRESENT_VALUE,
1285 None,
1286 PropertyValue::Real(50.0),
1287 Some(0),
1288 );
1289 assert!(result.is_err());
1290 }
1291
1292 #[test]
1293 fn ao_write_with_priority_17_rejected() {
1294 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1295 let result = ao.write_property(
1297 PropertyIdentifier::PRESENT_VALUE,
1298 None,
1299 PropertyValue::Real(50.0),
1300 Some(17),
1301 );
1302 assert!(result.is_err());
1303 }
1304
1305 #[test]
1306 fn ao_write_with_priority_255_rejected() {
1307 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1308 let result = ao.write_property(
1310 PropertyIdentifier::PRESENT_VALUE,
1311 None,
1312 PropertyValue::Real(50.0),
1313 Some(255),
1314 );
1315 assert!(result.is_err());
1316 }
1317
1318 #[test]
1319 fn ao_write_with_all_valid_priorities() {
1320 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1321 for prio in 1..=16u8 {
1323 ao.write_property(
1324 PropertyIdentifier::PRESENT_VALUE,
1325 None,
1326 PropertyValue::Real(prio as f32),
1327 Some(prio),
1328 )
1329 .unwrap();
1330 }
1331 let val = ao
1333 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1334 .unwrap();
1335 assert_eq!(val, PropertyValue::Real(1.0));
1336 }
1337
1338 #[test]
1339 fn ao_priority_array_read_all_slots_none_by_default() {
1340 let ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1341 let val = ao
1343 .read_property(PropertyIdentifier::PRIORITY_ARRAY, None)
1344 .unwrap();
1345 if let PropertyValue::List(elements) = val {
1346 assert_eq!(elements.len(), 16);
1347 for elem in &elements {
1348 assert_eq!(elem, &PropertyValue::Null);
1349 }
1350 } else {
1351 panic!("Expected List for priority array without index");
1352 }
1353 }
1354
1355 #[test]
1358 fn ao_direct_priority_array_write_value() {
1359 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1360 ao.write_property(
1362 PropertyIdentifier::PRIORITY_ARRAY,
1363 Some(5),
1364 PropertyValue::Real(42.0),
1365 None,
1366 )
1367 .unwrap();
1368 assert_eq!(
1370 ao.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1371 .unwrap(),
1372 PropertyValue::Real(42.0)
1373 );
1374 assert_eq!(
1376 ao.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
1377 .unwrap(),
1378 PropertyValue::Real(42.0)
1379 );
1380 }
1381
1382 #[test]
1383 fn ao_direct_priority_array_relinquish() {
1384 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1385 ao.write_property(
1387 PropertyIdentifier::PRIORITY_ARRAY,
1388 Some(5),
1389 PropertyValue::Real(42.0),
1390 None,
1391 )
1392 .unwrap();
1393 ao.write_property(
1395 PropertyIdentifier::PRIORITY_ARRAY,
1396 Some(5),
1397 PropertyValue::Null,
1398 None,
1399 )
1400 .unwrap();
1401 assert_eq!(
1403 ao.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1404 .unwrap(),
1405 PropertyValue::Real(0.0)
1406 );
1407 assert_eq!(
1408 ao.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
1409 .unwrap(),
1410 PropertyValue::Null
1411 );
1412 }
1413
1414 #[test]
1415 fn ao_direct_priority_array_no_index_error() {
1416 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1417 let result = ao.write_property(
1419 PropertyIdentifier::PRIORITY_ARRAY,
1420 None,
1421 PropertyValue::Real(42.0),
1422 None,
1423 );
1424 assert!(result.is_err());
1425 }
1426
1427 #[test]
1428 fn ao_direct_priority_array_index_zero_error() {
1429 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1430 let result = ao.write_property(
1431 PropertyIdentifier::PRIORITY_ARRAY,
1432 Some(0),
1433 PropertyValue::Real(42.0),
1434 None,
1435 );
1436 assert!(result.is_err());
1437 }
1438
1439 #[test]
1440 fn ao_direct_priority_array_index_17_error() {
1441 let mut ao = AnalogOutputObject::new(1, "AO-1", 62).unwrap();
1442 let result = ao.write_property(
1443 PropertyIdentifier::PRIORITY_ARRAY,
1444 Some(17),
1445 PropertyValue::Real(42.0),
1446 None,
1447 );
1448 assert!(result.is_err());
1449 }
1450
1451 #[test]
1454 fn av_read_present_value_default() {
1455 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1456 let val = av
1457 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1458 .unwrap();
1459 assert_eq!(val, PropertyValue::Real(0.0));
1460 }
1461
1462 #[test]
1463 fn av_set_present_value() {
1464 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1465 av.set_present_value(42.5);
1466 let val = av
1467 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1468 .unwrap();
1469 assert_eq!(val, PropertyValue::Real(42.5));
1470 }
1471
1472 #[test]
1473 fn av_read_object_type_returns_analog_value() {
1474 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1475 let val = av
1476 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1477 .unwrap();
1478 assert_eq!(
1479 val,
1480 PropertyValue::Enumerated(ObjectType::ANALOG_VALUE.to_raw())
1481 );
1482 }
1483
1484 #[test]
1485 fn av_read_units() {
1486 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1487 let val = av.read_property(PropertyIdentifier::UNITS, None).unwrap();
1488 assert_eq!(val, PropertyValue::Enumerated(62));
1489 }
1490
1491 #[test]
1492 fn av_write_with_priority() {
1493 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1494
1495 av.write_property(
1497 PropertyIdentifier::PRESENT_VALUE,
1498 None,
1499 PropertyValue::Real(55.0),
1500 Some(8),
1501 )
1502 .unwrap();
1503
1504 let val = av
1505 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1506 .unwrap();
1507 assert_eq!(val, PropertyValue::Real(55.0));
1508
1509 let slot = av
1511 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(8))
1512 .unwrap();
1513 assert_eq!(slot, PropertyValue::Real(55.0));
1514
1515 let slot = av
1517 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(1))
1518 .unwrap();
1519 assert_eq!(slot, PropertyValue::Null);
1520 }
1521
1522 #[test]
1523 fn av_relinquish_falls_to_default() {
1524 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1525
1526 av.write_property(
1528 PropertyIdentifier::PRESENT_VALUE,
1529 None,
1530 PropertyValue::Real(75.0),
1531 Some(16),
1532 )
1533 .unwrap();
1534 assert_eq!(
1535 av.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1536 .unwrap(),
1537 PropertyValue::Real(75.0)
1538 );
1539
1540 av.write_property(
1542 PropertyIdentifier::PRESENT_VALUE,
1543 None,
1544 PropertyValue::Null,
1545 Some(16),
1546 )
1547 .unwrap();
1548
1549 assert_eq!(
1551 av.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1552 .unwrap(),
1553 PropertyValue::Real(0.0)
1554 );
1555 }
1556
1557 #[test]
1558 fn av_higher_priority_wins() {
1559 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1560
1561 av.write_property(
1562 PropertyIdentifier::PRESENT_VALUE,
1563 None,
1564 PropertyValue::Real(10.0),
1565 Some(16),
1566 )
1567 .unwrap();
1568 av.write_property(
1569 PropertyIdentifier::PRESENT_VALUE,
1570 None,
1571 PropertyValue::Real(90.0),
1572 Some(8),
1573 )
1574 .unwrap();
1575
1576 assert_eq!(
1578 av.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1579 .unwrap(),
1580 PropertyValue::Real(90.0)
1581 );
1582 }
1583
1584 #[test]
1585 fn av_priority_array_read_all_slots_none_by_default() {
1586 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1587 let val = av
1588 .read_property(PropertyIdentifier::PRIORITY_ARRAY, None)
1589 .unwrap();
1590 if let PropertyValue::List(elements) = val {
1591 assert_eq!(elements.len(), 16);
1592 for elem in &elements {
1593 assert_eq!(elem, &PropertyValue::Null);
1594 }
1595 } else {
1596 panic!("Expected List for priority array without index");
1597 }
1598 }
1599
1600 #[test]
1601 fn av_priority_array_index_zero_returns_size() {
1602 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1603 let val = av
1604 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(0))
1605 .unwrap();
1606 assert_eq!(val, PropertyValue::Unsigned(16));
1607 }
1608
1609 #[test]
1610 fn av_priority_array_index_out_of_bounds() {
1611 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1612 let result = av.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(17));
1613 assert!(result.is_err());
1614 }
1615
1616 #[test]
1617 fn av_priority_array_index_u32_max_out_of_bounds() {
1618 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1619 let result = av.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(u32::MAX));
1620 assert!(result.is_err());
1621 }
1622
1623 #[test]
1624 fn av_write_with_priority_zero_rejected() {
1625 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1626 let result = av.write_property(
1627 PropertyIdentifier::PRESENT_VALUE,
1628 None,
1629 PropertyValue::Real(50.0),
1630 Some(0),
1631 );
1632 assert!(result.is_err());
1633 }
1634
1635 #[test]
1636 fn av_write_with_priority_17_rejected() {
1637 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1638 let result = av.write_property(
1639 PropertyIdentifier::PRESENT_VALUE,
1640 None,
1641 PropertyValue::Real(50.0),
1642 Some(17),
1643 );
1644 assert!(result.is_err());
1645 }
1646
1647 #[test]
1648 fn av_write_with_all_valid_priorities() {
1649 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1650 for prio in 1..=16u8 {
1651 av.write_property(
1652 PropertyIdentifier::PRESENT_VALUE,
1653 None,
1654 PropertyValue::Real(prio as f32),
1655 Some(prio),
1656 )
1657 .unwrap();
1658 }
1659 let val = av
1661 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1662 .unwrap();
1663 assert_eq!(val, PropertyValue::Real(1.0));
1664 }
1665
1666 #[test]
1667 fn av_direct_priority_array_write_value() {
1668 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1669 av.write_property(
1670 PropertyIdentifier::PRIORITY_ARRAY,
1671 Some(5),
1672 PropertyValue::Real(42.0),
1673 None,
1674 )
1675 .unwrap();
1676 assert_eq!(
1677 av.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1678 .unwrap(),
1679 PropertyValue::Real(42.0)
1680 );
1681 assert_eq!(
1682 av.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
1683 .unwrap(),
1684 PropertyValue::Real(42.0)
1685 );
1686 }
1687
1688 #[test]
1689 fn av_direct_priority_array_relinquish() {
1690 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1691 av.write_property(
1692 PropertyIdentifier::PRIORITY_ARRAY,
1693 Some(5),
1694 PropertyValue::Real(42.0),
1695 None,
1696 )
1697 .unwrap();
1698 av.write_property(
1699 PropertyIdentifier::PRIORITY_ARRAY,
1700 Some(5),
1701 PropertyValue::Null,
1702 None,
1703 )
1704 .unwrap();
1705 assert_eq!(
1706 av.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1707 .unwrap(),
1708 PropertyValue::Real(0.0)
1709 );
1710 assert_eq!(
1711 av.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
1712 .unwrap(),
1713 PropertyValue::Null
1714 );
1715 }
1716
1717 #[test]
1718 fn av_direct_priority_array_no_index_error() {
1719 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1720 let result = av.write_property(
1721 PropertyIdentifier::PRIORITY_ARRAY,
1722 None,
1723 PropertyValue::Real(42.0),
1724 None,
1725 );
1726 assert!(result.is_err());
1727 }
1728
1729 #[test]
1730 fn av_direct_priority_array_index_zero_error() {
1731 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1732 let result = av.write_property(
1733 PropertyIdentifier::PRIORITY_ARRAY,
1734 Some(0),
1735 PropertyValue::Real(42.0),
1736 None,
1737 );
1738 assert!(result.is_err());
1739 }
1740
1741 #[test]
1742 fn av_direct_priority_array_index_17_error() {
1743 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1744 let result = av.write_property(
1745 PropertyIdentifier::PRIORITY_ARRAY,
1746 Some(17),
1747 PropertyValue::Real(42.0),
1748 None,
1749 );
1750 assert!(result.is_err());
1751 }
1752
1753 #[test]
1754 fn av_intrinsic_reporting_normal_to_high_limit_to_normal() {
1755 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1756 av.write_property(
1758 PropertyIdentifier::HIGH_LIMIT,
1759 None,
1760 PropertyValue::Real(80.0),
1761 None,
1762 )
1763 .unwrap();
1764 av.write_property(
1765 PropertyIdentifier::LOW_LIMIT,
1766 None,
1767 PropertyValue::Real(20.0),
1768 None,
1769 )
1770 .unwrap();
1771 av.write_property(
1772 PropertyIdentifier::DEADBAND,
1773 None,
1774 PropertyValue::Real(2.0),
1775 None,
1776 )
1777 .unwrap();
1778 av.write_property(
1779 PropertyIdentifier::LIMIT_ENABLE,
1780 None,
1781 PropertyValue::BitString {
1782 unused_bits: 6,
1783 data: vec![LimitEnable::BOTH.to_bits()],
1784 },
1785 None,
1786 )
1787 .unwrap();
1788 av.write_property(
1789 PropertyIdentifier::EVENT_ENABLE,
1790 None,
1791 PropertyValue::BitString {
1792 unused_bits: 5,
1793 data: vec![0x07 << 5],
1794 },
1795 None,
1796 )
1797 .unwrap();
1798
1799 av.set_present_value(50.0);
1801 assert!(av.evaluate_intrinsic_reporting().is_none());
1802
1803 av.set_present_value(81.0);
1805 let change = av.evaluate_intrinsic_reporting().unwrap();
1806 assert_eq!(change.from, EventState::NORMAL);
1807 assert_eq!(change.to, EventState::HIGH_LIMIT);
1808
1809 assert_eq!(
1811 av.read_property(PropertyIdentifier::EVENT_STATE, None)
1812 .unwrap(),
1813 PropertyValue::Enumerated(EventState::HIGH_LIMIT.to_raw())
1814 );
1815
1816 av.set_present_value(77.0);
1818 let change = av.evaluate_intrinsic_reporting().unwrap();
1819 assert_eq!(change.to, EventState::NORMAL);
1820 }
1821
1822 #[test]
1823 fn av_intrinsic_reporting_after_priority_write() {
1824 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1825 av.write_property(
1826 PropertyIdentifier::HIGH_LIMIT,
1827 None,
1828 PropertyValue::Real(80.0),
1829 None,
1830 )
1831 .unwrap();
1832 av.write_property(
1833 PropertyIdentifier::LOW_LIMIT,
1834 None,
1835 PropertyValue::Real(20.0),
1836 None,
1837 )
1838 .unwrap();
1839 av.write_property(
1840 PropertyIdentifier::DEADBAND,
1841 None,
1842 PropertyValue::Real(2.0),
1843 None,
1844 )
1845 .unwrap();
1846 av.write_property(
1847 PropertyIdentifier::LIMIT_ENABLE,
1848 None,
1849 PropertyValue::BitString {
1850 unused_bits: 6,
1851 data: vec![LimitEnable::BOTH.to_bits()],
1852 },
1853 None,
1854 )
1855 .unwrap();
1856 av.write_property(
1857 PropertyIdentifier::EVENT_ENABLE,
1858 None,
1859 PropertyValue::BitString {
1860 unused_bits: 5,
1861 data: vec![0x07 << 5],
1862 },
1863 None,
1864 )
1865 .unwrap();
1866
1867 av.write_property(
1869 PropertyIdentifier::PRESENT_VALUE,
1870 None,
1871 PropertyValue::Real(85.0),
1872 Some(8),
1873 )
1874 .unwrap();
1875 let change = av.evaluate_intrinsic_reporting().unwrap();
1876 assert_eq!(change.to, EventState::HIGH_LIMIT);
1877 }
1878
1879 #[test]
1880 fn av_read_reliability_default() {
1881 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1882 let val = av
1883 .read_property(PropertyIdentifier::RELIABILITY, None)
1884 .unwrap();
1885 assert_eq!(val, PropertyValue::Enumerated(0)); }
1887
1888 #[test]
1889 fn av_description_read_write() {
1890 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1891 let val = av
1893 .read_property(PropertyIdentifier::DESCRIPTION, None)
1894 .unwrap();
1895 assert_eq!(val, PropertyValue::CharacterString(String::new()));
1896 av.write_property(
1898 PropertyIdentifier::DESCRIPTION,
1899 None,
1900 PropertyValue::CharacterString("Setpoint".into()),
1901 None,
1902 )
1903 .unwrap();
1904 let val = av
1905 .read_property(PropertyIdentifier::DESCRIPTION, None)
1906 .unwrap();
1907 assert_eq!(val, PropertyValue::CharacterString("Setpoint".into()));
1908 }
1909
1910 #[test]
1911 fn av_set_description_convenience() {
1912 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1913 av.set_description("Zone temperature setpoint");
1914 assert_eq!(
1915 av.read_property(PropertyIdentifier::DESCRIPTION, None)
1916 .unwrap(),
1917 PropertyValue::CharacterString("Zone temperature setpoint".into())
1918 );
1919 }
1920
1921 #[test]
1922 fn av_description_in_property_list() {
1923 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1924 assert!(av
1925 .property_list()
1926 .contains(&PropertyIdentifier::DESCRIPTION));
1927 }
1928
1929 #[test]
1930 fn av_property_list_includes_priority_array_and_relinquish_default() {
1931 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1932 let list = av.property_list();
1933 assert!(list.contains(&PropertyIdentifier::PRIORITY_ARRAY));
1934 assert!(list.contains(&PropertyIdentifier::RELINQUISH_DEFAULT));
1935 assert!(list.contains(&PropertyIdentifier::COV_INCREMENT));
1936 assert!(list.contains(&PropertyIdentifier::UNITS));
1937 }
1938
1939 #[test]
1940 fn av_read_event_state_default_normal() {
1941 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1942 let val = av
1943 .read_property(PropertyIdentifier::EVENT_STATE, None)
1944 .unwrap();
1945 assert_eq!(val, PropertyValue::Enumerated(EventState::NORMAL.to_raw()));
1946 }
1947
1948 #[test]
1949 fn av_cov_increment_read_write() {
1950 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1951 av.write_property(
1952 PropertyIdentifier::COV_INCREMENT,
1953 None,
1954 PropertyValue::Real(1.5),
1955 None,
1956 )
1957 .unwrap();
1958 assert_eq!(
1959 av.read_property(PropertyIdentifier::COV_INCREMENT, None)
1960 .unwrap(),
1961 PropertyValue::Real(1.5)
1962 );
1963 assert_eq!(av.cov_increment(), Some(1.5));
1964 }
1965
1966 #[test]
1967 fn av_read_relinquish_default() {
1968 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1969 let val = av
1970 .read_property(PropertyIdentifier::RELINQUISH_DEFAULT, None)
1971 .unwrap();
1972 assert_eq!(val, PropertyValue::Real(0.0));
1973 }
1974
1975 #[test]
1976 fn av_unknown_property_returns_error() {
1977 let av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
1978 let result = av.read_property(PropertyIdentifier::FILE_SIZE, None);
1980 assert!(result.is_err());
1981 }
1982
1983 #[test]
1986 fn ai_property_list_returns_full_list() {
1987 let ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
1988 let result = ai
1989 .read_property(PropertyIdentifier::PROPERTY_LIST, None)
1990 .unwrap();
1991 if let PropertyValue::List(elements) = result {
1992 assert!(!elements.is_empty());
1993 assert!(matches!(elements[0], PropertyValue::Enumerated(_)));
1994 } else {
1995 panic!("Expected PropertyValue::List");
1996 }
1997 }
1998
1999 #[test]
2000 fn ai_property_list_index_zero_returns_count() {
2001 let ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
2002 let filtered_count = ai
2005 .property_list()
2006 .iter()
2007 .filter(|p| {
2008 **p != PropertyIdentifier::OBJECT_IDENTIFIER
2009 && **p != PropertyIdentifier::OBJECT_NAME
2010 && **p != PropertyIdentifier::OBJECT_TYPE
2011 && **p != PropertyIdentifier::PROPERTY_LIST
2012 })
2013 .count() as u64;
2014 let result = ai
2015 .read_property(PropertyIdentifier::PROPERTY_LIST, Some(0))
2016 .unwrap();
2017 assert_eq!(result, PropertyValue::Unsigned(filtered_count));
2018 }
2019
2020 #[test]
2021 fn ai_property_list_index_one_returns_first_prop() {
2022 let ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
2023 let first_filtered = ai
2025 .property_list()
2026 .iter()
2027 .copied()
2028 .find(|p| {
2029 *p != PropertyIdentifier::OBJECT_IDENTIFIER
2030 && *p != PropertyIdentifier::OBJECT_NAME
2031 && *p != PropertyIdentifier::OBJECT_TYPE
2032 && *p != PropertyIdentifier::PROPERTY_LIST
2033 })
2034 .unwrap();
2035 let result = ai
2036 .read_property(PropertyIdentifier::PROPERTY_LIST, Some(1))
2037 .unwrap();
2038 assert_eq!(result, PropertyValue::Enumerated(first_filtered.to_raw()));
2039 }
2040
2041 #[test]
2042 fn ai_property_list_invalid_index_returns_error() {
2043 let ai = AnalogInputObject::new(1, "AI-1", 62).unwrap();
2044 let count = ai.property_list().len() as u32;
2045 let result = ai.read_property(PropertyIdentifier::PROPERTY_LIST, Some(count + 1));
2046 assert!(result.is_err());
2047 }
2048}