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