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::traits::{BACnetObject, ReliabilityEvaluation};
17
18pub(crate) const MAX_ALARM_VALUES: usize = 1024;
21
22fn decode_alarm_values_write(
28 array_index: Option<u32>,
29 value: PropertyValue,
30 number_of_states: u32,
31) -> Result<Vec<u32>, Error> {
32 if array_index.is_some() {
33 return Err(Error::Protocol {
34 class: ErrorClass::PROPERTY.to_raw() as u32,
35 code: ErrorCode::PROPERTY_IS_NOT_AN_ARRAY.to_raw() as u32,
36 });
37 }
38 let PropertyValue::List(values) = value else {
39 return Err(common::invalid_data_type_error());
40 };
41 if values.len() > MAX_ALARM_VALUES {
44 return Err(common::at_list_element(
45 Error::Protocol {
46 class: ErrorClass::RESOURCES.to_raw() as u32,
47 code: ErrorCode::NO_SPACE_TO_WRITE_PROPERTY.to_raw() as u32,
48 },
49 MAX_ALARM_VALUES,
50 ));
51 }
52 values
53 .into_iter()
54 .enumerate()
55 .map(|(index, value)| {
56 match value {
57 PropertyValue::Unsigned(state)
58 if (1..=u64::from(number_of_states)).contains(&state) =>
59 {
60 Ok(state as u32)
61 }
62 PropertyValue::Unsigned(_) => Err(common::value_out_of_range_error()),
63 _ => Err(common::invalid_data_type_error()),
64 }
65 .map_err(|error| common::at_list_element(error, index))
66 })
67 .collect()
68}
69
70fn require_nonzero_states(number_of_states: u32) -> Result<(), Error> {
77 if number_of_states == 0 {
78 return Err(Error::OutOfRange(
79 "number_of_states must be at least 1".into(),
80 ));
81 }
82 Ok(())
83}
84
85fn resize_state_text(
90 current_number_of_states: &mut u32,
91 state_text: &mut Vec<String>,
92 number_of_states: u32,
93) -> Result<(), Error> {
94 require_nonzero_states(number_of_states)?;
95 let new_len = number_of_states as usize;
96 if new_len < state_text.len() {
97 state_text.truncate(new_len);
98 } else {
99 state_text.extend((state_text.len() + 1..=new_len).map(|state| format!("State {state}")));
100 }
101 *current_number_of_states = number_of_states;
102 Ok(())
103}
104
105const CREATION_ONLY: &[PropertyIdentifier] = &[PropertyIdentifier::NUMBER_OF_STATES];
109
110pub const MAX_NUMBER_OF_STATES: u32 = 1024;
115
116fn check_state_count(
129 current: u32,
130 count: u64,
131 held: impl IntoIterator<Item = u32>,
132) -> Result<u32, Error> {
133 let count = u32::try_from(count)
134 .ok()
135 .filter(|count| (1..=MAX_NUMBER_OF_STATES).contains(count))
136 .ok_or_else(common::value_out_of_range_error)?;
137 let stranded = |state: u32| (1..=current).contains(&state) && state > count;
138 if held.into_iter().any(stranded) {
139 return Err(common::value_out_of_range_error());
140 }
141 Ok(count)
142}
143
144fn initialize_states(
150 number_of_states: &mut u32,
151 state_text: &mut Vec<String>,
152 held: impl IntoIterator<Item = u32>,
153 property: PropertyIdentifier,
154 value: PropertyValue,
155) -> Result<(), Error> {
156 if property != PropertyIdentifier::NUMBER_OF_STATES {
157 return Err(common::write_access_denied_error());
158 }
159 let PropertyValue::Unsigned(count) = value else {
160 return Err(common::invalid_data_type_error());
161 };
162 let count = check_state_count(*number_of_states, count, held)?;
163 resize_state_text(number_of_states, state_text, count)
164}
165
166fn write_whole_state_text(
175 number_of_states: &mut u32,
176 state_text: &mut Vec<String>,
177 held: impl IntoIterator<Item = u32>,
178 value: PropertyValue,
179) -> Result<(), Error> {
180 let labels = match value {
181 PropertyValue::CharacterString(label) => vec![label],
182 PropertyValue::List(values) => values
183 .into_iter()
184 .map(|value| match value {
185 PropertyValue::CharacterString(label) => Ok(label),
186 _ => Err(common::invalid_data_type_error()),
187 })
188 .collect::<Result<Vec<_>, _>>()?,
189 _ => return Err(common::invalid_data_type_error()),
190 };
191 let count = if labels.len() == *number_of_states as usize {
192 *number_of_states
193 } else {
194 check_state_count(*number_of_states, labels.len() as u64, held)?
195 };
196 *state_text = labels;
197 *number_of_states = count;
198 Ok(())
199}
200
201fn write_state_text(
212 number_of_states: &mut u32,
213 state_text: &mut Vec<String>,
214 held: impl IntoIterator<Item = u32>,
215 array_index: Option<u32>,
216 value: PropertyValue,
217) -> Result<(), Error> {
218 match array_index {
219 None => write_whole_state_text(number_of_states, state_text, held, value),
220 Some(0) => {
221 let PropertyValue::Unsigned(count) = value else {
222 return Err(common::invalid_data_type_error());
223 };
224 let count = check_state_count(*number_of_states, count, held)?;
225 resize_state_text(number_of_states, state_text, count)
226 }
227 Some(index) if (index as usize) <= state_text.len() => {
228 let PropertyValue::CharacterString(label) = value else {
229 return Err(common::invalid_data_type_error());
230 };
231 state_text[(index - 1) as usize] = label;
232 Ok(())
233 }
234 Some(_) => Err(common::invalid_array_index_error()),
235 }
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub(crate) enum OwnedMultiStateFault {
240 ConfigurationError,
241 MultiStateOutOfRange,
242}
243
244impl OwnedMultiStateFault {
245 fn reliability(self) -> Reliability {
246 match self {
247 Self::ConfigurationError => Reliability::CONFIGURATION_ERROR,
248 Self::MultiStateOutOfRange => Reliability::MULTI_STATE_OUT_OF_RANGE,
249 }
250 }
251}
252
253#[derive(Debug, Default)]
255struct MultiStateReliabilityState {
256 pub(crate) owned_fault: Option<OwnedMultiStateFault>,
257}
258
259impl MultiStateReliabilityState {
260 fn clear_ownership(&mut self) {
261 self.owned_fault = None;
262 }
263
264 fn evaluate(
265 &mut self,
266 configuration_invalid: bool,
267 present_value: u32,
268 number_of_states: u32,
269 reliability: &mut Reliability,
270 ) -> ReliabilityEvaluation {
271 let observed_fault = if configuration_invalid {
272 Some(OwnedMultiStateFault::ConfigurationError)
273 } else if !(1..=number_of_states).contains(&present_value) {
274 Some(OwnedMultiStateFault::MultiStateOutOfRange)
275 } else {
276 None
277 };
278
279 let (new_reliability, new_owner) = if self.owned_fault.is_some() {
280 (
281 observed_fault
282 .map(OwnedMultiStateFault::reliability)
283 .unwrap_or(Reliability::NO_FAULT_DETECTED),
284 observed_fault,
285 )
286 } else if *reliability == Reliability::NO_FAULT_DETECTED {
287 let Some(fault) = observed_fault else {
288 return ReliabilityEvaluation::Unchanged;
289 };
290 (fault.reliability(), Some(fault))
291 } else {
292 return ReliabilityEvaluation::Unchanged;
293 };
294
295 self.owned_fault = new_owner;
300 if new_reliability == *reliability {
301 return ReliabilityEvaluation::Unchanged;
302 }
303 let old_reliability = *reliability;
304 *reliability = new_reliability;
305 ReliabilityEvaluation::Changed {
306 old_reliability,
307 new_reliability,
308 }
309 }
310}
311
312mod input;
313mod output;
314mod value;
315pub use input::*;
316pub use output::*;
317pub use value::*;
318#[cfg(test)]
319mod tests;