Skip to main content

bacnet_objects/
binary.rs

1//! Binary Input (type 3), Binary Output (type 4), and Binary Value (type 5)
2//! objects per ASHRAE 135-2020 Clauses 12.4, 12.5, 12.6.
3
4use bacnet_types::enums::{ObjectType, PropertyIdentifier};
5use bacnet_types::error::Error;
6use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, StatusFlags};
7use std::borrow::Cow;
8
9use crate::common::{self, read_common_properties};
10use crate::event::{ChangeOfStateDetector, EventStateChange};
11use crate::traits::BACnetObject;
12
13// ---------------------------------------------------------------------------
14// BinaryInput (type 3)
15// ---------------------------------------------------------------------------
16
17/// BACnet Binary Input object.
18///
19/// Read-only binary point. Present_Value is writable only when out-of-service.
20/// Uses Enumerated values: 0 = inactive, 1 = active.
21pub struct BinaryInputObject {
22    oid: ObjectIdentifier,
23    name: String,
24    description: String,
25    present_value: u32,
26    out_of_service: bool,
27    status_flags: StatusFlags,
28    /// Polarity: 0 = normal, 1 = reverse.
29    polarity: u32,
30    /// Reliability: 0 = NO_FAULT_DETECTED.
31    reliability: u32,
32    active_text: String,
33    inactive_text: String,
34    /// CHANGE_OF_STATE event detector.
35    event_detector: ChangeOfStateDetector,
36}
37
38impl BinaryInputObject {
39    pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
40        let oid = ObjectIdentifier::new(ObjectType::BINARY_INPUT, instance)?;
41        Ok(Self {
42            oid,
43            name: name.into(),
44            description: String::new(),
45            present_value: 0,
46            out_of_service: false,
47            status_flags: StatusFlags::empty(),
48            polarity: 0,
49            reliability: 0,
50            active_text: "Active".into(),
51            inactive_text: "Inactive".into(),
52            event_detector: ChangeOfStateDetector::default(),
53        })
54    }
55
56    /// Set the present value (used by application to update input state).
57    pub fn set_present_value(&mut self, value: u32) {
58        self.present_value = value;
59    }
60
61    /// Set the description string.
62    pub fn set_description(&mut self, desc: impl Into<String>) {
63        self.description = desc.into();
64    }
65}
66
67impl BACnetObject for BinaryInputObject {
68    fn object_identifier(&self) -> ObjectIdentifier {
69        self.oid
70    }
71
72    fn object_name(&self) -> &str {
73        &self.name
74    }
75
76    fn supports_cov(&self) -> bool {
77        true
78    }
79
80    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
81        self.event_detector.evaluate(self.present_value)
82    }
83
84    fn read_property(
85        &self,
86        property: PropertyIdentifier,
87        array_index: Option<u32>,
88    ) -> Result<PropertyValue, Error> {
89        if property == PropertyIdentifier::STATUS_FLAGS {
90            return Ok(common::compute_status_flags(
91                self.status_flags,
92                self.reliability,
93                self.out_of_service,
94                self.event_detector.event_state.to_raw(),
95            ));
96        }
97        if let Some(result) = read_common_properties!(self, property, array_index) {
98            return result;
99        }
100        match property {
101            p if p == PropertyIdentifier::OBJECT_TYPE => {
102                Ok(PropertyValue::Enumerated(ObjectType::BINARY_INPUT.to_raw()))
103            }
104            p if p == PropertyIdentifier::PRESENT_VALUE => {
105                Ok(PropertyValue::Enumerated(self.present_value))
106            }
107            p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
108                self.event_detector.event_state.to_raw(),
109            )),
110            p if p == PropertyIdentifier::POLARITY => Ok(PropertyValue::Enumerated(self.polarity)),
111            p if p == PropertyIdentifier::ACTIVE_TEXT => {
112                Ok(PropertyValue::CharacterString(self.active_text.clone()))
113            }
114            p if p == PropertyIdentifier::INACTIVE_TEXT => {
115                Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
116            }
117            p if p == PropertyIdentifier::ALARM_VALUES => Ok(PropertyValue::List(
118                self.event_detector
119                    .alarm_values
120                    .iter()
121                    .map(|v| PropertyValue::Enumerated(*v))
122                    .collect(),
123            )),
124            p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
125                unused_bits: 5,
126                data: vec![self.event_detector.event_enable << 5],
127            }),
128            p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
129                unused_bits: 5,
130                data: vec![self.event_detector.acked_transitions << 5],
131            }),
132            p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
133                self.event_detector.notification_class as u64,
134            )),
135            p if p == PropertyIdentifier::EVENT_TIME_STAMPS => Ok(PropertyValue::List(vec![
136                PropertyValue::Unsigned(0),
137                PropertyValue::Unsigned(0),
138                PropertyValue::Unsigned(0),
139            ])),
140            _ => Err(common::unknown_property_error()),
141        }
142    }
143
144    fn write_property(
145        &mut self,
146        property: PropertyIdentifier,
147        _array_index: Option<u32>,
148        value: PropertyValue,
149        _priority: Option<u8>,
150    ) -> Result<(), Error> {
151        if property == PropertyIdentifier::PRESENT_VALUE {
152            if !self.out_of_service {
153                return Err(common::write_access_denied_error());
154            }
155            if let PropertyValue::Enumerated(v) = value {
156                if v > 1 {
157                    return Err(common::value_out_of_range_error());
158                }
159                self.present_value = v;
160                return Ok(());
161            }
162            return Err(common::invalid_data_type_error());
163        }
164        if property == PropertyIdentifier::ACTIVE_TEXT {
165            if let PropertyValue::CharacterString(s) = value {
166                self.active_text = s;
167                return Ok(());
168            }
169            return Err(common::invalid_data_type_error());
170        }
171        if property == PropertyIdentifier::INACTIVE_TEXT {
172            if let PropertyValue::CharacterString(s) = value {
173                self.inactive_text = s;
174                return Ok(());
175            }
176            return Err(common::invalid_data_type_error());
177        }
178        if let Some(result) =
179            common::write_out_of_service(&mut self.out_of_service, property, &value)
180        {
181            return result;
182        }
183        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
184            return result;
185        }
186        if let Some(result) = common::write_description(&mut self.description, property, &value) {
187            return result;
188        }
189        Err(common::write_access_denied_error())
190    }
191
192    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
193        static PROPS: &[PropertyIdentifier] = &[
194            PropertyIdentifier::OBJECT_IDENTIFIER,
195            PropertyIdentifier::OBJECT_NAME,
196            PropertyIdentifier::DESCRIPTION,
197            PropertyIdentifier::OBJECT_TYPE,
198            PropertyIdentifier::PRESENT_VALUE,
199            PropertyIdentifier::STATUS_FLAGS,
200            PropertyIdentifier::EVENT_STATE,
201            PropertyIdentifier::OUT_OF_SERVICE,
202            PropertyIdentifier::POLARITY,
203            PropertyIdentifier::RELIABILITY,
204            PropertyIdentifier::ACTIVE_TEXT,
205            PropertyIdentifier::INACTIVE_TEXT,
206        ];
207        Cow::Borrowed(PROPS)
208    }
209}
210
211// ---------------------------------------------------------------------------
212// BinaryOutput (type 4)
213// ---------------------------------------------------------------------------
214
215/// BACnet Binary Output object.
216///
217/// Commandable binary output with 16-level priority array.
218/// Uses Enumerated values: 0 = inactive, 1 = active.
219pub struct BinaryOutputObject {
220    oid: ObjectIdentifier,
221    name: String,
222    description: String,
223    present_value: u32,
224    out_of_service: bool,
225    status_flags: StatusFlags,
226    priority_array: [Option<u32>; 16],
227    relinquish_default: u32,
228    /// Polarity: 0 = normal, 1 = reverse.
229    polarity: u32,
230    /// Reliability: 0 = NO_FAULT_DETECTED.
231    reliability: u32,
232    active_text: String,
233    inactive_text: String,
234    /// COMMAND_FAILURE event detector.
235    event_detector: ChangeOfStateDetector,
236    /// Value source tracking (optional per spec — exposed via VALUE_SOURCE property).
237    value_source: common::ValueSourceTracking,
238}
239
240impl BinaryOutputObject {
241    pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
242        let oid = ObjectIdentifier::new(ObjectType::BINARY_OUTPUT, instance)?;
243        Ok(Self {
244            oid,
245            name: name.into(),
246            description: String::new(),
247            present_value: 0,
248            out_of_service: false,
249            status_flags: StatusFlags::empty(),
250            priority_array: [None; 16],
251            relinquish_default: 0,
252            polarity: 0,
253            reliability: 0,
254            active_text: "Active".into(),
255            inactive_text: "Inactive".into(),
256            event_detector: ChangeOfStateDetector::default(),
257            value_source: common::ValueSourceTracking::default(),
258        })
259    }
260
261    /// Set the description string.
262    pub fn set_description(&mut self, desc: impl Into<String>) {
263        self.description = desc.into();
264    }
265
266    fn recalculate_present_value(&mut self) {
267        self.present_value =
268            common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
269    }
270}
271
272impl BACnetObject for BinaryOutputObject {
273    fn object_identifier(&self) -> ObjectIdentifier {
274        self.oid
275    }
276
277    fn object_name(&self) -> &str {
278        &self.name
279    }
280
281    fn supports_cov(&self) -> bool {
282        true
283    }
284
285    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
286        self.event_detector.evaluate(self.present_value)
287    }
288
289    fn read_property(
290        &self,
291        property: PropertyIdentifier,
292        array_index: Option<u32>,
293    ) -> Result<PropertyValue, Error> {
294        if property == PropertyIdentifier::STATUS_FLAGS {
295            return Ok(common::compute_status_flags(
296                self.status_flags,
297                self.reliability,
298                self.out_of_service,
299                self.event_detector.event_state.to_raw(),
300            ));
301        }
302        if let Some(result) = read_common_properties!(self, property, array_index) {
303            return result;
304        }
305        match property {
306            p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
307                ObjectType::BINARY_OUTPUT.to_raw(),
308            )),
309            p if p == PropertyIdentifier::PRESENT_VALUE => {
310                Ok(PropertyValue::Enumerated(self.present_value))
311            }
312            p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
313                self.event_detector.event_state.to_raw(),
314            )),
315            p if p == PropertyIdentifier::PRIORITY_ARRAY => {
316                common::read_priority_array!(self, array_index, PropertyValue::Enumerated)
317            }
318            p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
319                Ok(PropertyValue::Enumerated(self.relinquish_default))
320            }
321            p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
322                Ok(common::current_command_priority(&self.priority_array))
323            }
324            p if p == PropertyIdentifier::VALUE_SOURCE => {
325                Ok(self.value_source.value_source.clone())
326            }
327            p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
328                match self.value_source.last_command_time {
329                    BACnetTimeStamp::SequenceNumber(n) => n,
330                    _ => 0,
331                },
332            )),
333            p if p == PropertyIdentifier::POLARITY => Ok(PropertyValue::Enumerated(self.polarity)),
334            p if p == PropertyIdentifier::ACTIVE_TEXT => {
335                Ok(PropertyValue::CharacterString(self.active_text.clone()))
336            }
337            p if p == PropertyIdentifier::INACTIVE_TEXT => {
338                Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
339            }
340            p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
341                unused_bits: 5,
342                data: vec![self.event_detector.event_enable << 5],
343            }),
344            p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
345                unused_bits: 5,
346                data: vec![self.event_detector.acked_transitions << 5],
347            }),
348            p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
349                self.event_detector.notification_class as u64,
350            )),
351            p if p == PropertyIdentifier::EVENT_TIME_STAMPS => Ok(PropertyValue::List(vec![
352                PropertyValue::Unsigned(0),
353                PropertyValue::Unsigned(0),
354                PropertyValue::Unsigned(0),
355            ])),
356            _ => Err(common::unknown_property_error()),
357        }
358    }
359
360    fn write_property(
361        &mut self,
362        property: PropertyIdentifier,
363        array_index: Option<u32>,
364        value: PropertyValue,
365        priority: Option<u8>,
366    ) -> Result<(), Error> {
367        common::write_priority_array_direct!(self, property, array_index, value, |v| {
368            if let PropertyValue::Enumerated(e) = v {
369                if e > 1 {
370                    Err(common::value_out_of_range_error())
371                } else {
372                    Ok(e)
373                }
374            } else {
375                Err(common::invalid_data_type_error())
376            }
377        });
378        if property == PropertyIdentifier::PRESENT_VALUE {
379            return common::write_priority_array!(self, value, priority, |v| {
380                if let PropertyValue::Enumerated(e) = v {
381                    if e > 1 {
382                        Err(common::value_out_of_range_error())
383                    } else {
384                        Ok(e)
385                    }
386                } else {
387                    Err(common::invalid_data_type_error())
388                }
389            });
390        }
391        if property == PropertyIdentifier::ACTIVE_TEXT {
392            if let PropertyValue::CharacterString(s) = value {
393                self.active_text = s;
394                return Ok(());
395            }
396            return Err(common::invalid_data_type_error());
397        }
398        if property == PropertyIdentifier::INACTIVE_TEXT {
399            if let PropertyValue::CharacterString(s) = value {
400                self.inactive_text = s;
401                return Ok(());
402            }
403            return Err(common::invalid_data_type_error());
404        }
405        if let Some(result) =
406            common::write_out_of_service(&mut self.out_of_service, property, &value)
407        {
408            return result;
409        }
410        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
411            return result;
412        }
413        if let Some(result) = common::write_description(&mut self.description, property, &value) {
414            return result;
415        }
416        Err(common::write_access_denied_error())
417    }
418
419    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
420        static PROPS: &[PropertyIdentifier] = &[
421            PropertyIdentifier::OBJECT_IDENTIFIER,
422            PropertyIdentifier::OBJECT_NAME,
423            PropertyIdentifier::DESCRIPTION,
424            PropertyIdentifier::OBJECT_TYPE,
425            PropertyIdentifier::PRESENT_VALUE,
426            PropertyIdentifier::STATUS_FLAGS,
427            PropertyIdentifier::EVENT_STATE,
428            PropertyIdentifier::OUT_OF_SERVICE,
429            PropertyIdentifier::PRIORITY_ARRAY,
430            PropertyIdentifier::RELINQUISH_DEFAULT,
431            PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
432            PropertyIdentifier::POLARITY,
433            PropertyIdentifier::RELIABILITY,
434            PropertyIdentifier::ACTIVE_TEXT,
435            PropertyIdentifier::INACTIVE_TEXT,
436        ];
437        Cow::Borrowed(PROPS)
438    }
439}
440
441// ---------------------------------------------------------------------------
442// BinaryValue (type 5)
443// ---------------------------------------------------------------------------
444
445/// BACnet Binary Value object.
446///
447/// Commandable binary value with 16-level priority array.
448/// Uses Enumerated values: 0 = inactive, 1 = active.
449pub struct BinaryValueObject {
450    oid: ObjectIdentifier,
451    name: String,
452    description: String,
453    present_value: u32, // 0 = inactive, 1 = active
454    out_of_service: bool,
455    status_flags: StatusFlags,
456    priority_array: [Option<u32>; 16],
457    relinquish_default: u32,
458    /// Reliability: 0 = NO_FAULT_DETECTED.
459    reliability: u32,
460    active_text: String,
461    inactive_text: String,
462    /// CHANGE_OF_STATE event detector.
463    event_detector: ChangeOfStateDetector,
464    /// Value source tracking (optional per spec — exposed via VALUE_SOURCE property).
465    value_source: common::ValueSourceTracking,
466}
467
468impl BinaryValueObject {
469    /// Create a new Binary Value object.
470    pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
471        let oid = ObjectIdentifier::new(ObjectType::BINARY_VALUE, instance)?;
472        Ok(Self {
473            oid,
474            name: name.into(),
475            description: String::new(),
476            present_value: 0, // inactive
477            out_of_service: false,
478            status_flags: StatusFlags::empty(),
479            priority_array: [None; 16],
480            relinquish_default: 0,
481            reliability: 0,
482            active_text: "Active".into(),
483            inactive_text: "Inactive".into(),
484            event_detector: ChangeOfStateDetector::default(),
485            value_source: common::ValueSourceTracking::default(),
486        })
487    }
488
489    /// Set the description string.
490    pub fn set_description(&mut self, desc: impl Into<String>) {
491        self.description = desc.into();
492    }
493
494    fn recalculate_present_value(&mut self) {
495        self.present_value =
496            common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
497    }
498}
499
500impl BACnetObject for BinaryValueObject {
501    fn object_identifier(&self) -> ObjectIdentifier {
502        self.oid
503    }
504
505    fn object_name(&self) -> &str {
506        &self.name
507    }
508
509    fn supports_cov(&self) -> bool {
510        true
511    }
512
513    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
514        self.event_detector.evaluate(self.present_value)
515    }
516
517    fn read_property(
518        &self,
519        property: PropertyIdentifier,
520        array_index: Option<u32>,
521    ) -> Result<PropertyValue, Error> {
522        if property == PropertyIdentifier::STATUS_FLAGS {
523            return Ok(common::compute_status_flags(
524                self.status_flags,
525                self.reliability,
526                self.out_of_service,
527                self.event_detector.event_state.to_raw(),
528            ));
529        }
530        if let Some(result) = read_common_properties!(self, property, array_index) {
531            return result;
532        }
533        match property {
534            p if p == PropertyIdentifier::OBJECT_TYPE => {
535                Ok(PropertyValue::Enumerated(ObjectType::BINARY_VALUE.to_raw()))
536            }
537            p if p == PropertyIdentifier::PRESENT_VALUE => {
538                Ok(PropertyValue::Enumerated(self.present_value))
539            }
540            p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
541                self.event_detector.event_state.to_raw(),
542            )),
543            p if p == PropertyIdentifier::PRIORITY_ARRAY => {
544                common::read_priority_array!(self, array_index, PropertyValue::Enumerated)
545            }
546            p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
547                Ok(PropertyValue::Enumerated(self.relinquish_default))
548            }
549            p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
550                Ok(common::current_command_priority(&self.priority_array))
551            }
552            p if p == PropertyIdentifier::VALUE_SOURCE => {
553                Ok(self.value_source.value_source.clone())
554            }
555            p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
556                match self.value_source.last_command_time {
557                    BACnetTimeStamp::SequenceNumber(n) => n,
558                    _ => 0,
559                },
560            )),
561            p if p == PropertyIdentifier::ACTIVE_TEXT => {
562                Ok(PropertyValue::CharacterString(self.active_text.clone()))
563            }
564            p if p == PropertyIdentifier::INACTIVE_TEXT => {
565                Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
566            }
567            p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
568                unused_bits: 5,
569                data: vec![self.event_detector.event_enable << 5],
570            }),
571            p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
572                unused_bits: 5,
573                data: vec![self.event_detector.acked_transitions << 5],
574            }),
575            p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
576                self.event_detector.notification_class as u64,
577            )),
578            p if p == PropertyIdentifier::EVENT_TIME_STAMPS => Ok(PropertyValue::List(vec![
579                PropertyValue::Unsigned(0),
580                PropertyValue::Unsigned(0),
581                PropertyValue::Unsigned(0),
582            ])),
583            _ => Err(common::unknown_property_error()),
584        }
585    }
586
587    fn write_property(
588        &mut self,
589        property: PropertyIdentifier,
590        array_index: Option<u32>,
591        value: PropertyValue,
592        priority: Option<u8>,
593    ) -> Result<(), Error> {
594        common::write_priority_array_direct!(self, property, array_index, value, |v| {
595            if let PropertyValue::Enumerated(e) = v {
596                if e > 1 {
597                    Err(common::value_out_of_range_error())
598                } else {
599                    Ok(e)
600                }
601            } else {
602                Err(common::invalid_data_type_error())
603            }
604        });
605        if property == PropertyIdentifier::PRESENT_VALUE {
606            return common::write_priority_array!(self, value, priority, |v| {
607                if let PropertyValue::Enumerated(e) = v {
608                    if e > 1 {
609                        Err(common::value_out_of_range_error())
610                    } else {
611                        Ok(e)
612                    }
613                } else {
614                    Err(common::invalid_data_type_error())
615                }
616            });
617        }
618        if property == PropertyIdentifier::ACTIVE_TEXT {
619            if let PropertyValue::CharacterString(s) = value {
620                self.active_text = s;
621                return Ok(());
622            }
623            return Err(common::invalid_data_type_error());
624        }
625        if property == PropertyIdentifier::INACTIVE_TEXT {
626            if let PropertyValue::CharacterString(s) = value {
627                self.inactive_text = s;
628                return Ok(());
629            }
630            return Err(common::invalid_data_type_error());
631        }
632        if let Some(result) =
633            common::write_out_of_service(&mut self.out_of_service, property, &value)
634        {
635            return result;
636        }
637        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
638            return result;
639        }
640        if let Some(result) = common::write_description(&mut self.description, property, &value) {
641            return result;
642        }
643        Err(common::write_access_denied_error())
644    }
645
646    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
647        static PROPS: &[PropertyIdentifier] = &[
648            PropertyIdentifier::OBJECT_IDENTIFIER,
649            PropertyIdentifier::OBJECT_NAME,
650            PropertyIdentifier::DESCRIPTION,
651            PropertyIdentifier::OBJECT_TYPE,
652            PropertyIdentifier::PRESENT_VALUE,
653            PropertyIdentifier::STATUS_FLAGS,
654            PropertyIdentifier::EVENT_STATE,
655            PropertyIdentifier::OUT_OF_SERVICE,
656            PropertyIdentifier::PRIORITY_ARRAY,
657            PropertyIdentifier::RELINQUISH_DEFAULT,
658            PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
659            PropertyIdentifier::RELIABILITY,
660            PropertyIdentifier::ACTIVE_TEXT,
661            PropertyIdentifier::INACTIVE_TEXT,
662        ];
663        Cow::Borrowed(PROPS)
664    }
665}
666
667#[cfg(test)]
668mod tests {
669    use super::*;
670
671    #[test]
672    fn bv_read_present_value_default() {
673        let bv = BinaryValueObject::new(1, "BV-1").unwrap();
674        let val = bv
675            .read_property(PropertyIdentifier::PRESENT_VALUE, None)
676            .unwrap();
677        assert_eq!(val, PropertyValue::Enumerated(0)); // inactive
678    }
679
680    #[test]
681    fn bv_write_present_value() {
682        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
683        bv.write_property(
684            PropertyIdentifier::PRESENT_VALUE,
685            None,
686            PropertyValue::Enumerated(1), // active
687            Some(8),
688        )
689        .unwrap();
690        let val = bv
691            .read_property(PropertyIdentifier::PRESENT_VALUE, None)
692            .unwrap();
693        assert_eq!(val, PropertyValue::Enumerated(1));
694    }
695
696    #[test]
697    fn bv_write_invalid_value_rejected() {
698        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
699        let result = bv.write_property(
700            PropertyIdentifier::PRESENT_VALUE,
701            None,
702            PropertyValue::Enumerated(2), // invalid -- only 0 or 1
703            Some(8),
704        );
705        assert!(result.is_err());
706    }
707
708    #[test]
709    fn bv_write_wrong_type_rejected() {
710        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
711        let result = bv.write_property(
712            PropertyIdentifier::PRESENT_VALUE,
713            None,
714            PropertyValue::Real(1.0), // wrong type
715            Some(8),
716        );
717        assert!(result.is_err());
718    }
719
720    #[test]
721    fn bv_read_object_type() {
722        let bv = BinaryValueObject::new(1, "BV-1").unwrap();
723        let val = bv
724            .read_property(PropertyIdentifier::OBJECT_TYPE, None)
725            .unwrap();
726        assert_eq!(
727            val,
728            PropertyValue::Enumerated(ObjectType::BINARY_VALUE.to_raw())
729        );
730    }
731
732    #[test]
733    fn bv_read_reliability_default() {
734        let bv = BinaryValueObject::new(1, "BV-1").unwrap();
735        let val = bv
736            .read_property(PropertyIdentifier::RELIABILITY, None)
737            .unwrap();
738        assert_eq!(val, PropertyValue::Enumerated(0)); // NO_FAULT_DETECTED
739    }
740
741    // --- BinaryInput ---
742
743    #[test]
744    fn bi_read_present_value_default() {
745        let bi = BinaryInputObject::new(1, "BI-1").unwrap();
746        let val = bi
747            .read_property(PropertyIdentifier::PRESENT_VALUE, None)
748            .unwrap();
749        assert_eq!(val, PropertyValue::Enumerated(0));
750    }
751
752    #[test]
753    fn bi_write_denied_when_in_service() {
754        let mut bi = BinaryInputObject::new(1, "BI-1").unwrap();
755        let result = bi.write_property(
756            PropertyIdentifier::PRESENT_VALUE,
757            None,
758            PropertyValue::Enumerated(1),
759            None,
760        );
761        assert!(result.is_err());
762    }
763
764    #[test]
765    fn bi_write_allowed_when_out_of_service() {
766        let mut bi = BinaryInputObject::new(1, "BI-1").unwrap();
767        bi.write_property(
768            PropertyIdentifier::OUT_OF_SERVICE,
769            None,
770            PropertyValue::Boolean(true),
771            None,
772        )
773        .unwrap();
774        bi.write_property(
775            PropertyIdentifier::PRESENT_VALUE,
776            None,
777            PropertyValue::Enumerated(1),
778            None,
779        )
780        .unwrap();
781        let val = bi
782            .read_property(PropertyIdentifier::PRESENT_VALUE, None)
783            .unwrap();
784        assert_eq!(val, PropertyValue::Enumerated(1));
785    }
786
787    #[test]
788    fn bi_set_present_value() {
789        let mut bi = BinaryInputObject::new(1, "BI-1").unwrap();
790        bi.set_present_value(1);
791        let val = bi
792            .read_property(PropertyIdentifier::PRESENT_VALUE, None)
793            .unwrap();
794        assert_eq!(val, PropertyValue::Enumerated(1));
795    }
796
797    #[test]
798    fn bi_read_polarity_default() {
799        let bi = BinaryInputObject::new(1, "BI-1").unwrap();
800        let val = bi
801            .read_property(PropertyIdentifier::POLARITY, None)
802            .unwrap();
803        assert_eq!(val, PropertyValue::Enumerated(0)); // normal
804    }
805
806    #[test]
807    fn bi_read_reliability_default() {
808        let bi = BinaryInputObject::new(1, "BI-1").unwrap();
809        let val = bi
810            .read_property(PropertyIdentifier::RELIABILITY, None)
811            .unwrap();
812        assert_eq!(val, PropertyValue::Enumerated(0)); // NO_FAULT_DETECTED
813    }
814
815    // --- BinaryOutput ---
816
817    #[test]
818    fn bo_write_with_priority() {
819        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
820        bo.write_property(
821            PropertyIdentifier::PRESENT_VALUE,
822            None,
823            PropertyValue::Enumerated(1),
824            Some(8),
825        )
826        .unwrap();
827        let val = bo
828            .read_property(PropertyIdentifier::PRESENT_VALUE, None)
829            .unwrap();
830        assert_eq!(val, PropertyValue::Enumerated(1));
831        let slot = bo
832            .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(8))
833            .unwrap();
834        assert_eq!(slot, PropertyValue::Enumerated(1));
835    }
836
837    #[test]
838    fn bo_relinquish_falls_to_default() {
839        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
840        bo.write_property(
841            PropertyIdentifier::PRESENT_VALUE,
842            None,
843            PropertyValue::Enumerated(1),
844            Some(16),
845        )
846        .unwrap();
847        assert_eq!(
848            bo.read_property(PropertyIdentifier::PRESENT_VALUE, None)
849                .unwrap(),
850            PropertyValue::Enumerated(1)
851        );
852        bo.write_property(
853            PropertyIdentifier::PRESENT_VALUE,
854            None,
855            PropertyValue::Null,
856            Some(16),
857        )
858        .unwrap();
859        assert_eq!(
860            bo.read_property(PropertyIdentifier::PRESENT_VALUE, None)
861                .unwrap(),
862            PropertyValue::Enumerated(0)
863        );
864    }
865
866    #[test]
867    fn bo_invalid_value_rejected() {
868        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
869        let result = bo.write_property(
870            PropertyIdentifier::PRESENT_VALUE,
871            None,
872            PropertyValue::Enumerated(2),
873            None,
874        );
875        assert!(result.is_err());
876    }
877
878    #[test]
879    fn bo_read_polarity_default() {
880        let bo = BinaryOutputObject::new(1, "BO-1").unwrap();
881        let val = bo
882            .read_property(PropertyIdentifier::POLARITY, None)
883            .unwrap();
884        assert_eq!(val, PropertyValue::Enumerated(0)); // normal
885    }
886
887    #[test]
888    fn bo_read_reliability_default() {
889        let bo = BinaryOutputObject::new(1, "BO-1").unwrap();
890        let val = bo
891            .read_property(PropertyIdentifier::RELIABILITY, None)
892            .unwrap();
893        assert_eq!(val, PropertyValue::Enumerated(0)); // NO_FAULT_DETECTED
894    }
895
896    // --- Priority array bounds tests (BinaryOutput) ---
897
898    #[test]
899    fn bo_priority_array_index_zero_returns_size() {
900        let bo = BinaryOutputObject::new(1, "BO-1").unwrap();
901        let val = bo
902            .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(0))
903            .unwrap();
904        assert_eq!(val, PropertyValue::Unsigned(16));
905    }
906
907    #[test]
908    fn bo_priority_array_index_out_of_bounds() {
909        let bo = BinaryOutputObject::new(1, "BO-1").unwrap();
910        // Index 17 is out of bounds (valid: 0-16)
911        let result = bo.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(17));
912        assert!(result.is_err());
913    }
914
915    #[test]
916    fn bo_priority_array_index_far_out_of_bounds() {
917        let bo = BinaryOutputObject::new(1, "BO-1").unwrap();
918        let result = bo.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(100));
919        assert!(result.is_err());
920    }
921
922    // --- WriteProperty with invalid priority tests (BinaryOutput) ---
923
924    #[test]
925    fn bo_write_with_priority_zero_rejected() {
926        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
927        // Priority 0 is invalid (valid range is 1-16)
928        let result = bo.write_property(
929            PropertyIdentifier::PRESENT_VALUE,
930            None,
931            PropertyValue::Enumerated(1),
932            Some(0),
933        );
934        assert!(result.is_err());
935    }
936
937    #[test]
938    fn bo_write_with_priority_17_rejected() {
939        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
940        // Priority 17 is invalid (valid range is 1-16)
941        let result = bo.write_property(
942            PropertyIdentifier::PRESENT_VALUE,
943            None,
944            PropertyValue::Enumerated(1),
945            Some(17),
946        );
947        assert!(result.is_err());
948    }
949
950    #[test]
951    fn bo_write_with_priority_255_rejected() {
952        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
953        // Priority 255 is invalid
954        let result = bo.write_property(
955            PropertyIdentifier::PRESENT_VALUE,
956            None,
957            PropertyValue::Enumerated(1),
958            Some(255),
959        );
960        assert!(result.is_err());
961    }
962
963    // --- BinaryInput read-only properties ---
964
965    #[test]
966    fn bi_polarity_is_readable_as_enumerated() {
967        let bi = BinaryInputObject::new(1, "BI-1").unwrap();
968        let val = bi
969            .read_property(PropertyIdentifier::POLARITY, None)
970            .unwrap();
971        // Polarity default is 0 (normal), verify it comes back as Enumerated
972        match val {
973            PropertyValue::Enumerated(v) => assert_eq!(v, 0),
974            other => panic!("Expected Enumerated for POLARITY, got {:?}", other),
975        }
976    }
977
978    #[test]
979    fn bi_reliability_is_readable_as_enumerated() {
980        let bi = BinaryInputObject::new(1, "BI-1").unwrap();
981        let val = bi
982            .read_property(PropertyIdentifier::RELIABILITY, None)
983            .unwrap();
984        // Reliability default is 0 (NO_FAULT_DETECTED), verify correct type
985        match val {
986            PropertyValue::Enumerated(v) => assert_eq!(v, 0),
987            other => panic!("Expected Enumerated for RELIABILITY, got {:?}", other),
988        }
989    }
990
991    #[test]
992    fn bo_polarity_is_readable_as_enumerated() {
993        let bo = BinaryOutputObject::new(1, "BO-1").unwrap();
994        let val = bo
995            .read_property(PropertyIdentifier::POLARITY, None)
996            .unwrap();
997        match val {
998            PropertyValue::Enumerated(v) => assert_eq!(v, 0),
999            other => panic!("Expected Enumerated for POLARITY, got {:?}", other),
1000        }
1001    }
1002
1003    #[test]
1004    fn bo_reliability_is_readable_as_enumerated() {
1005        let bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1006        let val = bo
1007            .read_property(PropertyIdentifier::RELIABILITY, None)
1008            .unwrap();
1009        match val {
1010            PropertyValue::Enumerated(v) => assert_eq!(v, 0),
1011            other => panic!("Expected Enumerated for RELIABILITY, got {:?}", other),
1012        }
1013    }
1014
1015    #[test]
1016    fn bi_polarity_in_property_list() {
1017        let bi = BinaryInputObject::new(1, "BI-1").unwrap();
1018        let props = bi.property_list();
1019        assert!(props.contains(&PropertyIdentifier::POLARITY));
1020        assert!(props.contains(&PropertyIdentifier::RELIABILITY));
1021    }
1022
1023    #[test]
1024    fn bo_priority_array_read_all_slots_none_by_default() {
1025        let bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1026        let val = bo
1027            .read_property(PropertyIdentifier::PRIORITY_ARRAY, None)
1028            .unwrap();
1029        if let PropertyValue::List(elements) = val {
1030            assert_eq!(elements.len(), 16);
1031            for elem in &elements {
1032                assert_eq!(elem, &PropertyValue::Null);
1033            }
1034        } else {
1035            panic!("Expected List for priority array without index");
1036        }
1037    }
1038
1039    // --- Direct PRIORITY_ARRAY writes ---
1040
1041    #[test]
1042    fn bo_direct_priority_array_write_value() {
1043        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1044        bo.write_property(
1045            PropertyIdentifier::PRIORITY_ARRAY,
1046            Some(5),
1047            PropertyValue::Enumerated(1), // active
1048            None,
1049        )
1050        .unwrap();
1051        assert_eq!(
1052            bo.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1053                .unwrap(),
1054            PropertyValue::Enumerated(1)
1055        );
1056        assert_eq!(
1057            bo.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
1058                .unwrap(),
1059            PropertyValue::Enumerated(1)
1060        );
1061    }
1062
1063    #[test]
1064    fn bo_direct_priority_array_relinquish() {
1065        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1066        bo.write_property(
1067            PropertyIdentifier::PRIORITY_ARRAY,
1068            Some(5),
1069            PropertyValue::Enumerated(1),
1070            None,
1071        )
1072        .unwrap();
1073        bo.write_property(
1074            PropertyIdentifier::PRIORITY_ARRAY,
1075            Some(5),
1076            PropertyValue::Null,
1077            None,
1078        )
1079        .unwrap();
1080        // Fall back to relinquish default (0 = inactive)
1081        assert_eq!(
1082            bo.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1083                .unwrap(),
1084            PropertyValue::Enumerated(0)
1085        );
1086    }
1087
1088    #[test]
1089    fn bo_direct_priority_array_no_index_error() {
1090        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1091        let result = bo.write_property(
1092            PropertyIdentifier::PRIORITY_ARRAY,
1093            None,
1094            PropertyValue::Enumerated(1),
1095            None,
1096        );
1097        assert!(result.is_err());
1098    }
1099
1100    #[test]
1101    fn bo_direct_priority_array_index_zero_error() {
1102        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1103        let result = bo.write_property(
1104            PropertyIdentifier::PRIORITY_ARRAY,
1105            Some(0),
1106            PropertyValue::Enumerated(1),
1107            None,
1108        );
1109        assert!(result.is_err());
1110    }
1111
1112    #[test]
1113    fn bo_direct_priority_array_index_17_error() {
1114        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1115        let result = bo.write_property(
1116            PropertyIdentifier::PRIORITY_ARRAY,
1117            Some(17),
1118            PropertyValue::Enumerated(1),
1119            None,
1120        );
1121        assert!(result.is_err());
1122    }
1123
1124    // --- BinaryValue commandable tests ---
1125
1126    #[test]
1127    fn bv_write_with_priority() {
1128        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
1129        bv.write_property(
1130            PropertyIdentifier::PRESENT_VALUE,
1131            None,
1132            PropertyValue::Enumerated(1),
1133            Some(8),
1134        )
1135        .unwrap();
1136        let val = bv
1137            .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1138            .unwrap();
1139        assert_eq!(val, PropertyValue::Enumerated(1));
1140        let slot = bv
1141            .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(8))
1142            .unwrap();
1143        assert_eq!(slot, PropertyValue::Enumerated(1));
1144    }
1145
1146    #[test]
1147    fn bv_relinquish_falls_to_default() {
1148        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
1149        bv.write_property(
1150            PropertyIdentifier::PRESENT_VALUE,
1151            None,
1152            PropertyValue::Enumerated(1),
1153            Some(16),
1154        )
1155        .unwrap();
1156        assert_eq!(
1157            bv.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1158                .unwrap(),
1159            PropertyValue::Enumerated(1)
1160        );
1161        bv.write_property(
1162            PropertyIdentifier::PRESENT_VALUE,
1163            None,
1164            PropertyValue::Null,
1165            Some(16),
1166        )
1167        .unwrap();
1168        assert_eq!(
1169            bv.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1170                .unwrap(),
1171            PropertyValue::Enumerated(0) // relinquish_default
1172        );
1173    }
1174
1175    #[test]
1176    fn bv_read_priority_array_all_none() {
1177        let bv = BinaryValueObject::new(1, "BV-1").unwrap();
1178        let val = bv
1179            .read_property(PropertyIdentifier::PRIORITY_ARRAY, None)
1180            .unwrap();
1181        if let PropertyValue::List(elements) = val {
1182            assert_eq!(elements.len(), 16);
1183            for elem in &elements {
1184                assert_eq!(elem, &PropertyValue::Null);
1185            }
1186        } else {
1187            panic!("Expected List for priority array without index");
1188        }
1189    }
1190
1191    #[test]
1192    fn bv_read_relinquish_default() {
1193        let bv = BinaryValueObject::new(1, "BV-1").unwrap();
1194        let val = bv
1195            .read_property(PropertyIdentifier::RELINQUISH_DEFAULT, None)
1196            .unwrap();
1197        assert_eq!(val, PropertyValue::Enumerated(0));
1198    }
1199
1200    #[test]
1201    fn bv_priority_array_in_property_list() {
1202        let bv = BinaryValueObject::new(1, "BV-1").unwrap();
1203        let props = bv.property_list();
1204        assert!(props.contains(&PropertyIdentifier::PRIORITY_ARRAY));
1205        assert!(props.contains(&PropertyIdentifier::RELINQUISH_DEFAULT));
1206    }
1207
1208    #[test]
1209    fn bv_direct_priority_array_write() {
1210        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
1211        bv.write_property(
1212            PropertyIdentifier::PRIORITY_ARRAY,
1213            Some(5),
1214            PropertyValue::Enumerated(1),
1215            None,
1216        )
1217        .unwrap();
1218        assert_eq!(
1219            bv.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1220                .unwrap(),
1221            PropertyValue::Enumerated(1)
1222        );
1223        assert_eq!(
1224            bv.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
1225                .unwrap(),
1226            PropertyValue::Enumerated(1)
1227        );
1228    }
1229
1230    #[test]
1231    fn bv_direct_priority_array_relinquish() {
1232        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
1233        bv.write_property(
1234            PropertyIdentifier::PRIORITY_ARRAY,
1235            Some(5),
1236            PropertyValue::Enumerated(1),
1237            None,
1238        )
1239        .unwrap();
1240        bv.write_property(
1241            PropertyIdentifier::PRIORITY_ARRAY,
1242            Some(5),
1243            PropertyValue::Null,
1244            None,
1245        )
1246        .unwrap();
1247        assert_eq!(
1248            bv.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1249                .unwrap(),
1250            PropertyValue::Enumerated(0) // relinquish_default
1251        );
1252    }
1253
1254    // --- Description tests ---
1255
1256    #[test]
1257    fn bv_description_read_write() {
1258        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
1259        // Default is empty
1260        assert_eq!(
1261            bv.read_property(PropertyIdentifier::DESCRIPTION, None)
1262                .unwrap(),
1263            PropertyValue::CharacterString(String::new())
1264        );
1265        bv.write_property(
1266            PropertyIdentifier::DESCRIPTION,
1267            None,
1268            PropertyValue::CharacterString("Occupied/Unoccupied".into()),
1269            None,
1270        )
1271        .unwrap();
1272        assert_eq!(
1273            bv.read_property(PropertyIdentifier::DESCRIPTION, None)
1274                .unwrap(),
1275            PropertyValue::CharacterString("Occupied/Unoccupied".into())
1276        );
1277    }
1278
1279    #[test]
1280    fn bv_description_in_property_list() {
1281        let bv = BinaryValueObject::new(1, "BV-1").unwrap();
1282        assert!(bv
1283            .property_list()
1284            .contains(&PropertyIdentifier::DESCRIPTION));
1285    }
1286
1287    #[test]
1288    fn bi_description_read_write() {
1289        let mut bi = BinaryInputObject::new(1, "BI-1").unwrap();
1290        bi.write_property(
1291            PropertyIdentifier::DESCRIPTION,
1292            None,
1293            PropertyValue::CharacterString("Door contact".into()),
1294            None,
1295        )
1296        .unwrap();
1297        assert_eq!(
1298            bi.read_property(PropertyIdentifier::DESCRIPTION, None)
1299                .unwrap(),
1300            PropertyValue::CharacterString("Door contact".into())
1301        );
1302    }
1303
1304    #[test]
1305    fn bo_description_read_write() {
1306        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1307        bo.write_property(
1308            PropertyIdentifier::DESCRIPTION,
1309            None,
1310            PropertyValue::CharacterString("Fan enable".into()),
1311            None,
1312        )
1313        .unwrap();
1314        assert_eq!(
1315            bo.read_property(PropertyIdentifier::DESCRIPTION, None)
1316                .unwrap(),
1317            PropertyValue::CharacterString("Fan enable".into())
1318        );
1319    }
1320
1321    #[test]
1322    fn bv_higher_priority_wins() {
1323        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
1324        // Write inactive at priority 10
1325        bv.write_property(
1326            PropertyIdentifier::PRESENT_VALUE,
1327            None,
1328            PropertyValue::Enumerated(0),
1329            Some(10),
1330        )
1331        .unwrap();
1332        // Write active at priority 5 (higher)
1333        bv.write_property(
1334            PropertyIdentifier::PRESENT_VALUE,
1335            None,
1336            PropertyValue::Enumerated(1),
1337            Some(5),
1338        )
1339        .unwrap();
1340        assert_eq!(
1341            bv.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1342                .unwrap(),
1343            PropertyValue::Enumerated(1) // priority 5 wins
1344        );
1345        // Relinquish priority 5, falls to priority 10
1346        bv.write_property(
1347            PropertyIdentifier::PRESENT_VALUE,
1348            None,
1349            PropertyValue::Null,
1350            Some(5),
1351        )
1352        .unwrap();
1353        assert_eq!(
1354            bv.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1355                .unwrap(),
1356            PropertyValue::Enumerated(0) // priority 10 value
1357        );
1358    }
1359
1360    // --- active_text / inactive_text tests ---
1361
1362    #[test]
1363    fn bi_active_inactive_text_defaults() {
1364        let bi = BinaryInputObject::new(1, "BI-1").unwrap();
1365        assert_eq!(
1366            bi.read_property(PropertyIdentifier::ACTIVE_TEXT, None)
1367                .unwrap(),
1368            PropertyValue::CharacterString("Active".into())
1369        );
1370        assert_eq!(
1371            bi.read_property(PropertyIdentifier::INACTIVE_TEXT, None)
1372                .unwrap(),
1373            PropertyValue::CharacterString("Inactive".into())
1374        );
1375    }
1376
1377    #[test]
1378    fn bi_active_inactive_text_write_read() {
1379        let mut bi = BinaryInputObject::new(1, "BI-1").unwrap();
1380        bi.write_property(
1381            PropertyIdentifier::ACTIVE_TEXT,
1382            None,
1383            PropertyValue::CharacterString("On".into()),
1384            None,
1385        )
1386        .unwrap();
1387        bi.write_property(
1388            PropertyIdentifier::INACTIVE_TEXT,
1389            None,
1390            PropertyValue::CharacterString("Off".into()),
1391            None,
1392        )
1393        .unwrap();
1394        assert_eq!(
1395            bi.read_property(PropertyIdentifier::ACTIVE_TEXT, None)
1396                .unwrap(),
1397            PropertyValue::CharacterString("On".into())
1398        );
1399        assert_eq!(
1400            bi.read_property(PropertyIdentifier::INACTIVE_TEXT, None)
1401                .unwrap(),
1402            PropertyValue::CharacterString("Off".into())
1403        );
1404    }
1405
1406    #[test]
1407    fn bi_active_text_wrong_type_rejected() {
1408        let mut bi = BinaryInputObject::new(1, "BI-1").unwrap();
1409        assert!(bi
1410            .write_property(
1411                PropertyIdentifier::ACTIVE_TEXT,
1412                None,
1413                PropertyValue::Enumerated(1),
1414                None,
1415            )
1416            .is_err());
1417    }
1418
1419    #[test]
1420    fn bi_inactive_text_wrong_type_rejected() {
1421        let mut bi = BinaryInputObject::new(1, "BI-1").unwrap();
1422        assert!(bi
1423            .write_property(
1424                PropertyIdentifier::INACTIVE_TEXT,
1425                None,
1426                PropertyValue::Boolean(false),
1427                None,
1428            )
1429            .is_err());
1430    }
1431
1432    #[test]
1433    fn bi_active_inactive_text_in_property_list() {
1434        let bi = BinaryInputObject::new(1, "BI-1").unwrap();
1435        let props = bi.property_list();
1436        assert!(props.contains(&PropertyIdentifier::ACTIVE_TEXT));
1437        assert!(props.contains(&PropertyIdentifier::INACTIVE_TEXT));
1438    }
1439
1440    #[test]
1441    fn bo_active_inactive_text_defaults() {
1442        let bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1443        assert_eq!(
1444            bo.read_property(PropertyIdentifier::ACTIVE_TEXT, None)
1445                .unwrap(),
1446            PropertyValue::CharacterString("Active".into())
1447        );
1448        assert_eq!(
1449            bo.read_property(PropertyIdentifier::INACTIVE_TEXT, None)
1450                .unwrap(),
1451            PropertyValue::CharacterString("Inactive".into())
1452        );
1453    }
1454
1455    #[test]
1456    fn bo_active_inactive_text_write_read() {
1457        let mut bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1458        bo.write_property(
1459            PropertyIdentifier::ACTIVE_TEXT,
1460            None,
1461            PropertyValue::CharacterString("Running".into()),
1462            None,
1463        )
1464        .unwrap();
1465        bo.write_property(
1466            PropertyIdentifier::INACTIVE_TEXT,
1467            None,
1468            PropertyValue::CharacterString("Stopped".into()),
1469            None,
1470        )
1471        .unwrap();
1472        assert_eq!(
1473            bo.read_property(PropertyIdentifier::ACTIVE_TEXT, None)
1474                .unwrap(),
1475            PropertyValue::CharacterString("Running".into())
1476        );
1477        assert_eq!(
1478            bo.read_property(PropertyIdentifier::INACTIVE_TEXT, None)
1479                .unwrap(),
1480            PropertyValue::CharacterString("Stopped".into())
1481        );
1482    }
1483
1484    #[test]
1485    fn bo_active_inactive_text_in_property_list() {
1486        let bo = BinaryOutputObject::new(1, "BO-1").unwrap();
1487        let props = bo.property_list();
1488        assert!(props.contains(&PropertyIdentifier::ACTIVE_TEXT));
1489        assert!(props.contains(&PropertyIdentifier::INACTIVE_TEXT));
1490    }
1491
1492    #[test]
1493    fn bv_active_inactive_text_defaults() {
1494        let bv = BinaryValueObject::new(1, "BV-1").unwrap();
1495        assert_eq!(
1496            bv.read_property(PropertyIdentifier::ACTIVE_TEXT, None)
1497                .unwrap(),
1498            PropertyValue::CharacterString("Active".into())
1499        );
1500        assert_eq!(
1501            bv.read_property(PropertyIdentifier::INACTIVE_TEXT, None)
1502                .unwrap(),
1503            PropertyValue::CharacterString("Inactive".into())
1504        );
1505    }
1506
1507    #[test]
1508    fn bv_active_inactive_text_write_read() {
1509        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
1510        bv.write_property(
1511            PropertyIdentifier::ACTIVE_TEXT,
1512            None,
1513            PropertyValue::CharacterString("Occupied".into()),
1514            None,
1515        )
1516        .unwrap();
1517        bv.write_property(
1518            PropertyIdentifier::INACTIVE_TEXT,
1519            None,
1520            PropertyValue::CharacterString("Unoccupied".into()),
1521            None,
1522        )
1523        .unwrap();
1524        assert_eq!(
1525            bv.read_property(PropertyIdentifier::ACTIVE_TEXT, None)
1526                .unwrap(),
1527            PropertyValue::CharacterString("Occupied".into())
1528        );
1529        assert_eq!(
1530            bv.read_property(PropertyIdentifier::INACTIVE_TEXT, None)
1531                .unwrap(),
1532            PropertyValue::CharacterString("Unoccupied".into())
1533        );
1534    }
1535
1536    #[test]
1537    fn bv_active_inactive_text_in_property_list() {
1538        let bv = BinaryValueObject::new(1, "BV-1").unwrap();
1539        let props = bv.property_list();
1540        assert!(props.contains(&PropertyIdentifier::ACTIVE_TEXT));
1541        assert!(props.contains(&PropertyIdentifier::INACTIVE_TEXT));
1542    }
1543
1544    #[test]
1545    fn bv_active_text_wrong_type_rejected() {
1546        let mut bv = BinaryValueObject::new(1, "BV-1").unwrap();
1547        assert!(bv
1548            .write_property(
1549                PropertyIdentifier::ACTIVE_TEXT,
1550                None,
1551                PropertyValue::Real(1.0),
1552                None,
1553            )
1554            .is_err());
1555    }
1556}