1use bacnet_types::enums::{ObjectType, PropertyIdentifier};
5use bacnet_types::error::Error;
6use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
7use std::borrow::Cow;
8
9use crate::common::{self, read_common_properties};
10use crate::traits::BACnetObject;
11
12pub struct MultiStateInputObject {
21 oid: ObjectIdentifier,
22 name: String,
23 description: String,
24 present_value: u32,
25 number_of_states: u32,
26 out_of_service: bool,
27 status_flags: StatusFlags,
28 reliability: u32,
30 state_text: Vec<String>,
31}
32
33impl MultiStateInputObject {
34 pub fn new(
35 instance: u32,
36 name: impl Into<String>,
37 number_of_states: u32,
38 ) -> Result<Self, Error> {
39 let oid = ObjectIdentifier::new(ObjectType::MULTI_STATE_INPUT, instance)?;
40 Ok(Self {
41 oid,
42 name: name.into(),
43 description: String::new(),
44 present_value: 1,
45 number_of_states,
46 out_of_service: false,
47 status_flags: StatusFlags::empty(),
48 reliability: 0,
49 state_text: (1..=number_of_states)
50 .map(|i| format!("State {i}"))
51 .collect(),
52 })
53 }
54
55 pub fn set_present_value(&mut self, value: u32) {
57 self.present_value = value;
58 }
59
60 pub fn set_description(&mut self, desc: impl Into<String>) {
62 self.description = desc.into();
63 }
64}
65
66impl BACnetObject for MultiStateInputObject {
67 fn object_identifier(&self) -> ObjectIdentifier {
68 self.oid
69 }
70
71 fn object_name(&self) -> &str {
72 &self.name
73 }
74
75 fn read_property(
76 &self,
77 property: PropertyIdentifier,
78 array_index: Option<u32>,
79 ) -> Result<PropertyValue, Error> {
80 if let Some(result) = read_common_properties!(self, property, array_index) {
81 return result;
82 }
83 match property {
84 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
85 ObjectType::MULTI_STATE_INPUT.to_raw(),
86 )),
87 p if p == PropertyIdentifier::PRESENT_VALUE => {
88 Ok(PropertyValue::Unsigned(self.present_value as u64))
89 }
90 p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(0)),
91 p if p == PropertyIdentifier::NUMBER_OF_STATES => {
92 Ok(PropertyValue::Unsigned(self.number_of_states as u64))
93 }
94 p if p == PropertyIdentifier::STATE_TEXT => match array_index {
95 None => Ok(PropertyValue::List(
96 self.state_text
97 .iter()
98 .map(|s| PropertyValue::CharacterString(s.clone()))
99 .collect(),
100 )),
101 Some(0) => Ok(PropertyValue::Unsigned(self.state_text.len() as u64)),
102 Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => Ok(
103 PropertyValue::CharacterString(self.state_text[(idx - 1) as usize].clone()),
104 ),
105 _ => Err(common::invalid_array_index_error()),
106 },
107 _ => Err(common::unknown_property_error()),
108 }
109 }
110
111 fn write_property(
112 &mut self,
113 property: PropertyIdentifier,
114 array_index: Option<u32>,
115 value: PropertyValue,
116 _priority: Option<u8>,
117 ) -> Result<(), Error> {
118 if property == PropertyIdentifier::PRESENT_VALUE {
119 if !self.out_of_service {
120 return Err(common::write_access_denied_error());
121 }
122 if let PropertyValue::Unsigned(v) = value {
123 if v < 1 || v > self.number_of_states as u64 {
124 return Err(common::value_out_of_range_error());
125 }
126 self.present_value = v as u32;
127 return Ok(());
128 }
129 return Err(common::invalid_data_type_error());
130 }
131 if property == PropertyIdentifier::STATE_TEXT {
132 match array_index {
133 Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => {
134 if let PropertyValue::CharacterString(s) = value {
135 self.state_text[(idx - 1) as usize] = s;
136 return Ok(());
137 }
138 return Err(common::invalid_data_type_error());
139 }
140 None => return Err(common::write_access_denied_error()),
141 _ => return Err(common::invalid_array_index_error()),
142 }
143 }
144 if let Some(result) =
145 common::write_out_of_service(&mut self.out_of_service, property, &value)
146 {
147 return result;
148 }
149 if let Some(result) = common::write_description(&mut self.description, property, &value) {
150 return result;
151 }
152 Err(common::write_access_denied_error())
153 }
154
155 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
156 static PROPS: &[PropertyIdentifier] = &[
157 PropertyIdentifier::OBJECT_IDENTIFIER,
158 PropertyIdentifier::OBJECT_NAME,
159 PropertyIdentifier::DESCRIPTION,
160 PropertyIdentifier::OBJECT_TYPE,
161 PropertyIdentifier::PRESENT_VALUE,
162 PropertyIdentifier::STATUS_FLAGS,
163 PropertyIdentifier::EVENT_STATE,
164 PropertyIdentifier::OUT_OF_SERVICE,
165 PropertyIdentifier::NUMBER_OF_STATES,
166 PropertyIdentifier::RELIABILITY,
167 PropertyIdentifier::STATE_TEXT,
168 ];
169 Cow::Borrowed(PROPS)
170 }
171}
172
173pub struct MultiStateOutputObject {
182 oid: ObjectIdentifier,
183 name: String,
184 description: String,
185 present_value: u32,
186 number_of_states: u32,
187 out_of_service: bool,
188 status_flags: StatusFlags,
189 priority_array: [Option<u32>; 16],
190 relinquish_default: u32,
191 reliability: u32,
193 state_text: Vec<String>,
194}
195
196impl MultiStateOutputObject {
197 pub fn new(
198 instance: u32,
199 name: impl Into<String>,
200 number_of_states: u32,
201 ) -> Result<Self, Error> {
202 let oid = ObjectIdentifier::new(ObjectType::MULTI_STATE_OUTPUT, instance)?;
203 Ok(Self {
204 oid,
205 name: name.into(),
206 description: String::new(),
207 present_value: 1,
208 number_of_states,
209 out_of_service: false,
210 status_flags: StatusFlags::empty(),
211 priority_array: [None; 16],
212 relinquish_default: 1,
213 reliability: 0,
214 state_text: (1..=number_of_states)
215 .map(|i| format!("State {i}"))
216 .collect(),
217 })
218 }
219
220 pub fn set_description(&mut self, desc: impl Into<String>) {
222 self.description = desc.into();
223 }
224
225 fn recalculate_present_value(&mut self) {
226 self.present_value =
227 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
228 }
229}
230
231impl BACnetObject for MultiStateOutputObject {
232 fn object_identifier(&self) -> ObjectIdentifier {
233 self.oid
234 }
235
236 fn object_name(&self) -> &str {
237 &self.name
238 }
239
240 fn read_property(
241 &self,
242 property: PropertyIdentifier,
243 array_index: Option<u32>,
244 ) -> Result<PropertyValue, Error> {
245 if let Some(result) = read_common_properties!(self, property, array_index) {
246 return result;
247 }
248 match property {
249 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
250 ObjectType::MULTI_STATE_OUTPUT.to_raw(),
251 )),
252 p if p == PropertyIdentifier::PRESENT_VALUE => {
253 Ok(PropertyValue::Unsigned(self.present_value as u64))
254 }
255 p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(0)),
256 p if p == PropertyIdentifier::NUMBER_OF_STATES => {
257 Ok(PropertyValue::Unsigned(self.number_of_states as u64))
258 }
259 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
260 common::read_priority_array!(self, array_index, |v: u32| PropertyValue::Unsigned(
261 v as u64
262 ))
263 }
264 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
265 Ok(PropertyValue::Unsigned(self.relinquish_default as u64))
266 }
267 p if p == PropertyIdentifier::STATE_TEXT => match array_index {
268 None => Ok(PropertyValue::List(
269 self.state_text
270 .iter()
271 .map(|s| PropertyValue::CharacterString(s.clone()))
272 .collect(),
273 )),
274 Some(0) => Ok(PropertyValue::Unsigned(self.state_text.len() as u64)),
275 Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => Ok(
276 PropertyValue::CharacterString(self.state_text[(idx - 1) as usize].clone()),
277 ),
278 _ => Err(common::invalid_array_index_error()),
279 },
280 _ => Err(common::unknown_property_error()),
281 }
282 }
283
284 fn write_property(
285 &mut self,
286 property: PropertyIdentifier,
287 array_index: Option<u32>,
288 value: PropertyValue,
289 priority: Option<u8>,
290 ) -> Result<(), Error> {
291 {
292 let num_states = self.number_of_states;
293 common::write_priority_array_direct!(self, property, array_index, value, |v| {
294 if let PropertyValue::Unsigned(u) = v {
295 if u < 1 || u > num_states as u64 {
296 Err(common::value_out_of_range_error())
297 } else {
298 Ok(u as u32)
299 }
300 } else {
301 Err(common::invalid_data_type_error())
302 }
303 });
304 }
305 if property == PropertyIdentifier::PRESENT_VALUE {
306 let num_states = self.number_of_states;
307 return common::write_priority_array!(self, value, priority, |v| {
308 if let PropertyValue::Unsigned(u) = v {
309 if u < 1 || u > num_states as u64 {
310 Err(common::value_out_of_range_error())
311 } else {
312 Ok(u as u32)
313 }
314 } else {
315 Err(common::invalid_data_type_error())
316 }
317 });
318 }
319 if property == PropertyIdentifier::STATE_TEXT {
320 match array_index {
321 Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => {
322 if let PropertyValue::CharacterString(s) = value {
323 self.state_text[(idx - 1) as usize] = s;
324 return Ok(());
325 }
326 return Err(common::invalid_data_type_error());
327 }
328 None => return Err(common::write_access_denied_error()),
329 _ => return Err(common::invalid_array_index_error()),
330 }
331 }
332 if let Some(result) =
333 common::write_out_of_service(&mut self.out_of_service, property, &value)
334 {
335 return result;
336 }
337 if let Some(result) = common::write_description(&mut self.description, property, &value) {
338 return result;
339 }
340 Err(common::write_access_denied_error())
341 }
342
343 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
344 static PROPS: &[PropertyIdentifier] = &[
345 PropertyIdentifier::OBJECT_IDENTIFIER,
346 PropertyIdentifier::OBJECT_NAME,
347 PropertyIdentifier::DESCRIPTION,
348 PropertyIdentifier::OBJECT_TYPE,
349 PropertyIdentifier::PRESENT_VALUE,
350 PropertyIdentifier::STATUS_FLAGS,
351 PropertyIdentifier::EVENT_STATE,
352 PropertyIdentifier::OUT_OF_SERVICE,
353 PropertyIdentifier::NUMBER_OF_STATES,
354 PropertyIdentifier::PRIORITY_ARRAY,
355 PropertyIdentifier::RELINQUISH_DEFAULT,
356 PropertyIdentifier::RELIABILITY,
357 PropertyIdentifier::STATE_TEXT,
358 ];
359 Cow::Borrowed(PROPS)
360 }
361}
362
363pub struct MultiStateValueObject {
372 oid: ObjectIdentifier,
373 name: String,
374 description: String,
375 present_value: u32,
376 number_of_states: u32,
377 out_of_service: bool,
378 status_flags: StatusFlags,
379 priority_array: [Option<u32>; 16],
380 relinquish_default: u32,
381 reliability: u32,
383 state_text: Vec<String>,
384}
385
386impl MultiStateValueObject {
387 pub fn new(
388 instance: u32,
389 name: impl Into<String>,
390 number_of_states: u32,
391 ) -> Result<Self, Error> {
392 let oid = ObjectIdentifier::new(ObjectType::MULTI_STATE_VALUE, instance)?;
393 Ok(Self {
394 oid,
395 name: name.into(),
396 description: String::new(),
397 present_value: 1,
398 number_of_states,
399 out_of_service: false,
400 status_flags: StatusFlags::empty(),
401 priority_array: [None; 16],
402 relinquish_default: 1,
403 reliability: 0,
404 state_text: (1..=number_of_states)
405 .map(|i| format!("State {i}"))
406 .collect(),
407 })
408 }
409
410 pub fn set_description(&mut self, desc: impl Into<String>) {
412 self.description = desc.into();
413 }
414
415 fn recalculate_present_value(&mut self) {
416 self.present_value =
417 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
418 }
419}
420
421impl BACnetObject for MultiStateValueObject {
422 fn object_identifier(&self) -> ObjectIdentifier {
423 self.oid
424 }
425
426 fn object_name(&self) -> &str {
427 &self.name
428 }
429
430 fn read_property(
431 &self,
432 property: PropertyIdentifier,
433 array_index: Option<u32>,
434 ) -> Result<PropertyValue, Error> {
435 if let Some(result) = read_common_properties!(self, property, array_index) {
436 return result;
437 }
438 match property {
439 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
440 ObjectType::MULTI_STATE_VALUE.to_raw(),
441 )),
442 p if p == PropertyIdentifier::PRESENT_VALUE => {
443 Ok(PropertyValue::Unsigned(self.present_value as u64))
444 }
445 p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(0)),
446 p if p == PropertyIdentifier::NUMBER_OF_STATES => {
447 Ok(PropertyValue::Unsigned(self.number_of_states as u64))
448 }
449 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
450 common::read_priority_array!(self, array_index, |v: u32| PropertyValue::Unsigned(
451 v as u64
452 ))
453 }
454 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
455 Ok(PropertyValue::Unsigned(self.relinquish_default as u64))
456 }
457 p if p == PropertyIdentifier::STATE_TEXT => match array_index {
458 None => Ok(PropertyValue::List(
459 self.state_text
460 .iter()
461 .map(|s| PropertyValue::CharacterString(s.clone()))
462 .collect(),
463 )),
464 Some(0) => Ok(PropertyValue::Unsigned(self.state_text.len() as u64)),
465 Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => Ok(
466 PropertyValue::CharacterString(self.state_text[(idx - 1) as usize].clone()),
467 ),
468 _ => Err(common::invalid_array_index_error()),
469 },
470 _ => Err(common::unknown_property_error()),
471 }
472 }
473
474 fn write_property(
475 &mut self,
476 property: PropertyIdentifier,
477 array_index: Option<u32>,
478 value: PropertyValue,
479 priority: Option<u8>,
480 ) -> Result<(), Error> {
481 {
482 let num_states = self.number_of_states;
483 common::write_priority_array_direct!(self, property, array_index, value, |v| {
484 if let PropertyValue::Unsigned(u) = v {
485 if u < 1 || u > num_states as u64 {
486 Err(common::value_out_of_range_error())
487 } else {
488 Ok(u as u32)
489 }
490 } else {
491 Err(common::invalid_data_type_error())
492 }
493 });
494 }
495 if property == PropertyIdentifier::PRESENT_VALUE {
496 let num_states = self.number_of_states;
497 return common::write_priority_array!(self, value, priority, |v| {
498 if let PropertyValue::Unsigned(u) = v {
499 if u < 1 || u > num_states as u64 {
500 Err(common::value_out_of_range_error())
501 } else {
502 Ok(u as u32)
503 }
504 } else {
505 Err(common::invalid_data_type_error())
506 }
507 });
508 }
509 if property == PropertyIdentifier::STATE_TEXT {
510 match array_index {
511 Some(idx) if idx >= 1 && (idx as usize) <= self.state_text.len() => {
512 if let PropertyValue::CharacterString(s) = value {
513 self.state_text[(idx - 1) as usize] = s;
514 return Ok(());
515 }
516 return Err(common::invalid_data_type_error());
517 }
518 None => return Err(common::write_access_denied_error()),
519 _ => return Err(common::invalid_array_index_error()),
520 }
521 }
522 if let Some(result) =
523 common::write_out_of_service(&mut self.out_of_service, property, &value)
524 {
525 return result;
526 }
527 if let Some(result) = common::write_description(&mut self.description, property, &value) {
528 return result;
529 }
530 Err(common::write_access_denied_error())
531 }
532
533 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
534 static PROPS: &[PropertyIdentifier] = &[
535 PropertyIdentifier::OBJECT_IDENTIFIER,
536 PropertyIdentifier::OBJECT_NAME,
537 PropertyIdentifier::DESCRIPTION,
538 PropertyIdentifier::OBJECT_TYPE,
539 PropertyIdentifier::PRESENT_VALUE,
540 PropertyIdentifier::STATUS_FLAGS,
541 PropertyIdentifier::EVENT_STATE,
542 PropertyIdentifier::OUT_OF_SERVICE,
543 PropertyIdentifier::NUMBER_OF_STATES,
544 PropertyIdentifier::PRIORITY_ARRAY,
545 PropertyIdentifier::RELINQUISH_DEFAULT,
546 PropertyIdentifier::RELIABILITY,
547 PropertyIdentifier::STATE_TEXT,
548 ];
549 Cow::Borrowed(PROPS)
550 }
551}
552
553#[cfg(test)]
554mod tests {
555 use super::*;
556
557 #[test]
560 fn msi_read_present_value_default() {
561 let msi = MultiStateInputObject::new(1, "MSI-1", 4).unwrap();
562 let val = msi
563 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
564 .unwrap();
565 assert_eq!(val, PropertyValue::Unsigned(1));
566 }
567
568 #[test]
569 fn msi_read_number_of_states() {
570 let msi = MultiStateInputObject::new(1, "MSI-1", 4).unwrap();
571 let val = msi
572 .read_property(PropertyIdentifier::NUMBER_OF_STATES, None)
573 .unwrap();
574 assert_eq!(val, PropertyValue::Unsigned(4));
575 }
576
577 #[test]
578 fn msi_write_denied_when_in_service() {
579 let mut msi = MultiStateInputObject::new(1, "MSI-1", 4).unwrap();
580 let result = msi.write_property(
581 PropertyIdentifier::PRESENT_VALUE,
582 None,
583 PropertyValue::Unsigned(2),
584 None,
585 );
586 assert!(result.is_err());
587 }
588
589 #[test]
590 fn msi_write_allowed_when_out_of_service() {
591 let mut msi = MultiStateInputObject::new(1, "MSI-1", 4).unwrap();
592 msi.write_property(
593 PropertyIdentifier::OUT_OF_SERVICE,
594 None,
595 PropertyValue::Boolean(true),
596 None,
597 )
598 .unwrap();
599 msi.write_property(
600 PropertyIdentifier::PRESENT_VALUE,
601 None,
602 PropertyValue::Unsigned(3),
603 None,
604 )
605 .unwrap();
606 let val = msi
607 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
608 .unwrap();
609 assert_eq!(val, PropertyValue::Unsigned(3));
610 }
611
612 #[test]
613 fn msi_write_out_of_range_rejected() {
614 let mut msi = MultiStateInputObject::new(1, "MSI-1", 4).unwrap();
615 msi.write_property(
616 PropertyIdentifier::OUT_OF_SERVICE,
617 None,
618 PropertyValue::Boolean(true),
619 None,
620 )
621 .unwrap();
622 assert!(msi
623 .write_property(
624 PropertyIdentifier::PRESENT_VALUE,
625 None,
626 PropertyValue::Unsigned(0),
627 None
628 )
629 .is_err());
630 assert!(msi
631 .write_property(
632 PropertyIdentifier::PRESENT_VALUE,
633 None,
634 PropertyValue::Unsigned(5),
635 None
636 )
637 .is_err());
638 }
639
640 #[test]
641 fn msi_read_reliability_default() {
642 let msi = MultiStateInputObject::new(1, "MSI-1", 4).unwrap();
643 let val = msi
644 .read_property(PropertyIdentifier::RELIABILITY, None)
645 .unwrap();
646 assert_eq!(val, PropertyValue::Enumerated(0)); }
648
649 #[test]
652 fn mso_write_with_priority() {
653 let mut mso = MultiStateOutputObject::new(1, "MSO-1", 5).unwrap();
654 mso.write_property(
655 PropertyIdentifier::PRESENT_VALUE,
656 None,
657 PropertyValue::Unsigned(3),
658 Some(8),
659 )
660 .unwrap();
661 let val = mso
662 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
663 .unwrap();
664 assert_eq!(val, PropertyValue::Unsigned(3));
665 let slot = mso
666 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(8))
667 .unwrap();
668 assert_eq!(slot, PropertyValue::Unsigned(3));
669 }
670
671 #[test]
672 fn mso_relinquish_falls_to_default() {
673 let mut mso = MultiStateOutputObject::new(1, "MSO-1", 5).unwrap();
674 mso.write_property(
675 PropertyIdentifier::PRESENT_VALUE,
676 None,
677 PropertyValue::Unsigned(4),
678 Some(16),
679 )
680 .unwrap();
681 assert_eq!(
682 mso.read_property(PropertyIdentifier::PRESENT_VALUE, None)
683 .unwrap(),
684 PropertyValue::Unsigned(4)
685 );
686 mso.write_property(
687 PropertyIdentifier::PRESENT_VALUE,
688 None,
689 PropertyValue::Null,
690 Some(16),
691 )
692 .unwrap();
693 assert_eq!(
694 mso.read_property(PropertyIdentifier::PRESENT_VALUE, None)
695 .unwrap(),
696 PropertyValue::Unsigned(1)
697 ); }
699
700 #[test]
701 fn mso_out_of_range_rejected() {
702 let mut mso = MultiStateOutputObject::new(1, "MSO-1", 5).unwrap();
703 assert!(mso
704 .write_property(
705 PropertyIdentifier::PRESENT_VALUE,
706 None,
707 PropertyValue::Unsigned(0),
708 None
709 )
710 .is_err());
711 assert!(mso
712 .write_property(
713 PropertyIdentifier::PRESENT_VALUE,
714 None,
715 PropertyValue::Unsigned(6),
716 None
717 )
718 .is_err());
719 }
720
721 #[test]
722 fn mso_read_reliability_default() {
723 let mso = MultiStateOutputObject::new(1, "MSO-1", 5).unwrap();
724 let val = mso
725 .read_property(PropertyIdentifier::RELIABILITY, None)
726 .unwrap();
727 assert_eq!(val, PropertyValue::Enumerated(0)); }
729
730 #[test]
733 fn msv_read_present_value_default() {
734 let msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
735 let val = msv
736 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
737 .unwrap();
738 assert_eq!(val, PropertyValue::Unsigned(1));
739 }
740
741 #[test]
742 fn msv_write_with_priority() {
743 let mut msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
744 msv.write_property(
745 PropertyIdentifier::PRESENT_VALUE,
746 None,
747 PropertyValue::Unsigned(2),
748 Some(8),
749 )
750 .unwrap();
751 let val = msv
752 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
753 .unwrap();
754 assert_eq!(val, PropertyValue::Unsigned(2));
755 let slot = msv
756 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(8))
757 .unwrap();
758 assert_eq!(slot, PropertyValue::Unsigned(2));
759 }
760
761 #[test]
762 fn msv_relinquish_falls_to_default() {
763 let mut msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
764 msv.write_property(
765 PropertyIdentifier::PRESENT_VALUE,
766 None,
767 PropertyValue::Unsigned(3),
768 Some(16),
769 )
770 .unwrap();
771 assert_eq!(
772 msv.read_property(PropertyIdentifier::PRESENT_VALUE, None)
773 .unwrap(),
774 PropertyValue::Unsigned(3)
775 );
776 msv.write_property(
777 PropertyIdentifier::PRESENT_VALUE,
778 None,
779 PropertyValue::Null,
780 Some(16),
781 )
782 .unwrap();
783 assert_eq!(
784 msv.read_property(PropertyIdentifier::PRESENT_VALUE, None)
785 .unwrap(),
786 PropertyValue::Unsigned(1)
787 ); }
789
790 #[test]
791 fn msv_read_priority_array_all_none() {
792 let msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
793 let val = msv
794 .read_property(PropertyIdentifier::PRIORITY_ARRAY, None)
795 .unwrap();
796 if let PropertyValue::List(elements) = val {
797 assert_eq!(elements.len(), 16);
798 for elem in &elements {
799 assert_eq!(elem, &PropertyValue::Null);
800 }
801 } else {
802 panic!("Expected List for priority array without index");
803 }
804 }
805
806 #[test]
807 fn msv_read_relinquish_default() {
808 let msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
809 let val = msv
810 .read_property(PropertyIdentifier::RELINQUISH_DEFAULT, None)
811 .unwrap();
812 assert_eq!(val, PropertyValue::Unsigned(1));
813 }
814
815 #[test]
816 fn msv_write_out_of_range_rejected() {
817 let mut msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
818 assert!(msv
819 .write_property(
820 PropertyIdentifier::PRESENT_VALUE,
821 None,
822 PropertyValue::Unsigned(0),
823 None
824 )
825 .is_err());
826 assert!(msv
827 .write_property(
828 PropertyIdentifier::PRESENT_VALUE,
829 None,
830 PropertyValue::Unsigned(4),
831 None
832 )
833 .is_err());
834 }
835
836 #[test]
837 fn msv_write_wrong_type_rejected() {
838 let mut msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
839 let result = msv.write_property(
840 PropertyIdentifier::PRESENT_VALUE,
841 None,
842 PropertyValue::Real(1.0),
843 None,
844 );
845 assert!(result.is_err());
846 }
847
848 #[test]
849 fn msv_read_object_type() {
850 let msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
851 let val = msv
852 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
853 .unwrap();
854 assert_eq!(
855 val,
856 PropertyValue::Enumerated(ObjectType::MULTI_STATE_VALUE.to_raw())
857 );
858 }
859
860 #[test]
861 fn msv_read_reliability_default() {
862 let msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
863 let val = msv
864 .read_property(PropertyIdentifier::RELIABILITY, None)
865 .unwrap();
866 assert_eq!(val, PropertyValue::Enumerated(0)); }
868
869 #[test]
872 fn msv_direct_priority_array_write_value() {
873 let mut msv = MultiStateValueObject::new(1, "MSV-1", 5).unwrap();
874 msv.write_property(
875 PropertyIdentifier::PRIORITY_ARRAY,
876 Some(5),
877 PropertyValue::Unsigned(3),
878 None,
879 )
880 .unwrap();
881 assert_eq!(
882 msv.read_property(PropertyIdentifier::PRESENT_VALUE, None)
883 .unwrap(),
884 PropertyValue::Unsigned(3)
885 );
886 assert_eq!(
887 msv.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
888 .unwrap(),
889 PropertyValue::Unsigned(3)
890 );
891 }
892
893 #[test]
894 fn msv_direct_priority_array_relinquish() {
895 let mut msv = MultiStateValueObject::new(1, "MSV-1", 5).unwrap();
896 msv.write_property(
897 PropertyIdentifier::PRIORITY_ARRAY,
898 Some(5),
899 PropertyValue::Unsigned(3),
900 None,
901 )
902 .unwrap();
903 msv.write_property(
904 PropertyIdentifier::PRIORITY_ARRAY,
905 Some(5),
906 PropertyValue::Null,
907 None,
908 )
909 .unwrap();
910 assert_eq!(
911 msv.read_property(PropertyIdentifier::PRESENT_VALUE, None)
912 .unwrap(),
913 PropertyValue::Unsigned(1)
914 ); }
916
917 #[test]
918 fn msv_direct_priority_array_no_index_error() {
919 let mut msv = MultiStateValueObject::new(1, "MSV-1", 5).unwrap();
920 let result = msv.write_property(
921 PropertyIdentifier::PRIORITY_ARRAY,
922 None,
923 PropertyValue::Unsigned(3),
924 None,
925 );
926 assert!(result.is_err());
927 }
928
929 #[test]
930 fn msv_direct_priority_array_index_zero_error() {
931 let mut msv = MultiStateValueObject::new(1, "MSV-1", 5).unwrap();
932 let result = msv.write_property(
933 PropertyIdentifier::PRIORITY_ARRAY,
934 Some(0),
935 PropertyValue::Unsigned(3),
936 None,
937 );
938 assert!(result.is_err());
939 }
940
941 #[test]
942 fn msv_direct_priority_array_index_17_error() {
943 let mut msv = MultiStateValueObject::new(1, "MSV-1", 5).unwrap();
944 let result = msv.write_property(
945 PropertyIdentifier::PRIORITY_ARRAY,
946 Some(17),
947 PropertyValue::Unsigned(3),
948 None,
949 );
950 assert!(result.is_err());
951 }
952
953 #[test]
954 fn msv_direct_priority_array_range_validation() {
955 let mut msv = MultiStateValueObject::new(1, "MSV-1", 5).unwrap();
956 assert!(msv
958 .write_property(
959 PropertyIdentifier::PRIORITY_ARRAY,
960 Some(1),
961 PropertyValue::Unsigned(0),
962 None
963 )
964 .is_err());
965 assert!(msv
967 .write_property(
968 PropertyIdentifier::PRIORITY_ARRAY,
969 Some(1),
970 PropertyValue::Unsigned(6),
971 None
972 )
973 .is_err());
974 msv.write_property(
976 PropertyIdentifier::PRIORITY_ARRAY,
977 Some(1),
978 PropertyValue::Unsigned(5),
979 None,
980 )
981 .unwrap();
982 }
983
984 #[test]
987 fn mso_direct_priority_array_write_value() {
988 let mut mso = MultiStateOutputObject::new(1, "MSO-1", 5).unwrap();
989 mso.write_property(
990 PropertyIdentifier::PRIORITY_ARRAY,
991 Some(5),
992 PropertyValue::Unsigned(3),
993 None,
994 )
995 .unwrap();
996 assert_eq!(
997 mso.read_property(PropertyIdentifier::PRESENT_VALUE, None)
998 .unwrap(),
999 PropertyValue::Unsigned(3)
1000 );
1001 assert_eq!(
1002 mso.read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
1003 .unwrap(),
1004 PropertyValue::Unsigned(3)
1005 );
1006 }
1007
1008 #[test]
1009 fn mso_direct_priority_array_relinquish() {
1010 let mut mso = MultiStateOutputObject::new(1, "MSO-1", 5).unwrap();
1011 mso.write_property(
1012 PropertyIdentifier::PRIORITY_ARRAY,
1013 Some(5),
1014 PropertyValue::Unsigned(3),
1015 None,
1016 )
1017 .unwrap();
1018 mso.write_property(
1019 PropertyIdentifier::PRIORITY_ARRAY,
1020 Some(5),
1021 PropertyValue::Null,
1022 None,
1023 )
1024 .unwrap();
1025 assert_eq!(
1027 mso.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1028 .unwrap(),
1029 PropertyValue::Unsigned(1)
1030 );
1031 }
1032
1033 #[test]
1034 fn mso_direct_priority_array_no_index_error() {
1035 let mut mso = MultiStateOutputObject::new(1, "MSO-1", 5).unwrap();
1036 let result = mso.write_property(
1037 PropertyIdentifier::PRIORITY_ARRAY,
1038 None,
1039 PropertyValue::Unsigned(3),
1040 None,
1041 );
1042 assert!(result.is_err());
1043 }
1044
1045 #[test]
1046 fn mso_direct_priority_array_index_zero_error() {
1047 let mut mso = MultiStateOutputObject::new(1, "MSO-1", 5).unwrap();
1048 let result = mso.write_property(
1049 PropertyIdentifier::PRIORITY_ARRAY,
1050 Some(0),
1051 PropertyValue::Unsigned(3),
1052 None,
1053 );
1054 assert!(result.is_err());
1055 }
1056
1057 #[test]
1058 fn mso_direct_priority_array_index_17_error() {
1059 let mut mso = MultiStateOutputObject::new(1, "MSO-1", 5).unwrap();
1060 let result = mso.write_property(
1061 PropertyIdentifier::PRIORITY_ARRAY,
1062 Some(17),
1063 PropertyValue::Unsigned(3),
1064 None,
1065 );
1066 assert!(result.is_err());
1067 }
1068
1069 #[test]
1072 fn msi_state_text_defaults() {
1073 let msi = MultiStateInputObject::new(1, "MSI-1", 3).unwrap();
1074 let val = msi
1075 .read_property(PropertyIdentifier::STATE_TEXT, None)
1076 .unwrap();
1077 assert_eq!(
1078 val,
1079 PropertyValue::List(vec![
1080 PropertyValue::CharacterString("State 1".into()),
1081 PropertyValue::CharacterString("State 2".into()),
1082 PropertyValue::CharacterString("State 3".into()),
1083 ])
1084 );
1085 }
1086
1087 #[test]
1088 fn msi_state_text_index_zero_returns_length() {
1089 let msi = MultiStateInputObject::new(1, "MSI-1", 4).unwrap();
1090 let val = msi
1091 .read_property(PropertyIdentifier::STATE_TEXT, Some(0))
1092 .unwrap();
1093 assert_eq!(val, PropertyValue::Unsigned(4));
1094 }
1095
1096 #[test]
1097 fn msi_state_text_valid_index() {
1098 let msi = MultiStateInputObject::new(1, "MSI-1", 3).unwrap();
1099 let val = msi
1100 .read_property(PropertyIdentifier::STATE_TEXT, Some(2))
1101 .unwrap();
1102 assert_eq!(val, PropertyValue::CharacterString("State 2".into()));
1103 }
1104
1105 #[test]
1106 fn msi_state_text_invalid_index_error() {
1107 let msi = MultiStateInputObject::new(1, "MSI-1", 3).unwrap();
1108 assert!(msi
1109 .read_property(PropertyIdentifier::STATE_TEXT, Some(4))
1110 .is_err());
1111 assert!(msi
1112 .read_property(PropertyIdentifier::STATE_TEXT, Some(100))
1113 .is_err());
1114 }
1115
1116 #[test]
1117 fn msi_state_text_write_at_index() {
1118 let mut msi = MultiStateInputObject::new(1, "MSI-1", 3).unwrap();
1119 msi.write_property(
1120 PropertyIdentifier::STATE_TEXT,
1121 Some(2),
1122 PropertyValue::CharacterString("Occupied".into()),
1123 None,
1124 )
1125 .unwrap();
1126 let val = msi
1127 .read_property(PropertyIdentifier::STATE_TEXT, Some(2))
1128 .unwrap();
1129 assert_eq!(val, PropertyValue::CharacterString("Occupied".into()));
1130 }
1131
1132 #[test]
1133 fn msi_state_text_write_wrong_type_rejected() {
1134 let mut msi = MultiStateInputObject::new(1, "MSI-1", 3).unwrap();
1135 assert!(msi
1136 .write_property(
1137 PropertyIdentifier::STATE_TEXT,
1138 Some(1),
1139 PropertyValue::Unsigned(42),
1140 None,
1141 )
1142 .is_err());
1143 }
1144
1145 #[test]
1146 fn msi_state_text_write_bad_index_rejected() {
1147 let mut msi = MultiStateInputObject::new(1, "MSI-1", 3).unwrap();
1148 assert!(msi
1150 .write_property(
1151 PropertyIdentifier::STATE_TEXT,
1152 Some(0),
1153 PropertyValue::CharacterString("X".into()),
1154 None,
1155 )
1156 .is_err());
1157 assert!(msi
1159 .write_property(
1160 PropertyIdentifier::STATE_TEXT,
1161 Some(4),
1162 PropertyValue::CharacterString("X".into()),
1163 None,
1164 )
1165 .is_err());
1166 assert!(msi
1168 .write_property(
1169 PropertyIdentifier::STATE_TEXT,
1170 None,
1171 PropertyValue::CharacterString("X".into()),
1172 None,
1173 )
1174 .is_err());
1175 }
1176
1177 #[test]
1178 fn msi_state_text_in_property_list() {
1179 let msi = MultiStateInputObject::new(1, "MSI-1", 3).unwrap();
1180 assert!(msi
1181 .property_list()
1182 .contains(&PropertyIdentifier::STATE_TEXT));
1183 }
1184
1185 #[test]
1186 fn mso_state_text_defaults() {
1187 let mso = MultiStateOutputObject::new(1, "MSO-1", 2).unwrap();
1188 let val = mso
1189 .read_property(PropertyIdentifier::STATE_TEXT, None)
1190 .unwrap();
1191 assert_eq!(
1192 val,
1193 PropertyValue::List(vec![
1194 PropertyValue::CharacterString("State 1".into()),
1195 PropertyValue::CharacterString("State 2".into()),
1196 ])
1197 );
1198 }
1199
1200 #[test]
1201 fn mso_state_text_index_zero_returns_length() {
1202 let mso = MultiStateOutputObject::new(1, "MSO-1", 5).unwrap();
1203 let val = mso
1204 .read_property(PropertyIdentifier::STATE_TEXT, Some(0))
1205 .unwrap();
1206 assert_eq!(val, PropertyValue::Unsigned(5));
1207 }
1208
1209 #[test]
1210 fn mso_state_text_valid_index() {
1211 let mso = MultiStateOutputObject::new(1, "MSO-1", 3).unwrap();
1212 let val = mso
1213 .read_property(PropertyIdentifier::STATE_TEXT, Some(3))
1214 .unwrap();
1215 assert_eq!(val, PropertyValue::CharacterString("State 3".into()));
1216 }
1217
1218 #[test]
1219 fn mso_state_text_invalid_index_error() {
1220 let mso = MultiStateOutputObject::new(1, "MSO-1", 3).unwrap();
1221 assert!(mso
1222 .read_property(PropertyIdentifier::STATE_TEXT, Some(4))
1223 .is_err());
1224 }
1225
1226 #[test]
1227 fn mso_state_text_write_at_index() {
1228 let mut mso = MultiStateOutputObject::new(1, "MSO-1", 3).unwrap();
1229 mso.write_property(
1230 PropertyIdentifier::STATE_TEXT,
1231 Some(1),
1232 PropertyValue::CharacterString("Low".into()),
1233 None,
1234 )
1235 .unwrap();
1236 assert_eq!(
1237 mso.read_property(PropertyIdentifier::STATE_TEXT, Some(1))
1238 .unwrap(),
1239 PropertyValue::CharacterString("Low".into())
1240 );
1241 }
1242
1243 #[test]
1244 fn mso_state_text_in_property_list() {
1245 let mso = MultiStateOutputObject::new(1, "MSO-1", 3).unwrap();
1246 assert!(mso
1247 .property_list()
1248 .contains(&PropertyIdentifier::STATE_TEXT));
1249 }
1250
1251 #[test]
1252 fn msv_state_text_defaults() {
1253 let msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
1254 let val = msv
1255 .read_property(PropertyIdentifier::STATE_TEXT, None)
1256 .unwrap();
1257 assert_eq!(
1258 val,
1259 PropertyValue::List(vec![
1260 PropertyValue::CharacterString("State 1".into()),
1261 PropertyValue::CharacterString("State 2".into()),
1262 PropertyValue::CharacterString("State 3".into()),
1263 ])
1264 );
1265 }
1266
1267 #[test]
1268 fn msv_state_text_index_zero_returns_length() {
1269 let msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
1270 let val = msv
1271 .read_property(PropertyIdentifier::STATE_TEXT, Some(0))
1272 .unwrap();
1273 assert_eq!(val, PropertyValue::Unsigned(3));
1274 }
1275
1276 #[test]
1277 fn msv_state_text_valid_index() {
1278 let msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
1279 let val = msv
1280 .read_property(PropertyIdentifier::STATE_TEXT, Some(1))
1281 .unwrap();
1282 assert_eq!(val, PropertyValue::CharacterString("State 1".into()));
1283 }
1284
1285 #[test]
1286 fn msv_state_text_invalid_index_error() {
1287 let msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
1288 assert!(msv
1289 .read_property(PropertyIdentifier::STATE_TEXT, Some(4))
1290 .is_err());
1291 assert!(msv
1292 .read_property(PropertyIdentifier::STATE_TEXT, Some(0xFF))
1293 .is_err());
1294 }
1295
1296 #[test]
1297 fn msv_state_text_write_at_index() {
1298 let mut msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
1299 msv.write_property(
1300 PropertyIdentifier::STATE_TEXT,
1301 Some(2),
1302 PropertyValue::CharacterString("Comfort".into()),
1303 None,
1304 )
1305 .unwrap();
1306 assert_eq!(
1307 msv.read_property(PropertyIdentifier::STATE_TEXT, Some(2))
1308 .unwrap(),
1309 PropertyValue::CharacterString("Comfort".into())
1310 );
1311 }
1312
1313 #[test]
1314 fn msv_state_text_write_bad_index_rejected() {
1315 let mut msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
1316 assert!(msv
1317 .write_property(
1318 PropertyIdentifier::STATE_TEXT,
1319 None,
1320 PropertyValue::CharacterString("X".into()),
1321 None,
1322 )
1323 .is_err());
1324 assert!(msv
1325 .write_property(
1326 PropertyIdentifier::STATE_TEXT,
1327 Some(0),
1328 PropertyValue::CharacterString("X".into()),
1329 None,
1330 )
1331 .is_err());
1332 assert!(msv
1333 .write_property(
1334 PropertyIdentifier::STATE_TEXT,
1335 Some(4),
1336 PropertyValue::CharacterString("X".into()),
1337 None,
1338 )
1339 .is_err());
1340 }
1341
1342 #[test]
1343 fn msv_state_text_write_wrong_type_rejected() {
1344 let mut msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
1345 assert!(msv
1346 .write_property(
1347 PropertyIdentifier::STATE_TEXT,
1348 Some(1),
1349 PropertyValue::Unsigned(1),
1350 None,
1351 )
1352 .is_err());
1353 }
1354
1355 #[test]
1356 fn msv_state_text_in_property_list() {
1357 let msv = MultiStateValueObject::new(1, "MSV-1", 3).unwrap();
1358 assert!(msv
1359 .property_list()
1360 .contains(&PropertyIdentifier::STATE_TEXT));
1361 }
1362}