bacnet_objects/multistate/
mod.rs1use bacnet_types::enums::{
6 ErrorClass, ErrorCode, EventState, ObjectType, PropertyIdentifier, Reliability,
7};
8use bacnet_types::error::Error;
9use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, StatusFlags};
10use std::borrow::Cow;
11
12use crate::common::{
13 self, read_common_properties, read_generic_event_properties, write_generic_event_properties,
14};
15use crate::event::{history::EventHistory, ChangeOfStateDetector};
16use crate::rollback::impl_intrinsic_write_rollback;
17use crate::traits::{BACnetObject, ReliabilityEvaluation};
18
19pub(crate) const MAX_ALARM_VALUES: usize = 1024;
23
24fn decode_alarm_values_write(
25 array_index: Option<u32>,
26 value: PropertyValue,
27) -> Result<Vec<u32>, Error> {
28 if array_index.is_some() {
29 return Err(Error::Protocol {
30 class: ErrorClass::PROPERTY.to_raw() as u32,
31 code: ErrorCode::PROPERTY_IS_NOT_AN_ARRAY.to_raw() as u32,
32 });
33 }
34 let PropertyValue::List(values) = value else {
35 return Err(common::invalid_data_type_error());
36 };
37 if values.len() > MAX_ALARM_VALUES {
38 return Err(Error::Protocol {
39 class: ErrorClass::RESOURCES.to_raw() as u32,
40 code: ErrorCode::NO_SPACE_TO_WRITE_PROPERTY.to_raw() as u32,
41 });
42 }
43 values
44 .into_iter()
45 .map(|value| match value {
46 PropertyValue::Unsigned(value) => common::u64_to_u32(value),
47 _ => Err(common::invalid_data_type_error()),
48 })
49 .collect()
50}
51
52fn require_nonzero_states(number_of_states: u32) -> Result<(), Error> {
59 if number_of_states == 0 {
60 return Err(Error::OutOfRange(
61 "number_of_states must be at least 1".into(),
62 ));
63 }
64 Ok(())
65}
66
67fn resize_state_text(
72 current_number_of_states: &mut u32,
73 state_text: &mut Vec<String>,
74 number_of_states: u32,
75) -> Result<(), Error> {
76 require_nonzero_states(number_of_states)?;
77 let new_len = number_of_states as usize;
78 if new_len < state_text.len() {
79 state_text.truncate(new_len);
80 } else {
81 state_text.extend((state_text.len() + 1..=new_len).map(|state| format!("State {state}")));
82 }
83 *current_number_of_states = number_of_states;
84 Ok(())
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub(crate) enum OwnedMultiStateFault {
89 ConfigurationError,
90 MultiStateOutOfRange,
91}
92
93impl OwnedMultiStateFault {
94 fn reliability(self) -> u32 {
95 match self {
96 Self::ConfigurationError => Reliability::CONFIGURATION_ERROR.to_raw(),
97 Self::MultiStateOutOfRange => Reliability::MULTI_STATE_OUT_OF_RANGE.to_raw(),
98 }
99 }
100}
101
102#[derive(Debug, Default)]
104struct MultiStateReliabilityState {
105 pub(crate) owned_fault: Option<OwnedMultiStateFault>,
106}
107
108impl MultiStateReliabilityState {
109 fn clear_ownership(&mut self) {
110 self.owned_fault = None;
111 }
112
113 fn evaluate(
114 &mut self,
115 configuration_invalid: bool,
116 present_value: u32,
117 number_of_states: u32,
118 reliability: &mut u32,
119 ) -> ReliabilityEvaluation {
120 let observed_fault = if configuration_invalid {
121 Some(OwnedMultiStateFault::ConfigurationError)
122 } else if !(1..=number_of_states).contains(&present_value) {
123 Some(OwnedMultiStateFault::MultiStateOutOfRange)
124 } else {
125 None
126 };
127
128 let (new_reliability, new_owner) = if self.owned_fault.is_some() {
129 (
130 observed_fault
131 .map(OwnedMultiStateFault::reliability)
132 .unwrap_or_else(|| Reliability::NO_FAULT_DETECTED.to_raw()),
133 observed_fault,
134 )
135 } else if *reliability == Reliability::NO_FAULT_DETECTED.to_raw() {
136 let Some(fault) = observed_fault else {
137 return ReliabilityEvaluation::Unchanged;
138 };
139 (fault.reliability(), Some(fault))
140 } else {
141 return ReliabilityEvaluation::Unchanged;
142 };
143
144 self.owned_fault = new_owner;
149 if new_reliability == *reliability {
150 return ReliabilityEvaluation::Unchanged;
151 }
152 let old_reliability = *reliability;
153 *reliability = new_reliability;
154 ReliabilityEvaluation::Changed {
155 old_reliability,
156 new_reliability,
157 }
158 }
159}
160
161mod input;
162mod output;
163mod value;
164pub use input::*;
165pub use output::*;
166pub use value::*;
167#[cfg(test)]
168mod tests;