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