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::property_metadata::{
15 PropertyConformance, PropertyMetadata, PropertyPresenceCondition, PropertyWriteCapability,
16};
17use crate::traits::BACnetObject;
18
19macro_rules! define_value_object_commandable {
36 (
37 name: $struct_name:ident,
38 doc: $doc:expr,
39 object_type: $obj_type:expr,
40 value_type: $val_type:ty,
41 default_value: $default:expr,
42 pv_to_property: $pv_to_prop:expr,
43 property_to_pv: $prop_to_pv:expr,
44 pa_wrap: $pa_wrap:expr,
45 rd_wrap: $rd_wrap:expr,
46 rd_validate: $rd_validate:expr,
47 rd_access: $rd_access:ident,
48 copy_type: $is_copy:tt
49 $(, property_metadata: $property_metadata:expr)?
50 $(,)?
51 ) => {
52 #[doc = $doc]
53 pub struct $struct_name {
54 oid: ObjectIdentifier,
55 name: String,
56 description: String,
57 present_value: $val_type,
58 out_of_service: bool,
59 status_flags: StatusFlags,
60 reliability: u32,
61 priority_array: [Option<$val_type>; 16],
63 relinquish_default: $val_type,
64 }
65
66 impl $struct_name {
67 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
69 let oid = ObjectIdentifier::new($obj_type, instance)?;
70 Ok(Self {
71 oid,
72 name: name.into(),
73 description: String::new(),
74 present_value: $default,
75 out_of_service: false,
76 status_flags: StatusFlags::empty(),
77 reliability: 0,
78 priority_array: Default::default(),
79 relinquish_default: $default,
80 })
81 }
82
83 fn recalculate_present_value(&mut self) {
85 define_value_object_commandable!(@recalc self, $is_copy);
86 }
87
88 pub fn set_relinquish_default(&mut self, value: $val_type) -> Result<(), Error> {
98 ($rd_validate)(&value)?;
99 self.relinquish_default = value;
100 self.recalculate_present_value();
101 Ok(())
102 }
103 }
104
105 impl BACnetObject for $struct_name {
106 fn object_identifier(&self) -> ObjectIdentifier {
107 self.oid
108 }
109
110 fn object_name(&self) -> &str {
111 &self.name
112 }
113
114 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
115 define_value_object_commandable!(@property_metadata $($property_metadata)?)
116 }
117
118 fn read_property(
119 &self,
120 property: PropertyIdentifier,
121 array_index: Option<u32>,
122 ) -> Result<PropertyValue, Error> {
123 if let Some(result) = read_common_properties!(self, property, array_index) {
124 return result;
125 }
126 match property {
127 p if p == PropertyIdentifier::OBJECT_TYPE => {
128 Ok(PropertyValue::Enumerated($obj_type.to_raw()))
129 }
130 p if p == PropertyIdentifier::PRESENT_VALUE => {
131 Ok(($pv_to_prop)(&self.present_value))
132 }
133 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
134 define_value_object_commandable!(@read_pa self, array_index, $pa_wrap, $is_copy)
135 }
136 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
137 Ok(($rd_wrap)(&self.relinquish_default))
138 }
139 _ => Err(common::unknown_property_error()),
140 }
141 }
142
143 fn write_property(
144 &mut self,
145 property: PropertyIdentifier,
146 array_index: Option<u32>,
147 value: PropertyValue,
148 priority: Option<u8>,
149 ) -> Result<(), Error> {
150 if property == PropertyIdentifier::PRIORITY_ARRAY {
155 let idx = match array_index {
156 Some(n) if (1..=16).contains(&n) => (n - 1) as usize,
157 Some(_) => return Err(common::invalid_array_index_error()),
158 None => return Err(common::write_access_denied_error()),
159 };
160 match value {
161 PropertyValue::Null => {
162 self.priority_array[idx] = None;
163 }
164 other => {
165 let extracted = ($prop_to_pv)(other)?;
166 self.priority_array[idx] = Some(extracted);
167 }
168 }
169 self.recalculate_present_value();
170 return Ok(());
171 }
172 if property == PropertyIdentifier::PRESENT_VALUE {
174 let prio = priority.unwrap_or(16);
175 if !(1..=16).contains(&prio) {
176 return Err(common::value_out_of_range_error());
177 }
178 let idx = (prio - 1) as usize;
179 match value {
180 PropertyValue::Null => {
181 self.priority_array[idx] = None;
182 }
183 other => {
184 let extracted = ($prop_to_pv)(other)?;
185 self.priority_array[idx] = Some(extracted);
186 }
187 }
188 self.recalculate_present_value();
189 return Ok(());
190 }
191 define_value_object_commandable!(@rd_write self, property, value, $prop_to_pv, $rd_access);
194 if let Some(result) =
195 common::write_out_of_service(&mut self.out_of_service, property, &value)
196 {
197 return result;
198 }
199 if let Some(result) =
200 common::write_object_name(&mut self.name, property, &value)
201 {
202 return result;
203 }
204 if let Some(result) =
205 common::write_description(&mut self.description, property, &value)
206 {
207 return result;
208 }
209 Err(common::write_access_denied_error())
210 }
211
212 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
213 static PROPS: &[PropertyIdentifier] = &[
214 PropertyIdentifier::OBJECT_IDENTIFIER,
215 PropertyIdentifier::OBJECT_NAME,
216 PropertyIdentifier::DESCRIPTION,
217 PropertyIdentifier::OBJECT_TYPE,
218 PropertyIdentifier::PRESENT_VALUE,
219 PropertyIdentifier::STATUS_FLAGS,
220 PropertyIdentifier::OUT_OF_SERVICE,
221 PropertyIdentifier::RELIABILITY,
222 PropertyIdentifier::PRIORITY_ARRAY,
223 PropertyIdentifier::RELINQUISH_DEFAULT,
224 ];
225 let metadata = self.property_metadata();
226 if metadata.is_empty() {
227 Cow::Borrowed(PROPS)
228 } else {
229 crate::property_metadata::property_list_from_metadata(metadata.as_ref())
230 }
231 }
232
233 fn supports_cov(&self) -> bool {
234 true
235 }
236
237 define_value_object_commandable!(@is_writable $rd_access, $($property_metadata)?);
238 }
239 };
240
241 (@property_metadata) => {
242 Cow::Borrowed(&[])
243 };
244 (@property_metadata $metadata:expr) => {
245 Cow::Borrowed($metadata)
246 };
247
248 (@rd_write $self:ident, $property:ident, $value:ident, $prop_to_pv:expr, writable) => {
251 if $property == PropertyIdentifier::RELINQUISH_DEFAULT {
252 let extracted = ($prop_to_pv)($value)?;
253 return $self.set_relinquish_default(extracted);
254 }
255 };
256 (@rd_write $self:ident, $property:ident, $value:ident, $prop_to_pv:expr, readonly) => {};
259
260 (@is_writable writable,) => {
263 fn is_writable_property(&self, property: PropertyIdentifier) -> bool {
264 common::is_common_writable(property)
265 || property == PropertyIdentifier::PRESENT_VALUE
266 || property == PropertyIdentifier::PRIORITY_ARRAY
267 || property == PropertyIdentifier::RELINQUISH_DEFAULT
268 }
269 };
270 (@is_writable readonly,) => {
271 fn is_writable_property(&self, property: PropertyIdentifier) -> bool {
272 common::is_common_writable(property)
273 || property == PropertyIdentifier::PRESENT_VALUE
274 || property == PropertyIdentifier::PRIORITY_ARRAY
275 }
276 };
277 (@is_writable $rd_access:ident, $metadata:expr) => {
278 fn is_writable_property(&self, property: PropertyIdentifier) -> bool {
279 crate::property_metadata::is_writable_in_metadata($metadata, property)
280 }
281 };
282
283 (@recalc $self:ident, copy) => {
287 $self.present_value = common::recalculate_from_priority_array(
288 &$self.priority_array,
289 $self.relinquish_default,
290 );
291 };
292 (@recalc $self:ident, clone) => {
294 $self.present_value = $self
295 .priority_array
296 .iter()
297 .find_map(|slot| slot.as_ref().cloned())
298 .unwrap_or_else(|| $self.relinquish_default.clone());
299 };
300
301 (@read_pa $self:ident, $array_index:ident, $wrap:expr, copy) => {{
303 common::read_priority_array!($self, $array_index, $wrap)
304 }};
305 (@read_pa $self:ident, $array_index:ident, $wrap:expr, clone) => {{
307 let wrap_fn = $wrap;
308 match $array_index {
309 None => {
310 let elements = $self
311 .priority_array
312 .iter()
313 .map(|slot| match slot {
314 Some(v) => wrap_fn(v),
315 None => PropertyValue::Null,
316 })
317 .collect();
318 Ok(PropertyValue::List(elements))
319 }
320 Some(0) => Ok(PropertyValue::Unsigned(16)),
321 Some(idx) if (1..=16).contains(&idx) => {
322 match &$self.priority_array[(idx - 1) as usize] {
323 Some(v) => Ok(wrap_fn(v)),
324 None => Ok(PropertyValue::Null),
325 }
326 }
327 _ => Err(common::invalid_array_index_error()),
328 }
329 }};
330}
331
332#[allow(unused_macros)]
339macro_rules! define_value_object_simple {
340 (
341 name: $struct_name:ident,
342 doc: $doc:expr,
343 object_type: $obj_type:expr,
344 value_type: $val_type:ty,
345 default_value: $default:expr,
346 pv_to_property: $pv_to_prop:expr,
347 property_to_pv: $prop_to_pv:expr
348 $(,)?
349 ) => {
350 #[doc = $doc]
351 pub struct $struct_name {
352 oid: ObjectIdentifier,
353 name: String,
354 description: String,
355 present_value: $val_type,
356 out_of_service: bool,
357 status_flags: StatusFlags,
358 reliability: u32,
359 }
360
361 impl $struct_name {
362 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
364 let oid = ObjectIdentifier::new($obj_type, instance)?;
365 Ok(Self {
366 oid,
367 name: name.into(),
368 description: String::new(),
369 present_value: $default,
370 out_of_service: false,
371 status_flags: StatusFlags::empty(),
372 reliability: 0,
373 })
374 }
375 }
376
377 impl BACnetObject for $struct_name {
378 fn object_identifier(&self) -> ObjectIdentifier {
379 self.oid
380 }
381
382 fn object_name(&self) -> &str {
383 &self.name
384 }
385
386 fn read_property(
387 &self,
388 property: PropertyIdentifier,
389 array_index: Option<u32>,
390 ) -> Result<PropertyValue, Error> {
391 if let Some(result) = read_common_properties!(self, property, array_index) {
392 return result;
393 }
394 match property {
395 p if p == PropertyIdentifier::OBJECT_TYPE => {
396 Ok(PropertyValue::Enumerated($obj_type.to_raw()))
397 }
398 p if p == PropertyIdentifier::PRESENT_VALUE => {
399 Ok(($pv_to_prop)(&self.present_value))
400 }
401 _ => Err(common::unknown_property_error()),
402 }
403 }
404
405 fn write_property(
406 &mut self,
407 property: PropertyIdentifier,
408 _array_index: Option<u32>,
409 value: PropertyValue,
410 _priority: Option<u8>,
411 ) -> Result<(), Error> {
412 if property == PropertyIdentifier::PRESENT_VALUE {
413 let extracted = ($prop_to_pv)(value)?;
414 self.present_value = extracted;
415 return Ok(());
416 }
417 if let Some(result) =
418 common::write_out_of_service(&mut self.out_of_service, property, &value)
419 {
420 return result;
421 }
422 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
423 return result;
424 }
425 if let Some(result) =
426 common::write_description(&mut self.description, property, &value)
427 {
428 return result;
429 }
430 Err(common::write_access_denied_error())
431 }
432
433 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
434 static PROPS: &[PropertyIdentifier] = &[
435 PropertyIdentifier::OBJECT_IDENTIFIER,
436 PropertyIdentifier::OBJECT_NAME,
437 PropertyIdentifier::DESCRIPTION,
438 PropertyIdentifier::OBJECT_TYPE,
439 PropertyIdentifier::PRESENT_VALUE,
440 PropertyIdentifier::STATUS_FLAGS,
441 PropertyIdentifier::OUT_OF_SERVICE,
442 PropertyIdentifier::RELIABILITY,
443 ];
444 Cow::Borrowed(PROPS)
445 }
446
447 fn supports_cov(&self) -> bool {
448 true
449 }
450 }
451 };
452}
453
454fn clone_string_to_pv(v: &str) -> PropertyValue {
459 PropertyValue::CharacterString(v.to_owned())
460}
461
462fn clone_octetstring_to_pv(v: &[u8]) -> PropertyValue {
463 PropertyValue::OctetString(v.to_owned())
464}
465
466fn clone_bitstring_to_pv(v: &(u8, Vec<u8>)) -> PropertyValue {
467 PropertyValue::BitString {
468 unused_bits: v.0,
469 data: v.1.clone(),
470 }
471}
472
473fn datetime_copy_to_pv(v: (Date, Time)) -> PropertyValue {
474 PropertyValue::List(vec![PropertyValue::Date(v.0), PropertyValue::Time(v.1)])
475}
476
477fn datetime_to_pv(dt: &(Date, Time)) -> PropertyValue {
478 PropertyValue::List(vec![PropertyValue::Date(dt.0), PropertyValue::Time(dt.1)])
479}
480
481fn pv_to_date(v: PropertyValue) -> Result<Date, Error> {
482 if let PropertyValue::Date(d) = v {
483 Ok(d)
484 } else {
485 Err(common::invalid_data_type_error())
486 }
487}
488
489fn pv_to_time(v: PropertyValue) -> Result<Time, Error> {
490 if let PropertyValue::Time(t) = v {
491 Ok(t)
492 } else {
493 Err(common::invalid_data_type_error())
494 }
495}
496
497fn pv_to_datetime(v: PropertyValue) -> Result<(Date, Time), Error> {
498 if let PropertyValue::List(items) = v {
499 if items.len() == 2 {
500 let d = if let PropertyValue::Date(d) = &items[0] {
501 *d
502 } else {
503 return Err(common::invalid_data_type_error());
504 };
505 let t = if let PropertyValue::Time(t) = &items[1] {
506 *t
507 } else {
508 return Err(common::invalid_data_type_error());
509 };
510 Ok((d, t))
511 } else {
512 Err(common::invalid_data_type_error())
513 }
514 } else {
515 Err(common::invalid_data_type_error())
516 }
517}
518
519define_value_object_commandable! {
524 name: IntegerValueObject,
525 doc: "BACnet Integer Value object (type 45).",
526 object_type: ObjectType::INTEGER_VALUE,
527 value_type: i32,
528 default_value: 0,
529 pv_to_property: (|v: &i32| PropertyValue::Signed(*v)),
530 property_to_pv: (|v: PropertyValue| -> Result<i32, Error> {
531 if let PropertyValue::Signed(n) = v { Ok(n) }
532 else { Err(common::invalid_data_type_error()) }
533 }),
534 pa_wrap: PropertyValue::Signed,
535 rd_wrap: (|v: &i32| PropertyValue::Signed(*v)),
536 rd_validate: (|_: &i32| -> Result<(), Error> { Ok(()) }),
537 rd_access: writable,
538 copy_type: copy,
539}
540
541define_value_object_commandable! {
542 name: PositiveIntegerValueObject,
543 doc: "BACnet Positive Integer Value object (type 48).",
544 object_type: ObjectType::POSITIVE_INTEGER_VALUE,
545 value_type: u64,
546 default_value: 0,
547 pv_to_property: (|v: &u64| PropertyValue::Unsigned(*v)),
548 property_to_pv: (|v: PropertyValue| -> Result<u64, Error> {
549 if let PropertyValue::Unsigned(n) = v { Ok(n) }
550 else { Err(common::invalid_data_type_error()) }
551 }),
552 pa_wrap: PropertyValue::Unsigned,
553 rd_wrap: (|v: &u64| PropertyValue::Unsigned(*v)),
554 rd_validate: (|_: &u64| -> Result<(), Error> { Ok(()) }),
555 rd_access: writable,
556 copy_type: copy,
557}
558
559define_value_object_commandable! {
560 name: LargeAnalogValueObject,
561 doc: "BACnet Large Analog Value object (type 46).",
562 object_type: ObjectType::LARGE_ANALOG_VALUE,
563 value_type: f64,
564 default_value: 0.0,
565 pv_to_property: (|v: &f64| PropertyValue::Double(*v)),
566 property_to_pv: (|v: PropertyValue| -> Result<f64, Error> {
567 if let PropertyValue::Double(n) = v { Ok(n) }
568 else { Err(common::invalid_data_type_error()) }
569 }),
570 pa_wrap: PropertyValue::Double,
571 rd_wrap: (|v: &f64| PropertyValue::Double(*v)),
572 rd_validate: (|v: &f64| -> Result<(), Error> {
573 if v.is_finite() {
574 Ok(())
575 } else {
576 Err(common::value_out_of_range_error())
577 }
578 }),
579 rd_access: writable,
580 copy_type: copy,
581}
582
583define_value_object_commandable! {
584 name: CharacterStringValueObject,
585 doc: "BACnet CharacterString Value object (type 40).",
586 object_type: ObjectType::CHARACTERSTRING_VALUE,
587 value_type: String,
588 default_value: String::new(),
589 pv_to_property: (|v: &String| PropertyValue::CharacterString(v.clone())),
590 property_to_pv: (|v: PropertyValue| -> Result<String, Error> {
591 if let PropertyValue::CharacterString(s) = v { Ok(s) }
592 else { Err(common::invalid_data_type_error()) }
593 }),
594 pa_wrap: clone_string_to_pv,
595 rd_wrap: (|v: &String| PropertyValue::CharacterString(v.clone())),
596 rd_validate: (|_: &String| -> Result<(), Error> { Ok(()) }),
597 rd_access: writable,
598 copy_type: clone,
599}
600
601define_value_object_commandable! {
602 name: OctetStringValueObject,
603 doc: "BACnet OctetString Value object (type 47).",
604 object_type: ObjectType::OCTETSTRING_VALUE,
605 value_type: Vec<u8>,
606 default_value: Vec::new(),
607 pv_to_property: (|v: &Vec<u8>| PropertyValue::OctetString(v.clone())),
608 property_to_pv: (|v: PropertyValue| -> Result<Vec<u8>, Error> {
609 if let PropertyValue::OctetString(b) = v { Ok(b) }
610 else { Err(common::invalid_data_type_error()) }
611 }),
612 pa_wrap: clone_octetstring_to_pv,
613 rd_wrap: (|v: &Vec<u8>| PropertyValue::OctetString(v.clone())),
614 rd_validate: (|_: &Vec<u8>| -> Result<(), Error> { Ok(()) }),
615 rd_access: writable,
616 copy_type: clone,
617}
618
619define_value_object_commandable! {
620 name: BitStringValueObject,
621 doc: "BACnet BitString Value object (type 39).",
622 object_type: ObjectType::BITSTRING_VALUE,
623 value_type: (u8, Vec<u8>),
624 default_value: (0, Vec::new()),
625 pv_to_property: (|v: &(u8, Vec<u8>)| PropertyValue::BitString {
626 unused_bits: v.0,
627 data: v.1.clone(),
628 }),
629 property_to_pv: (|v: PropertyValue| -> Result<(u8, Vec<u8>), Error> {
630 if let PropertyValue::BitString { unused_bits, data } = v { Ok((unused_bits, data)) }
631 else { Err(common::invalid_data_type_error()) }
632 }),
633 pa_wrap: clone_bitstring_to_pv,
634 rd_wrap: (|v: &(u8, Vec<u8>)| PropertyValue::BitString {
635 unused_bits: v.0,
636 data: v.1.clone(),
637 }),
638 rd_validate: (|_: &(u8, Vec<u8>)| -> Result<(), Error> { Ok(()) }),
639 rd_access: writable,
640 copy_type: clone,
641}
642
643define_value_object_commandable! {
644 name: DateValueObject,
645 doc: "BACnet Date Value object (type 42).",
646 object_type: ObjectType::DATE_VALUE,
647 value_type: Date,
648 default_value: Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
649 pv_to_property: (|v: &Date| PropertyValue::Date(*v)),
650 property_to_pv: pv_to_date,
651 pa_wrap: PropertyValue::Date,
652 rd_wrap: (|v: &Date| PropertyValue::Date(*v)),
653 rd_validate: (|_: &Date| -> Result<(), Error> { Ok(()) }),
654 rd_access: writable,
655 copy_type: copy,
656}
657
658const TIME_VALUE_PROPERTY_METADATA: &[PropertyMetadata] = &[
659 PropertyMetadata::new(
660 PropertyIdentifier::OBJECT_IDENTIFIER,
661 PropertyConformance::RequiredRead,
662 None,
663 PropertyWriteCapability::ReadOnly,
664 ),
665 PropertyMetadata::new(
666 PropertyIdentifier::OBJECT_NAME,
667 PropertyConformance::RequiredRead,
668 None,
669 PropertyWriteCapability::Always,
670 ),
671 PropertyMetadata::new(
672 PropertyIdentifier::DESCRIPTION,
673 PropertyConformance::Optional,
674 None,
675 PropertyWriteCapability::Always,
676 ),
677 PropertyMetadata::new(
678 PropertyIdentifier::OBJECT_TYPE,
679 PropertyConformance::RequiredRead,
680 None,
681 PropertyWriteCapability::ReadOnly,
682 ),
683 PropertyMetadata::new(
684 PropertyIdentifier::PRESENT_VALUE,
685 PropertyConformance::RequiredRead,
686 None,
687 PropertyWriteCapability::Always,
688 ),
689 PropertyMetadata::new(
690 PropertyIdentifier::STATUS_FLAGS,
691 PropertyConformance::RequiredRead,
692 None,
693 PropertyWriteCapability::ReadOnly,
694 ),
695 PropertyMetadata::new(
696 PropertyIdentifier::OUT_OF_SERVICE,
697 PropertyConformance::Optional,
698 None,
699 PropertyWriteCapability::Always,
700 ),
701 PropertyMetadata::new(
702 PropertyIdentifier::RELIABILITY,
703 PropertyConformance::Optional,
704 None,
705 PropertyWriteCapability::ReadOnly,
706 ),
707 PropertyMetadata::new(
708 PropertyIdentifier::PRIORITY_ARRAY,
709 PropertyConformance::Optional,
710 Some(PropertyPresenceCondition::Commandable),
711 PropertyWriteCapability::Always,
712 ),
713 PropertyMetadata::new(
714 PropertyIdentifier::RELINQUISH_DEFAULT,
715 PropertyConformance::Optional,
716 Some(PropertyPresenceCondition::Commandable),
717 PropertyWriteCapability::Always,
718 ),
719 PropertyMetadata::new(
720 PropertyIdentifier::PROPERTY_LIST,
721 PropertyConformance::RequiredRead,
722 None,
723 PropertyWriteCapability::ReadOnly,
724 ),
725];
726
727define_value_object_commandable! {
728 name: TimeValueObject,
729 doc: "BACnet Time Value object (type 50).",
730 object_type: ObjectType::TIME_VALUE,
731 value_type: Time,
732 default_value: Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
733 pv_to_property: (|v: &Time| PropertyValue::Time(*v)),
734 property_to_pv: pv_to_time,
735 pa_wrap: PropertyValue::Time,
736 rd_wrap: (|v: &Time| PropertyValue::Time(*v)),
737 rd_validate: (|_: &Time| -> Result<(), Error> { Ok(()) }),
738 rd_access: writable,
739 copy_type: copy,
740 property_metadata: TIME_VALUE_PROPERTY_METADATA,
741}
742
743define_value_object_commandable! {
744 name: DateTimeValueObject,
745 doc: "BACnet DateTime Value object (type 44).",
746 object_type: ObjectType::DATETIME_VALUE,
747 value_type: (Date, Time),
748 default_value: (
749 Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
750 Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
751 ),
752 pv_to_property: (|v: &(Date, Time)| datetime_to_pv(v)),
753 property_to_pv: pv_to_datetime,
754 pa_wrap: datetime_copy_to_pv,
755 rd_wrap: (|v: &(Date, Time)| datetime_to_pv(v)),
756 rd_validate: (|_: &(Date, Time)| -> Result<(), Error> { Ok(()) }),
757 rd_access: writable,
758 copy_type: copy,
759}
760
761define_value_object_commandable! {
766 name: DatePatternValueObject,
767 doc: "BACnet Date Pattern Value object (type 41).",
768 object_type: ObjectType::DATEPATTERN_VALUE,
769 value_type: Date,
770 default_value: Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
771 pv_to_property: (|v: &Date| PropertyValue::Date(*v)),
772 property_to_pv: pv_to_date,
773 pa_wrap: PropertyValue::Date,
774 rd_wrap: (|v: &Date| PropertyValue::Date(*v)),
775 rd_validate: (|_: &Date| -> Result<(), Error> { Ok(()) }),
776 rd_access: writable,
777 copy_type: copy,
778}
779
780define_value_object_commandable! {
781 name: TimePatternValueObject,
782 doc: "BACnet Time Pattern Value object (type 49).",
783 object_type: ObjectType::TIMEPATTERN_VALUE,
784 value_type: Time,
785 default_value: Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
786 pv_to_property: (|v: &Time| PropertyValue::Time(*v)),
787 property_to_pv: pv_to_time,
788 pa_wrap: PropertyValue::Time,
789 rd_wrap: (|v: &Time| PropertyValue::Time(*v)),
790 rd_validate: (|_: &Time| -> Result<(), Error> { Ok(()) }),
791 rd_access: writable,
792 copy_type: copy,
793}
794
795define_value_object_commandable! {
796 name: DateTimePatternValueObject,
797 doc: "BACnet DateTime Pattern Value object (type 43).",
798 object_type: ObjectType::DATETIMEPATTERN_VALUE,
799 value_type: (Date, Time),
800 default_value: (
801 Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
802 Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
803 ),
804 pv_to_property: (|v: &(Date, Time)| datetime_to_pv(v)),
805 property_to_pv: pv_to_datetime,
806 pa_wrap: datetime_copy_to_pv,
807 rd_wrap: (|v: &(Date, Time)| datetime_to_pv(v)),
808 rd_validate: (|_: &(Date, Time)| -> Result<(), Error> { Ok(()) }),
809 rd_access: writable,
810 copy_type: copy,
811}
812
813#[cfg(test)]
818mod tests;