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