1use crate::prelude::*;
9use alloc::borrow::Cow;
10use bacnet_types::enums::{EngineeringUnits, ObjectType, PropertyIdentifier, Reliability};
11use bacnet_types::error::Error;
12use bacnet_types::primitives::{Date, ObjectIdentifier, PropertyValue, StatusFlags, Time};
13
14use crate::common::{self, read_common_properties};
15use crate::property_metadata::{
16 PropertyConformance, PropertyMetadata, PropertyPresenceCondition, PropertyWriteCapability,
17};
18use crate::traits::BACnetObject;
19use cov_increment::CovIncrement;
20
21mod cov_increment;
22mod metadata;
23
24macro_rules! define_value_object_commandable {
53 (
54 name: $struct_name:ident,
55 doc: $doc:expr_2021,
56 object_type: $obj_type:expr_2021,
57 value_type: $val_type:ty,
58 default_value: $default:expr_2021,
59 pv_to_property: $pv_to_prop:expr_2021,
60 property_to_pv: $prop_to_pv:expr_2021,
61 pa_wrap: $pa_wrap:expr_2021,
62 rd_wrap: $rd_wrap:expr_2021,
63 rd_validate: $rd_validate:expr_2021,
64 copy_type: $is_copy:tt,
65 $(units: $units_ty:ty,)?
66 $(cov_increment: $cov_ty:ty,)?
67 property_metadata: $property_metadata:expr_2021
68 $(,)?
69 ) => {
70 #[doc = $doc]
71 pub struct $struct_name {
72 oid: ObjectIdentifier,
73 name: String,
74 description: String,
75 present_value: $val_type,
76 out_of_service: bool,
77 status_flags: StatusFlags,
78 reliability: Reliability,
79 priority_array: [Option<$val_type>; 16],
81 relinquish_default: $val_type,
82 $(units: $units_ty,)?
83 $(
84 cov_increment: $cov_ty,
86 )?
87 }
88
89 impl $struct_name {
90 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
92 let oid = ObjectIdentifier::new($obj_type, instance)?;
93 Ok(Self {
94 oid,
95 name: name.into(),
96 description: String::new(),
97 present_value: $default,
98 out_of_service: false,
99 status_flags: StatusFlags::empty(),
100 reliability: Reliability::NO_FAULT_DETECTED,
101 priority_array: Default::default(),
102 relinquish_default: $default,
103 $(units: <$units_ty>::NO_UNITS,)?
104 $(cov_increment: <$cov_ty as CovIncrement>::ZERO,)?
105 })
106 }
107
108 fn recalculate_present_value(&mut self) {
110 define_value_object_commandable!(@recalc self, $is_copy);
111 }
112
113 pub fn set_relinquish_default(&mut self, value: $val_type) -> Result<(), Error> {
123 ($rd_validate)(&value)?;
124 self.relinquish_default = value;
125 self.recalculate_present_value();
126 Ok(())
127 }
128
129 $(
130 pub fn units(&self) -> $units_ty {
132 self.units
133 }
134
135 pub fn set_units(&mut self, units: $units_ty) -> Result<(), Error> {
142 if units.to_raw() > 65_535 {
143 return Err(common::value_out_of_range_error());
144 }
145 self.units = units;
146 Ok(())
147 }
148 )?
149
150 $(
151 pub fn set_cov_increment(&mut self, increment: $cov_ty) -> Result<(), Error> {
160 self.cov_increment = <$cov_ty as CovIncrement>::validate(increment)?;
161 Ok(())
162 }
163 )?
164 }
165
166 impl BACnetObject for $struct_name {
167 fn object_identifier(&self) -> ObjectIdentifier {
168 self.oid
169 }
170
171 fn object_name(&self) -> &str {
172 &self.name
173 }
174
175 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
176 Cow::Borrowed($property_metadata)
177 }
178
179 fn read_property(
180 &self,
181 property: PropertyIdentifier,
182 array_index: Option<u32>,
183 ) -> Result<PropertyValue, Error> {
184 if let Some(result) = read_common_properties!(self, property, array_index) {
185 return result;
186 }
187 match property {
188 p if p == PropertyIdentifier::OBJECT_TYPE => {
189 Ok(PropertyValue::Enumerated($obj_type.to_raw()))
190 }
191 p if p == PropertyIdentifier::PRESENT_VALUE => {
192 Ok(($pv_to_prop)(&self.present_value))
193 }
194 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
195 define_value_object_commandable!(@read_pa self, array_index, $pa_wrap, $is_copy)
196 }
197 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
198 Ok(($rd_wrap)(&self.relinquish_default))
199 }
200 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
201 Ok(common::current_command_priority(&self.priority_array))
202 }
203 $(
204 p if p == PropertyIdentifier::UNITS => {
205 Ok(PropertyValue::Enumerated(<$units_ty>::to_raw(self.units)))
206 }
207 )?
208 $(
209 p if p == PropertyIdentifier::COV_INCREMENT => {
210 Ok(<$cov_ty as CovIncrement>::to_property(self.cov_increment))
211 }
212 )?
213 _ => Err(common::unknown_property_error()),
214 }
215 }
216
217 fn write_property(
218 &mut self,
219 property: PropertyIdentifier,
220 _array_index: Option<u32>,
221 value: PropertyValue,
222 priority: Option<u8>,
223 ) -> Result<(), Error> {
224 if property == PropertyIdentifier::PRESENT_VALUE {
226 let prio = priority.unwrap_or(16);
227 if !(1..=16).contains(&prio) {
228 return Err(common::value_out_of_range_error());
229 }
230 let idx = (prio - 1) as usize;
231 match value {
232 PropertyValue::Null => {
233 self.priority_array[idx] = None;
234 }
235 other => {
236 let extracted = ($prop_to_pv)(other)?;
237 self.priority_array[idx] = Some(extracted);
238 }
239 }
240 self.recalculate_present_value();
241 return Ok(());
242 }
243 if property == PropertyIdentifier::RELINQUISH_DEFAULT {
245 let extracted = ($prop_to_pv)(value)?;
246 return self.set_relinquish_default(extracted);
247 }
248 $(
249 if property == PropertyIdentifier::COV_INCREMENT {
250 let increment = <$cov_ty as CovIncrement>::from_property(value)?;
251 return self.set_cov_increment(increment);
252 }
253 )?
254 if let Some(result) =
255 common::write_out_of_service(&mut self.out_of_service, property, &value)
256 {
257 return result;
258 }
259 if let Some(result) =
260 common::write_object_name(&mut self.name, property, &value)
261 {
262 return result;
263 }
264 if let Some(result) =
265 common::write_description(&mut self.description, property, &value)
266 {
267 return result;
268 }
269 Err(crate::common::unhandled_write_error(self.property_metadata().as_ref(), property, _array_index))
270 }
271
272 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
273 crate::property_metadata::property_list_from_metadata($property_metadata)
274 }
275
276 fn supports_cov(&self) -> bool {
277 true
278 }
279
280 $(
281 fn cov_increment(&self) -> Option<f64> {
282 Some(<$cov_ty as CovIncrement>::as_f64(self.cov_increment))
283 }
284 )?
285
286 fn is_writable_property(&self, property: PropertyIdentifier) -> bool {
287 crate::property_metadata::is_writable_in_metadata($property_metadata, property)
288 }
289 }
290 };
291
292 (@recalc $self:ident, copy) => {
296 $self.present_value = common::recalculate_from_priority_array(
297 &$self.priority_array,
298 $self.relinquish_default,
299 );
300 };
301 (@recalc $self:ident, clone) => {
303 $self.present_value = $self
304 .priority_array
305 .iter()
306 .find_map(|slot| slot.as_ref().cloned())
307 .unwrap_or_else(|| $self.relinquish_default.clone());
308 };
309
310 (@read_pa $self:ident, $array_index:ident, $wrap:expr_2021, copy) => {{
312 common::read_priority_array!($self, $array_index, $wrap)
313 }};
314 (@read_pa $self:ident, $array_index:ident, $wrap:expr_2021, clone) => {{
316 let wrap_fn = $wrap;
317 match $array_index {
318 None => {
319 let elements = $self
320 .priority_array
321 .iter()
322 .map(|slot| match slot {
323 Some(v) => wrap_fn(v),
324 None => PropertyValue::Null,
325 })
326 .collect();
327 Ok(PropertyValue::List(elements))
328 }
329 Some(0) => Ok(PropertyValue::Unsigned(16)),
330 Some(idx) if (1..=16).contains(&idx) => {
331 match &$self.priority_array[(idx - 1) as usize] {
332 Some(v) => Ok(wrap_fn(v)),
333 None => Ok(PropertyValue::Null),
334 }
335 }
336 _ => Err(common::invalid_array_index_error()),
337 }
338 }};
339}
340
341fn clone_string_to_pv(v: &str) -> PropertyValue {
346 PropertyValue::CharacterString(v.to_owned())
347}
348
349fn clone_octetstring_to_pv(v: &[u8]) -> PropertyValue {
350 PropertyValue::OctetString(v.to_owned())
351}
352
353fn clone_bitstring_to_pv(v: &(u8, Vec<u8>)) -> PropertyValue {
354 PropertyValue::BitString {
355 unused_bits: v.0,
356 data: v.1.clone(),
357 }
358}
359
360fn datetime_priority_value(v: (Date, Time)) -> PropertyValue {
365 let mut buf = bytes::BytesMut::with_capacity(12);
366 bacnet_encoding::primitives::encode_priority_date_time(&mut buf, &v.0, &v.1);
367 PropertyValue::ApplicationData(buf.to_vec())
368}
369
370fn datetime_to_pv(dt: &(Date, Time)) -> PropertyValue {
371 PropertyValue::List(vec![PropertyValue::Date(dt.0), PropertyValue::Time(dt.1)])
372}
373
374fn pv_to_date(v: PropertyValue) -> Result<Date, Error> {
375 if let PropertyValue::Date(d) = v {
376 Ok(d)
377 } else {
378 Err(common::invalid_data_type_error())
379 }
380}
381
382fn pv_to_time(v: PropertyValue) -> Result<Time, Error> {
383 if let PropertyValue::Time(t) = v {
384 Ok(t)
385 } else {
386 Err(common::invalid_data_type_error())
387 }
388}
389
390fn pv_to_datetime(v: PropertyValue) -> Result<(Date, Time), Error> {
391 if let PropertyValue::List(items) = v {
392 if items.len() == 2 {
393 let d = if let PropertyValue::Date(d) = &items[0] {
394 *d
395 } else {
396 return Err(common::invalid_data_type_error());
397 };
398 let t = if let PropertyValue::Time(t) = &items[1] {
399 *t
400 } else {
401 return Err(common::invalid_data_type_error());
402 };
403 Ok((d, t))
404 } else {
405 Err(common::invalid_data_type_error())
406 }
407 } else {
408 Err(common::invalid_data_type_error())
409 }
410}
411
412define_value_object_commandable! {
417 name: IntegerValueObject,
418 doc: "BACnet Integer Value object (type 45).",
419 object_type: ObjectType::INTEGER_VALUE,
420 value_type: i32,
421 default_value: 0,
422 pv_to_property: (|v: &i32| PropertyValue::Signed(*v)),
423 property_to_pv: (|v: PropertyValue| -> Result<i32, Error> {
424 if let PropertyValue::Signed(n) = v { Ok(n) }
425 else { Err(common::invalid_data_type_error()) }
426 }),
427 pa_wrap: PropertyValue::Signed,
428 rd_wrap: (|v: &i32| PropertyValue::Signed(*v)),
429 rd_validate: (|_: &i32| -> Result<(), Error> { Ok(()) }),
430 copy_type: copy,
431 units: EngineeringUnits,
432 cov_increment: u64,
433 property_metadata: metadata::INTEGER_VALUE_BASE,
434}
435
436define_value_object_commandable! {
437 name: PositiveIntegerValueObject,
438 doc: "BACnet Positive Integer Value object (type 48).",
439 object_type: ObjectType::POSITIVE_INTEGER_VALUE,
440 value_type: u64,
441 default_value: 0,
442 pv_to_property: (|v: &u64| PropertyValue::Unsigned(*v)),
443 property_to_pv: (|v: PropertyValue| -> Result<u64, Error> {
444 if let PropertyValue::Unsigned(n) = v { Ok(n) }
445 else { Err(common::invalid_data_type_error()) }
446 }),
447 pa_wrap: PropertyValue::Unsigned,
448 rd_wrap: (|v: &u64| PropertyValue::Unsigned(*v)),
449 rd_validate: (|_: &u64| -> Result<(), Error> { Ok(()) }),
450 copy_type: copy,
451 units: EngineeringUnits,
452 cov_increment: u64,
453 property_metadata: metadata::POSITIVE_INTEGER_VALUE_BASE,
454}
455
456define_value_object_commandable! {
457 name: LargeAnalogValueObject,
458 doc: "BACnet Large Analog Value object (type 46).",
459 object_type: ObjectType::LARGE_ANALOG_VALUE,
460 value_type: f64,
461 default_value: 0.0,
462 pv_to_property: (|v: &f64| PropertyValue::Double(*v)),
463 property_to_pv: (|v: PropertyValue| -> Result<f64, Error> {
464 if let PropertyValue::Double(n) = v { Ok(n) }
465 else { Err(common::invalid_data_type_error()) }
466 }),
467 pa_wrap: PropertyValue::Double,
468 rd_wrap: (|v: &f64| PropertyValue::Double(*v)),
469 rd_validate: (|v: &f64| -> Result<(), Error> {
470 if v.is_finite() {
471 Ok(())
472 } else {
473 Err(common::value_out_of_range_error())
474 }
475 }),
476 copy_type: copy,
477 units: EngineeringUnits,
478 cov_increment: f64,
479 property_metadata: metadata::LARGE_ANALOG_VALUE_BASE,
480}
481
482define_value_object_commandable! {
483 name: CharacterStringValueObject,
484 doc: "BACnet CharacterString Value object (type 40).",
485 object_type: ObjectType::CHARACTERSTRING_VALUE,
486 value_type: String,
487 default_value: String::new(),
488 pv_to_property: (|v: &String| PropertyValue::CharacterString(v.clone())),
489 property_to_pv: (|v: PropertyValue| -> Result<String, Error> {
490 if let PropertyValue::CharacterString(s) = v { Ok(s) }
491 else { Err(common::invalid_data_type_error()) }
492 }),
493 pa_wrap: clone_string_to_pv,
494 rd_wrap: (|v: &String| PropertyValue::CharacterString(v.clone())),
495 rd_validate: (|_: &String| -> Result<(), Error> { Ok(()) }),
496 copy_type: clone,
497 property_metadata: metadata::CHARACTERSTRING_VALUE_BASE,
498}
499
500define_value_object_commandable! {
501 name: OctetStringValueObject,
502 doc: "BACnet OctetString Value object (type 47).",
503 object_type: ObjectType::OCTETSTRING_VALUE,
504 value_type: Vec<u8>,
505 default_value: Vec::new(),
506 pv_to_property: (|v: &Vec<u8>| PropertyValue::OctetString(v.clone())),
507 property_to_pv: (|v: PropertyValue| -> Result<Vec<u8>, Error> {
508 if let PropertyValue::OctetString(b) = v { Ok(b) }
509 else { Err(common::invalid_data_type_error()) }
510 }),
511 pa_wrap: clone_octetstring_to_pv,
512 rd_wrap: (|v: &Vec<u8>| PropertyValue::OctetString(v.clone())),
513 rd_validate: (|_: &Vec<u8>| -> Result<(), Error> { Ok(()) }),
514 copy_type: clone,
515 property_metadata: metadata::OCTETSTRING_VALUE_BASE,
516}
517
518define_value_object_commandable! {
519 name: BitStringValueObject,
520 doc: "BACnet BitString Value object (type 39).",
521 object_type: ObjectType::BITSTRING_VALUE,
522 value_type: (u8, Vec<u8>),
523 default_value: (0, Vec::new()),
524 pv_to_property: (|v: &(u8, Vec<u8>)| PropertyValue::BitString {
525 unused_bits: v.0,
526 data: v.1.clone(),
527 }),
528 property_to_pv: (|v: PropertyValue| -> Result<(u8, Vec<u8>), Error> {
529 if let PropertyValue::BitString { unused_bits, data } = v { Ok((unused_bits, data)) }
530 else { Err(common::invalid_data_type_error()) }
531 }),
532 pa_wrap: clone_bitstring_to_pv,
533 rd_wrap: (|v: &(u8, Vec<u8>)| PropertyValue::BitString {
534 unused_bits: v.0,
535 data: v.1.clone(),
536 }),
537 rd_validate: (|_: &(u8, Vec<u8>)| -> Result<(), Error> { Ok(()) }),
538 copy_type: clone,
539 property_metadata: metadata::BITSTRING_VALUE_BASE,
540}
541
542define_value_object_commandable! {
543 name: DateValueObject,
544 doc: "BACnet Date Value object (type 42).",
545 object_type: ObjectType::DATE_VALUE,
546 value_type: Date,
547 default_value: Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
548 pv_to_property: (|v: &Date| PropertyValue::Date(*v)),
549 property_to_pv: pv_to_date,
550 pa_wrap: PropertyValue::Date,
551 rd_wrap: (|v: &Date| PropertyValue::Date(*v)),
552 rd_validate: (|_: &Date| -> Result<(), Error> { Ok(()) }),
553 copy_type: copy,
554 property_metadata: metadata::DATE_VALUE_BASE,
555}
556
557const TIME_VALUE_PROPERTY_METADATA: &[PropertyMetadata] = &[
558 PropertyMetadata::new(
559 PropertyIdentifier::OBJECT_IDENTIFIER,
560 PropertyConformance::RequiredRead,
561 None,
562 PropertyWriteCapability::ReadOnly,
563 ),
564 PropertyMetadata::new(
565 PropertyIdentifier::OBJECT_NAME,
566 PropertyConformance::RequiredRead,
567 None,
568 PropertyWriteCapability::Always,
569 ),
570 PropertyMetadata::new(
571 PropertyIdentifier::DESCRIPTION,
572 PropertyConformance::Optional,
573 None,
574 PropertyWriteCapability::Always,
575 ),
576 PropertyMetadata::new(
577 PropertyIdentifier::OBJECT_TYPE,
578 PropertyConformance::RequiredRead,
579 None,
580 PropertyWriteCapability::ReadOnly,
581 ),
582 PropertyMetadata::new(
583 PropertyIdentifier::PRESENT_VALUE,
584 PropertyConformance::RequiredRead,
585 None,
586 PropertyWriteCapability::Always,
587 ),
588 PropertyMetadata::new(
589 PropertyIdentifier::STATUS_FLAGS,
590 PropertyConformance::RequiredRead,
591 None,
592 PropertyWriteCapability::ReadOnly,
593 ),
594 PropertyMetadata::new(
595 PropertyIdentifier::OUT_OF_SERVICE,
596 PropertyConformance::Optional,
597 None,
598 PropertyWriteCapability::Always,
599 ),
600 PropertyMetadata::new(
601 PropertyIdentifier::RELIABILITY,
602 PropertyConformance::Optional,
603 None,
604 PropertyWriteCapability::ReadOnly,
605 ),
606 PropertyMetadata::new(
607 PropertyIdentifier::PRIORITY_ARRAY,
608 PropertyConformance::Optional,
609 Some(PropertyPresenceCondition::Commandable),
610 PropertyWriteCapability::ReadOnly,
611 ),
612 PropertyMetadata::new(
613 PropertyIdentifier::RELINQUISH_DEFAULT,
614 PropertyConformance::Optional,
615 Some(PropertyPresenceCondition::Commandable),
616 PropertyWriteCapability::Always,
617 ),
618 PropertyMetadata::new(
619 PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
620 PropertyConformance::Optional,
621 Some(PropertyPresenceCondition::Commandable),
622 PropertyWriteCapability::ReadOnly,
623 ),
624 PropertyMetadata::new(
625 PropertyIdentifier::PROPERTY_LIST,
626 PropertyConformance::RequiredRead,
627 None,
628 PropertyWriteCapability::ReadOnly,
629 ),
630];
631
632define_value_object_commandable! {
633 name: TimeValueObject,
634 doc: "BACnet Time Value object (type 50).",
635 object_type: ObjectType::TIME_VALUE,
636 value_type: Time,
637 default_value: Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
638 pv_to_property: (|v: &Time| PropertyValue::Time(*v)),
639 property_to_pv: pv_to_time,
640 pa_wrap: PropertyValue::Time,
641 rd_wrap: (|v: &Time| PropertyValue::Time(*v)),
642 rd_validate: (|_: &Time| -> Result<(), Error> { Ok(()) }),
643 copy_type: copy,
644 property_metadata: TIME_VALUE_PROPERTY_METADATA,
645}
646
647define_value_object_commandable! {
648 name: DateTimeValueObject,
649 doc: "BACnet DateTime Value object (type 44).",
650 object_type: ObjectType::DATETIME_VALUE,
651 value_type: (Date, Time),
652 default_value: (
653 Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
654 Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
655 ),
656 pv_to_property: (|v: &(Date, Time)| datetime_to_pv(v)),
657 property_to_pv: pv_to_datetime,
658 pa_wrap: datetime_priority_value,
659 rd_wrap: (|v: &(Date, Time)| datetime_to_pv(v)),
660 rd_validate: (|_: &(Date, Time)| -> Result<(), Error> { Ok(()) }),
661 copy_type: copy,
662 property_metadata: metadata::DATETIME_VALUE_BASE,
663}
664
665define_value_object_commandable! {
670 name: DatePatternValueObject,
671 doc: "BACnet Date Pattern Value object (type 41).",
672 object_type: ObjectType::DATEPATTERN_VALUE,
673 value_type: Date,
674 default_value: Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
675 pv_to_property: (|v: &Date| PropertyValue::Date(*v)),
676 property_to_pv: pv_to_date,
677 pa_wrap: PropertyValue::Date,
678 rd_wrap: (|v: &Date| PropertyValue::Date(*v)),
679 rd_validate: (|_: &Date| -> Result<(), Error> { Ok(()) }),
680 copy_type: copy,
681 property_metadata: metadata::DATEPATTERN_VALUE_BASE,
682}
683
684define_value_object_commandable! {
685 name: TimePatternValueObject,
686 doc: "BACnet Time Pattern Value object (type 49).",
687 object_type: ObjectType::TIMEPATTERN_VALUE,
688 value_type: Time,
689 default_value: Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
690 pv_to_property: (|v: &Time| PropertyValue::Time(*v)),
691 property_to_pv: pv_to_time,
692 pa_wrap: PropertyValue::Time,
693 rd_wrap: (|v: &Time| PropertyValue::Time(*v)),
694 rd_validate: (|_: &Time| -> Result<(), Error> { Ok(()) }),
695 copy_type: copy,
696 property_metadata: metadata::TIMEPATTERN_VALUE_BASE,
697}
698
699define_value_object_commandable! {
700 name: DateTimePatternValueObject,
701 doc: "BACnet DateTime Pattern Value object (type 43).",
702 object_type: ObjectType::DATETIMEPATTERN_VALUE,
703 value_type: (Date, Time),
704 default_value: (
705 Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
706 Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
707 ),
708 pv_to_property: (|v: &(Date, Time)| datetime_to_pv(v)),
709 property_to_pv: pv_to_datetime,
710 pa_wrap: datetime_priority_value,
711 rd_wrap: (|v: &(Date, Time)| datetime_to_pv(v)),
712 rd_validate: (|_: &(Date, Time)| -> Result<(), Error> { Ok(()) }),
713 copy_type: copy,
714 property_metadata: metadata::DATETIMEPATTERN_VALUE_BASE,
715}
716
717#[cfg(test)]
722mod tests;