Skip to main content

bacnet_objects/multistate/
mod.rs

1//! Multi-State Input (type 13), Multi-State Output (type 14), and
2//! Multi-State Value (type 19) objects per ASHRAE 135-2020 Clauses 12.20-12.22.
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// MultiStateInput (type 13)
15// ---------------------------------------------------------------------------
16
17/// BACnet Multi-State Input object.
18///
19/// Read-only multi-state point. Present_Value is writable only when out-of-service.
20/// Present_Value is Unsigned, range 1..=number_of_states.
21pub struct MultiStateInputObject {
22    oid: ObjectIdentifier,
23    name: String,
24    description: String,
25    present_value: u32,
26    number_of_states: u32,
27    out_of_service: bool,
28    status_flags: StatusFlags,
29    /// Reliability: 0 = NO_FAULT_DETECTED.
30    reliability: u32,
31    state_text: Vec<String>,
32    /// Alarm_Values — state values that trigger OFFNORMAL.
33    alarm_values: Vec<u32>,
34    /// Fault_Values — state values that indicate a fault.
35    fault_values: Vec<u32>,
36    /// CHANGE_OF_STATE event detector.
37    event_detector: ChangeOfStateDetector,
38}
39
40impl MultiStateInputObject {
41    pub fn new(
42        instance: u32,
43        name: impl Into<String>,
44        number_of_states: u32,
45    ) -> Result<Self, Error> {
46        let oid = ObjectIdentifier::new(ObjectType::MULTI_STATE_INPUT, instance)?;
47        Ok(Self {
48            oid,
49            name: name.into(),
50            description: String::new(),
51            present_value: 1,
52            number_of_states,
53            out_of_service: false,
54            status_flags: StatusFlags::empty(),
55            reliability: 0,
56            state_text: (1..=number_of_states)
57                .map(|i| format!("State {i}"))
58                .collect(),
59            alarm_values: Vec::new(),
60            fault_values: Vec::new(),
61            event_detector: ChangeOfStateDetector::default(),
62        })
63    }
64
65    /// Set the alarm values (states that trigger OFFNORMAL).
66    pub fn set_alarm_values(&mut self, values: Vec<u32>) {
67        self.alarm_values = values.clone();
68        self.event_detector.alarm_values = values;
69    }
70
71    /// Set the fault values (states that indicate a fault).
72    pub fn set_fault_values(&mut self, values: Vec<u32>) {
73        self.fault_values = values;
74    }
75
76    /// Set the present value (used by application to update input state).
77    pub fn set_present_value(&mut self, value: u32) {
78        self.present_value = value;
79    }
80
81    /// Set the description string.
82    pub fn set_description(&mut self, desc: impl Into<String>) {
83        self.description = desc.into();
84    }
85}
86
87impl BACnetObject for MultiStateInputObject {
88    fn object_identifier(&self) -> ObjectIdentifier {
89        self.oid
90    }
91
92    fn object_name(&self) -> &str {
93        &self.name
94    }
95
96    fn supports_cov(&self) -> bool {
97        true
98    }
99
100    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
101        self.event_detector.evaluate(self.present_value)
102    }
103
104    fn read_property(
105        &self,
106        property: PropertyIdentifier,
107        array_index: Option<u32>,
108    ) -> Result<PropertyValue, Error> {
109        if property == PropertyIdentifier::STATUS_FLAGS {
110            return Ok(common::compute_status_flags(
111                self.status_flags,
112                self.reliability,
113                self.out_of_service,
114                self.event_detector.event_state.to_raw(),
115            ));
116        }
117        if let Some(result) = read_common_properties!(self, property, array_index) {
118            return result;
119        }
120        match property {
121            p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
122                ObjectType::MULTI_STATE_INPUT.to_raw(),
123            )),
124            p if p == PropertyIdentifier::PRESENT_VALUE => {
125                Ok(PropertyValue::Unsigned(self.present_value as u64))
126            }
127            p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
128                self.event_detector.event_state.to_raw(),
129            )),
130            p if p == PropertyIdentifier::NUMBER_OF_STATES => {
131                Ok(PropertyValue::Unsigned(self.number_of_states as u64))
132            }
133            p if p == PropertyIdentifier::STATE_TEXT => match array_index {
134                None => Ok(PropertyValue::List(
135                    self.state_text
136                        .iter()
137                        .map(|s| PropertyValue::CharacterString(s.clone()))
138                        .collect(),
139                )),
140                Some(0) => Ok(PropertyValue::Unsigned(self.state_text.len() as u64)),
141                Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => Ok(
142                    PropertyValue::CharacterString(self.state_text[(idx - 1) as usize].clone()),
143                ),
144                _ => Err(common::invalid_array_index_error()),
145            },
146            p if p == PropertyIdentifier::ALARM_VALUES => Ok(PropertyValue::List(
147                self.alarm_values
148                    .iter()
149                    .map(|v| PropertyValue::Unsigned(*v as u64))
150                    .collect(),
151            )),
152            p if p == PropertyIdentifier::FAULT_VALUES => Ok(PropertyValue::List(
153                self.fault_values
154                    .iter()
155                    .map(|v| PropertyValue::Unsigned(*v as u64))
156                    .collect(),
157            )),
158            p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
159                unused_bits: 5,
160                data: vec![self.event_detector.event_enable << 5],
161            }),
162            p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
163                unused_bits: 5,
164                data: vec![self.event_detector.acked_transitions << 5],
165            }),
166            p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
167                self.event_detector.notification_class as u64,
168            )),
169            _ => Err(common::unknown_property_error()),
170        }
171    }
172
173    fn write_property(
174        &mut self,
175        property: PropertyIdentifier,
176        array_index: Option<u32>,
177        value: PropertyValue,
178        _priority: Option<u8>,
179    ) -> Result<(), Error> {
180        if property == PropertyIdentifier::PRESENT_VALUE {
181            if !self.out_of_service {
182                return Err(common::write_access_denied_error());
183            }
184            if let PropertyValue::Unsigned(v) = value {
185                if v < 1 || v > self.number_of_states as u64 {
186                    return Err(common::value_out_of_range_error());
187                }
188                self.present_value = v as u32;
189                return Ok(());
190            }
191            return Err(common::invalid_data_type_error());
192        }
193        if property == PropertyIdentifier::STATE_TEXT {
194            match array_index {
195                Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => {
196                    if let PropertyValue::CharacterString(s) = value {
197                        self.state_text[(idx - 1) as usize] = s;
198                        return Ok(());
199                    }
200                    return Err(common::invalid_data_type_error());
201                }
202                None => return Err(common::write_access_denied_error()),
203                _ => return Err(common::invalid_array_index_error()),
204            }
205        }
206        if let Some(result) =
207            common::write_out_of_service(&mut self.out_of_service, property, &value)
208        {
209            return result;
210        }
211        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
212            return result;
213        }
214        if let Some(result) = common::write_description(&mut self.description, property, &value) {
215            return result;
216        }
217        Err(common::write_access_denied_error())
218    }
219
220    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
221        static PROPS: &[PropertyIdentifier] = &[
222            PropertyIdentifier::OBJECT_IDENTIFIER,
223            PropertyIdentifier::OBJECT_NAME,
224            PropertyIdentifier::DESCRIPTION,
225            PropertyIdentifier::OBJECT_TYPE,
226            PropertyIdentifier::PRESENT_VALUE,
227            PropertyIdentifier::STATUS_FLAGS,
228            PropertyIdentifier::EVENT_STATE,
229            PropertyIdentifier::OUT_OF_SERVICE,
230            PropertyIdentifier::NUMBER_OF_STATES,
231            PropertyIdentifier::RELIABILITY,
232            PropertyIdentifier::STATE_TEXT,
233            PropertyIdentifier::ALARM_VALUES,
234            PropertyIdentifier::FAULT_VALUES,
235        ];
236        Cow::Borrowed(PROPS)
237    }
238}
239
240// ---------------------------------------------------------------------------
241// MultiStateOutput (type 14)
242// ---------------------------------------------------------------------------
243
244/// BACnet Multi-State Output object.
245///
246/// Commandable multi-state output with 16-level priority array.
247/// Present_Value is Unsigned, range 1..=number_of_states.
248pub struct MultiStateOutputObject {
249    oid: ObjectIdentifier,
250    name: String,
251    description: String,
252    present_value: u32,
253    number_of_states: u32,
254    out_of_service: bool,
255    status_flags: StatusFlags,
256    priority_array: [Option<u32>; 16],
257    relinquish_default: u32,
258    /// Reliability: 0 = NO_FAULT_DETECTED.
259    reliability: u32,
260    state_text: Vec<String>,
261    alarm_values: Vec<u32>,
262    fault_values: Vec<u32>,
263    /// CHANGE_OF_STATE event detector.
264    event_detector: ChangeOfStateDetector,
265    /// Value source tracking (optional per spec — exposed via VALUE_SOURCE property).
266    value_source: common::ValueSourceTracking,
267}
268
269impl MultiStateOutputObject {
270    pub fn new(
271        instance: u32,
272        name: impl Into<String>,
273        number_of_states: u32,
274    ) -> Result<Self, Error> {
275        let oid = ObjectIdentifier::new(ObjectType::MULTI_STATE_OUTPUT, instance)?;
276        Ok(Self {
277            oid,
278            name: name.into(),
279            description: String::new(),
280            present_value: 1,
281            number_of_states,
282            out_of_service: false,
283            status_flags: StatusFlags::empty(),
284            priority_array: [None; 16],
285            relinquish_default: 1,
286            reliability: 0,
287            state_text: (1..=number_of_states)
288                .map(|i| format!("State {i}"))
289                .collect(),
290            alarm_values: Vec::new(),
291            fault_values: Vec::new(),
292            event_detector: ChangeOfStateDetector::default(),
293            value_source: common::ValueSourceTracking::default(),
294        })
295    }
296
297    /// Set the description string.
298    pub fn set_description(&mut self, desc: impl Into<String>) {
299        self.description = desc.into();
300    }
301
302    fn recalculate_present_value(&mut self) {
303        self.present_value =
304            common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
305    }
306}
307
308impl BACnetObject for MultiStateOutputObject {
309    fn object_identifier(&self) -> ObjectIdentifier {
310        self.oid
311    }
312
313    fn object_name(&self) -> &str {
314        &self.name
315    }
316
317    fn supports_cov(&self) -> bool {
318        true
319    }
320
321    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
322        self.event_detector.evaluate(self.present_value)
323    }
324
325    fn read_property(
326        &self,
327        property: PropertyIdentifier,
328        array_index: Option<u32>,
329    ) -> Result<PropertyValue, Error> {
330        if property == PropertyIdentifier::STATUS_FLAGS {
331            return Ok(common::compute_status_flags(
332                self.status_flags,
333                self.reliability,
334                self.out_of_service,
335                self.event_detector.event_state.to_raw(),
336            ));
337        }
338        if let Some(result) = read_common_properties!(self, property, array_index) {
339            return result;
340        }
341        match property {
342            p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
343                ObjectType::MULTI_STATE_OUTPUT.to_raw(),
344            )),
345            p if p == PropertyIdentifier::PRESENT_VALUE => {
346                Ok(PropertyValue::Unsigned(self.present_value as u64))
347            }
348            p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
349                self.event_detector.event_state.to_raw(),
350            )),
351            p if p == PropertyIdentifier::NUMBER_OF_STATES => {
352                Ok(PropertyValue::Unsigned(self.number_of_states as u64))
353            }
354            p if p == PropertyIdentifier::VALUE_SOURCE => {
355                Ok(self.value_source.value_source.clone())
356            }
357            p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
358                match self.value_source.last_command_time {
359                    BACnetTimeStamp::SequenceNumber(n) => n,
360                    _ => 0,
361                },
362            )),
363            p if p == PropertyIdentifier::PRIORITY_ARRAY => {
364                common::read_priority_array!(self, array_index, |v: u32| PropertyValue::Unsigned(
365                    v as u64
366                ))
367            }
368            p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
369                Ok(PropertyValue::Unsigned(self.relinquish_default as u64))
370            }
371            p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
372                Ok(common::current_command_priority(&self.priority_array))
373            }
374            p if p == PropertyIdentifier::STATE_TEXT => match array_index {
375                None => Ok(PropertyValue::List(
376                    self.state_text
377                        .iter()
378                        .map(|s| PropertyValue::CharacterString(s.clone()))
379                        .collect(),
380                )),
381                Some(0) => Ok(PropertyValue::Unsigned(self.state_text.len() as u64)),
382                Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => Ok(
383                    PropertyValue::CharacterString(self.state_text[(idx - 1) as usize].clone()),
384                ),
385                _ => Err(common::invalid_array_index_error()),
386            },
387            p if p == PropertyIdentifier::ALARM_VALUES => Ok(PropertyValue::List(
388                self.alarm_values
389                    .iter()
390                    .map(|v| PropertyValue::Unsigned(*v as u64))
391                    .collect(),
392            )),
393            p if p == PropertyIdentifier::FAULT_VALUES => Ok(PropertyValue::List(
394                self.fault_values
395                    .iter()
396                    .map(|v| PropertyValue::Unsigned(*v as u64))
397                    .collect(),
398            )),
399            p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
400                unused_bits: 5,
401                data: vec![self.event_detector.event_enable << 5],
402            }),
403            p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
404                unused_bits: 5,
405                data: vec![self.event_detector.acked_transitions << 5],
406            }),
407            p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
408                self.event_detector.notification_class as u64,
409            )),
410            _ => Err(common::unknown_property_error()),
411        }
412    }
413
414    fn write_property(
415        &mut self,
416        property: PropertyIdentifier,
417        array_index: Option<u32>,
418        value: PropertyValue,
419        priority: Option<u8>,
420    ) -> Result<(), Error> {
421        {
422            let num_states = self.number_of_states;
423            common::write_priority_array_direct!(self, property, array_index, value, |v| {
424                if let PropertyValue::Unsigned(u) = v {
425                    if u < 1 || u > num_states as u64 {
426                        Err(common::value_out_of_range_error())
427                    } else {
428                        Ok(u as u32)
429                    }
430                } else {
431                    Err(common::invalid_data_type_error())
432                }
433            });
434        }
435        if property == PropertyIdentifier::PRESENT_VALUE {
436            let num_states = self.number_of_states;
437            return common::write_priority_array!(self, value, priority, |v| {
438                if let PropertyValue::Unsigned(u) = v {
439                    if u < 1 || u > num_states as u64 {
440                        Err(common::value_out_of_range_error())
441                    } else {
442                        Ok(u as u32)
443                    }
444                } else {
445                    Err(common::invalid_data_type_error())
446                }
447            });
448        }
449        if property == PropertyIdentifier::STATE_TEXT {
450            match array_index {
451                Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => {
452                    if let PropertyValue::CharacterString(s) = value {
453                        self.state_text[(idx - 1) as usize] = s;
454                        return Ok(());
455                    }
456                    return Err(common::invalid_data_type_error());
457                }
458                None => return Err(common::write_access_denied_error()),
459                _ => return Err(common::invalid_array_index_error()),
460            }
461        }
462        if let Some(result) =
463            common::write_out_of_service(&mut self.out_of_service, property, &value)
464        {
465            return result;
466        }
467        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
468            return result;
469        }
470        if let Some(result) = common::write_description(&mut self.description, property, &value) {
471            return result;
472        }
473        Err(common::write_access_denied_error())
474    }
475
476    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
477        static PROPS: &[PropertyIdentifier] = &[
478            PropertyIdentifier::OBJECT_IDENTIFIER,
479            PropertyIdentifier::OBJECT_NAME,
480            PropertyIdentifier::DESCRIPTION,
481            PropertyIdentifier::OBJECT_TYPE,
482            PropertyIdentifier::PRESENT_VALUE,
483            PropertyIdentifier::STATUS_FLAGS,
484            PropertyIdentifier::EVENT_STATE,
485            PropertyIdentifier::OUT_OF_SERVICE,
486            PropertyIdentifier::NUMBER_OF_STATES,
487            PropertyIdentifier::PRIORITY_ARRAY,
488            PropertyIdentifier::RELINQUISH_DEFAULT,
489            PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
490            PropertyIdentifier::RELIABILITY,
491            PropertyIdentifier::STATE_TEXT,
492            PropertyIdentifier::ALARM_VALUES,
493            PropertyIdentifier::FAULT_VALUES,
494        ];
495        Cow::Borrowed(PROPS)
496    }
497}
498
499// ---------------------------------------------------------------------------
500// MultiStateValue (type 19)
501// ---------------------------------------------------------------------------
502
503/// BACnet Multi-State Value object.
504///
505/// Commandable multi-state value with 16-level priority array.
506/// Present_Value is Unsigned, range 1..=number_of_states.
507pub struct MultiStateValueObject {
508    oid: ObjectIdentifier,
509    name: String,
510    description: String,
511    present_value: u32,
512    number_of_states: u32,
513    out_of_service: bool,
514    status_flags: StatusFlags,
515    priority_array: [Option<u32>; 16],
516    relinquish_default: u32,
517    /// Reliability: 0 = NO_FAULT_DETECTED.
518    reliability: u32,
519    state_text: Vec<String>,
520    alarm_values: Vec<u32>,
521    fault_values: Vec<u32>,
522    /// CHANGE_OF_STATE event detector.
523    event_detector: ChangeOfStateDetector,
524    /// Value source tracking (optional per spec — exposed via VALUE_SOURCE property).
525    value_source: common::ValueSourceTracking,
526}
527
528impl MultiStateValueObject {
529    pub fn new(
530        instance: u32,
531        name: impl Into<String>,
532        number_of_states: u32,
533    ) -> Result<Self, Error> {
534        let oid = ObjectIdentifier::new(ObjectType::MULTI_STATE_VALUE, instance)?;
535        Ok(Self {
536            oid,
537            name: name.into(),
538            description: String::new(),
539            present_value: 1,
540            number_of_states,
541            out_of_service: false,
542            status_flags: StatusFlags::empty(),
543            priority_array: [None; 16],
544            relinquish_default: 1,
545            reliability: 0,
546            state_text: (1..=number_of_states)
547                .map(|i| format!("State {i}"))
548                .collect(),
549            alarm_values: Vec::new(),
550            fault_values: Vec::new(),
551            event_detector: ChangeOfStateDetector::default(),
552            value_source: common::ValueSourceTracking::default(),
553        })
554    }
555
556    /// Set the description string.
557    pub fn set_description(&mut self, desc: impl Into<String>) {
558        self.description = desc.into();
559    }
560
561    fn recalculate_present_value(&mut self) {
562        self.present_value =
563            common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
564    }
565}
566
567impl BACnetObject for MultiStateValueObject {
568    fn object_identifier(&self) -> ObjectIdentifier {
569        self.oid
570    }
571
572    fn object_name(&self) -> &str {
573        &self.name
574    }
575
576    fn supports_cov(&self) -> bool {
577        true
578    }
579
580    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
581        self.event_detector.evaluate(self.present_value)
582    }
583
584    fn read_property(
585        &self,
586        property: PropertyIdentifier,
587        array_index: Option<u32>,
588    ) -> Result<PropertyValue, Error> {
589        if property == PropertyIdentifier::STATUS_FLAGS {
590            return Ok(common::compute_status_flags(
591                self.status_flags,
592                self.reliability,
593                self.out_of_service,
594                self.event_detector.event_state.to_raw(),
595            ));
596        }
597        if let Some(result) = read_common_properties!(self, property, array_index) {
598            return result;
599        }
600        match property {
601            p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
602                ObjectType::MULTI_STATE_VALUE.to_raw(),
603            )),
604            p if p == PropertyIdentifier::PRESENT_VALUE => {
605                Ok(PropertyValue::Unsigned(self.present_value as u64))
606            }
607            p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
608                self.event_detector.event_state.to_raw(),
609            )),
610            p if p == PropertyIdentifier::NUMBER_OF_STATES => {
611                Ok(PropertyValue::Unsigned(self.number_of_states as u64))
612            }
613            p if p == PropertyIdentifier::VALUE_SOURCE => {
614                Ok(self.value_source.value_source.clone())
615            }
616            p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
617                match self.value_source.last_command_time {
618                    BACnetTimeStamp::SequenceNumber(n) => n,
619                    _ => 0,
620                },
621            )),
622            p if p == PropertyIdentifier::PRIORITY_ARRAY => {
623                common::read_priority_array!(self, array_index, |v: u32| PropertyValue::Unsigned(
624                    v as u64
625                ))
626            }
627            p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
628                Ok(PropertyValue::Unsigned(self.relinquish_default as u64))
629            }
630            p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
631                Ok(common::current_command_priority(&self.priority_array))
632            }
633            p if p == PropertyIdentifier::STATE_TEXT => match array_index {
634                None => Ok(PropertyValue::List(
635                    self.state_text
636                        .iter()
637                        .map(|s| PropertyValue::CharacterString(s.clone()))
638                        .collect(),
639                )),
640                Some(0) => Ok(PropertyValue::Unsigned(self.state_text.len() as u64)),
641                Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => Ok(
642                    PropertyValue::CharacterString(self.state_text[(idx - 1) as usize].clone()),
643                ),
644                _ => Err(common::invalid_array_index_error()),
645            },
646            p if p == PropertyIdentifier::ALARM_VALUES => Ok(PropertyValue::List(
647                self.alarm_values
648                    .iter()
649                    .map(|v| PropertyValue::Unsigned(*v as u64))
650                    .collect(),
651            )),
652            p if p == PropertyIdentifier::FAULT_VALUES => Ok(PropertyValue::List(
653                self.fault_values
654                    .iter()
655                    .map(|v| PropertyValue::Unsigned(*v as u64))
656                    .collect(),
657            )),
658            p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
659                unused_bits: 5,
660                data: vec![self.event_detector.event_enable << 5],
661            }),
662            p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
663                unused_bits: 5,
664                data: vec![self.event_detector.acked_transitions << 5],
665            }),
666            p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
667                self.event_detector.notification_class as u64,
668            )),
669            _ => Err(common::unknown_property_error()),
670        }
671    }
672
673    fn write_property(
674        &mut self,
675        property: PropertyIdentifier,
676        array_index: Option<u32>,
677        value: PropertyValue,
678        priority: Option<u8>,
679    ) -> Result<(), Error> {
680        {
681            let num_states = self.number_of_states;
682            common::write_priority_array_direct!(self, property, array_index, value, |v| {
683                if let PropertyValue::Unsigned(u) = v {
684                    if u < 1 || u > num_states as u64 {
685                        Err(common::value_out_of_range_error())
686                    } else {
687                        Ok(u as u32)
688                    }
689                } else {
690                    Err(common::invalid_data_type_error())
691                }
692            });
693        }
694        if property == PropertyIdentifier::PRESENT_VALUE {
695            let num_states = self.number_of_states;
696            return common::write_priority_array!(self, value, priority, |v| {
697                if let PropertyValue::Unsigned(u) = v {
698                    if u < 1 || u > num_states as u64 {
699                        Err(common::value_out_of_range_error())
700                    } else {
701                        Ok(u as u32)
702                    }
703                } else {
704                    Err(common::invalid_data_type_error())
705                }
706            });
707        }
708        if property == PropertyIdentifier::STATE_TEXT {
709            match array_index {
710                Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => {
711                    if let PropertyValue::CharacterString(s) = value {
712                        self.state_text[(idx - 1) as usize] = s;
713                        return Ok(());
714                    }
715                    return Err(common::invalid_data_type_error());
716                }
717                None => return Err(common::write_access_denied_error()),
718                _ => return Err(common::invalid_array_index_error()),
719            }
720        }
721        if let Some(result) =
722            common::write_out_of_service(&mut self.out_of_service, property, &value)
723        {
724            return result;
725        }
726        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
727            return result;
728        }
729        if let Some(result) = common::write_description(&mut self.description, property, &value) {
730            return result;
731        }
732        Err(common::write_access_denied_error())
733    }
734
735    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
736        static PROPS: &[PropertyIdentifier] = &[
737            PropertyIdentifier::OBJECT_IDENTIFIER,
738            PropertyIdentifier::OBJECT_NAME,
739            PropertyIdentifier::DESCRIPTION,
740            PropertyIdentifier::OBJECT_TYPE,
741            PropertyIdentifier::PRESENT_VALUE,
742            PropertyIdentifier::STATUS_FLAGS,
743            PropertyIdentifier::EVENT_STATE,
744            PropertyIdentifier::OUT_OF_SERVICE,
745            PropertyIdentifier::NUMBER_OF_STATES,
746            PropertyIdentifier::PRIORITY_ARRAY,
747            PropertyIdentifier::RELINQUISH_DEFAULT,
748            PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
749            PropertyIdentifier::RELIABILITY,
750            PropertyIdentifier::STATE_TEXT,
751        ];
752        Cow::Borrowed(PROPS)
753    }
754}
755
756#[cfg(test)]
757mod tests;