1use bacnet_types::enums::{ObjectType, PropertyIdentifier};
9use bacnet_types::error::Error;
10use bacnet_types::primitives::{Date, ObjectIdentifier, PropertyValue, StatusFlags, Time};
11use std::borrow::Cow;
12
13use crate::common::{self, read_common_properties};
14use crate::traits::BACnetObject;
15
16macro_rules! define_value_object_commandable {
26 (
27 name: $struct_name:ident,
28 doc: $doc:expr,
29 object_type: $obj_type:expr,
30 value_type: $val_type:ty,
31 default_value: $default:expr,
32 pv_to_property: $pv_to_prop:expr,
33 property_to_pv: $prop_to_pv:expr,
34 pa_wrap: $pa_wrap:expr,
35 rd_wrap: $rd_wrap:expr,
36 copy_type: $is_copy:tt
37 $(,)?
38 ) => {
39 #[doc = $doc]
40 pub struct $struct_name {
41 oid: ObjectIdentifier,
42 name: String,
43 description: String,
44 present_value: $val_type,
45 out_of_service: bool,
46 status_flags: StatusFlags,
47 reliability: u32,
48 priority_array: [Option<$val_type>; 16],
50 relinquish_default: $val_type,
51 }
52
53 impl $struct_name {
54 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
56 let oid = ObjectIdentifier::new($obj_type, instance)?;
57 Ok(Self {
58 oid,
59 name: name.into(),
60 description: String::new(),
61 present_value: $default,
62 out_of_service: false,
63 status_flags: StatusFlags::empty(),
64 reliability: 0,
65 priority_array: Default::default(),
66 relinquish_default: $default,
67 })
68 }
69
70 fn recalculate_present_value(&mut self) {
72 define_value_object_commandable!(@recalc self, $is_copy);
73 }
74 }
75
76 impl BACnetObject for $struct_name {
77 fn object_identifier(&self) -> ObjectIdentifier {
78 self.oid
79 }
80
81 fn object_name(&self) -> &str {
82 &self.name
83 }
84
85 fn read_property(
86 &self,
87 property: PropertyIdentifier,
88 array_index: Option<u32>,
89 ) -> Result<PropertyValue, Error> {
90 if let Some(result) = read_common_properties!(self, property, array_index) {
91 return result;
92 }
93 match property {
94 p if p == PropertyIdentifier::OBJECT_TYPE => {
95 Ok(PropertyValue::Enumerated($obj_type.to_raw()))
96 }
97 p if p == PropertyIdentifier::PRESENT_VALUE => {
98 Ok(($pv_to_prop)(&self.present_value))
99 }
100 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
101 define_value_object_commandable!(@read_pa self, array_index, $pa_wrap, $is_copy)
102 }
103 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
104 Ok(($rd_wrap)(&self.relinquish_default))
105 }
106 _ => Err(common::unknown_property_error()),
107 }
108 }
109
110 fn write_property(
111 &mut self,
112 property: PropertyIdentifier,
113 array_index: Option<u32>,
114 value: PropertyValue,
115 priority: Option<u8>,
116 ) -> Result<(), Error> {
117 if property == PropertyIdentifier::PRIORITY_ARRAY {
119 let idx = match array_index {
120 Some(n) if (1..=16).contains(&n) => (n - 1) as usize,
121 Some(_) => return Err(common::invalid_array_index_error()),
122 None => {
123 return Err(Error::Encoding(
124 "PRIORITY_ARRAY requires array_index (1-16)".into(),
125 ))
126 }
127 };
128 match value {
129 PropertyValue::Null => {
130 self.priority_array[idx] = None;
131 }
132 other => {
133 let extracted = ($prop_to_pv)(other)?;
134 self.priority_array[idx] = Some(extracted);
135 }
136 }
137 self.recalculate_present_value();
138 return Ok(());
139 }
140 if property == PropertyIdentifier::PRESENT_VALUE {
142 let prio = priority.unwrap_or(16);
143 if !(1..=16).contains(&prio) {
144 return Err(common::value_out_of_range_error());
145 }
146 let idx = (prio - 1) as usize;
147 match value {
148 PropertyValue::Null => {
149 self.priority_array[idx] = None;
150 }
151 other => {
152 let extracted = ($prop_to_pv)(other)?;
153 self.priority_array[idx] = Some(extracted);
154 }
155 }
156 self.recalculate_present_value();
157 return Ok(());
158 }
159 if let Some(result) =
160 common::write_out_of_service(&mut self.out_of_service, property, &value)
161 {
162 return result;
163 }
164 if let Some(result) =
165 common::write_object_name(&mut self.name, property, &value)
166 {
167 return result;
168 }
169 if let Some(result) =
170 common::write_description(&mut self.description, property, &value)
171 {
172 return result;
173 }
174 Err(common::write_access_denied_error())
175 }
176
177 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
178 static PROPS: &[PropertyIdentifier] = &[
179 PropertyIdentifier::OBJECT_IDENTIFIER,
180 PropertyIdentifier::OBJECT_NAME,
181 PropertyIdentifier::DESCRIPTION,
182 PropertyIdentifier::OBJECT_TYPE,
183 PropertyIdentifier::PRESENT_VALUE,
184 PropertyIdentifier::STATUS_FLAGS,
185 PropertyIdentifier::OUT_OF_SERVICE,
186 PropertyIdentifier::RELIABILITY,
187 PropertyIdentifier::PRIORITY_ARRAY,
188 PropertyIdentifier::RELINQUISH_DEFAULT,
189 ];
190 Cow::Borrowed(PROPS)
191 }
192
193 fn supports_cov(&self) -> bool {
194 true
195 }
196 }
197 };
198
199 (@recalc $self:ident, copy) => {
203 $self.present_value = common::recalculate_from_priority_array(
204 &$self.priority_array,
205 $self.relinquish_default,
206 );
207 };
208 (@recalc $self:ident, clone) => {
210 $self.present_value = $self
211 .priority_array
212 .iter()
213 .find_map(|slot| slot.as_ref().cloned())
214 .unwrap_or_else(|| $self.relinquish_default.clone());
215 };
216
217 (@read_pa $self:ident, $array_index:ident, $wrap:expr, copy) => {{
219 common::read_priority_array!($self, $array_index, $wrap)
220 }};
221 (@read_pa $self:ident, $array_index:ident, $wrap:expr, clone) => {{
223 let wrap_fn = $wrap;
224 match $array_index {
225 None => {
226 let elements = $self
227 .priority_array
228 .iter()
229 .map(|slot| match slot {
230 Some(v) => wrap_fn(v),
231 None => PropertyValue::Null,
232 })
233 .collect();
234 Ok(PropertyValue::List(elements))
235 }
236 Some(0) => Ok(PropertyValue::Unsigned(16)),
237 Some(idx) if (1..=16).contains(&idx) => {
238 match &$self.priority_array[(idx - 1) as usize] {
239 Some(v) => Ok(wrap_fn(v)),
240 None => Ok(PropertyValue::Null),
241 }
242 }
243 _ => Err(common::invalid_array_index_error()),
244 }
245 }};
246}
247
248#[allow(unused_macros)]
255macro_rules! define_value_object_simple {
256 (
257 name: $struct_name:ident,
258 doc: $doc:expr,
259 object_type: $obj_type:expr,
260 value_type: $val_type:ty,
261 default_value: $default:expr,
262 pv_to_property: $pv_to_prop:expr,
263 property_to_pv: $prop_to_pv:expr
264 $(,)?
265 ) => {
266 #[doc = $doc]
267 pub struct $struct_name {
268 oid: ObjectIdentifier,
269 name: String,
270 description: String,
271 present_value: $val_type,
272 out_of_service: bool,
273 status_flags: StatusFlags,
274 reliability: u32,
275 }
276
277 impl $struct_name {
278 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
280 let oid = ObjectIdentifier::new($obj_type, instance)?;
281 Ok(Self {
282 oid,
283 name: name.into(),
284 description: String::new(),
285 present_value: $default,
286 out_of_service: false,
287 status_flags: StatusFlags::empty(),
288 reliability: 0,
289 })
290 }
291 }
292
293 impl BACnetObject for $struct_name {
294 fn object_identifier(&self) -> ObjectIdentifier {
295 self.oid
296 }
297
298 fn object_name(&self) -> &str {
299 &self.name
300 }
301
302 fn read_property(
303 &self,
304 property: PropertyIdentifier,
305 array_index: Option<u32>,
306 ) -> Result<PropertyValue, Error> {
307 if let Some(result) = read_common_properties!(self, property, array_index) {
308 return result;
309 }
310 match property {
311 p if p == PropertyIdentifier::OBJECT_TYPE => {
312 Ok(PropertyValue::Enumerated($obj_type.to_raw()))
313 }
314 p if p == PropertyIdentifier::PRESENT_VALUE => {
315 Ok(($pv_to_prop)(&self.present_value))
316 }
317 _ => Err(common::unknown_property_error()),
318 }
319 }
320
321 fn write_property(
322 &mut self,
323 property: PropertyIdentifier,
324 _array_index: Option<u32>,
325 value: PropertyValue,
326 _priority: Option<u8>,
327 ) -> Result<(), Error> {
328 if property == PropertyIdentifier::PRESENT_VALUE {
329 let extracted = ($prop_to_pv)(value)?;
330 self.present_value = extracted;
331 return Ok(());
332 }
333 if let Some(result) =
334 common::write_out_of_service(&mut self.out_of_service, property, &value)
335 {
336 return result;
337 }
338 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
339 return result;
340 }
341 if let Some(result) =
342 common::write_description(&mut self.description, property, &value)
343 {
344 return result;
345 }
346 Err(common::write_access_denied_error())
347 }
348
349 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
350 static PROPS: &[PropertyIdentifier] = &[
351 PropertyIdentifier::OBJECT_IDENTIFIER,
352 PropertyIdentifier::OBJECT_NAME,
353 PropertyIdentifier::DESCRIPTION,
354 PropertyIdentifier::OBJECT_TYPE,
355 PropertyIdentifier::PRESENT_VALUE,
356 PropertyIdentifier::STATUS_FLAGS,
357 PropertyIdentifier::OUT_OF_SERVICE,
358 PropertyIdentifier::RELIABILITY,
359 ];
360 Cow::Borrowed(PROPS)
361 }
362
363 fn supports_cov(&self) -> bool {
364 true
365 }
366 }
367 };
368}
369
370fn clone_string_to_pv(v: &str) -> PropertyValue {
375 PropertyValue::CharacterString(v.to_owned())
376}
377
378fn clone_octetstring_to_pv(v: &[u8]) -> PropertyValue {
379 PropertyValue::OctetString(v.to_owned())
380}
381
382fn clone_bitstring_to_pv(v: &(u8, Vec<u8>)) -> PropertyValue {
383 PropertyValue::BitString {
384 unused_bits: v.0,
385 data: v.1.clone(),
386 }
387}
388
389fn datetime_copy_to_pv(v: (Date, Time)) -> PropertyValue {
390 PropertyValue::List(vec![PropertyValue::Date(v.0), PropertyValue::Time(v.1)])
391}
392
393fn datetime_to_pv(dt: &(Date, Time)) -> PropertyValue {
394 PropertyValue::List(vec![PropertyValue::Date(dt.0), PropertyValue::Time(dt.1)])
395}
396
397fn pv_to_date(v: PropertyValue) -> Result<Date, Error> {
398 if let PropertyValue::Date(d) = v {
399 Ok(d)
400 } else {
401 Err(common::invalid_data_type_error())
402 }
403}
404
405fn pv_to_time(v: PropertyValue) -> Result<Time, Error> {
406 if let PropertyValue::Time(t) = v {
407 Ok(t)
408 } else {
409 Err(common::invalid_data_type_error())
410 }
411}
412
413fn pv_to_datetime(v: PropertyValue) -> Result<(Date, Time), Error> {
414 if let PropertyValue::List(items) = v {
415 if items.len() == 2 {
416 let d = if let PropertyValue::Date(d) = &items[0] {
417 *d
418 } else {
419 return Err(common::invalid_data_type_error());
420 };
421 let t = if let PropertyValue::Time(t) = &items[1] {
422 *t
423 } else {
424 return Err(common::invalid_data_type_error());
425 };
426 Ok((d, t))
427 } else {
428 Err(common::invalid_data_type_error())
429 }
430 } else {
431 Err(common::invalid_data_type_error())
432 }
433}
434
435define_value_object_commandable! {
440 name: IntegerValueObject,
441 doc: "BACnet Integer Value object (type 45).",
442 object_type: ObjectType::INTEGER_VALUE,
443 value_type: i32,
444 default_value: 0,
445 pv_to_property: (|v: &i32| PropertyValue::Signed(*v)),
446 property_to_pv: (|v: PropertyValue| -> Result<i32, Error> {
447 if let PropertyValue::Signed(n) = v { Ok(n) }
448 else { Err(common::invalid_data_type_error()) }
449 }),
450 pa_wrap: PropertyValue::Signed,
451 rd_wrap: (|v: &i32| PropertyValue::Signed(*v)),
452 copy_type: copy,
453}
454
455define_value_object_commandable! {
456 name: PositiveIntegerValueObject,
457 doc: "BACnet Positive Integer Value object (type 48).",
458 object_type: ObjectType::POSITIVE_INTEGER_VALUE,
459 value_type: u64,
460 default_value: 0,
461 pv_to_property: (|v: &u64| PropertyValue::Unsigned(*v)),
462 property_to_pv: (|v: PropertyValue| -> Result<u64, Error> {
463 if let PropertyValue::Unsigned(n) = v { Ok(n) }
464 else { Err(common::invalid_data_type_error()) }
465 }),
466 pa_wrap: PropertyValue::Unsigned,
467 rd_wrap: (|v: &u64| PropertyValue::Unsigned(*v)),
468 copy_type: copy,
469}
470
471define_value_object_commandable! {
472 name: LargeAnalogValueObject,
473 doc: "BACnet Large Analog Value object (type 46).",
474 object_type: ObjectType::LARGE_ANALOG_VALUE,
475 value_type: f64,
476 default_value: 0.0,
477 pv_to_property: (|v: &f64| PropertyValue::Double(*v)),
478 property_to_pv: (|v: PropertyValue| -> Result<f64, Error> {
479 if let PropertyValue::Double(n) = v { Ok(n) }
480 else { Err(common::invalid_data_type_error()) }
481 }),
482 pa_wrap: PropertyValue::Double,
483 rd_wrap: (|v: &f64| PropertyValue::Double(*v)),
484 copy_type: copy,
485}
486
487define_value_object_commandable! {
488 name: CharacterStringValueObject,
489 doc: "BACnet CharacterString Value object (type 40).",
490 object_type: ObjectType::CHARACTERSTRING_VALUE,
491 value_type: String,
492 default_value: String::new(),
493 pv_to_property: (|v: &String| PropertyValue::CharacterString(v.clone())),
494 property_to_pv: (|v: PropertyValue| -> Result<String, Error> {
495 if let PropertyValue::CharacterString(s) = v { Ok(s) }
496 else { Err(common::invalid_data_type_error()) }
497 }),
498 pa_wrap: clone_string_to_pv,
499 rd_wrap: (|v: &String| PropertyValue::CharacterString(v.clone())),
500 copy_type: clone,
501}
502
503define_value_object_commandable! {
504 name: OctetStringValueObject,
505 doc: "BACnet OctetString Value object (type 47).",
506 object_type: ObjectType::OCTETSTRING_VALUE,
507 value_type: Vec<u8>,
508 default_value: Vec::new(),
509 pv_to_property: (|v: &Vec<u8>| PropertyValue::OctetString(v.clone())),
510 property_to_pv: (|v: PropertyValue| -> Result<Vec<u8>, Error> {
511 if let PropertyValue::OctetString(b) = v { Ok(b) }
512 else { Err(common::invalid_data_type_error()) }
513 }),
514 pa_wrap: clone_octetstring_to_pv,
515 rd_wrap: (|v: &Vec<u8>| PropertyValue::OctetString(v.clone())),
516 copy_type: clone,
517}
518
519define_value_object_commandable! {
520 name: BitStringValueObject,
521 doc: "BACnet BitString Value object (type 39).",
522 object_type: ObjectType::BITSTRING_VALUE,
523 value_type: (u8, Vec<u8>),
524 default_value: (0, Vec::new()),
525 pv_to_property: (|v: &(u8, Vec<u8>)| PropertyValue::BitString {
526 unused_bits: v.0,
527 data: v.1.clone(),
528 }),
529 property_to_pv: (|v: PropertyValue| -> Result<(u8, Vec<u8>), Error> {
530 if let PropertyValue::BitString { unused_bits, data } = v { Ok((unused_bits, data)) }
531 else { Err(common::invalid_data_type_error()) }
532 }),
533 pa_wrap: clone_bitstring_to_pv,
534 rd_wrap: (|v: &(u8, Vec<u8>)| PropertyValue::BitString {
535 unused_bits: v.0,
536 data: v.1.clone(),
537 }),
538 copy_type: clone,
539}
540
541define_value_object_commandable! {
542 name: DateValueObject,
543 doc: "BACnet Date Value object (type 42).",
544 object_type: ObjectType::DATE_VALUE,
545 value_type: Date,
546 default_value: Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
547 pv_to_property: (|v: &Date| PropertyValue::Date(*v)),
548 property_to_pv: pv_to_date,
549 pa_wrap: PropertyValue::Date,
550 rd_wrap: (|v: &Date| PropertyValue::Date(*v)),
551 copy_type: copy,
552}
553
554define_value_object_commandable! {
555 name: TimeValueObject,
556 doc: "BACnet Time Value object (type 50).",
557 object_type: ObjectType::TIME_VALUE,
558 value_type: Time,
559 default_value: Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
560 pv_to_property: (|v: &Time| PropertyValue::Time(*v)),
561 property_to_pv: pv_to_time,
562 pa_wrap: PropertyValue::Time,
563 rd_wrap: (|v: &Time| PropertyValue::Time(*v)),
564 copy_type: copy,
565}
566
567define_value_object_commandable! {
568 name: DateTimeValueObject,
569 doc: "BACnet DateTime Value object (type 44).",
570 object_type: ObjectType::DATETIME_VALUE,
571 value_type: (Date, Time),
572 default_value: (
573 Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
574 Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
575 ),
576 pv_to_property: (|v: &(Date, Time)| datetime_to_pv(v)),
577 property_to_pv: pv_to_datetime,
578 pa_wrap: datetime_copy_to_pv,
579 rd_wrap: (|v: &(Date, Time)| datetime_to_pv(v)),
580 copy_type: copy,
581}
582
583define_value_object_commandable! {
588 name: DatePatternValueObject,
589 doc: "BACnet Date Pattern Value object (type 41).",
590 object_type: ObjectType::DATEPATTERN_VALUE,
591 value_type: Date,
592 default_value: Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
593 pv_to_property: (|v: &Date| PropertyValue::Date(*v)),
594 property_to_pv: pv_to_date,
595 pa_wrap: PropertyValue::Date,
596 rd_wrap: (|v: &Date| PropertyValue::Date(*v)),
597 copy_type: copy,
598}
599
600define_value_object_commandable! {
601 name: TimePatternValueObject,
602 doc: "BACnet Time Pattern Value object (type 49).",
603 object_type: ObjectType::TIMEPATTERN_VALUE,
604 value_type: Time,
605 default_value: Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
606 pv_to_property: (|v: &Time| PropertyValue::Time(*v)),
607 property_to_pv: pv_to_time,
608 pa_wrap: PropertyValue::Time,
609 rd_wrap: (|v: &Time| PropertyValue::Time(*v)),
610 copy_type: copy,
611}
612
613define_value_object_commandable! {
614 name: DateTimePatternValueObject,
615 doc: "BACnet DateTime Pattern Value object (type 43).",
616 object_type: ObjectType::DATETIMEPATTERN_VALUE,
617 value_type: (Date, Time),
618 default_value: (
619 Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
620 Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
621 ),
622 pv_to_property: (|v: &(Date, Time)| datetime_to_pv(v)),
623 property_to_pv: pv_to_datetime,
624 pa_wrap: datetime_copy_to_pv,
625 rd_wrap: (|v: &(Date, Time)| datetime_to_pv(v)),
626 copy_type: copy,
627}
628
629#[cfg(test)]
634mod tests {
635 use super::*;
636 use bacnet_types::enums::ObjectType;
637
638 #[test]
643 fn integer_value_construct_and_read_object_type() {
644 let obj = IntegerValueObject::new(1, "IV-1").unwrap();
645 let ot = obj
646 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
647 .unwrap();
648 assert_eq!(
649 ot,
650 PropertyValue::Enumerated(ObjectType::INTEGER_VALUE.to_raw())
651 );
652 }
653
654 #[test]
655 fn integer_value_read_write_pv() {
656 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
657 let pv = obj
659 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
660 .unwrap();
661 assert_eq!(pv, PropertyValue::Signed(0));
662
663 obj.write_property(
665 PropertyIdentifier::PRESENT_VALUE,
666 None,
667 PropertyValue::Signed(-42),
668 Some(8),
669 )
670 .unwrap();
671 let pv = obj
672 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
673 .unwrap();
674 assert_eq!(pv, PropertyValue::Signed(-42));
675 }
676
677 #[test]
678 fn integer_value_priority_array() {
679 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
680 obj.write_property(
682 PropertyIdentifier::PRESENT_VALUE,
683 None,
684 PropertyValue::Signed(100),
685 Some(10),
686 )
687 .unwrap();
688 obj.write_property(
690 PropertyIdentifier::PRESENT_VALUE,
691 None,
692 PropertyValue::Signed(50),
693 Some(5),
694 )
695 .unwrap();
696 let pv = obj
697 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
698 .unwrap();
699 assert_eq!(pv, PropertyValue::Signed(50));
700
701 obj.write_property(
703 PropertyIdentifier::PRESENT_VALUE,
704 None,
705 PropertyValue::Null,
706 Some(5),
707 )
708 .unwrap();
709 let pv = obj
710 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
711 .unwrap();
712 assert_eq!(pv, PropertyValue::Signed(100));
713
714 let pa_size = obj
716 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(0))
717 .unwrap();
718 assert_eq!(pa_size, PropertyValue::Unsigned(16));
719 }
720
721 #[test]
722 fn integer_value_invalid_data_type() {
723 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
724 let result = obj.write_property(
725 PropertyIdentifier::PRESENT_VALUE,
726 None,
727 PropertyValue::CharacterString("bad".into()),
728 Some(16),
729 );
730 assert!(result.is_err());
731 }
732
733 #[test]
738 fn positive_integer_value_read_write() {
739 let mut obj = PositiveIntegerValueObject::new(1, "PIV-1").unwrap();
740 let pv = obj
741 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
742 .unwrap();
743 assert_eq!(pv, PropertyValue::Unsigned(0));
744
745 obj.write_property(
746 PropertyIdentifier::PRESENT_VALUE,
747 None,
748 PropertyValue::Unsigned(9999),
749 Some(16),
750 )
751 .unwrap();
752 let pv = obj
753 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
754 .unwrap();
755 assert_eq!(pv, PropertyValue::Unsigned(9999));
756 }
757
758 #[test]
759 fn positive_integer_value_object_type() {
760 let obj = PositiveIntegerValueObject::new(1, "PIV-1").unwrap();
761 let ot = obj
762 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
763 .unwrap();
764 assert_eq!(
765 ot,
766 PropertyValue::Enumerated(ObjectType::POSITIVE_INTEGER_VALUE.to_raw())
767 );
768 }
769
770 #[test]
775 fn large_analog_value_read_write() {
776 let mut obj = LargeAnalogValueObject::new(1, "LAV-1").unwrap();
777 obj.write_property(
778 PropertyIdentifier::PRESENT_VALUE,
779 None,
780 PropertyValue::Double(1.23456789012345),
781 Some(16),
782 )
783 .unwrap();
784 let pv = obj
785 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
786 .unwrap();
787 assert_eq!(pv, PropertyValue::Double(1.23456789012345));
788 }
789
790 #[test]
791 fn large_analog_value_object_type() {
792 let obj = LargeAnalogValueObject::new(1, "LAV-1").unwrap();
793 let ot = obj
794 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
795 .unwrap();
796 assert_eq!(
797 ot,
798 PropertyValue::Enumerated(ObjectType::LARGE_ANALOG_VALUE.to_raw())
799 );
800 }
801
802 #[test]
807 fn characterstring_value_read_write() {
808 let mut obj = CharacterStringValueObject::new(1, "CSV-1").unwrap();
809 let pv = obj
810 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
811 .unwrap();
812 assert_eq!(pv, PropertyValue::CharacterString(String::new()));
813
814 obj.write_property(
815 PropertyIdentifier::PRESENT_VALUE,
816 None,
817 PropertyValue::CharacterString("hello world".into()),
818 Some(16),
819 )
820 .unwrap();
821 let pv = obj
822 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
823 .unwrap();
824 assert_eq!(pv, PropertyValue::CharacterString("hello world".into()));
825 }
826
827 #[test]
828 fn characterstring_value_priority_array() {
829 let mut obj = CharacterStringValueObject::new(1, "CSV-1").unwrap();
830 obj.write_property(
831 PropertyIdentifier::PRESENT_VALUE,
832 None,
833 PropertyValue::CharacterString("low".into()),
834 Some(16),
835 )
836 .unwrap();
837 obj.write_property(
838 PropertyIdentifier::PRESENT_VALUE,
839 None,
840 PropertyValue::CharacterString("high".into()),
841 Some(1),
842 )
843 .unwrap();
844 let pv = obj
845 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
846 .unwrap();
847 assert_eq!(pv, PropertyValue::CharacterString("high".into()));
848
849 obj.write_property(
851 PropertyIdentifier::PRESENT_VALUE,
852 None,
853 PropertyValue::Null,
854 Some(1),
855 )
856 .unwrap();
857 let pv = obj
858 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
859 .unwrap();
860 assert_eq!(pv, PropertyValue::CharacterString("low".into()));
861 }
862
863 #[test]
868 fn octetstring_value_read_write() {
869 let mut obj = OctetStringValueObject::new(1, "OSV-1").unwrap();
870 obj.write_property(
871 PropertyIdentifier::PRESENT_VALUE,
872 None,
873 PropertyValue::OctetString(vec![0xDE, 0xAD, 0xBE, 0xEF]),
874 Some(16),
875 )
876 .unwrap();
877 let pv = obj
878 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
879 .unwrap();
880 assert_eq!(pv, PropertyValue::OctetString(vec![0xDE, 0xAD, 0xBE, 0xEF]));
881 }
882
883 #[test]
884 fn octetstring_value_object_type() {
885 let obj = OctetStringValueObject::new(1, "OSV-1").unwrap();
886 let ot = obj
887 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
888 .unwrap();
889 assert_eq!(
890 ot,
891 PropertyValue::Enumerated(ObjectType::OCTETSTRING_VALUE.to_raw())
892 );
893 }
894
895 #[test]
900 fn bitstring_value_read_write() {
901 let mut obj = BitStringValueObject::new(1, "BSV-1").unwrap();
902 obj.write_property(
903 PropertyIdentifier::PRESENT_VALUE,
904 None,
905 PropertyValue::BitString {
906 unused_bits: 3,
907 data: vec![0b11010000],
908 },
909 Some(16),
910 )
911 .unwrap();
912 let pv = obj
913 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
914 .unwrap();
915 assert_eq!(
916 pv,
917 PropertyValue::BitString {
918 unused_bits: 3,
919 data: vec![0b11010000],
920 }
921 );
922 }
923
924 #[test]
925 fn bitstring_value_object_type() {
926 let obj = BitStringValueObject::new(1, "BSV-1").unwrap();
927 let ot = obj
928 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
929 .unwrap();
930 assert_eq!(
931 ot,
932 PropertyValue::Enumerated(ObjectType::BITSTRING_VALUE.to_raw())
933 );
934 }
935
936 #[test]
941 fn date_value_read_write() {
942 let mut obj = DateValueObject::new(1, "DV-1").unwrap();
943 let d = Date {
944 year: 124,
945 month: 3,
946 day: 15,
947 day_of_week: 5,
948 };
949 obj.write_property(
950 PropertyIdentifier::PRESENT_VALUE,
951 None,
952 PropertyValue::Date(d),
953 Some(16),
954 )
955 .unwrap();
956 let pv = obj
957 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
958 .unwrap();
959 assert_eq!(pv, PropertyValue::Date(d));
960 }
961
962 #[test]
963 fn date_value_priority_array() {
964 let mut obj = DateValueObject::new(1, "DV-1").unwrap();
965 let d1 = Date {
966 year: 124,
967 month: 1,
968 day: 1,
969 day_of_week: 1,
970 };
971 let d2 = Date {
972 year: 124,
973 month: 12,
974 day: 25,
975 day_of_week: 3,
976 };
977 obj.write_property(
978 PropertyIdentifier::PRESENT_VALUE,
979 None,
980 PropertyValue::Date(d1),
981 Some(16),
982 )
983 .unwrap();
984 obj.write_property(
985 PropertyIdentifier::PRESENT_VALUE,
986 None,
987 PropertyValue::Date(d2),
988 Some(8),
989 )
990 .unwrap();
991 let pv = obj
992 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
993 .unwrap();
994 assert_eq!(pv, PropertyValue::Date(d2));
995 }
996
997 #[test]
1002 fn time_value_read_write() {
1003 let mut obj = TimeValueObject::new(1, "TV-1").unwrap();
1004 let t = Time {
1005 hour: 14,
1006 minute: 30,
1007 second: 0,
1008 hundredths: 0,
1009 };
1010 obj.write_property(
1011 PropertyIdentifier::PRESENT_VALUE,
1012 None,
1013 PropertyValue::Time(t),
1014 Some(16),
1015 )
1016 .unwrap();
1017 let pv = obj
1018 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1019 .unwrap();
1020 assert_eq!(pv, PropertyValue::Time(t));
1021 }
1022
1023 #[test]
1024 fn time_value_object_type() {
1025 let obj = TimeValueObject::new(1, "TV-1").unwrap();
1026 let ot = obj
1027 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1028 .unwrap();
1029 assert_eq!(
1030 ot,
1031 PropertyValue::Enumerated(ObjectType::TIME_VALUE.to_raw())
1032 );
1033 }
1034
1035 #[test]
1040 fn datetime_value_read_write() {
1041 let mut obj = DateTimeValueObject::new(1, "DTV-1").unwrap();
1042 let d = Date {
1043 year: 124,
1044 month: 6,
1045 day: 15,
1046 day_of_week: 6,
1047 };
1048 let t = Time {
1049 hour: 12,
1050 minute: 0,
1051 second: 0,
1052 hundredths: 0,
1053 };
1054 obj.write_property(
1055 PropertyIdentifier::PRESENT_VALUE,
1056 None,
1057 PropertyValue::List(vec![PropertyValue::Date(d), PropertyValue::Time(t)]),
1058 Some(16),
1059 )
1060 .unwrap();
1061 let pv = obj
1062 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1063 .unwrap();
1064 assert_eq!(
1065 pv,
1066 PropertyValue::List(vec![PropertyValue::Date(d), PropertyValue::Time(t)])
1067 );
1068 }
1069
1070 #[test]
1071 fn datetime_value_object_type() {
1072 let obj = DateTimeValueObject::new(1, "DTV-1").unwrap();
1073 let ot = obj
1074 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1075 .unwrap();
1076 assert_eq!(
1077 ot,
1078 PropertyValue::Enumerated(ObjectType::DATETIME_VALUE.to_raw())
1079 );
1080 }
1081
1082 #[test]
1083 fn datetime_value_priority_array() {
1084 let mut obj = DateTimeValueObject::new(1, "DTV-1").unwrap();
1085 let d1 = Date {
1086 year: 124,
1087 month: 1,
1088 day: 1,
1089 day_of_week: 1,
1090 };
1091 let t1 = Time {
1092 hour: 0,
1093 minute: 0,
1094 second: 0,
1095 hundredths: 0,
1096 };
1097 let d2 = Date {
1098 year: 124,
1099 month: 12,
1100 day: 31,
1101 day_of_week: 2,
1102 };
1103 let t2 = Time {
1104 hour: 23,
1105 minute: 59,
1106 second: 59,
1107 hundredths: 99,
1108 };
1109 obj.write_property(
1110 PropertyIdentifier::PRESENT_VALUE,
1111 None,
1112 PropertyValue::List(vec![PropertyValue::Date(d1), PropertyValue::Time(t1)]),
1113 Some(16),
1114 )
1115 .unwrap();
1116 obj.write_property(
1117 PropertyIdentifier::PRESENT_VALUE,
1118 None,
1119 PropertyValue::List(vec![PropertyValue::Date(d2), PropertyValue::Time(t2)]),
1120 Some(4),
1121 )
1122 .unwrap();
1123 let pv = obj
1124 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1125 .unwrap();
1126 assert_eq!(
1127 pv,
1128 PropertyValue::List(vec![PropertyValue::Date(d2), PropertyValue::Time(t2)])
1129 );
1130 }
1131
1132 #[test]
1137 fn date_pattern_value_read_write() {
1138 let mut obj = DatePatternValueObject::new(1, "DPV-1").unwrap();
1139 let d = Date {
1140 year: 0xFF,
1141 month: 0xFF,
1142 day: 25,
1143 day_of_week: 0xFF,
1144 };
1145 obj.write_property(
1146 PropertyIdentifier::PRESENT_VALUE,
1147 None,
1148 PropertyValue::Date(d),
1149 None,
1150 )
1151 .unwrap();
1152 let pv = obj
1153 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1154 .unwrap();
1155 assert_eq!(pv, PropertyValue::Date(d));
1156 }
1157
1158 #[test]
1159 fn date_pattern_value_object_type() {
1160 let obj = DatePatternValueObject::new(1, "DPV-1").unwrap();
1161 let ot = obj
1162 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1163 .unwrap();
1164 assert_eq!(
1165 ot,
1166 PropertyValue::Enumerated(ObjectType::DATEPATTERN_VALUE.to_raw())
1167 );
1168 }
1169
1170 #[test]
1171 fn date_pattern_value_has_priority_array() {
1172 let obj = DatePatternValueObject::new(1, "DPV-1").unwrap();
1173 let props = obj.property_list();
1174 assert!(props.contains(&PropertyIdentifier::PRIORITY_ARRAY));
1175 assert!(props.contains(&PropertyIdentifier::RELINQUISH_DEFAULT));
1176 }
1177
1178 #[test]
1183 fn time_pattern_value_read_write() {
1184 let mut obj = TimePatternValueObject::new(1, "TPV-1").unwrap();
1185 let t = Time {
1186 hour: 12,
1187 minute: 0xFF,
1188 second: 0xFF,
1189 hundredths: 0xFF,
1190 };
1191 obj.write_property(
1192 PropertyIdentifier::PRESENT_VALUE,
1193 None,
1194 PropertyValue::Time(t),
1195 None,
1196 )
1197 .unwrap();
1198 let pv = obj
1199 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1200 .unwrap();
1201 assert_eq!(pv, PropertyValue::Time(t));
1202 }
1203
1204 #[test]
1205 fn time_pattern_value_object_type() {
1206 let obj = TimePatternValueObject::new(1, "TPV-1").unwrap();
1207 let ot = obj
1208 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1209 .unwrap();
1210 assert_eq!(
1211 ot,
1212 PropertyValue::Enumerated(ObjectType::TIMEPATTERN_VALUE.to_raw())
1213 );
1214 }
1215
1216 #[test]
1221 fn datetime_pattern_value_read_write() {
1222 let mut obj = DateTimePatternValueObject::new(1, "DTPV-1").unwrap();
1223 let d = Date {
1224 year: 0xFF,
1225 month: 12,
1226 day: 25,
1227 day_of_week: 0xFF,
1228 };
1229 let t = Time {
1230 hour: 0xFF,
1231 minute: 0xFF,
1232 second: 0xFF,
1233 hundredths: 0xFF,
1234 };
1235 obj.write_property(
1236 PropertyIdentifier::PRESENT_VALUE,
1237 None,
1238 PropertyValue::List(vec![PropertyValue::Date(d), PropertyValue::Time(t)]),
1239 None,
1240 )
1241 .unwrap();
1242 let pv = obj
1243 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1244 .unwrap();
1245 assert_eq!(
1246 pv,
1247 PropertyValue::List(vec![PropertyValue::Date(d), PropertyValue::Time(t)])
1248 );
1249 }
1250
1251 #[test]
1252 fn datetime_pattern_value_object_type() {
1253 let obj = DateTimePatternValueObject::new(1, "DTPV-1").unwrap();
1254 let ot = obj
1255 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1256 .unwrap();
1257 assert_eq!(
1258 ot,
1259 PropertyValue::Enumerated(ObjectType::DATETIMEPATTERN_VALUE.to_raw())
1260 );
1261 }
1262
1263 #[test]
1264 fn datetime_pattern_value_has_priority_array() {
1265 let obj = DateTimePatternValueObject::new(1, "DTPV-1").unwrap();
1266 let props = obj.property_list();
1267 assert!(props.contains(&PropertyIdentifier::PRIORITY_ARRAY));
1268 assert!(props.contains(&PropertyIdentifier::RELINQUISH_DEFAULT));
1269 }
1270
1271 #[test]
1276 fn value_object_read_common_properties() {
1277 let obj = IntegerValueObject::new(42, "TestObj").unwrap();
1278
1279 let name = obj
1281 .read_property(PropertyIdentifier::OBJECT_NAME, None)
1282 .unwrap();
1283 assert_eq!(name, PropertyValue::CharacterString("TestObj".into()));
1284
1285 let oid = obj
1287 .read_property(PropertyIdentifier::OBJECT_IDENTIFIER, None)
1288 .unwrap();
1289 assert!(matches!(oid, PropertyValue::ObjectIdentifier(_)));
1290
1291 let sf = obj
1293 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
1294 .unwrap();
1295 assert!(matches!(sf, PropertyValue::BitString { .. }));
1296
1297 let oos = obj
1299 .read_property(PropertyIdentifier::OUT_OF_SERVICE, None)
1300 .unwrap();
1301 assert_eq!(oos, PropertyValue::Boolean(false));
1302
1303 let rel = obj
1305 .read_property(PropertyIdentifier::RELIABILITY, None)
1306 .unwrap();
1307 assert_eq!(rel, PropertyValue::Enumerated(0));
1308 }
1309
1310 #[test]
1311 fn value_object_write_description() {
1312 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
1313 obj.write_property(
1314 PropertyIdentifier::DESCRIPTION,
1315 None,
1316 PropertyValue::CharacterString("A test integer".into()),
1317 None,
1318 )
1319 .unwrap();
1320 let desc = obj
1321 .read_property(PropertyIdentifier::DESCRIPTION, None)
1322 .unwrap();
1323 assert_eq!(
1324 desc,
1325 PropertyValue::CharacterString("A test integer".into())
1326 );
1327 }
1328
1329 #[test]
1330 fn value_object_write_out_of_service() {
1331 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
1332 obj.write_property(
1333 PropertyIdentifier::OUT_OF_SERVICE,
1334 None,
1335 PropertyValue::Boolean(true),
1336 None,
1337 )
1338 .unwrap();
1339 let oos = obj
1340 .read_property(PropertyIdentifier::OUT_OF_SERVICE, None)
1341 .unwrap();
1342 assert_eq!(oos, PropertyValue::Boolean(true));
1343 }
1344
1345 #[test]
1346 fn value_object_relinquish_default() {
1347 let obj = IntegerValueObject::new(1, "IV-1").unwrap();
1348 let rd = obj
1349 .read_property(PropertyIdentifier::RELINQUISH_DEFAULT, None)
1350 .unwrap();
1351 assert_eq!(rd, PropertyValue::Signed(0));
1352 }
1353
1354 #[test]
1355 fn value_object_priority_array_direct_write() {
1356 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
1357
1358 obj.write_property(
1360 PropertyIdentifier::PRIORITY_ARRAY,
1361 Some(5),
1362 PropertyValue::Signed(77),
1363 None,
1364 )
1365 .unwrap();
1366
1367 let slot = obj
1369 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
1370 .unwrap();
1371 assert_eq!(slot, PropertyValue::Signed(77));
1372
1373 let pv = obj
1375 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1376 .unwrap();
1377 assert_eq!(pv, PropertyValue::Signed(77));
1378
1379 obj.write_property(
1381 PropertyIdentifier::PRIORITY_ARRAY,
1382 Some(5),
1383 PropertyValue::Null,
1384 None,
1385 )
1386 .unwrap();
1387
1388 let pv = obj
1390 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1391 .unwrap();
1392 assert_eq!(pv, PropertyValue::Signed(0));
1393 }
1394
1395 #[test]
1396 fn value_object_unknown_property() {
1397 let obj = IntegerValueObject::new(1, "IV-1").unwrap();
1398 let result = obj.read_property(PropertyIdentifier::UNITS, None);
1399 assert!(result.is_err());
1400 }
1401
1402 #[test]
1403 fn value_object_write_object_name() {
1404 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
1405 let result = obj.write_property(
1406 PropertyIdentifier::OBJECT_NAME,
1407 None,
1408 PropertyValue::CharacterString("new-name".into()),
1409 None,
1410 );
1411 assert!(result.is_ok());
1412 assert_eq!(obj.object_name(), "new-name");
1413 }
1414
1415 #[test]
1416 fn value_object_write_access_denied() {
1417 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
1419 let result = obj.write_property(
1420 PropertyIdentifier::OBJECT_TYPE,
1421 None,
1422 PropertyValue::Enumerated(0),
1423 None,
1424 );
1425 assert!(result.is_err());
1426 }
1427}