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::{
10 self, read_common_properties, read_priority_array, write_priority_array,
11 write_priority_array_direct,
12};
13use crate::traits::BACnetObject;
14
15pub struct LightingOutputObject {
24 oid: ObjectIdentifier,
25 name: String,
26 description: String,
27 present_value: f32,
28 tracking_value: f32,
29 lighting_command: Vec<u8>,
31 lighting_command_default_priority: u32,
32 in_progress: u32,
34 blink_warn_enable: bool,
35 egress_time: u32,
36 egress_active: bool,
37 out_of_service: bool,
38 status_flags: StatusFlags,
39 reliability: u32,
41 priority_array: [Option<f32>; 16],
42 relinquish_default: f32,
43}
44
45impl LightingOutputObject {
46 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
48 let oid = ObjectIdentifier::new(ObjectType::LIGHTING_OUTPUT, instance)?;
49 Ok(Self {
50 oid,
51 name: name.into(),
52 description: String::new(),
53 present_value: 0.0,
54 tracking_value: 0.0,
55 lighting_command: Vec::new(),
56 lighting_command_default_priority: 16,
57 in_progress: 0, blink_warn_enable: false,
59 egress_time: 0,
60 egress_active: false,
61 out_of_service: false,
62 status_flags: StatusFlags::empty(),
63 reliability: 0,
64 priority_array: [None; 16],
65 relinquish_default: 0.0,
66 })
67 }
68
69 pub fn set_description(&mut self, desc: impl Into<String>) {
71 self.description = desc.into();
72 }
73
74 fn recalculate_present_value(&mut self) {
76 self.present_value =
77 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
78 }
79}
80
81impl BACnetObject for LightingOutputObject {
82 fn object_identifier(&self) -> ObjectIdentifier {
83 self.oid
84 }
85
86 fn object_name(&self) -> &str {
87 &self.name
88 }
89
90 fn read_property(
91 &self,
92 property: PropertyIdentifier,
93 array_index: Option<u32>,
94 ) -> Result<PropertyValue, Error> {
95 if let Some(result) = read_common_properties!(self, property, array_index) {
96 return result;
97 }
98 match property {
99 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
100 ObjectType::LIGHTING_OUTPUT.to_raw(),
101 )),
102 p if p == PropertyIdentifier::PRESENT_VALUE => {
103 Ok(PropertyValue::Real(self.present_value))
104 }
105 p if p == PropertyIdentifier::TRACKING_VALUE => {
106 Ok(PropertyValue::Real(self.tracking_value))
107 }
108 p if p == PropertyIdentifier::LIGHTING_COMMAND => {
109 Ok(PropertyValue::OctetString(self.lighting_command.clone()))
110 }
111 p if p == PropertyIdentifier::LIGHTING_COMMAND_DEFAULT_PRIORITY => Ok(
112 PropertyValue::Unsigned(self.lighting_command_default_priority as u64),
113 ),
114 p if p == PropertyIdentifier::IN_PROGRESS => {
115 Ok(PropertyValue::Enumerated(self.in_progress))
116 }
117 p if p == PropertyIdentifier::BLINK_WARN_ENABLE => {
118 Ok(PropertyValue::Boolean(self.blink_warn_enable))
119 }
120 p if p == PropertyIdentifier::EGRESS_TIME => {
121 Ok(PropertyValue::Unsigned(self.egress_time as u64))
122 }
123 p if p == PropertyIdentifier::EGRESS_ACTIVE => {
124 Ok(PropertyValue::Boolean(self.egress_active))
125 }
126 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
127 read_priority_array!(self, array_index, PropertyValue::Real)
128 }
129 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
130 Ok(PropertyValue::Real(self.relinquish_default))
131 }
132 _ => Err(common::unknown_property_error()),
133 }
134 }
135
136 fn write_property(
137 &mut self,
138 property: PropertyIdentifier,
139 array_index: Option<u32>,
140 value: PropertyValue,
141 priority: Option<u8>,
142 ) -> Result<(), Error> {
143 write_priority_array_direct!(self, property, array_index, value, |v| {
145 match v {
146 PropertyValue::Real(f) => {
147 if !(0.0..=100.0).contains(&f) {
148 Err(common::value_out_of_range_error())
149 } else {
150 Ok(f)
151 }
152 }
153 _ => Err(common::invalid_data_type_error()),
154 }
155 });
156
157 if property == PropertyIdentifier::PRESENT_VALUE {
159 return write_priority_array!(self, value, priority, |v| {
160 match v {
161 PropertyValue::Real(f) => {
162 if !(0.0..=100.0).contains(&f) {
163 Err(common::value_out_of_range_error())
164 } else {
165 Ok(f)
166 }
167 }
168 _ => Err(common::invalid_data_type_error()),
169 }
170 });
171 }
172
173 if property == PropertyIdentifier::LIGHTING_COMMAND {
175 if let PropertyValue::OctetString(data) = value {
176 self.lighting_command = data;
177 return Ok(());
178 }
179 return Err(common::invalid_data_type_error());
180 }
181
182 if property == PropertyIdentifier::LIGHTING_COMMAND_DEFAULT_PRIORITY {
184 if let PropertyValue::Unsigned(v) = value {
185 if !(1..=16).contains(&v) {
186 return Err(common::value_out_of_range_error());
187 }
188 self.lighting_command_default_priority = v as u32;
189 return Ok(());
190 }
191 return Err(common::invalid_data_type_error());
192 }
193
194 if property == PropertyIdentifier::BLINK_WARN_ENABLE {
196 if let PropertyValue::Boolean(v) = value {
197 self.blink_warn_enable = v;
198 return Ok(());
199 }
200 return Err(common::invalid_data_type_error());
201 }
202
203 if property == PropertyIdentifier::EGRESS_TIME {
205 if let PropertyValue::Unsigned(v) = value {
206 self.egress_time = common::u64_to_u32(v)?;
207 return Ok(());
208 }
209 return Err(common::invalid_data_type_error());
210 }
211
212 if let Some(result) =
213 common::write_out_of_service(&mut self.out_of_service, property, &value)
214 {
215 return result;
216 }
217 if let Some(result) = common::write_description(&mut self.description, property, &value) {
218 return result;
219 }
220 Err(common::write_access_denied_error())
221 }
222
223 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
224 static PROPS: &[PropertyIdentifier] = &[
225 PropertyIdentifier::OBJECT_IDENTIFIER,
226 PropertyIdentifier::OBJECT_NAME,
227 PropertyIdentifier::DESCRIPTION,
228 PropertyIdentifier::OBJECT_TYPE,
229 PropertyIdentifier::PRESENT_VALUE,
230 PropertyIdentifier::TRACKING_VALUE,
231 PropertyIdentifier::LIGHTING_COMMAND,
232 PropertyIdentifier::LIGHTING_COMMAND_DEFAULT_PRIORITY,
233 PropertyIdentifier::IN_PROGRESS,
234 PropertyIdentifier::BLINK_WARN_ENABLE,
235 PropertyIdentifier::EGRESS_TIME,
236 PropertyIdentifier::EGRESS_ACTIVE,
237 PropertyIdentifier::STATUS_FLAGS,
238 PropertyIdentifier::OUT_OF_SERVICE,
239 PropertyIdentifier::RELIABILITY,
240 PropertyIdentifier::PRIORITY_ARRAY,
241 PropertyIdentifier::RELINQUISH_DEFAULT,
242 ];
243 Cow::Borrowed(PROPS)
244 }
245}
246
247pub struct BinaryLightingOutputObject {
256 oid: ObjectIdentifier,
257 name: String,
258 description: String,
259 present_value: u32,
260 blink_warn_enable: bool,
261 egress_time: u32,
262 egress_active: bool,
263 out_of_service: bool,
264 status_flags: StatusFlags,
265 reliability: u32,
267 priority_array: [Option<u32>; 16],
268 relinquish_default: u32,
269}
270
271impl BinaryLightingOutputObject {
272 const MAX_PV: u32 = 4;
274
275 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
277 let oid = ObjectIdentifier::new(ObjectType::BINARY_LIGHTING_OUTPUT, instance)?;
278 Ok(Self {
279 oid,
280 name: name.into(),
281 description: String::new(),
282 present_value: 0, blink_warn_enable: false,
284 egress_time: 0,
285 egress_active: false,
286 out_of_service: false,
287 status_flags: StatusFlags::empty(),
288 reliability: 0,
289 priority_array: [None; 16],
290 relinquish_default: 0,
291 })
292 }
293
294 pub fn set_description(&mut self, desc: impl Into<String>) {
296 self.description = desc.into();
297 }
298
299 fn recalculate_present_value(&mut self) {
301 self.present_value =
302 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
303 }
304}
305
306impl BACnetObject for BinaryLightingOutputObject {
307 fn object_identifier(&self) -> ObjectIdentifier {
308 self.oid
309 }
310
311 fn object_name(&self) -> &str {
312 &self.name
313 }
314
315 fn read_property(
316 &self,
317 property: PropertyIdentifier,
318 array_index: Option<u32>,
319 ) -> Result<PropertyValue, Error> {
320 if let Some(result) = read_common_properties!(self, property, array_index) {
321 return result;
322 }
323 match property {
324 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
325 ObjectType::BINARY_LIGHTING_OUTPUT.to_raw(),
326 )),
327 p if p == PropertyIdentifier::PRESENT_VALUE => {
328 Ok(PropertyValue::Enumerated(self.present_value))
329 }
330 p if p == PropertyIdentifier::BLINK_WARN_ENABLE => {
331 Ok(PropertyValue::Boolean(self.blink_warn_enable))
332 }
333 p if p == PropertyIdentifier::EGRESS_TIME => {
334 Ok(PropertyValue::Unsigned(self.egress_time as u64))
335 }
336 p if p == PropertyIdentifier::EGRESS_ACTIVE => {
337 Ok(PropertyValue::Boolean(self.egress_active))
338 }
339 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
340 read_priority_array!(self, array_index, PropertyValue::Enumerated)
341 }
342 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
343 Ok(PropertyValue::Enumerated(self.relinquish_default))
344 }
345 _ => Err(common::unknown_property_error()),
346 }
347 }
348
349 fn write_property(
350 &mut self,
351 property: PropertyIdentifier,
352 array_index: Option<u32>,
353 value: PropertyValue,
354 priority: Option<u8>,
355 ) -> Result<(), Error> {
356 write_priority_array_direct!(self, property, array_index, value, |v| {
358 if let PropertyValue::Enumerated(e) = v {
359 if e > Self::MAX_PV {
360 Err(common::value_out_of_range_error())
361 } else {
362 Ok(e)
363 }
364 } else {
365 Err(common::invalid_data_type_error())
366 }
367 });
368
369 if property == PropertyIdentifier::PRESENT_VALUE {
371 return write_priority_array!(self, value, priority, |v| {
372 if let PropertyValue::Enumerated(e) = v {
373 if e > Self::MAX_PV {
374 Err(common::value_out_of_range_error())
375 } else {
376 Ok(e)
377 }
378 } else {
379 Err(common::invalid_data_type_error())
380 }
381 });
382 }
383
384 if property == PropertyIdentifier::BLINK_WARN_ENABLE {
386 if let PropertyValue::Boolean(v) = value {
387 self.blink_warn_enable = v;
388 return Ok(());
389 }
390 return Err(common::invalid_data_type_error());
391 }
392
393 if property == PropertyIdentifier::EGRESS_TIME {
395 if let PropertyValue::Unsigned(v) = value {
396 self.egress_time = common::u64_to_u32(v)?;
397 return Ok(());
398 }
399 return Err(common::invalid_data_type_error());
400 }
401
402 if let Some(result) =
403 common::write_out_of_service(&mut self.out_of_service, property, &value)
404 {
405 return result;
406 }
407 if let Some(result) = common::write_description(&mut self.description, property, &value) {
408 return result;
409 }
410 Err(common::write_access_denied_error())
411 }
412
413 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
414 static PROPS: &[PropertyIdentifier] = &[
415 PropertyIdentifier::OBJECT_IDENTIFIER,
416 PropertyIdentifier::OBJECT_NAME,
417 PropertyIdentifier::DESCRIPTION,
418 PropertyIdentifier::OBJECT_TYPE,
419 PropertyIdentifier::PRESENT_VALUE,
420 PropertyIdentifier::BLINK_WARN_ENABLE,
421 PropertyIdentifier::EGRESS_TIME,
422 PropertyIdentifier::EGRESS_ACTIVE,
423 PropertyIdentifier::STATUS_FLAGS,
424 PropertyIdentifier::OUT_OF_SERVICE,
425 PropertyIdentifier::RELIABILITY,
426 PropertyIdentifier::PRIORITY_ARRAY,
427 PropertyIdentifier::RELINQUISH_DEFAULT,
428 ];
429 Cow::Borrowed(PROPS)
430 }
431}
432
433pub struct ChannelObject {
442 oid: ObjectIdentifier,
443 name: String,
444 description: String,
445 present_value: u32,
447 last_priority: u32,
449 write_status: u32,
451 channel_number: u32,
453 list_of_object_property_references_count: u32,
455 out_of_service: bool,
456 status_flags: StatusFlags,
457 reliability: u32,
459}
460
461impl ChannelObject {
462 pub fn new(instance: u32, name: impl Into<String>, channel_number: u32) -> Result<Self, Error> {
464 let oid = ObjectIdentifier::new(ObjectType::CHANNEL, instance)?;
465 Ok(Self {
466 oid,
467 name: name.into(),
468 description: String::new(),
469 present_value: 0,
470 last_priority: 16,
471 write_status: 0, channel_number,
473 list_of_object_property_references_count: 0,
474 out_of_service: false,
475 status_flags: StatusFlags::empty(),
476 reliability: 0,
477 })
478 }
479
480 pub fn set_description(&mut self, desc: impl Into<String>) {
482 self.description = desc.into();
483 }
484}
485
486impl BACnetObject for ChannelObject {
487 fn object_identifier(&self) -> ObjectIdentifier {
488 self.oid
489 }
490
491 fn object_name(&self) -> &str {
492 &self.name
493 }
494
495 fn read_property(
496 &self,
497 property: PropertyIdentifier,
498 array_index: Option<u32>,
499 ) -> Result<PropertyValue, Error> {
500 if let Some(result) = read_common_properties!(self, property, array_index) {
501 return result;
502 }
503 match property {
504 p if p == PropertyIdentifier::OBJECT_TYPE => {
505 Ok(PropertyValue::Enumerated(ObjectType::CHANNEL.to_raw()))
506 }
507 p if p == PropertyIdentifier::PRESENT_VALUE => {
508 Ok(PropertyValue::Unsigned(self.present_value as u64))
509 }
510 p if p == PropertyIdentifier::LAST_PRIORITY => {
511 Ok(PropertyValue::Unsigned(self.last_priority as u64))
512 }
513 p if p == PropertyIdentifier::WRITE_STATUS => {
514 Ok(PropertyValue::Enumerated(self.write_status))
515 }
516 p if p == PropertyIdentifier::CHANNEL_NUMBER => {
517 Ok(PropertyValue::Unsigned(self.channel_number as u64))
518 }
519 p if p == PropertyIdentifier::LIST_OF_OBJECT_PROPERTY_REFERENCES => Ok(
520 PropertyValue::Unsigned(self.list_of_object_property_references_count as u64),
521 ),
522 _ => Err(common::unknown_property_error()),
523 }
524 }
525
526 fn write_property(
527 &mut self,
528 property: PropertyIdentifier,
529 _array_index: Option<u32>,
530 value: PropertyValue,
531 priority: Option<u8>,
532 ) -> Result<(), Error> {
533 if property == PropertyIdentifier::PRESENT_VALUE {
535 if let PropertyValue::Unsigned(v) = value {
536 self.present_value = common::u64_to_u32(v)?;
537 self.last_priority = priority.unwrap_or(16) as u32;
538 return Ok(());
539 }
540 return Err(common::invalid_data_type_error());
541 }
542
543 if property == PropertyIdentifier::CHANNEL_NUMBER {
545 if let PropertyValue::Unsigned(v) = value {
546 self.channel_number = common::u64_to_u32(v)?;
547 return Ok(());
548 }
549 return Err(common::invalid_data_type_error());
550 }
551
552 if let Some(result) =
553 common::write_out_of_service(&mut self.out_of_service, property, &value)
554 {
555 return result;
556 }
557 if let Some(result) = common::write_description(&mut self.description, property, &value) {
558 return result;
559 }
560 Err(common::write_access_denied_error())
561 }
562
563 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
564 static PROPS: &[PropertyIdentifier] = &[
565 PropertyIdentifier::OBJECT_IDENTIFIER,
566 PropertyIdentifier::OBJECT_NAME,
567 PropertyIdentifier::DESCRIPTION,
568 PropertyIdentifier::OBJECT_TYPE,
569 PropertyIdentifier::PRESENT_VALUE,
570 PropertyIdentifier::LAST_PRIORITY,
571 PropertyIdentifier::WRITE_STATUS,
572 PropertyIdentifier::CHANNEL_NUMBER,
573 PropertyIdentifier::LIST_OF_OBJECT_PROPERTY_REFERENCES,
574 PropertyIdentifier::STATUS_FLAGS,
575 PropertyIdentifier::OUT_OF_SERVICE,
576 PropertyIdentifier::RELIABILITY,
577 ];
578 Cow::Borrowed(PROPS)
579 }
580}
581
582#[cfg(test)]
587mod tests {
588 use super::*;
589
590 #[test]
593 fn lighting_output_create() {
594 let obj = LightingOutputObject::new(1, "LO-1").unwrap();
595 assert_eq!(obj.object_name(), "LO-1");
596 assert_eq!(
597 obj.object_identifier().object_type(),
598 ObjectType::LIGHTING_OUTPUT
599 );
600 assert_eq!(obj.object_identifier().instance_number(), 1);
601 }
602
603 #[test]
604 fn lighting_output_read_present_value() {
605 let obj = LightingOutputObject::new(1, "LO-1").unwrap();
606 let pv = obj.read_property(PropertyIdentifier::PRESENT_VALUE, None);
607 assert_eq!(pv.unwrap(), PropertyValue::Real(0.0));
608 }
609
610 #[test]
611 fn lighting_output_read_object_type() {
612 let obj = LightingOutputObject::new(1, "LO-1").unwrap();
613 let ot = obj
614 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
615 .unwrap();
616 assert_eq!(
617 ot,
618 PropertyValue::Enumerated(ObjectType::LIGHTING_OUTPUT.to_raw())
619 );
620 }
621
622 #[test]
623 fn lighting_output_write_pv_commandable() {
624 let mut obj = LightingOutputObject::new(1, "LO-1").unwrap();
625 obj.write_property(
627 PropertyIdentifier::PRESENT_VALUE,
628 None,
629 PropertyValue::Real(75.0),
630 Some(8),
631 )
632 .unwrap();
633 let pv = obj
634 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
635 .unwrap();
636 assert_eq!(pv, PropertyValue::Real(75.0));
637
638 obj.write_property(
640 PropertyIdentifier::PRESENT_VALUE,
641 None,
642 PropertyValue::Real(50.0),
643 Some(1),
644 )
645 .unwrap();
646 let pv = obj
647 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
648 .unwrap();
649 assert_eq!(pv, PropertyValue::Real(50.0));
650
651 obj.write_property(
653 PropertyIdentifier::PRESENT_VALUE,
654 None,
655 PropertyValue::Null,
656 Some(1),
657 )
658 .unwrap();
659 let pv = obj
660 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
661 .unwrap();
662 assert_eq!(pv, PropertyValue::Real(75.0));
663 }
664
665 #[test]
666 fn lighting_output_pv_out_of_range() {
667 let mut obj = LightingOutputObject::new(1, "LO-1").unwrap();
668 let result = obj.write_property(
669 PropertyIdentifier::PRESENT_VALUE,
670 None,
671 PropertyValue::Real(101.0),
672 Some(16),
673 );
674 assert!(result.is_err());
675
676 let result = obj.write_property(
677 PropertyIdentifier::PRESENT_VALUE,
678 None,
679 PropertyValue::Real(-1.0),
680 Some(16),
681 );
682 assert!(result.is_err());
683 }
684
685 #[test]
686 fn lighting_output_priority_array_read() {
687 let mut obj = LightingOutputObject::new(1, "LO-1").unwrap();
688 obj.write_property(
689 PropertyIdentifier::PRESENT_VALUE,
690 None,
691 PropertyValue::Real(50.0),
692 Some(8),
693 )
694 .unwrap();
695
696 let size = obj
698 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(0))
699 .unwrap();
700 assert_eq!(size, PropertyValue::Unsigned(16));
701
702 let slot = obj
704 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(8))
705 .unwrap();
706 assert_eq!(slot, PropertyValue::Real(50.0));
707
708 let slot = obj
710 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(1))
711 .unwrap();
712 assert_eq!(slot, PropertyValue::Null);
713 }
714
715 #[test]
716 fn lighting_output_priority_array_direct_write() {
717 let mut obj = LightingOutputObject::new(1, "LO-1").unwrap();
718 obj.write_property(
719 PropertyIdentifier::PRIORITY_ARRAY,
720 Some(5),
721 PropertyValue::Real(33.0),
722 None,
723 )
724 .unwrap();
725 let pv = obj
726 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
727 .unwrap();
728 assert_eq!(pv, PropertyValue::Real(33.0));
729 }
730
731 #[test]
732 fn lighting_output_relinquish_default() {
733 let obj = LightingOutputObject::new(1, "LO-1").unwrap();
734 let rd = obj
735 .read_property(PropertyIdentifier::RELINQUISH_DEFAULT, None)
736 .unwrap();
737 assert_eq!(rd, PropertyValue::Real(0.0));
738 }
739
740 #[test]
741 fn lighting_output_lighting_properties() {
742 let mut obj = LightingOutputObject::new(1, "LO-1").unwrap();
743
744 let tv = obj
746 .read_property(PropertyIdentifier::TRACKING_VALUE, None)
747 .unwrap();
748 assert_eq!(tv, PropertyValue::Real(0.0));
749
750 let lc = obj
752 .read_property(PropertyIdentifier::LIGHTING_COMMAND, None)
753 .unwrap();
754 assert_eq!(lc, PropertyValue::OctetString(vec![]));
755
756 obj.write_property(
758 PropertyIdentifier::LIGHTING_COMMAND,
759 None,
760 PropertyValue::OctetString(vec![0x01, 0x02]),
761 None,
762 )
763 .unwrap();
764 let lc = obj
765 .read_property(PropertyIdentifier::LIGHTING_COMMAND, None)
766 .unwrap();
767 assert_eq!(lc, PropertyValue::OctetString(vec![0x01, 0x02]));
768
769 let lcdp = obj
771 .read_property(PropertyIdentifier::LIGHTING_COMMAND_DEFAULT_PRIORITY, None)
772 .unwrap();
773 assert_eq!(lcdp, PropertyValue::Unsigned(16));
774
775 let ip = obj
777 .read_property(PropertyIdentifier::IN_PROGRESS, None)
778 .unwrap();
779 assert_eq!(ip, PropertyValue::Enumerated(0));
780
781 let bwe = obj
783 .read_property(PropertyIdentifier::BLINK_WARN_ENABLE, None)
784 .unwrap();
785 assert_eq!(bwe, PropertyValue::Boolean(false));
786
787 let et = obj
789 .read_property(PropertyIdentifier::EGRESS_TIME, None)
790 .unwrap();
791 assert_eq!(et, PropertyValue::Unsigned(0));
792
793 let ea = obj
795 .read_property(PropertyIdentifier::EGRESS_ACTIVE, None)
796 .unwrap();
797 assert_eq!(ea, PropertyValue::Boolean(false));
798 }
799
800 #[test]
801 fn lighting_output_property_list() {
802 let obj = LightingOutputObject::new(1, "LO-1").unwrap();
803 let props = obj.property_list();
804 assert!(props.contains(&PropertyIdentifier::PRESENT_VALUE));
805 assert!(props.contains(&PropertyIdentifier::TRACKING_VALUE));
806 assert!(props.contains(&PropertyIdentifier::LIGHTING_COMMAND));
807 assert!(props.contains(&PropertyIdentifier::PRIORITY_ARRAY));
808 assert!(props.contains(&PropertyIdentifier::RELINQUISH_DEFAULT));
809 }
810
811 #[test]
814 fn binary_lighting_output_create() {
815 let obj = BinaryLightingOutputObject::new(1, "BLO-1").unwrap();
816 assert_eq!(obj.object_name(), "BLO-1");
817 assert_eq!(
818 obj.object_identifier().object_type(),
819 ObjectType::BINARY_LIGHTING_OUTPUT
820 );
821 assert_eq!(obj.object_identifier().instance_number(), 1);
822 }
823
824 #[test]
825 fn binary_lighting_output_read_present_value() {
826 let obj = BinaryLightingOutputObject::new(1, "BLO-1").unwrap();
827 let pv = obj.read_property(PropertyIdentifier::PRESENT_VALUE, None);
828 assert_eq!(pv.unwrap(), PropertyValue::Enumerated(0)); }
830
831 #[test]
832 fn binary_lighting_output_read_object_type() {
833 let obj = BinaryLightingOutputObject::new(1, "BLO-1").unwrap();
834 let ot = obj
835 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
836 .unwrap();
837 assert_eq!(
838 ot,
839 PropertyValue::Enumerated(ObjectType::BINARY_LIGHTING_OUTPUT.to_raw())
840 );
841 }
842
843 #[test]
844 fn binary_lighting_output_write_pv_commandable() {
845 let mut obj = BinaryLightingOutputObject::new(1, "BLO-1").unwrap();
846 obj.write_property(
848 PropertyIdentifier::PRESENT_VALUE,
849 None,
850 PropertyValue::Enumerated(1),
851 Some(8),
852 )
853 .unwrap();
854 let pv = obj
855 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
856 .unwrap();
857 assert_eq!(pv, PropertyValue::Enumerated(1));
858
859 obj.write_property(
861 PropertyIdentifier::PRESENT_VALUE,
862 None,
863 PropertyValue::Enumerated(2),
864 Some(1),
865 )
866 .unwrap();
867 let pv = obj
868 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
869 .unwrap();
870 assert_eq!(pv, PropertyValue::Enumerated(2));
871
872 obj.write_property(
874 PropertyIdentifier::PRESENT_VALUE,
875 None,
876 PropertyValue::Null,
877 Some(1),
878 )
879 .unwrap();
880 let pv = obj
881 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
882 .unwrap();
883 assert_eq!(pv, PropertyValue::Enumerated(1));
884 }
885
886 #[test]
887 fn binary_lighting_output_pv_out_of_range() {
888 let mut obj = BinaryLightingOutputObject::new(1, "BLO-1").unwrap();
889 let result = obj.write_property(
890 PropertyIdentifier::PRESENT_VALUE,
891 None,
892 PropertyValue::Enumerated(5), Some(16),
894 );
895 assert!(result.is_err());
896 }
897
898 #[test]
899 fn binary_lighting_output_all_valid_pv_values() {
900 let mut obj = BinaryLightingOutputObject::new(1, "BLO-1").unwrap();
901 for val in 0..=4 {
902 obj.write_property(
903 PropertyIdentifier::PRESENT_VALUE,
904 None,
905 PropertyValue::Enumerated(val),
906 Some(16),
907 )
908 .unwrap();
909 let pv = obj
910 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
911 .unwrap();
912 assert_eq!(pv, PropertyValue::Enumerated(val));
913 }
914 }
915
916 #[test]
917 fn binary_lighting_output_priority_array() {
918 let mut obj = BinaryLightingOutputObject::new(1, "BLO-1").unwrap();
919 obj.write_property(
920 PropertyIdentifier::PRESENT_VALUE,
921 None,
922 PropertyValue::Enumerated(1),
923 Some(5),
924 )
925 .unwrap();
926
927 let size = obj
929 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(0))
930 .unwrap();
931 assert_eq!(size, PropertyValue::Unsigned(16));
932
933 let slot = obj
935 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
936 .unwrap();
937 assert_eq!(slot, PropertyValue::Enumerated(1));
938
939 let slot = obj
941 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(1))
942 .unwrap();
943 assert_eq!(slot, PropertyValue::Null);
944 }
945
946 #[test]
947 fn binary_lighting_output_priority_array_direct_write() {
948 let mut obj = BinaryLightingOutputObject::new(1, "BLO-1").unwrap();
949 obj.write_property(
950 PropertyIdentifier::PRIORITY_ARRAY,
951 Some(3),
952 PropertyValue::Enumerated(4), None,
954 )
955 .unwrap();
956 let pv = obj
957 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
958 .unwrap();
959 assert_eq!(pv, PropertyValue::Enumerated(4));
960 }
961
962 #[test]
963 fn binary_lighting_output_property_list() {
964 let obj = BinaryLightingOutputObject::new(1, "BLO-1").unwrap();
965 let props = obj.property_list();
966 assert!(props.contains(&PropertyIdentifier::PRESENT_VALUE));
967 assert!(props.contains(&PropertyIdentifier::BLINK_WARN_ENABLE));
968 assert!(props.contains(&PropertyIdentifier::EGRESS_TIME));
969 assert!(props.contains(&PropertyIdentifier::PRIORITY_ARRAY));
970 assert!(props.contains(&PropertyIdentifier::RELINQUISH_DEFAULT));
971 }
972
973 #[test]
976 fn channel_create() {
977 let obj = ChannelObject::new(1, "CH-1", 5).unwrap();
978 assert_eq!(obj.object_name(), "CH-1");
979 assert_eq!(obj.object_identifier().object_type(), ObjectType::CHANNEL);
980 assert_eq!(obj.object_identifier().instance_number(), 1);
981 }
982
983 #[test]
984 fn channel_read_present_value() {
985 let obj = ChannelObject::new(1, "CH-1", 5).unwrap();
986 let pv = obj.read_property(PropertyIdentifier::PRESENT_VALUE, None);
987 assert_eq!(pv.unwrap(), PropertyValue::Unsigned(0));
988 }
989
990 #[test]
991 fn channel_read_object_type() {
992 let obj = ChannelObject::new(1, "CH-1", 5).unwrap();
993 let ot = obj
994 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
995 .unwrap();
996 assert_eq!(ot, PropertyValue::Enumerated(ObjectType::CHANNEL.to_raw()));
997 }
998
999 #[test]
1000 fn channel_write_present_value() {
1001 let mut obj = ChannelObject::new(1, "CH-1", 5).unwrap();
1002 obj.write_property(
1003 PropertyIdentifier::PRESENT_VALUE,
1004 None,
1005 PropertyValue::Unsigned(42),
1006 Some(8),
1007 )
1008 .unwrap();
1009 let pv = obj
1010 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1011 .unwrap();
1012 assert_eq!(pv, PropertyValue::Unsigned(42));
1013
1014 let lp = obj
1016 .read_property(PropertyIdentifier::LAST_PRIORITY, None)
1017 .unwrap();
1018 assert_eq!(lp, PropertyValue::Unsigned(8));
1019 }
1020
1021 #[test]
1022 fn channel_read_channel_number() {
1023 let obj = ChannelObject::new(1, "CH-1", 5).unwrap();
1024 let cn = obj
1025 .read_property(PropertyIdentifier::CHANNEL_NUMBER, None)
1026 .unwrap();
1027 assert_eq!(cn, PropertyValue::Unsigned(5));
1028 }
1029
1030 #[test]
1031 fn channel_write_channel_number() {
1032 let mut obj = ChannelObject::new(1, "CH-1", 5).unwrap();
1033 obj.write_property(
1034 PropertyIdentifier::CHANNEL_NUMBER,
1035 None,
1036 PropertyValue::Unsigned(10),
1037 None,
1038 )
1039 .unwrap();
1040 let cn = obj
1041 .read_property(PropertyIdentifier::CHANNEL_NUMBER, None)
1042 .unwrap();
1043 assert_eq!(cn, PropertyValue::Unsigned(10));
1044 }
1045
1046 #[test]
1047 fn channel_read_write_status() {
1048 let obj = ChannelObject::new(1, "CH-1", 5).unwrap();
1049 let ws = obj
1050 .read_property(PropertyIdentifier::WRITE_STATUS, None)
1051 .unwrap();
1052 assert_eq!(ws, PropertyValue::Enumerated(0)); }
1054
1055 #[test]
1056 fn channel_read_last_priority_default() {
1057 let obj = ChannelObject::new(1, "CH-1", 5).unwrap();
1058 let lp = obj
1059 .read_property(PropertyIdentifier::LAST_PRIORITY, None)
1060 .unwrap();
1061 assert_eq!(lp, PropertyValue::Unsigned(16)); }
1063
1064 #[test]
1065 fn channel_property_list() {
1066 let obj = ChannelObject::new(1, "CH-1", 5).unwrap();
1067 let props = obj.property_list();
1068 assert!(props.contains(&PropertyIdentifier::PRESENT_VALUE));
1069 assert!(props.contains(&PropertyIdentifier::LAST_PRIORITY));
1070 assert!(props.contains(&PropertyIdentifier::WRITE_STATUS));
1071 assert!(props.contains(&PropertyIdentifier::CHANNEL_NUMBER));
1072 assert!(props.contains(&PropertyIdentifier::LIST_OF_OBJECT_PROPERTY_REFERENCES));
1073 }
1074
1075 #[test]
1076 fn channel_write_pv_default_priority() {
1077 let mut obj = ChannelObject::new(1, "CH-1", 5).unwrap();
1078 obj.write_property(
1080 PropertyIdentifier::PRESENT_VALUE,
1081 None,
1082 PropertyValue::Unsigned(99),
1083 None,
1084 )
1085 .unwrap();
1086 let lp = obj
1087 .read_property(PropertyIdentifier::LAST_PRIORITY, None)
1088 .unwrap();
1089 assert_eq!(lp, PropertyValue::Unsigned(16));
1090 }
1091}