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