1use crate::object::{
7 event_state::EventState, reliability::Reliability, BacnetObject, ObjectError, ObjectIdentifier,
8 ObjectType, PropertyIdentifier, PropertyValue, Result,
9};
10
11#[cfg(not(feature = "std"))]
12use alloc::{string::String, vec::Vec};
13
14#[derive(Debug, Clone)]
16pub struct MultiStateInput {
17 pub identifier: ObjectIdentifier,
19 pub object_name: String,
21 pub present_value: u32,
23 pub description: String,
25 pub device_type: String,
27 pub status_flags: u8,
29 pub event_state: EventState,
31 pub reliability: Reliability,
33 pub out_of_service: bool,
35 pub number_of_states: u32,
37 pub state_text: Vec<String>,
39}
40
41#[derive(Debug, Clone)]
43pub struct MultiStateOutput {
44 pub identifier: ObjectIdentifier,
46 pub object_name: String,
48 pub present_value: u32,
50 pub description: String,
52 pub device_type: String,
54 pub status_flags: u8,
56 pub event_state: EventState,
58 pub reliability: Reliability,
60 pub out_of_service: bool,
62 pub number_of_states: u32,
64 pub state_text: Vec<String>,
66 pub priority_array: [Option<u32>; 16],
68 pub relinquish_default: u32,
70}
71
72#[derive(Debug, Clone)]
74pub struct MultiStateValue {
75 pub identifier: ObjectIdentifier,
77 pub object_name: String,
79 pub present_value: u32,
81 pub description: String,
83 pub status_flags: u8,
85 pub event_state: EventState,
87 pub reliability: Reliability,
89 pub out_of_service: bool,
91 pub number_of_states: u32,
93 pub state_text: Vec<String>,
95 pub priority_array: [Option<u32>; 16],
97 pub relinquish_default: u32,
99}
100
101impl MultiStateInput {
102 pub fn new(instance: u32, object_name: String, number_of_states: u32) -> Self {
104 let mut state_text = Vec::with_capacity(number_of_states as usize);
105 for i in 1..=number_of_states {
106 state_text.push(format!("State {}", i));
107 }
108
109 Self {
110 identifier: ObjectIdentifier::new(ObjectType::MultiStateInput, instance),
111 object_name,
112 present_value: 1,
113 description: String::new(),
114 device_type: String::new(),
115 status_flags: 0,
116 event_state: EventState::Normal,
117 reliability: Reliability::NoFaultDetected,
118 out_of_service: false,
119 number_of_states,
120 state_text,
121 }
122 }
123
124 pub fn set_present_value(&mut self, value: u32) -> Result<()> {
126 if value < 1 || value > self.number_of_states {
127 return Err(ObjectError::InvalidValue(format!(
128 "Value must be between 1 and {}",
129 self.number_of_states
130 )));
131 }
132 self.present_value = value;
133 Ok(())
134 }
135
136 pub fn get_state_text(&self) -> Option<&str> {
138 if self.present_value > 0 && self.present_value <= self.state_text.len() as u32 {
139 Some(&self.state_text[(self.present_value - 1) as usize])
140 } else {
141 None
142 }
143 }
144
145 pub fn set_state_text(&mut self, state: u32, text: String) -> Result<()> {
147 if state < 1 || state > self.number_of_states {
148 return Err(ObjectError::InvalidValue(format!(
149 "State must be between 1 and {}",
150 self.number_of_states
151 )));
152 }
153 self.state_text[(state - 1) as usize] = text;
154 Ok(())
155 }
156}
157
158impl MultiStateOutput {
159 pub fn new(instance: u32, object_name: String, number_of_states: u32) -> Self {
161 let mut state_text = Vec::with_capacity(number_of_states as usize);
162 for i in 1..=number_of_states {
163 state_text.push(format!("State {}", i));
164 }
165
166 Self {
167 identifier: ObjectIdentifier::new(ObjectType::MultiStateOutput, instance),
168 object_name,
169 present_value: 1,
170 description: String::new(),
171 device_type: String::new(),
172 status_flags: 0,
173 event_state: EventState::Normal,
174 reliability: Reliability::NoFaultDetected,
175 out_of_service: false,
176 number_of_states,
177 state_text,
178 priority_array: [None; 16],
179 relinquish_default: 1,
180 }
181 }
182
183 pub fn write_priority(&mut self, priority: u8, value: Option<u32>) -> Result<()> {
185 if !(1..=16).contains(&priority) {
186 return Err(ObjectError::InvalidValue(
187 "Priority must be 1-16".to_string(),
188 ));
189 }
190
191 if let Some(val) = value {
192 if val < 1 || val > self.number_of_states {
193 return Err(ObjectError::InvalidValue(format!(
194 "Value must be between 1 and {}",
195 self.number_of_states
196 )));
197 }
198 }
199
200 self.priority_array[(priority - 1) as usize] = value;
201 self.update_present_value();
202 Ok(())
203 }
204
205 fn update_present_value(&mut self) {
207 if let Some(value) = self.priority_array.iter().flatten().next() {
209 self.present_value = *value;
210 return;
211 }
212 self.present_value = self.relinquish_default;
214 }
215
216 pub fn get_effective_priority(&self) -> Option<u8> {
218 for (i, priority_value) in self.priority_array.iter().enumerate() {
219 if priority_value.is_some() {
220 return Some((i + 1) as u8);
221 }
222 }
223 None
224 }
225}
226
227impl MultiStateValue {
228 pub fn new(instance: u32, object_name: String, number_of_states: u32) -> Self {
230 let mut state_text = Vec::with_capacity(number_of_states as usize);
231 for i in 1..=number_of_states {
232 state_text.push(format!("State {}", i));
233 }
234
235 Self {
236 identifier: ObjectIdentifier::new(ObjectType::MultiStateValue, instance),
237 object_name,
238 present_value: 1,
239 description: String::new(),
240 status_flags: 0,
241 event_state: EventState::Normal,
242 reliability: Reliability::NoFaultDetected,
243 out_of_service: false,
244 number_of_states,
245 state_text,
246 priority_array: [None; 16],
247 relinquish_default: 1,
248 }
249 }
250
251 pub fn write_priority(&mut self, priority: u8, value: Option<u32>) -> Result<()> {
253 if !(1..=16).contains(&priority) {
254 return Err(ObjectError::InvalidValue(
255 "Priority must be 1-16".to_string(),
256 ));
257 }
258
259 if let Some(val) = value {
260 if val < 1 || val > self.number_of_states {
261 return Err(ObjectError::InvalidValue(format!(
262 "Value must be between 1 and {}",
263 self.number_of_states
264 )));
265 }
266 }
267
268 self.priority_array[(priority - 1) as usize] = value;
269 self.update_present_value();
270 Ok(())
271 }
272
273 fn update_present_value(&mut self) {
275 if let Some(value) = self.priority_array.iter().flatten().next() {
277 self.present_value = *value;
278 return;
279 }
280 self.present_value = self.relinquish_default;
282 }
283}
284
285impl BacnetObject for MultiStateInput {
286 fn identifier(&self) -> ObjectIdentifier {
287 self.identifier
288 }
289
290 fn get_property(&self, property: PropertyIdentifier) -> Result<PropertyValue> {
291 match property {
292 PropertyIdentifier::ObjectIdentifier => {
293 Ok(PropertyValue::ObjectIdentifier(self.identifier))
294 }
295 PropertyIdentifier::ObjectName => {
296 Ok(PropertyValue::CharacterString(self.object_name.clone()))
297 }
298 PropertyIdentifier::ObjectType => Ok(PropertyValue::Enumerated(u32::from(
299 ObjectType::MultiStateInput,
300 ))),
301 PropertyIdentifier::PresentValue => {
302 Ok(PropertyValue::UnsignedInteger(self.present_value))
303 }
304 PropertyIdentifier::OutOfService => Ok(PropertyValue::Boolean(self.out_of_service)),
305 _ => Err(ObjectError::UnknownProperty),
306 }
307 }
308
309 fn set_property(&mut self, property: PropertyIdentifier, value: PropertyValue) -> Result<()> {
310 match property {
311 PropertyIdentifier::ObjectName => {
312 if let PropertyValue::CharacterString(name) = value {
313 self.object_name = name;
314 Ok(())
315 } else {
316 Err(ObjectError::InvalidPropertyType)
317 }
318 }
319 PropertyIdentifier::OutOfService => {
320 if let PropertyValue::Boolean(oos) = value {
321 self.out_of_service = oos;
322 Ok(())
323 } else {
324 Err(ObjectError::InvalidPropertyType)
325 }
326 }
327 _ => Err(ObjectError::PropertyNotWritable),
328 }
329 }
330
331 fn is_property_writable(&self, property: PropertyIdentifier) -> bool {
332 matches!(
333 property,
334 PropertyIdentifier::ObjectName | PropertyIdentifier::OutOfService
335 )
336 }
337
338 fn property_list(&self) -> Vec<PropertyIdentifier> {
339 vec![
340 PropertyIdentifier::ObjectIdentifier,
341 PropertyIdentifier::ObjectName,
342 PropertyIdentifier::ObjectType,
343 PropertyIdentifier::PresentValue,
344 PropertyIdentifier::OutOfService,
345 ]
346 }
347}
348
349impl BacnetObject for MultiStateOutput {
350 fn identifier(&self) -> ObjectIdentifier {
351 self.identifier
352 }
353
354 fn get_property(&self, property: PropertyIdentifier) -> Result<PropertyValue> {
355 match property {
356 PropertyIdentifier::ObjectIdentifier => {
357 Ok(PropertyValue::ObjectIdentifier(self.identifier))
358 }
359 PropertyIdentifier::ObjectName => {
360 Ok(PropertyValue::CharacterString(self.object_name.clone()))
361 }
362 PropertyIdentifier::ObjectType => Ok(PropertyValue::Enumerated(u32::from(
363 ObjectType::MultiStateOutput,
364 ))),
365 PropertyIdentifier::PresentValue => {
366 Ok(PropertyValue::UnsignedInteger(self.present_value))
367 }
368 PropertyIdentifier::OutOfService => Ok(PropertyValue::Boolean(self.out_of_service)),
369 PropertyIdentifier::PriorityArray => {
370 let array: Vec<PropertyValue> = self
371 .priority_array
372 .iter()
373 .map(|&v| match v {
374 Some(val) => PropertyValue::UnsignedInteger(val),
375 None => PropertyValue::Null,
376 })
377 .collect();
378 Ok(PropertyValue::Array(array))
379 }
380 _ => Err(ObjectError::UnknownProperty),
381 }
382 }
383
384 fn set_property(&mut self, property: PropertyIdentifier, value: PropertyValue) -> Result<()> {
385 match property {
386 PropertyIdentifier::ObjectName => {
387 if let PropertyValue::CharacterString(name) = value {
388 self.object_name = name;
389 Ok(())
390 } else {
391 Err(ObjectError::InvalidPropertyType)
392 }
393 }
394 PropertyIdentifier::PresentValue => {
395 if let PropertyValue::UnsignedInteger(val) = value {
396 self.write_priority(8, Some(val))
398 } else {
399 Err(ObjectError::InvalidPropertyType)
400 }
401 }
402 PropertyIdentifier::OutOfService => {
403 if let PropertyValue::Boolean(oos) = value {
404 self.out_of_service = oos;
405 Ok(())
406 } else {
407 Err(ObjectError::InvalidPropertyType)
408 }
409 }
410 _ => Err(ObjectError::PropertyNotWritable),
411 }
412 }
413
414 fn is_property_writable(&self, property: PropertyIdentifier) -> bool {
415 matches!(
416 property,
417 PropertyIdentifier::ObjectName
418 | PropertyIdentifier::PresentValue
419 | PropertyIdentifier::OutOfService
420 )
421 }
422
423 fn property_list(&self) -> Vec<PropertyIdentifier> {
424 vec![
425 PropertyIdentifier::ObjectIdentifier,
426 PropertyIdentifier::ObjectName,
427 PropertyIdentifier::ObjectType,
428 PropertyIdentifier::PresentValue,
429 PropertyIdentifier::OutOfService,
430 PropertyIdentifier::PriorityArray,
431 ]
432 }
433}
434
435impl BacnetObject for MultiStateValue {
436 fn identifier(&self) -> ObjectIdentifier {
437 self.identifier
438 }
439
440 fn get_property(&self, property: PropertyIdentifier) -> Result<PropertyValue> {
441 match property {
442 PropertyIdentifier::ObjectIdentifier => {
443 Ok(PropertyValue::ObjectIdentifier(self.identifier))
444 }
445 PropertyIdentifier::ObjectName => {
446 Ok(PropertyValue::CharacterString(self.object_name.clone()))
447 }
448 PropertyIdentifier::ObjectType => Ok(PropertyValue::Enumerated(u32::from(
449 ObjectType::MultiStateValue,
450 ))),
451 PropertyIdentifier::PresentValue => {
452 Ok(PropertyValue::UnsignedInteger(self.present_value))
453 }
454 PropertyIdentifier::OutOfService => Ok(PropertyValue::Boolean(self.out_of_service)),
455 PropertyIdentifier::PriorityArray => {
456 let array: Vec<PropertyValue> = self
457 .priority_array
458 .iter()
459 .map(|&v| match v {
460 Some(val) => PropertyValue::UnsignedInteger(val),
461 None => PropertyValue::Null,
462 })
463 .collect();
464 Ok(PropertyValue::Array(array))
465 }
466 _ => Err(ObjectError::UnknownProperty),
467 }
468 }
469
470 fn set_property(&mut self, property: PropertyIdentifier, value: PropertyValue) -> Result<()> {
471 match property {
472 PropertyIdentifier::ObjectName => {
473 if let PropertyValue::CharacterString(name) = value {
474 self.object_name = name;
475 Ok(())
476 } else {
477 Err(ObjectError::InvalidPropertyType)
478 }
479 }
480 PropertyIdentifier::PresentValue => {
481 if let PropertyValue::UnsignedInteger(val) = value {
482 self.write_priority(8, Some(val))
484 } else {
485 Err(ObjectError::InvalidPropertyType)
486 }
487 }
488 PropertyIdentifier::OutOfService => {
489 if let PropertyValue::Boolean(oos) = value {
490 self.out_of_service = oos;
491 Ok(())
492 } else {
493 Err(ObjectError::InvalidPropertyType)
494 }
495 }
496 _ => Err(ObjectError::PropertyNotWritable),
497 }
498 }
499
500 fn is_property_writable(&self, property: PropertyIdentifier) -> bool {
501 matches!(
502 property,
503 PropertyIdentifier::ObjectName
504 | PropertyIdentifier::PresentValue
505 | PropertyIdentifier::OutOfService
506 )
507 }
508
509 fn property_list(&self) -> Vec<PropertyIdentifier> {
510 vec![
511 PropertyIdentifier::ObjectIdentifier,
512 PropertyIdentifier::ObjectName,
513 PropertyIdentifier::ObjectType,
514 PropertyIdentifier::PresentValue,
515 PropertyIdentifier::OutOfService,
516 PropertyIdentifier::PriorityArray,
517 ]
518 }
519}
520
521#[cfg(test)]
522mod tests {
523 use super::*;
524
525 #[test]
526 fn test_multistate_input_creation() {
527 let msi = MultiStateInput::new(1, "Mode Selector".to_string(), 5);
528 assert_eq!(msi.identifier.instance, 1);
529 assert_eq!(msi.object_name, "Mode Selector");
530 assert_eq!(msi.number_of_states, 5);
531 assert_eq!(msi.present_value, 1);
532 assert_eq!(msi.state_text.len(), 5);
533 }
534
535 #[test]
536 fn test_multistate_state_text() {
537 let mut msi = MultiStateInput::new(1, "Mode".to_string(), 3);
538
539 msi.set_state_text(1, "OFF".to_string()).unwrap();
541 msi.set_state_text(2, "AUTO".to_string()).unwrap();
542 msi.set_state_text(3, "MANUAL".to_string()).unwrap();
543
544 assert_eq!(msi.get_state_text(), Some("OFF"));
545
546 msi.set_present_value(2).unwrap();
547 assert_eq!(msi.get_state_text(), Some("AUTO"));
548
549 assert!(msi.set_present_value(4).is_err());
551 }
552
553 #[test]
554 fn test_multistate_output_priority() {
555 let mut mso = MultiStateOutput::new(1, "Sequence Control".to_string(), 4);
556
557 mso.write_priority(8, Some(3)).unwrap();
559 assert_eq!(mso.present_value, 3);
560 assert_eq!(mso.get_effective_priority(), Some(8));
561
562 mso.write_priority(3, Some(2)).unwrap();
564 assert_eq!(mso.present_value, 2);
565 assert_eq!(mso.get_effective_priority(), Some(3));
566
567 assert!(mso.write_priority(3, Some(5)).is_err());
569
570 mso.write_priority(3, None).unwrap();
572 assert_eq!(mso.present_value, 3); }
574
575 #[test]
576 fn test_multistate_properties() {
577 let mut msv = MultiStateValue::new(1, "Operating Mode".to_string(), 4);
578
579 let name = msv.get_property(PropertyIdentifier::ObjectName).unwrap();
581 if let PropertyValue::CharacterString(n) = name {
582 assert_eq!(n, "Operating Mode");
583 } else {
584 panic!("Expected CharacterString");
585 }
586
587 msv.set_property(
589 PropertyIdentifier::PresentValue,
590 PropertyValue::UnsignedInteger(3),
591 )
592 .unwrap();
593 assert_eq!(msv.present_value, 3);
594 }
595}