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_description(&mut self.description, property, &value)
166 {
167 return result;
168 }
169 Err(common::write_access_denied_error())
170 }
171
172 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
173 static PROPS: &[PropertyIdentifier] = &[
174 PropertyIdentifier::OBJECT_IDENTIFIER,
175 PropertyIdentifier::OBJECT_NAME,
176 PropertyIdentifier::DESCRIPTION,
177 PropertyIdentifier::OBJECT_TYPE,
178 PropertyIdentifier::PRESENT_VALUE,
179 PropertyIdentifier::STATUS_FLAGS,
180 PropertyIdentifier::OUT_OF_SERVICE,
181 PropertyIdentifier::RELIABILITY,
182 PropertyIdentifier::PRIORITY_ARRAY,
183 PropertyIdentifier::RELINQUISH_DEFAULT,
184 ];
185 Cow::Borrowed(PROPS)
186 }
187 }
188 };
189
190 (@recalc $self:ident, copy) => {
194 $self.present_value = common::recalculate_from_priority_array(
195 &$self.priority_array,
196 $self.relinquish_default,
197 );
198 };
199 (@recalc $self:ident, clone) => {
201 $self.present_value = $self
202 .priority_array
203 .iter()
204 .find_map(|slot| slot.as_ref().cloned())
205 .unwrap_or_else(|| $self.relinquish_default.clone());
206 };
207
208 (@read_pa $self:ident, $array_index:ident, $wrap:expr, copy) => {{
210 common::read_priority_array!($self, $array_index, $wrap)
211 }};
212 (@read_pa $self:ident, $array_index:ident, $wrap:expr, clone) => {{
214 let wrap_fn = $wrap;
215 match $array_index {
216 None => {
217 let elements = $self
218 .priority_array
219 .iter()
220 .map(|slot| match slot {
221 Some(v) => wrap_fn(v),
222 None => PropertyValue::Null,
223 })
224 .collect();
225 Ok(PropertyValue::List(elements))
226 }
227 Some(0) => Ok(PropertyValue::Unsigned(16)),
228 Some(idx) if (1..=16).contains(&idx) => {
229 match &$self.priority_array[(idx - 1) as usize] {
230 Some(v) => Ok(wrap_fn(v)),
231 None => Ok(PropertyValue::Null),
232 }
233 }
234 _ => Err(common::invalid_array_index_error()),
235 }
236 }};
237}
238
239macro_rules! define_value_object_simple {
245 (
246 name: $struct_name:ident,
247 doc: $doc:expr,
248 object_type: $obj_type:expr,
249 value_type: $val_type:ty,
250 default_value: $default:expr,
251 pv_to_property: $pv_to_prop:expr,
252 property_to_pv: $prop_to_pv:expr
253 $(,)?
254 ) => {
255 #[doc = $doc]
256 pub struct $struct_name {
257 oid: ObjectIdentifier,
258 name: String,
259 description: String,
260 present_value: $val_type,
261 out_of_service: bool,
262 status_flags: StatusFlags,
263 reliability: u32,
264 }
265
266 impl $struct_name {
267 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
269 let oid = ObjectIdentifier::new($obj_type, instance)?;
270 Ok(Self {
271 oid,
272 name: name.into(),
273 description: String::new(),
274 present_value: $default,
275 out_of_service: false,
276 status_flags: StatusFlags::empty(),
277 reliability: 0,
278 })
279 }
280 }
281
282 impl BACnetObject for $struct_name {
283 fn object_identifier(&self) -> ObjectIdentifier {
284 self.oid
285 }
286
287 fn object_name(&self) -> &str {
288 &self.name
289 }
290
291 fn read_property(
292 &self,
293 property: PropertyIdentifier,
294 array_index: Option<u32>,
295 ) -> Result<PropertyValue, Error> {
296 if let Some(result) = read_common_properties!(self, property, array_index) {
297 return result;
298 }
299 match property {
300 p if p == PropertyIdentifier::OBJECT_TYPE => {
301 Ok(PropertyValue::Enumerated($obj_type.to_raw()))
302 }
303 p if p == PropertyIdentifier::PRESENT_VALUE => {
304 Ok(($pv_to_prop)(&self.present_value))
305 }
306 _ => Err(common::unknown_property_error()),
307 }
308 }
309
310 fn write_property(
311 &mut self,
312 property: PropertyIdentifier,
313 _array_index: Option<u32>,
314 value: PropertyValue,
315 _priority: Option<u8>,
316 ) -> Result<(), Error> {
317 if property == PropertyIdentifier::PRESENT_VALUE {
318 let extracted = ($prop_to_pv)(value)?;
319 self.present_value = extracted;
320 return Ok(());
321 }
322 if let Some(result) =
323 common::write_out_of_service(&mut self.out_of_service, property, &value)
324 {
325 return result;
326 }
327 if let Some(result) =
328 common::write_description(&mut self.description, property, &value)
329 {
330 return result;
331 }
332 Err(common::write_access_denied_error())
333 }
334
335 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
336 static PROPS: &[PropertyIdentifier] = &[
337 PropertyIdentifier::OBJECT_IDENTIFIER,
338 PropertyIdentifier::OBJECT_NAME,
339 PropertyIdentifier::DESCRIPTION,
340 PropertyIdentifier::OBJECT_TYPE,
341 PropertyIdentifier::PRESENT_VALUE,
342 PropertyIdentifier::STATUS_FLAGS,
343 PropertyIdentifier::OUT_OF_SERVICE,
344 PropertyIdentifier::RELIABILITY,
345 ];
346 Cow::Borrowed(PROPS)
347 }
348 }
349 };
350}
351
352fn clone_string_to_pv(v: &str) -> PropertyValue {
357 PropertyValue::CharacterString(v.to_owned())
358}
359
360fn clone_octetstring_to_pv(v: &[u8]) -> PropertyValue {
361 PropertyValue::OctetString(v.to_owned())
362}
363
364fn clone_bitstring_to_pv(v: &(u8, Vec<u8>)) -> PropertyValue {
365 PropertyValue::BitString {
366 unused_bits: v.0,
367 data: v.1.clone(),
368 }
369}
370
371fn datetime_copy_to_pv(v: (Date, Time)) -> PropertyValue {
372 PropertyValue::List(vec![PropertyValue::Date(v.0), PropertyValue::Time(v.1)])
373}
374
375fn datetime_to_pv(dt: &(Date, Time)) -> PropertyValue {
376 PropertyValue::List(vec![PropertyValue::Date(dt.0), PropertyValue::Time(dt.1)])
377}
378
379fn pv_to_date(v: PropertyValue) -> Result<Date, Error> {
380 if let PropertyValue::Date(d) = v {
381 Ok(d)
382 } else {
383 Err(common::invalid_data_type_error())
384 }
385}
386
387fn pv_to_time(v: PropertyValue) -> Result<Time, Error> {
388 if let PropertyValue::Time(t) = v {
389 Ok(t)
390 } else {
391 Err(common::invalid_data_type_error())
392 }
393}
394
395fn pv_to_datetime(v: PropertyValue) -> Result<(Date, Time), Error> {
396 if let PropertyValue::List(items) = v {
397 if items.len() == 2 {
398 let d = if let PropertyValue::Date(d) = &items[0] {
399 *d
400 } else {
401 return Err(common::invalid_data_type_error());
402 };
403 let t = if let PropertyValue::Time(t) = &items[1] {
404 *t
405 } else {
406 return Err(common::invalid_data_type_error());
407 };
408 Ok((d, t))
409 } else {
410 Err(common::invalid_data_type_error())
411 }
412 } else {
413 Err(common::invalid_data_type_error())
414 }
415}
416
417define_value_object_commandable! {
422 name: IntegerValueObject,
423 doc: "BACnet Integer Value object (type 45).",
424 object_type: ObjectType::INTEGER_VALUE,
425 value_type: i32,
426 default_value: 0,
427 pv_to_property: (|v: &i32| PropertyValue::Signed(*v)),
428 property_to_pv: (|v: PropertyValue| -> Result<i32, Error> {
429 if let PropertyValue::Signed(n) = v { Ok(n) }
430 else { Err(common::invalid_data_type_error()) }
431 }),
432 pa_wrap: PropertyValue::Signed,
433 rd_wrap: (|v: &i32| PropertyValue::Signed(*v)),
434 copy_type: copy,
435}
436
437define_value_object_commandable! {
438 name: PositiveIntegerValueObject,
439 doc: "BACnet Positive Integer Value object (type 48).",
440 object_type: ObjectType::POSITIVE_INTEGER_VALUE,
441 value_type: u64,
442 default_value: 0,
443 pv_to_property: (|v: &u64| PropertyValue::Unsigned(*v)),
444 property_to_pv: (|v: PropertyValue| -> Result<u64, Error> {
445 if let PropertyValue::Unsigned(n) = v { Ok(n) }
446 else { Err(common::invalid_data_type_error()) }
447 }),
448 pa_wrap: PropertyValue::Unsigned,
449 rd_wrap: (|v: &u64| PropertyValue::Unsigned(*v)),
450 copy_type: copy,
451}
452
453define_value_object_commandable! {
454 name: LargeAnalogValueObject,
455 doc: "BACnet Large Analog Value object (type 46).",
456 object_type: ObjectType::LARGE_ANALOG_VALUE,
457 value_type: f64,
458 default_value: 0.0,
459 pv_to_property: (|v: &f64| PropertyValue::Double(*v)),
460 property_to_pv: (|v: PropertyValue| -> Result<f64, Error> {
461 if let PropertyValue::Double(n) = v { Ok(n) }
462 else { Err(common::invalid_data_type_error()) }
463 }),
464 pa_wrap: PropertyValue::Double,
465 rd_wrap: (|v: &f64| PropertyValue::Double(*v)),
466 copy_type: copy,
467}
468
469define_value_object_commandable! {
470 name: CharacterStringValueObject,
471 doc: "BACnet CharacterString Value object (type 40).",
472 object_type: ObjectType::CHARACTERSTRING_VALUE,
473 value_type: String,
474 default_value: String::new(),
475 pv_to_property: (|v: &String| PropertyValue::CharacterString(v.clone())),
476 property_to_pv: (|v: PropertyValue| -> Result<String, Error> {
477 if let PropertyValue::CharacterString(s) = v { Ok(s) }
478 else { Err(common::invalid_data_type_error()) }
479 }),
480 pa_wrap: clone_string_to_pv,
481 rd_wrap: (|v: &String| PropertyValue::CharacterString(v.clone())),
482 copy_type: clone,
483}
484
485define_value_object_commandable! {
486 name: OctetStringValueObject,
487 doc: "BACnet OctetString Value object (type 47).",
488 object_type: ObjectType::OCTETSTRING_VALUE,
489 value_type: Vec<u8>,
490 default_value: Vec::new(),
491 pv_to_property: (|v: &Vec<u8>| PropertyValue::OctetString(v.clone())),
492 property_to_pv: (|v: PropertyValue| -> Result<Vec<u8>, Error> {
493 if let PropertyValue::OctetString(b) = v { Ok(b) }
494 else { Err(common::invalid_data_type_error()) }
495 }),
496 pa_wrap: clone_octetstring_to_pv,
497 rd_wrap: (|v: &Vec<u8>| PropertyValue::OctetString(v.clone())),
498 copy_type: clone,
499}
500
501define_value_object_commandable! {
502 name: BitStringValueObject,
503 doc: "BACnet BitString Value object (type 39).",
504 object_type: ObjectType::BITSTRING_VALUE,
505 value_type: (u8, Vec<u8>),
506 default_value: (0, Vec::new()),
507 pv_to_property: (|v: &(u8, Vec<u8>)| PropertyValue::BitString {
508 unused_bits: v.0,
509 data: v.1.clone(),
510 }),
511 property_to_pv: (|v: PropertyValue| -> Result<(u8, Vec<u8>), Error> {
512 if let PropertyValue::BitString { unused_bits, data } = v { Ok((unused_bits, data)) }
513 else { Err(common::invalid_data_type_error()) }
514 }),
515 pa_wrap: clone_bitstring_to_pv,
516 rd_wrap: (|v: &(u8, Vec<u8>)| PropertyValue::BitString {
517 unused_bits: v.0,
518 data: v.1.clone(),
519 }),
520 copy_type: clone,
521}
522
523define_value_object_commandable! {
524 name: DateValueObject,
525 doc: "BACnet Date Value object (type 42).",
526 object_type: ObjectType::DATE_VALUE,
527 value_type: Date,
528 default_value: Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
529 pv_to_property: (|v: &Date| PropertyValue::Date(*v)),
530 property_to_pv: pv_to_date,
531 pa_wrap: PropertyValue::Date,
532 rd_wrap: (|v: &Date| PropertyValue::Date(*v)),
533 copy_type: copy,
534}
535
536define_value_object_commandable! {
537 name: TimeValueObject,
538 doc: "BACnet Time Value object (type 50).",
539 object_type: ObjectType::TIME_VALUE,
540 value_type: Time,
541 default_value: Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
542 pv_to_property: (|v: &Time| PropertyValue::Time(*v)),
543 property_to_pv: pv_to_time,
544 pa_wrap: PropertyValue::Time,
545 rd_wrap: (|v: &Time| PropertyValue::Time(*v)),
546 copy_type: copy,
547}
548
549define_value_object_commandable! {
550 name: DateTimeValueObject,
551 doc: "BACnet DateTime Value object (type 44).",
552 object_type: ObjectType::DATETIME_VALUE,
553 value_type: (Date, Time),
554 default_value: (
555 Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
556 Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
557 ),
558 pv_to_property: (|v: &(Date, Time)| datetime_to_pv(v)),
559 property_to_pv: pv_to_datetime,
560 pa_wrap: datetime_copy_to_pv,
561 rd_wrap: (|v: &(Date, Time)| datetime_to_pv(v)),
562 copy_type: copy,
563}
564
565define_value_object_simple! {
570 name: DatePatternValueObject,
571 doc: "BACnet Date Pattern Value object (type 41).",
572 object_type: ObjectType::DATEPATTERN_VALUE,
573 value_type: Date,
574 default_value: Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
575 pv_to_property: (|v: &Date| PropertyValue::Date(*v)),
576 property_to_pv: pv_to_date,
577}
578
579define_value_object_simple! {
580 name: TimePatternValueObject,
581 doc: "BACnet Time Pattern Value object (type 49).",
582 object_type: ObjectType::TIMEPATTERN_VALUE,
583 value_type: Time,
584 default_value: Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
585 pv_to_property: (|v: &Time| PropertyValue::Time(*v)),
586 property_to_pv: pv_to_time,
587}
588
589define_value_object_simple! {
590 name: DateTimePatternValueObject,
591 doc: "BACnet DateTime Pattern Value object (type 43).",
592 object_type: ObjectType::DATETIMEPATTERN_VALUE,
593 value_type: (Date, Time),
594 default_value: (
595 Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
596 Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
597 ),
598 pv_to_property: (|v: &(Date, Time)| datetime_to_pv(v)),
599 property_to_pv: pv_to_datetime,
600}
601
602#[cfg(test)]
607mod tests {
608 use super::*;
609 use bacnet_types::enums::ObjectType;
610
611 #[test]
616 fn integer_value_construct_and_read_object_type() {
617 let obj = IntegerValueObject::new(1, "IV-1").unwrap();
618 let ot = obj
619 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
620 .unwrap();
621 assert_eq!(
622 ot,
623 PropertyValue::Enumerated(ObjectType::INTEGER_VALUE.to_raw())
624 );
625 }
626
627 #[test]
628 fn integer_value_read_write_pv() {
629 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
630 let pv = obj
632 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
633 .unwrap();
634 assert_eq!(pv, PropertyValue::Signed(0));
635
636 obj.write_property(
638 PropertyIdentifier::PRESENT_VALUE,
639 None,
640 PropertyValue::Signed(-42),
641 Some(8),
642 )
643 .unwrap();
644 let pv = obj
645 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
646 .unwrap();
647 assert_eq!(pv, PropertyValue::Signed(-42));
648 }
649
650 #[test]
651 fn integer_value_priority_array() {
652 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
653 obj.write_property(
655 PropertyIdentifier::PRESENT_VALUE,
656 None,
657 PropertyValue::Signed(100),
658 Some(10),
659 )
660 .unwrap();
661 obj.write_property(
663 PropertyIdentifier::PRESENT_VALUE,
664 None,
665 PropertyValue::Signed(50),
666 Some(5),
667 )
668 .unwrap();
669 let pv = obj
670 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
671 .unwrap();
672 assert_eq!(pv, PropertyValue::Signed(50));
673
674 obj.write_property(
676 PropertyIdentifier::PRESENT_VALUE,
677 None,
678 PropertyValue::Null,
679 Some(5),
680 )
681 .unwrap();
682 let pv = obj
683 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
684 .unwrap();
685 assert_eq!(pv, PropertyValue::Signed(100));
686
687 let pa_size = obj
689 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(0))
690 .unwrap();
691 assert_eq!(pa_size, PropertyValue::Unsigned(16));
692 }
693
694 #[test]
695 fn integer_value_invalid_data_type() {
696 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
697 let result = obj.write_property(
698 PropertyIdentifier::PRESENT_VALUE,
699 None,
700 PropertyValue::CharacterString("bad".into()),
701 Some(16),
702 );
703 assert!(result.is_err());
704 }
705
706 #[test]
711 fn positive_integer_value_read_write() {
712 let mut obj = PositiveIntegerValueObject::new(1, "PIV-1").unwrap();
713 let pv = obj
714 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
715 .unwrap();
716 assert_eq!(pv, PropertyValue::Unsigned(0));
717
718 obj.write_property(
719 PropertyIdentifier::PRESENT_VALUE,
720 None,
721 PropertyValue::Unsigned(9999),
722 Some(16),
723 )
724 .unwrap();
725 let pv = obj
726 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
727 .unwrap();
728 assert_eq!(pv, PropertyValue::Unsigned(9999));
729 }
730
731 #[test]
732 fn positive_integer_value_object_type() {
733 let obj = PositiveIntegerValueObject::new(1, "PIV-1").unwrap();
734 let ot = obj
735 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
736 .unwrap();
737 assert_eq!(
738 ot,
739 PropertyValue::Enumerated(ObjectType::POSITIVE_INTEGER_VALUE.to_raw())
740 );
741 }
742
743 #[test]
748 fn large_analog_value_read_write() {
749 let mut obj = LargeAnalogValueObject::new(1, "LAV-1").unwrap();
750 obj.write_property(
751 PropertyIdentifier::PRESENT_VALUE,
752 None,
753 PropertyValue::Double(1.23456789012345),
754 Some(16),
755 )
756 .unwrap();
757 let pv = obj
758 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
759 .unwrap();
760 assert_eq!(pv, PropertyValue::Double(1.23456789012345));
761 }
762
763 #[test]
764 fn large_analog_value_object_type() {
765 let obj = LargeAnalogValueObject::new(1, "LAV-1").unwrap();
766 let ot = obj
767 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
768 .unwrap();
769 assert_eq!(
770 ot,
771 PropertyValue::Enumerated(ObjectType::LARGE_ANALOG_VALUE.to_raw())
772 );
773 }
774
775 #[test]
780 fn characterstring_value_read_write() {
781 let mut obj = CharacterStringValueObject::new(1, "CSV-1").unwrap();
782 let pv = obj
783 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
784 .unwrap();
785 assert_eq!(pv, PropertyValue::CharacterString(String::new()));
786
787 obj.write_property(
788 PropertyIdentifier::PRESENT_VALUE,
789 None,
790 PropertyValue::CharacterString("hello world".into()),
791 Some(16),
792 )
793 .unwrap();
794 let pv = obj
795 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
796 .unwrap();
797 assert_eq!(pv, PropertyValue::CharacterString("hello world".into()));
798 }
799
800 #[test]
801 fn characterstring_value_priority_array() {
802 let mut obj = CharacterStringValueObject::new(1, "CSV-1").unwrap();
803 obj.write_property(
804 PropertyIdentifier::PRESENT_VALUE,
805 None,
806 PropertyValue::CharacterString("low".into()),
807 Some(16),
808 )
809 .unwrap();
810 obj.write_property(
811 PropertyIdentifier::PRESENT_VALUE,
812 None,
813 PropertyValue::CharacterString("high".into()),
814 Some(1),
815 )
816 .unwrap();
817 let pv = obj
818 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
819 .unwrap();
820 assert_eq!(pv, PropertyValue::CharacterString("high".into()));
821
822 obj.write_property(
824 PropertyIdentifier::PRESENT_VALUE,
825 None,
826 PropertyValue::Null,
827 Some(1),
828 )
829 .unwrap();
830 let pv = obj
831 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
832 .unwrap();
833 assert_eq!(pv, PropertyValue::CharacterString("low".into()));
834 }
835
836 #[test]
841 fn octetstring_value_read_write() {
842 let mut obj = OctetStringValueObject::new(1, "OSV-1").unwrap();
843 obj.write_property(
844 PropertyIdentifier::PRESENT_VALUE,
845 None,
846 PropertyValue::OctetString(vec![0xDE, 0xAD, 0xBE, 0xEF]),
847 Some(16),
848 )
849 .unwrap();
850 let pv = obj
851 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
852 .unwrap();
853 assert_eq!(pv, PropertyValue::OctetString(vec![0xDE, 0xAD, 0xBE, 0xEF]));
854 }
855
856 #[test]
857 fn octetstring_value_object_type() {
858 let obj = OctetStringValueObject::new(1, "OSV-1").unwrap();
859 let ot = obj
860 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
861 .unwrap();
862 assert_eq!(
863 ot,
864 PropertyValue::Enumerated(ObjectType::OCTETSTRING_VALUE.to_raw())
865 );
866 }
867
868 #[test]
873 fn bitstring_value_read_write() {
874 let mut obj = BitStringValueObject::new(1, "BSV-1").unwrap();
875 obj.write_property(
876 PropertyIdentifier::PRESENT_VALUE,
877 None,
878 PropertyValue::BitString {
879 unused_bits: 3,
880 data: vec![0b11010000],
881 },
882 Some(16),
883 )
884 .unwrap();
885 let pv = obj
886 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
887 .unwrap();
888 assert_eq!(
889 pv,
890 PropertyValue::BitString {
891 unused_bits: 3,
892 data: vec![0b11010000],
893 }
894 );
895 }
896
897 #[test]
898 fn bitstring_value_object_type() {
899 let obj = BitStringValueObject::new(1, "BSV-1").unwrap();
900 let ot = obj
901 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
902 .unwrap();
903 assert_eq!(
904 ot,
905 PropertyValue::Enumerated(ObjectType::BITSTRING_VALUE.to_raw())
906 );
907 }
908
909 #[test]
914 fn date_value_read_write() {
915 let mut obj = DateValueObject::new(1, "DV-1").unwrap();
916 let d = Date {
917 year: 124,
918 month: 3,
919 day: 15,
920 day_of_week: 5,
921 };
922 obj.write_property(
923 PropertyIdentifier::PRESENT_VALUE,
924 None,
925 PropertyValue::Date(d),
926 Some(16),
927 )
928 .unwrap();
929 let pv = obj
930 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
931 .unwrap();
932 assert_eq!(pv, PropertyValue::Date(d));
933 }
934
935 #[test]
936 fn date_value_priority_array() {
937 let mut obj = DateValueObject::new(1, "DV-1").unwrap();
938 let d1 = Date {
939 year: 124,
940 month: 1,
941 day: 1,
942 day_of_week: 1,
943 };
944 let d2 = Date {
945 year: 124,
946 month: 12,
947 day: 25,
948 day_of_week: 3,
949 };
950 obj.write_property(
951 PropertyIdentifier::PRESENT_VALUE,
952 None,
953 PropertyValue::Date(d1),
954 Some(16),
955 )
956 .unwrap();
957 obj.write_property(
958 PropertyIdentifier::PRESENT_VALUE,
959 None,
960 PropertyValue::Date(d2),
961 Some(8),
962 )
963 .unwrap();
964 let pv = obj
965 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
966 .unwrap();
967 assert_eq!(pv, PropertyValue::Date(d2));
968 }
969
970 #[test]
975 fn time_value_read_write() {
976 let mut obj = TimeValueObject::new(1, "TV-1").unwrap();
977 let t = Time {
978 hour: 14,
979 minute: 30,
980 second: 0,
981 hundredths: 0,
982 };
983 obj.write_property(
984 PropertyIdentifier::PRESENT_VALUE,
985 None,
986 PropertyValue::Time(t),
987 Some(16),
988 )
989 .unwrap();
990 let pv = obj
991 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
992 .unwrap();
993 assert_eq!(pv, PropertyValue::Time(t));
994 }
995
996 #[test]
997 fn time_value_object_type() {
998 let obj = TimeValueObject::new(1, "TV-1").unwrap();
999 let ot = obj
1000 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1001 .unwrap();
1002 assert_eq!(
1003 ot,
1004 PropertyValue::Enumerated(ObjectType::TIME_VALUE.to_raw())
1005 );
1006 }
1007
1008 #[test]
1013 fn datetime_value_read_write() {
1014 let mut obj = DateTimeValueObject::new(1, "DTV-1").unwrap();
1015 let d = Date {
1016 year: 124,
1017 month: 6,
1018 day: 15,
1019 day_of_week: 6,
1020 };
1021 let t = Time {
1022 hour: 12,
1023 minute: 0,
1024 second: 0,
1025 hundredths: 0,
1026 };
1027 obj.write_property(
1028 PropertyIdentifier::PRESENT_VALUE,
1029 None,
1030 PropertyValue::List(vec![PropertyValue::Date(d), PropertyValue::Time(t)]),
1031 Some(16),
1032 )
1033 .unwrap();
1034 let pv = obj
1035 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1036 .unwrap();
1037 assert_eq!(
1038 pv,
1039 PropertyValue::List(vec![PropertyValue::Date(d), PropertyValue::Time(t)])
1040 );
1041 }
1042
1043 #[test]
1044 fn datetime_value_object_type() {
1045 let obj = DateTimeValueObject::new(1, "DTV-1").unwrap();
1046 let ot = obj
1047 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1048 .unwrap();
1049 assert_eq!(
1050 ot,
1051 PropertyValue::Enumerated(ObjectType::DATETIME_VALUE.to_raw())
1052 );
1053 }
1054
1055 #[test]
1056 fn datetime_value_priority_array() {
1057 let mut obj = DateTimeValueObject::new(1, "DTV-1").unwrap();
1058 let d1 = Date {
1059 year: 124,
1060 month: 1,
1061 day: 1,
1062 day_of_week: 1,
1063 };
1064 let t1 = Time {
1065 hour: 0,
1066 minute: 0,
1067 second: 0,
1068 hundredths: 0,
1069 };
1070 let d2 = Date {
1071 year: 124,
1072 month: 12,
1073 day: 31,
1074 day_of_week: 2,
1075 };
1076 let t2 = Time {
1077 hour: 23,
1078 minute: 59,
1079 second: 59,
1080 hundredths: 99,
1081 };
1082 obj.write_property(
1083 PropertyIdentifier::PRESENT_VALUE,
1084 None,
1085 PropertyValue::List(vec![PropertyValue::Date(d1), PropertyValue::Time(t1)]),
1086 Some(16),
1087 )
1088 .unwrap();
1089 obj.write_property(
1090 PropertyIdentifier::PRESENT_VALUE,
1091 None,
1092 PropertyValue::List(vec![PropertyValue::Date(d2), PropertyValue::Time(t2)]),
1093 Some(4),
1094 )
1095 .unwrap();
1096 let pv = obj
1097 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1098 .unwrap();
1099 assert_eq!(
1100 pv,
1101 PropertyValue::List(vec![PropertyValue::Date(d2), PropertyValue::Time(t2)])
1102 );
1103 }
1104
1105 #[test]
1110 fn date_pattern_value_read_write() {
1111 let mut obj = DatePatternValueObject::new(1, "DPV-1").unwrap();
1112 let d = Date {
1113 year: 0xFF,
1114 month: 0xFF,
1115 day: 25,
1116 day_of_week: 0xFF,
1117 };
1118 obj.write_property(
1119 PropertyIdentifier::PRESENT_VALUE,
1120 None,
1121 PropertyValue::Date(d),
1122 None,
1123 )
1124 .unwrap();
1125 let pv = obj
1126 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1127 .unwrap();
1128 assert_eq!(pv, PropertyValue::Date(d));
1129 }
1130
1131 #[test]
1132 fn date_pattern_value_object_type() {
1133 let obj = DatePatternValueObject::new(1, "DPV-1").unwrap();
1134 let ot = obj
1135 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1136 .unwrap();
1137 assert_eq!(
1138 ot,
1139 PropertyValue::Enumerated(ObjectType::DATEPATTERN_VALUE.to_raw())
1140 );
1141 }
1142
1143 #[test]
1144 fn date_pattern_value_no_priority_array() {
1145 let obj = DatePatternValueObject::new(1, "DPV-1").unwrap();
1146 let props = obj.property_list();
1147 assert!(!props.contains(&PropertyIdentifier::PRIORITY_ARRAY));
1148 assert!(!props.contains(&PropertyIdentifier::RELINQUISH_DEFAULT));
1149 }
1150
1151 #[test]
1156 fn time_pattern_value_read_write() {
1157 let mut obj = TimePatternValueObject::new(1, "TPV-1").unwrap();
1158 let t = Time {
1159 hour: 12,
1160 minute: 0xFF,
1161 second: 0xFF,
1162 hundredths: 0xFF,
1163 };
1164 obj.write_property(
1165 PropertyIdentifier::PRESENT_VALUE,
1166 None,
1167 PropertyValue::Time(t),
1168 None,
1169 )
1170 .unwrap();
1171 let pv = obj
1172 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1173 .unwrap();
1174 assert_eq!(pv, PropertyValue::Time(t));
1175 }
1176
1177 #[test]
1178 fn time_pattern_value_object_type() {
1179 let obj = TimePatternValueObject::new(1, "TPV-1").unwrap();
1180 let ot = obj
1181 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1182 .unwrap();
1183 assert_eq!(
1184 ot,
1185 PropertyValue::Enumerated(ObjectType::TIMEPATTERN_VALUE.to_raw())
1186 );
1187 }
1188
1189 #[test]
1194 fn datetime_pattern_value_read_write() {
1195 let mut obj = DateTimePatternValueObject::new(1, "DTPV-1").unwrap();
1196 let d = Date {
1197 year: 0xFF,
1198 month: 12,
1199 day: 25,
1200 day_of_week: 0xFF,
1201 };
1202 let t = Time {
1203 hour: 0xFF,
1204 minute: 0xFF,
1205 second: 0xFF,
1206 hundredths: 0xFF,
1207 };
1208 obj.write_property(
1209 PropertyIdentifier::PRESENT_VALUE,
1210 None,
1211 PropertyValue::List(vec![PropertyValue::Date(d), PropertyValue::Time(t)]),
1212 None,
1213 )
1214 .unwrap();
1215 let pv = obj
1216 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1217 .unwrap();
1218 assert_eq!(
1219 pv,
1220 PropertyValue::List(vec![PropertyValue::Date(d), PropertyValue::Time(t)])
1221 );
1222 }
1223
1224 #[test]
1225 fn datetime_pattern_value_object_type() {
1226 let obj = DateTimePatternValueObject::new(1, "DTPV-1").unwrap();
1227 let ot = obj
1228 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1229 .unwrap();
1230 assert_eq!(
1231 ot,
1232 PropertyValue::Enumerated(ObjectType::DATETIMEPATTERN_VALUE.to_raw())
1233 );
1234 }
1235
1236 #[test]
1237 fn datetime_pattern_value_no_priority_array() {
1238 let obj = DateTimePatternValueObject::new(1, "DTPV-1").unwrap();
1239 let props = obj.property_list();
1240 assert!(!props.contains(&PropertyIdentifier::PRIORITY_ARRAY));
1241 assert!(!props.contains(&PropertyIdentifier::RELINQUISH_DEFAULT));
1242 }
1243
1244 #[test]
1249 fn value_object_read_common_properties() {
1250 let obj = IntegerValueObject::new(42, "TestObj").unwrap();
1251
1252 let name = obj
1254 .read_property(PropertyIdentifier::OBJECT_NAME, None)
1255 .unwrap();
1256 assert_eq!(name, PropertyValue::CharacterString("TestObj".into()));
1257
1258 let oid = obj
1260 .read_property(PropertyIdentifier::OBJECT_IDENTIFIER, None)
1261 .unwrap();
1262 assert!(matches!(oid, PropertyValue::ObjectIdentifier(_)));
1263
1264 let sf = obj
1266 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
1267 .unwrap();
1268 assert!(matches!(sf, PropertyValue::BitString { .. }));
1269
1270 let oos = obj
1272 .read_property(PropertyIdentifier::OUT_OF_SERVICE, None)
1273 .unwrap();
1274 assert_eq!(oos, PropertyValue::Boolean(false));
1275
1276 let rel = obj
1278 .read_property(PropertyIdentifier::RELIABILITY, None)
1279 .unwrap();
1280 assert_eq!(rel, PropertyValue::Enumerated(0));
1281 }
1282
1283 #[test]
1284 fn value_object_write_description() {
1285 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
1286 obj.write_property(
1287 PropertyIdentifier::DESCRIPTION,
1288 None,
1289 PropertyValue::CharacterString("A test integer".into()),
1290 None,
1291 )
1292 .unwrap();
1293 let desc = obj
1294 .read_property(PropertyIdentifier::DESCRIPTION, None)
1295 .unwrap();
1296 assert_eq!(
1297 desc,
1298 PropertyValue::CharacterString("A test integer".into())
1299 );
1300 }
1301
1302 #[test]
1303 fn value_object_write_out_of_service() {
1304 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
1305 obj.write_property(
1306 PropertyIdentifier::OUT_OF_SERVICE,
1307 None,
1308 PropertyValue::Boolean(true),
1309 None,
1310 )
1311 .unwrap();
1312 let oos = obj
1313 .read_property(PropertyIdentifier::OUT_OF_SERVICE, None)
1314 .unwrap();
1315 assert_eq!(oos, PropertyValue::Boolean(true));
1316 }
1317
1318 #[test]
1319 fn value_object_relinquish_default() {
1320 let obj = IntegerValueObject::new(1, "IV-1").unwrap();
1321 let rd = obj
1322 .read_property(PropertyIdentifier::RELINQUISH_DEFAULT, None)
1323 .unwrap();
1324 assert_eq!(rd, PropertyValue::Signed(0));
1325 }
1326
1327 #[test]
1328 fn value_object_priority_array_direct_write() {
1329 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
1330
1331 obj.write_property(
1333 PropertyIdentifier::PRIORITY_ARRAY,
1334 Some(5),
1335 PropertyValue::Signed(77),
1336 None,
1337 )
1338 .unwrap();
1339
1340 let slot = obj
1342 .read_property(PropertyIdentifier::PRIORITY_ARRAY, Some(5))
1343 .unwrap();
1344 assert_eq!(slot, PropertyValue::Signed(77));
1345
1346 let pv = obj
1348 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1349 .unwrap();
1350 assert_eq!(pv, PropertyValue::Signed(77));
1351
1352 obj.write_property(
1354 PropertyIdentifier::PRIORITY_ARRAY,
1355 Some(5),
1356 PropertyValue::Null,
1357 None,
1358 )
1359 .unwrap();
1360
1361 let pv = obj
1363 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1364 .unwrap();
1365 assert_eq!(pv, PropertyValue::Signed(0));
1366 }
1367
1368 #[test]
1369 fn value_object_unknown_property() {
1370 let obj = IntegerValueObject::new(1, "IV-1").unwrap();
1371 let result = obj.read_property(PropertyIdentifier::UNITS, None);
1372 assert!(result.is_err());
1373 }
1374
1375 #[test]
1376 fn value_object_write_access_denied() {
1377 let mut obj = IntegerValueObject::new(1, "IV-1").unwrap();
1378 let result = obj.write_property(
1379 PropertyIdentifier::OBJECT_NAME,
1380 None,
1381 PropertyValue::CharacterString("new-name".into()),
1382 None,
1383 );
1384 assert!(result.is_err());
1385 }
1386}