Skip to main content

bacnet_objects/analog/
mod.rs

1//! Analog Input (type 0), Analog Output (type 1), and Analog Value (type 2) objects.
2//!
3//! Per ASHRAE 135-2020 Clauses 12.1 (AI), 12.2 (AO), and 12.3 (AV).
4
5use 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
14// ---------------------------------------------------------------------------
15// AnalogInput (type 0)
16// ---------------------------------------------------------------------------
17
18/// BACnet Analog Input object.
19pub 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: minimum change threshold for COV notifications.
28    /// Default 0.0 means notify on any write (including no-change).
29    /// Set to a positive value for delta-based filtering.
30    cov_increment: f32,
31    event_detector: OutOfRangeDetector,
32    /// Reliability: 0 = NO_FAULT_DETECTED.
33    reliability: u32,
34    /// Optional minimum present value for fault detection.
35    min_pres_value: Option<f32>,
36    /// Optional maximum present value for fault detection.
37    max_pres_value: Option<f32>,
38    /// Event_Time_Stamps[3]: to-offnormal, to-fault, to-normal.
39    event_time_stamps: [BACnetTimeStamp; 3],
40    /// Event_Message_Texts[3]: to-offnormal, to-fault, to-normal.
41    event_message_texts: [String; 3],
42}
43
44impl AnalogInputObject {
45    /// Create a new Analog Input object.
46    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    /// Set the present value (used by the application to update sensor readings).
71    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    /// Set the description string.
80    pub fn set_description(&mut self, desc: impl Into<String>) {
81        self.description = desc.into();
82    }
83
84    /// Set the minimum present value for fault detection.
85    pub fn set_min_pres_value(&mut self, value: f32) {
86        self.min_pres_value = Some(value);
87    }
88
89    /// Set the maximum present value for fault detection.
90    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        // IN_ALARM: override STATUS_FLAGS with event_state before common macro
110        if property == PropertyIdentifier::STATUS_FLAGS {
111            return Ok(common::compute_status_flags(
112                self.status_flags,
113                self.reliability,
114                self.out_of_service,
115                self.event_detector.event_state.to_raw(),
116            ));
117        }
118        if let Some(result) = read_common_properties!(self, property, array_index) {
119            return result;
120        }
121        if let Some(result) = read_event_properties!(self, property) {
122            return result;
123        }
124        match property {
125            p if p == PropertyIdentifier::OBJECT_TYPE => {
126                Ok(PropertyValue::Enumerated(ObjectType::ANALOG_INPUT.to_raw()))
127            }
128            p if p == PropertyIdentifier::PRESENT_VALUE => {
129                Ok(PropertyValue::Real(self.present_value))
130            }
131            p if p == PropertyIdentifier::UNITS => Ok(PropertyValue::Enumerated(self.units)),
132            p if p == PropertyIdentifier::COV_INCREMENT => {
133                Ok(PropertyValue::Real(self.cov_increment))
134            }
135            p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
136                Some(v) => Ok(PropertyValue::Real(v)),
137                None => Err(common::unknown_property_error()),
138            },
139            p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
140                Some(v) => Ok(PropertyValue::Real(v)),
141                None => Err(common::unknown_property_error()),
142            },
143            _ => Err(common::unknown_property_error()),
144        }
145    }
146
147    fn write_property(
148        &mut self,
149        property: PropertyIdentifier,
150        _array_index: Option<u32>,
151        value: PropertyValue,
152        _priority: Option<u8>,
153    ) -> Result<(), Error> {
154        // AI present-value is writable only when out-of-service
155        if property == PropertyIdentifier::PRESENT_VALUE {
156            if !self.out_of_service {
157                return Err(common::write_access_denied_error());
158            }
159            if let PropertyValue::Real(v) = value {
160                if !v.is_finite() {
161                    return Err(common::value_out_of_range_error());
162                }
163                self.present_value = v;
164                return Ok(());
165            }
166            return Err(common::invalid_data_type_error());
167        }
168        if let Some(result) =
169            common::write_out_of_service(&mut self.out_of_service, property, &value)
170        {
171            return result;
172        }
173        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
174            return result;
175        }
176        if let Some(result) = common::write_description(&mut self.description, property, &value) {
177            return result;
178        }
179        if property == PropertyIdentifier::RELIABILITY {
180            if let PropertyValue::Enumerated(v) = value {
181                self.reliability = v;
182                return Ok(());
183            }
184            return Err(common::invalid_data_type_error());
185        }
186        if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
187        {
188            return result;
189        }
190        if let Some(result) = write_event_properties!(self, property, value) {
191            return result;
192        }
193        Err(common::write_access_denied_error())
194    }
195
196    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
197        static PROPS: &[PropertyIdentifier] = &[
198            PropertyIdentifier::OBJECT_IDENTIFIER,
199            PropertyIdentifier::OBJECT_NAME,
200            PropertyIdentifier::DESCRIPTION,
201            PropertyIdentifier::OBJECT_TYPE,
202            PropertyIdentifier::PRESENT_VALUE,
203            PropertyIdentifier::STATUS_FLAGS,
204            PropertyIdentifier::EVENT_STATE,
205            PropertyIdentifier::OUT_OF_SERVICE,
206            PropertyIdentifier::UNITS,
207            PropertyIdentifier::COV_INCREMENT,
208            PropertyIdentifier::HIGH_LIMIT,
209            PropertyIdentifier::LOW_LIMIT,
210            PropertyIdentifier::DEADBAND,
211            PropertyIdentifier::LIMIT_ENABLE,
212            PropertyIdentifier::EVENT_ENABLE,
213            PropertyIdentifier::NOTIFY_TYPE,
214            PropertyIdentifier::NOTIFICATION_CLASS,
215            PropertyIdentifier::TIME_DELAY,
216            PropertyIdentifier::RELIABILITY,
217            PropertyIdentifier::ACKED_TRANSITIONS,
218            PropertyIdentifier::EVENT_TIME_STAMPS,
219            PropertyIdentifier::EVENT_MESSAGE_TEXTS,
220        ];
221        Cow::Borrowed(PROPS)
222    }
223
224    fn supports_cov(&self) -> bool {
225        true
226    }
227
228    fn cov_increment(&self) -> Option<f32> {
229        Some(self.cov_increment)
230    }
231
232    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
233        self.event_detector.evaluate(self.present_value)
234    }
235
236    fn acknowledge_alarm(&mut self, transition_bit: u8) -> Result<(), bacnet_types::error::Error> {
237        self.event_detector.acked_transitions |= transition_bit & 0x07;
238        Ok(())
239    }
240}
241
242// ---------------------------------------------------------------------------
243// AnalogOutput (type 1)
244// ---------------------------------------------------------------------------
245
246/// BACnet Analog Output object.
247pub struct AnalogOutputObject {
248    oid: ObjectIdentifier,
249    name: String,
250    description: String,
251    present_value: f32,
252    units: u32,
253    out_of_service: bool,
254    status_flags: StatusFlags,
255    priority_array: [Option<f32>; 16],
256    relinquish_default: f32,
257    /// COV_Increment: minimum change threshold for COV notifications.
258    /// Default 0.0 means notify on any write (including no-change).
259    /// Set to a positive value for delta-based filtering.
260    cov_increment: f32,
261    event_detector: OutOfRangeDetector,
262    reliability: u32,
263    min_pres_value: Option<f32>,
264    max_pres_value: Option<f32>,
265    event_time_stamps: [BACnetTimeStamp; 3],
266    event_message_texts: [String; 3],
267    /// Value source tracking.
268    value_source: common::ValueSourceTracking,
269}
270
271impl AnalogOutputObject {
272    /// Create a new Analog Output object.
273    pub fn new(instance: u32, name: impl Into<String>, units: u32) -> Result<Self, Error> {
274        let oid = ObjectIdentifier::new(ObjectType::ANALOG_OUTPUT, instance)?;
275        Ok(Self {
276            oid,
277            name: name.into(),
278            description: String::new(),
279            present_value: 0.0,
280            units,
281            out_of_service: false,
282            status_flags: StatusFlags::empty(),
283            priority_array: [None; 16],
284            relinquish_default: 0.0,
285            cov_increment: 0.0,
286            event_detector: OutOfRangeDetector::default(),
287            reliability: 0,
288            min_pres_value: None,
289            max_pres_value: None,
290            event_time_stamps: [
291                BACnetTimeStamp::SequenceNumber(0),
292                BACnetTimeStamp::SequenceNumber(0),
293                BACnetTimeStamp::SequenceNumber(0),
294            ],
295            event_message_texts: [String::new(), String::new(), String::new()],
296            value_source: common::ValueSourceTracking::default(),
297        })
298    }
299
300    /// Set the description string.
301    pub fn set_description(&mut self, desc: impl Into<String>) {
302        self.description = desc.into();
303    }
304
305    /// Set the minimum present value for fault detection.
306    pub fn set_min_pres_value(&mut self, value: f32) {
307        self.min_pres_value = Some(value);
308    }
309
310    /// Set the maximum present value for fault detection.
311    pub fn set_max_pres_value(&mut self, value: f32) {
312        self.max_pres_value = Some(value);
313    }
314
315    /// Recalculate present-value from the priority array.
316    fn recalculate_present_value(&mut self) {
317        self.present_value =
318            common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
319    }
320}
321
322impl BACnetObject for AnalogOutputObject {
323    fn object_identifier(&self) -> ObjectIdentifier {
324        self.oid
325    }
326
327    fn object_name(&self) -> &str {
328        &self.name
329    }
330
331    fn read_property(
332        &self,
333        property: PropertyIdentifier,
334        array_index: Option<u32>,
335    ) -> Result<PropertyValue, Error> {
336        if property == PropertyIdentifier::STATUS_FLAGS {
337            return Ok(common::compute_status_flags(
338                self.status_flags,
339                self.reliability,
340                self.out_of_service,
341                self.event_detector.event_state.to_raw(),
342            ));
343        }
344        if let Some(result) = read_common_properties!(self, property, array_index) {
345            return result;
346        }
347        if let Some(result) = read_event_properties!(self, property) {
348            return result;
349        }
350        match property {
351            p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
352                ObjectType::ANALOG_OUTPUT.to_raw(),
353            )),
354            p if p == PropertyIdentifier::PRESENT_VALUE => {
355                Ok(PropertyValue::Real(self.present_value))
356            }
357            p if p == PropertyIdentifier::UNITS => Ok(PropertyValue::Enumerated(self.units)),
358            p if p == PropertyIdentifier::PRIORITY_ARRAY => {
359                common::read_priority_array!(self, array_index, PropertyValue::Real)
360            }
361            p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
362                Ok(PropertyValue::Real(self.relinquish_default))
363            }
364            p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
365                Ok(common::current_command_priority(&self.priority_array))
366            }
367            p if p == PropertyIdentifier::VALUE_SOURCE => {
368                Ok(self.value_source.value_source.clone())
369            }
370            p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
371                match self.value_source.last_command_time {
372                    BACnetTimeStamp::SequenceNumber(n) => n,
373                    _ => 0,
374                },
375            )),
376            p if p == PropertyIdentifier::COV_INCREMENT => {
377                Ok(PropertyValue::Real(self.cov_increment))
378            }
379            p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
380                Some(v) => Ok(PropertyValue::Real(v)),
381                None => Err(common::unknown_property_error()),
382            },
383            p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
384                Some(v) => Ok(PropertyValue::Real(v)),
385                None => Err(common::unknown_property_error()),
386            },
387            _ => Err(common::unknown_property_error()),
388        }
389    }
390
391    fn write_property(
392        &mut self,
393        property: PropertyIdentifier,
394        array_index: Option<u32>,
395        value: PropertyValue,
396        priority: Option<u8>,
397    ) -> Result<(), Error> {
398        common::write_priority_array_direct!(self, property, array_index, value, |v| {
399            if let PropertyValue::Real(f) = v {
400                if !f.is_finite() {
401                    return Err(common::value_out_of_range_error());
402                }
403                Ok(f)
404            } else {
405                Err(common::invalid_data_type_error())
406            }
407        });
408        if property == PropertyIdentifier::PRESENT_VALUE {
409            return common::write_priority_array!(self, value, priority, |v| {
410                if let PropertyValue::Real(f) = v {
411                    if !f.is_finite() {
412                        return Err(common::value_out_of_range_error());
413                    }
414                    Ok(f)
415                } else {
416                    Err(common::invalid_data_type_error())
417                }
418            });
419        }
420        if let Some(result) =
421            common::write_out_of_service(&mut self.out_of_service, property, &value)
422        {
423            return result;
424        }
425        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
426            return result;
427        }
428        if let Some(result) = common::write_description(&mut self.description, property, &value) {
429            return result;
430        }
431        if property == PropertyIdentifier::RELIABILITY {
432            if let PropertyValue::Enumerated(v) = value {
433                self.reliability = v;
434                return Ok(());
435            }
436            return Err(common::invalid_data_type_error());
437        }
438        if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
439        {
440            return result;
441        }
442        if let Some(result) = write_event_properties!(self, property, value) {
443            return result;
444        }
445        Err(common::write_access_denied_error())
446    }
447
448    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
449        static PROPS: &[PropertyIdentifier] = &[
450            PropertyIdentifier::OBJECT_IDENTIFIER,
451            PropertyIdentifier::OBJECT_NAME,
452            PropertyIdentifier::DESCRIPTION,
453            PropertyIdentifier::OBJECT_TYPE,
454            PropertyIdentifier::PRESENT_VALUE,
455            PropertyIdentifier::STATUS_FLAGS,
456            PropertyIdentifier::EVENT_STATE,
457            PropertyIdentifier::OUT_OF_SERVICE,
458            PropertyIdentifier::UNITS,
459            PropertyIdentifier::PRIORITY_ARRAY,
460            PropertyIdentifier::RELINQUISH_DEFAULT,
461            PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
462            PropertyIdentifier::COV_INCREMENT,
463            PropertyIdentifier::HIGH_LIMIT,
464            PropertyIdentifier::LOW_LIMIT,
465            PropertyIdentifier::DEADBAND,
466            PropertyIdentifier::LIMIT_ENABLE,
467            PropertyIdentifier::EVENT_ENABLE,
468            PropertyIdentifier::NOTIFY_TYPE,
469            PropertyIdentifier::NOTIFICATION_CLASS,
470            PropertyIdentifier::TIME_DELAY,
471            PropertyIdentifier::RELIABILITY,
472            PropertyIdentifier::ACKED_TRANSITIONS,
473            PropertyIdentifier::EVENT_TIME_STAMPS,
474            PropertyIdentifier::EVENT_MESSAGE_TEXTS,
475        ];
476        Cow::Borrowed(PROPS)
477    }
478
479    fn supports_cov(&self) -> bool {
480        true
481    }
482
483    fn cov_increment(&self) -> Option<f32> {
484        Some(self.cov_increment)
485    }
486
487    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
488        self.event_detector.evaluate(self.present_value)
489    }
490
491    fn acknowledge_alarm(&mut self, transition_bit: u8) -> Result<(), bacnet_types::error::Error> {
492        self.event_detector.acked_transitions |= transition_bit & 0x07;
493        Ok(())
494    }
495}
496
497// ---------------------------------------------------------------------------
498// AnalogValue (type 2)
499// ---------------------------------------------------------------------------
500
501/// BACnet Analog Value object.
502pub struct AnalogValueObject {
503    oid: ObjectIdentifier,
504    name: String,
505    description: String,
506    present_value: f32,
507    units: u32,
508    out_of_service: bool,
509    status_flags: StatusFlags,
510    /// 16-level priority array. `None` = no command at that level.
511    priority_array: [Option<f32>; 16],
512    relinquish_default: f32,
513    /// COV_Increment: minimum change threshold for COV notifications.
514    /// Default 0.0 means notify on any write (including no-change).
515    /// Set to a positive value for delta-based filtering.
516    cov_increment: f32,
517    event_detector: OutOfRangeDetector,
518    /// Reliability: 0 = NO_FAULT_DETECTED.
519    reliability: u32,
520    min_pres_value: Option<f32>,
521    max_pres_value: Option<f32>,
522    event_time_stamps: [BACnetTimeStamp; 3],
523    event_message_texts: [String; 3],
524    /// Value source tracking.
525    value_source: common::ValueSourceTracking,
526}
527
528impl AnalogValueObject {
529    /// Create a new Analog Value object.
530    pub fn new(instance: u32, name: impl Into<String>, units: u32) -> Result<Self, Error> {
531        let oid = ObjectIdentifier::new(ObjectType::ANALOG_VALUE, instance)?;
532        Ok(Self {
533            oid,
534            name: name.into(),
535            description: String::new(),
536            present_value: 0.0,
537            units,
538            out_of_service: false,
539            status_flags: StatusFlags::empty(),
540            priority_array: [None; 16],
541            relinquish_default: 0.0,
542            cov_increment: 0.0,
543            event_detector: OutOfRangeDetector::default(),
544            reliability: 0,
545            min_pres_value: None,
546            max_pres_value: None,
547            event_time_stamps: [
548                BACnetTimeStamp::SequenceNumber(0),
549                BACnetTimeStamp::SequenceNumber(0),
550                BACnetTimeStamp::SequenceNumber(0),
551            ],
552            event_message_texts: [String::new(), String::new(), String::new()],
553            value_source: common::ValueSourceTracking::default(),
554        })
555    }
556
557    /// Set the present value directly (bypasses priority array; use when out-of-service
558    /// or for initialisation before the priority-array mechanism takes over).
559    pub fn set_present_value(&mut self, value: f32) {
560        debug_assert!(
561            value.is_finite(),
562            "set_present_value called with non-finite value"
563        );
564        self.present_value = value;
565    }
566
567    /// Set the description string.
568    pub fn set_description(&mut self, desc: impl Into<String>) {
569        self.description = desc.into();
570    }
571
572    /// Set the minimum present value for fault detection.
573    pub fn set_min_pres_value(&mut self, value: f32) {
574        self.min_pres_value = Some(value);
575    }
576
577    /// Set the maximum present value for fault detection.
578    pub fn set_max_pres_value(&mut self, value: f32) {
579        self.max_pres_value = Some(value);
580    }
581
582    /// Recalculate present-value from the priority array.
583    fn recalculate_present_value(&mut self) {
584        self.present_value =
585            common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
586    }
587}
588
589impl BACnetObject for AnalogValueObject {
590    fn object_identifier(&self) -> ObjectIdentifier {
591        self.oid
592    }
593
594    fn object_name(&self) -> &str {
595        &self.name
596    }
597
598    fn read_property(
599        &self,
600        property: PropertyIdentifier,
601        array_index: Option<u32>,
602    ) -> Result<PropertyValue, Error> {
603        if property == PropertyIdentifier::STATUS_FLAGS {
604            return Ok(common::compute_status_flags(
605                self.status_flags,
606                self.reliability,
607                self.out_of_service,
608                self.event_detector.event_state.to_raw(),
609            ));
610        }
611        if let Some(result) = read_common_properties!(self, property, array_index) {
612            return result;
613        }
614        if let Some(result) = read_event_properties!(self, property) {
615            return result;
616        }
617        match property {
618            p if p == PropertyIdentifier::OBJECT_TYPE => {
619                Ok(PropertyValue::Enumerated(ObjectType::ANALOG_VALUE.to_raw()))
620            }
621            p if p == PropertyIdentifier::PRESENT_VALUE => {
622                Ok(PropertyValue::Real(self.present_value))
623            }
624            p if p == PropertyIdentifier::UNITS => Ok(PropertyValue::Enumerated(self.units)),
625            p if p == PropertyIdentifier::PRIORITY_ARRAY => {
626                common::read_priority_array!(self, array_index, PropertyValue::Real)
627            }
628            p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
629                Ok(PropertyValue::Real(self.relinquish_default))
630            }
631            p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
632                Ok(common::current_command_priority(&self.priority_array))
633            }
634            p if p == PropertyIdentifier::VALUE_SOURCE => {
635                Ok(self.value_source.value_source.clone())
636            }
637            p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
638                match self.value_source.last_command_time {
639                    BACnetTimeStamp::SequenceNumber(n) => n,
640                    _ => 0,
641                },
642            )),
643            p if p == PropertyIdentifier::COV_INCREMENT => {
644                Ok(PropertyValue::Real(self.cov_increment))
645            }
646            p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
647                Some(v) => Ok(PropertyValue::Real(v)),
648                None => Err(common::unknown_property_error()),
649            },
650            p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
651                Some(v) => Ok(PropertyValue::Real(v)),
652                None => Err(common::unknown_property_error()),
653            },
654            _ => Err(common::unknown_property_error()),
655        }
656    }
657
658    fn write_property(
659        &mut self,
660        property: PropertyIdentifier,
661        array_index: Option<u32>,
662        value: PropertyValue,
663        priority: Option<u8>,
664    ) -> Result<(), Error> {
665        common::write_priority_array_direct!(self, property, array_index, value, |v| {
666            if let PropertyValue::Real(f) = v {
667                if !f.is_finite() {
668                    return Err(common::value_out_of_range_error());
669                }
670                Ok(f)
671            } else {
672                Err(common::invalid_data_type_error())
673            }
674        });
675        if property == PropertyIdentifier::PRESENT_VALUE {
676            return common::write_priority_array!(self, value, priority, |v| {
677                if let PropertyValue::Real(f) = v {
678                    if !f.is_finite() {
679                        return Err(common::value_out_of_range_error());
680                    }
681                    Ok(f)
682                } else {
683                    Err(common::invalid_data_type_error())
684                }
685            });
686        }
687        if let Some(result) =
688            common::write_out_of_service(&mut self.out_of_service, property, &value)
689        {
690            return result;
691        }
692        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
693            return result;
694        }
695        if let Some(result) = common::write_description(&mut self.description, property, &value) {
696            return result;
697        }
698        if property == PropertyIdentifier::RELIABILITY {
699            if let PropertyValue::Enumerated(v) = value {
700                self.reliability = v;
701                return Ok(());
702            }
703            return Err(common::invalid_data_type_error());
704        }
705        if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
706        {
707            return result;
708        }
709        if let Some(result) = write_event_properties!(self, property, value) {
710            return result;
711        }
712        Err(common::write_access_denied_error())
713    }
714
715    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
716        static PROPS: &[PropertyIdentifier] = &[
717            PropertyIdentifier::OBJECT_IDENTIFIER,
718            PropertyIdentifier::OBJECT_NAME,
719            PropertyIdentifier::DESCRIPTION,
720            PropertyIdentifier::OBJECT_TYPE,
721            PropertyIdentifier::PRESENT_VALUE,
722            PropertyIdentifier::STATUS_FLAGS,
723            PropertyIdentifier::EVENT_STATE,
724            PropertyIdentifier::OUT_OF_SERVICE,
725            PropertyIdentifier::UNITS,
726            PropertyIdentifier::PRIORITY_ARRAY,
727            PropertyIdentifier::RELINQUISH_DEFAULT,
728            PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
729            PropertyIdentifier::COV_INCREMENT,
730            PropertyIdentifier::HIGH_LIMIT,
731            PropertyIdentifier::LOW_LIMIT,
732            PropertyIdentifier::DEADBAND,
733            PropertyIdentifier::LIMIT_ENABLE,
734            PropertyIdentifier::EVENT_ENABLE,
735            PropertyIdentifier::NOTIFY_TYPE,
736            PropertyIdentifier::NOTIFICATION_CLASS,
737            PropertyIdentifier::TIME_DELAY,
738            PropertyIdentifier::RELIABILITY,
739            PropertyIdentifier::ACKED_TRANSITIONS,
740            PropertyIdentifier::EVENT_TIME_STAMPS,
741            PropertyIdentifier::EVENT_MESSAGE_TEXTS,
742        ];
743        Cow::Borrowed(PROPS)
744    }
745
746    fn supports_cov(&self) -> bool {
747        true
748    }
749
750    fn cov_increment(&self) -> Option<f32> {
751        Some(self.cov_increment)
752    }
753
754    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
755        self.event_detector.evaluate(self.present_value)
756    }
757
758    fn acknowledge_alarm(&mut self, transition_bit: u8) -> Result<(), bacnet_types::error::Error> {
759        self.event_detector.acked_transitions |= transition_bit & 0x07;
760        Ok(())
761    }
762}
763
764// ---------------------------------------------------------------------------
765// Tests
766// ---------------------------------------------------------------------------
767
768#[cfg(test)]
769mod tests;