1use bacnet_types::enums::{ObjectType, PropertyIdentifier};
9use bacnet_types::error::Error;
10use bacnet_types::primitives::{Date, ObjectIdentifier, PropertyValue, StatusFlags, Time};
11use std::borrow::Cow;
12
13use crate::common::{self, read_common_properties};
14use crate::traits::BACnetObject;
15
16macro_rules! define_value_object_commandable {
26 (
27 name: $struct_name:ident,
28 doc: $doc:expr,
29 object_type: $obj_type:expr,
30 value_type: $val_type:ty,
31 default_value: $default:expr,
32 pv_to_property: $pv_to_prop:expr,
33 property_to_pv: $prop_to_pv:expr,
34 pa_wrap: $pa_wrap:expr,
35 rd_wrap: $rd_wrap:expr,
36 copy_type: $is_copy:tt
37 $(,)?
38 ) => {
39 #[doc = $doc]
40 pub struct $struct_name {
41 oid: ObjectIdentifier,
42 name: String,
43 description: String,
44 present_value: $val_type,
45 out_of_service: bool,
46 status_flags: StatusFlags,
47 reliability: u32,
48 priority_array: [Option<$val_type>; 16],
50 relinquish_default: $val_type,
51 }
52
53 impl $struct_name {
54 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
56 let oid = ObjectIdentifier::new($obj_type, instance)?;
57 Ok(Self {
58 oid,
59 name: name.into(),
60 description: String::new(),
61 present_value: $default,
62 out_of_service: false,
63 status_flags: StatusFlags::empty(),
64 reliability: 0,
65 priority_array: Default::default(),
66 relinquish_default: $default,
67 })
68 }
69
70 fn recalculate_present_value(&mut self) {
72 define_value_object_commandable!(@recalc self, $is_copy);
73 }
74 }
75
76 impl BACnetObject for $struct_name {
77 fn object_identifier(&self) -> ObjectIdentifier {
78 self.oid
79 }
80
81 fn object_name(&self) -> &str {
82 &self.name
83 }
84
85 fn read_property(
86 &self,
87 property: PropertyIdentifier,
88 array_index: Option<u32>,
89 ) -> Result<PropertyValue, Error> {
90 if let Some(result) = read_common_properties!(self, property, array_index) {
91 return result;
92 }
93 match property {
94 p if p == PropertyIdentifier::OBJECT_TYPE => {
95 Ok(PropertyValue::Enumerated($obj_type.to_raw()))
96 }
97 p if p == PropertyIdentifier::PRESENT_VALUE => {
98 Ok(($pv_to_prop)(&self.present_value))
99 }
100 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
101 define_value_object_commandable!(@read_pa self, array_index, $pa_wrap, $is_copy)
102 }
103 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
104 Ok(($rd_wrap)(&self.relinquish_default))
105 }
106 _ => Err(common::unknown_property_error()),
107 }
108 }
109
110 fn write_property(
111 &mut self,
112 property: PropertyIdentifier,
113 array_index: Option<u32>,
114 value: PropertyValue,
115 priority: Option<u8>,
116 ) -> Result<(), Error> {
117 if property == PropertyIdentifier::PRIORITY_ARRAY {
119 let idx = match array_index {
120 Some(n) if (1..=16).contains(&n) => (n - 1) as usize,
121 Some(_) => return Err(common::invalid_array_index_error()),
122 None => {
123 return Err(Error::Encoding(
124 "PRIORITY_ARRAY requires array_index (1-16)".into(),
125 ))
126 }
127 };
128 match value {
129 PropertyValue::Null => {
130 self.priority_array[idx] = None;
131 }
132 other => {
133 let extracted = ($prop_to_pv)(other)?;
134 self.priority_array[idx] = Some(extracted);
135 }
136 }
137 self.recalculate_present_value();
138 return Ok(());
139 }
140 if property == PropertyIdentifier::PRESENT_VALUE {
142 let prio = priority.unwrap_or(16);
143 if !(1..=16).contains(&prio) {
144 return Err(common::value_out_of_range_error());
145 }
146 let idx = (prio - 1) as usize;
147 match value {
148 PropertyValue::Null => {
149 self.priority_array[idx] = None;
150 }
151 other => {
152 let extracted = ($prop_to_pv)(other)?;
153 self.priority_array[idx] = Some(extracted);
154 }
155 }
156 self.recalculate_present_value();
157 return Ok(());
158 }
159 if let Some(result) =
160 common::write_out_of_service(&mut self.out_of_service, property, &value)
161 {
162 return result;
163 }
164 if let Some(result) =
165 common::write_object_name(&mut self.name, property, &value)
166 {
167 return result;
168 }
169 if let Some(result) =
170 common::write_description(&mut self.description, property, &value)
171 {
172 return result;
173 }
174 Err(common::write_access_denied_error())
175 }
176
177 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
178 static PROPS: &[PropertyIdentifier] = &[
179 PropertyIdentifier::OBJECT_IDENTIFIER,
180 PropertyIdentifier::OBJECT_NAME,
181 PropertyIdentifier::DESCRIPTION,
182 PropertyIdentifier::OBJECT_TYPE,
183 PropertyIdentifier::PRESENT_VALUE,
184 PropertyIdentifier::STATUS_FLAGS,
185 PropertyIdentifier::OUT_OF_SERVICE,
186 PropertyIdentifier::RELIABILITY,
187 PropertyIdentifier::PRIORITY_ARRAY,
188 PropertyIdentifier::RELINQUISH_DEFAULT,
189 ];
190 Cow::Borrowed(PROPS)
191 }
192
193 fn supports_cov(&self) -> bool {
194 true
195 }
196 }
197 };
198
199 (@recalc $self:ident, copy) => {
203 $self.present_value = common::recalculate_from_priority_array(
204 &$self.priority_array,
205 $self.relinquish_default,
206 );
207 };
208 (@recalc $self:ident, clone) => {
210 $self.present_value = $self
211 .priority_array
212 .iter()
213 .find_map(|slot| slot.as_ref().cloned())
214 .unwrap_or_else(|| $self.relinquish_default.clone());
215 };
216
217 (@read_pa $self:ident, $array_index:ident, $wrap:expr, copy) => {{
219 common::read_priority_array!($self, $array_index, $wrap)
220 }};
221 (@read_pa $self:ident, $array_index:ident, $wrap:expr, clone) => {{
223 let wrap_fn = $wrap;
224 match $array_index {
225 None => {
226 let elements = $self
227 .priority_array
228 .iter()
229 .map(|slot| match slot {
230 Some(v) => wrap_fn(v),
231 None => PropertyValue::Null,
232 })
233 .collect();
234 Ok(PropertyValue::List(elements))
235 }
236 Some(0) => Ok(PropertyValue::Unsigned(16)),
237 Some(idx) if (1..=16).contains(&idx) => {
238 match &$self.priority_array[(idx - 1) as usize] {
239 Some(v) => Ok(wrap_fn(v)),
240 None => Ok(PropertyValue::Null),
241 }
242 }
243 _ => Err(common::invalid_array_index_error()),
244 }
245 }};
246}
247
248#[allow(unused_macros)]
255macro_rules! define_value_object_simple {
256 (
257 name: $struct_name:ident,
258 doc: $doc:expr,
259 object_type: $obj_type:expr,
260 value_type: $val_type:ty,
261 default_value: $default:expr,
262 pv_to_property: $pv_to_prop:expr,
263 property_to_pv: $prop_to_pv:expr
264 $(,)?
265 ) => {
266 #[doc = $doc]
267 pub struct $struct_name {
268 oid: ObjectIdentifier,
269 name: String,
270 description: String,
271 present_value: $val_type,
272 out_of_service: bool,
273 status_flags: StatusFlags,
274 reliability: u32,
275 }
276
277 impl $struct_name {
278 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
280 let oid = ObjectIdentifier::new($obj_type, instance)?;
281 Ok(Self {
282 oid,
283 name: name.into(),
284 description: String::new(),
285 present_value: $default,
286 out_of_service: false,
287 status_flags: StatusFlags::empty(),
288 reliability: 0,
289 })
290 }
291 }
292
293 impl BACnetObject for $struct_name {
294 fn object_identifier(&self) -> ObjectIdentifier {
295 self.oid
296 }
297
298 fn object_name(&self) -> &str {
299 &self.name
300 }
301
302 fn read_property(
303 &self,
304 property: PropertyIdentifier,
305 array_index: Option<u32>,
306 ) -> Result<PropertyValue, Error> {
307 if let Some(result) = read_common_properties!(self, property, array_index) {
308 return result;
309 }
310 match property {
311 p if p == PropertyIdentifier::OBJECT_TYPE => {
312 Ok(PropertyValue::Enumerated($obj_type.to_raw()))
313 }
314 p if p == PropertyIdentifier::PRESENT_VALUE => {
315 Ok(($pv_to_prop)(&self.present_value))
316 }
317 _ => Err(common::unknown_property_error()),
318 }
319 }
320
321 fn write_property(
322 &mut self,
323 property: PropertyIdentifier,
324 _array_index: Option<u32>,
325 value: PropertyValue,
326 _priority: Option<u8>,
327 ) -> Result<(), Error> {
328 if property == PropertyIdentifier::PRESENT_VALUE {
329 let extracted = ($prop_to_pv)(value)?;
330 self.present_value = extracted;
331 return Ok(());
332 }
333 if let Some(result) =
334 common::write_out_of_service(&mut self.out_of_service, property, &value)
335 {
336 return result;
337 }
338 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
339 return result;
340 }
341 if let Some(result) =
342 common::write_description(&mut self.description, property, &value)
343 {
344 return result;
345 }
346 Err(common::write_access_denied_error())
347 }
348
349 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
350 static PROPS: &[PropertyIdentifier] = &[
351 PropertyIdentifier::OBJECT_IDENTIFIER,
352 PropertyIdentifier::OBJECT_NAME,
353 PropertyIdentifier::DESCRIPTION,
354 PropertyIdentifier::OBJECT_TYPE,
355 PropertyIdentifier::PRESENT_VALUE,
356 PropertyIdentifier::STATUS_FLAGS,
357 PropertyIdentifier::OUT_OF_SERVICE,
358 PropertyIdentifier::RELIABILITY,
359 ];
360 Cow::Borrowed(PROPS)
361 }
362
363 fn supports_cov(&self) -> bool {
364 true
365 }
366 }
367 };
368}
369
370fn clone_string_to_pv(v: &str) -> PropertyValue {
375 PropertyValue::CharacterString(v.to_owned())
376}
377
378fn clone_octetstring_to_pv(v: &[u8]) -> PropertyValue {
379 PropertyValue::OctetString(v.to_owned())
380}
381
382fn clone_bitstring_to_pv(v: &(u8, Vec<u8>)) -> PropertyValue {
383 PropertyValue::BitString {
384 unused_bits: v.0,
385 data: v.1.clone(),
386 }
387}
388
389fn datetime_copy_to_pv(v: (Date, Time)) -> PropertyValue {
390 PropertyValue::List(vec![PropertyValue::Date(v.0), PropertyValue::Time(v.1)])
391}
392
393fn datetime_to_pv(dt: &(Date, Time)) -> PropertyValue {
394 PropertyValue::List(vec![PropertyValue::Date(dt.0), PropertyValue::Time(dt.1)])
395}
396
397fn pv_to_date(v: PropertyValue) -> Result<Date, Error> {
398 if let PropertyValue::Date(d) = v {
399 Ok(d)
400 } else {
401 Err(common::invalid_data_type_error())
402 }
403}
404
405fn pv_to_time(v: PropertyValue) -> Result<Time, Error> {
406 if let PropertyValue::Time(t) = v {
407 Ok(t)
408 } else {
409 Err(common::invalid_data_type_error())
410 }
411}
412
413fn pv_to_datetime(v: PropertyValue) -> Result<(Date, Time), Error> {
414 if let PropertyValue::List(items) = v {
415 if items.len() == 2 {
416 let d = if let PropertyValue::Date(d) = &items[0] {
417 *d
418 } else {
419 return Err(common::invalid_data_type_error());
420 };
421 let t = if let PropertyValue::Time(t) = &items[1] {
422 *t
423 } else {
424 return Err(common::invalid_data_type_error());
425 };
426 Ok((d, t))
427 } else {
428 Err(common::invalid_data_type_error())
429 }
430 } else {
431 Err(common::invalid_data_type_error())
432 }
433}
434
435define_value_object_commandable! {
440 name: IntegerValueObject,
441 doc: "BACnet Integer Value object (type 45).",
442 object_type: ObjectType::INTEGER_VALUE,
443 value_type: i32,
444 default_value: 0,
445 pv_to_property: (|v: &i32| PropertyValue::Signed(*v)),
446 property_to_pv: (|v: PropertyValue| -> Result<i32, Error> {
447 if let PropertyValue::Signed(n) = v { Ok(n) }
448 else { Err(common::invalid_data_type_error()) }
449 }),
450 pa_wrap: PropertyValue::Signed,
451 rd_wrap: (|v: &i32| PropertyValue::Signed(*v)),
452 copy_type: copy,
453}
454
455define_value_object_commandable! {
456 name: PositiveIntegerValueObject,
457 doc: "BACnet Positive Integer Value object (type 48).",
458 object_type: ObjectType::POSITIVE_INTEGER_VALUE,
459 value_type: u64,
460 default_value: 0,
461 pv_to_property: (|v: &u64| PropertyValue::Unsigned(*v)),
462 property_to_pv: (|v: PropertyValue| -> Result<u64, Error> {
463 if let PropertyValue::Unsigned(n) = v { Ok(n) }
464 else { Err(common::invalid_data_type_error()) }
465 }),
466 pa_wrap: PropertyValue::Unsigned,
467 rd_wrap: (|v: &u64| PropertyValue::Unsigned(*v)),
468 copy_type: copy,
469}
470
471define_value_object_commandable! {
472 name: LargeAnalogValueObject,
473 doc: "BACnet Large Analog Value object (type 46).",
474 object_type: ObjectType::LARGE_ANALOG_VALUE,
475 value_type: f64,
476 default_value: 0.0,
477 pv_to_property: (|v: &f64| PropertyValue::Double(*v)),
478 property_to_pv: (|v: PropertyValue| -> Result<f64, Error> {
479 if let PropertyValue::Double(n) = v { Ok(n) }
480 else { Err(common::invalid_data_type_error()) }
481 }),
482 pa_wrap: PropertyValue::Double,
483 rd_wrap: (|v: &f64| PropertyValue::Double(*v)),
484 copy_type: copy,
485}
486
487define_value_object_commandable! {
488 name: CharacterStringValueObject,
489 doc: "BACnet CharacterString Value object (type 40).",
490 object_type: ObjectType::CHARACTERSTRING_VALUE,
491 value_type: String,
492 default_value: String::new(),
493 pv_to_property: (|v: &String| PropertyValue::CharacterString(v.clone())),
494 property_to_pv: (|v: PropertyValue| -> Result<String, Error> {
495 if let PropertyValue::CharacterString(s) = v { Ok(s) }
496 else { Err(common::invalid_data_type_error()) }
497 }),
498 pa_wrap: clone_string_to_pv,
499 rd_wrap: (|v: &String| PropertyValue::CharacterString(v.clone())),
500 copy_type: clone,
501}
502
503define_value_object_commandable! {
504 name: OctetStringValueObject,
505 doc: "BACnet OctetString Value object (type 47).",
506 object_type: ObjectType::OCTETSTRING_VALUE,
507 value_type: Vec<u8>,
508 default_value: Vec::new(),
509 pv_to_property: (|v: &Vec<u8>| PropertyValue::OctetString(v.clone())),
510 property_to_pv: (|v: PropertyValue| -> Result<Vec<u8>, Error> {
511 if let PropertyValue::OctetString(b) = v { Ok(b) }
512 else { Err(common::invalid_data_type_error()) }
513 }),
514 pa_wrap: clone_octetstring_to_pv,
515 rd_wrap: (|v: &Vec<u8>| PropertyValue::OctetString(v.clone())),
516 copy_type: clone,
517}
518
519define_value_object_commandable! {
520 name: BitStringValueObject,
521 doc: "BACnet BitString Value object (type 39).",
522 object_type: ObjectType::BITSTRING_VALUE,
523 value_type: (u8, Vec<u8>),
524 default_value: (0, Vec::new()),
525 pv_to_property: (|v: &(u8, Vec<u8>)| PropertyValue::BitString {
526 unused_bits: v.0,
527 data: v.1.clone(),
528 }),
529 property_to_pv: (|v: PropertyValue| -> Result<(u8, Vec<u8>), Error> {
530 if let PropertyValue::BitString { unused_bits, data } = v { Ok((unused_bits, data)) }
531 else { Err(common::invalid_data_type_error()) }
532 }),
533 pa_wrap: clone_bitstring_to_pv,
534 rd_wrap: (|v: &(u8, Vec<u8>)| PropertyValue::BitString {
535 unused_bits: v.0,
536 data: v.1.clone(),
537 }),
538 copy_type: clone,
539}
540
541define_value_object_commandable! {
542 name: DateValueObject,
543 doc: "BACnet Date Value object (type 42).",
544 object_type: ObjectType::DATE_VALUE,
545 value_type: Date,
546 default_value: Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
547 pv_to_property: (|v: &Date| PropertyValue::Date(*v)),
548 property_to_pv: pv_to_date,
549 pa_wrap: PropertyValue::Date,
550 rd_wrap: (|v: &Date| PropertyValue::Date(*v)),
551 copy_type: copy,
552}
553
554define_value_object_commandable! {
555 name: TimeValueObject,
556 doc: "BACnet Time Value object (type 50).",
557 object_type: ObjectType::TIME_VALUE,
558 value_type: Time,
559 default_value: Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
560 pv_to_property: (|v: &Time| PropertyValue::Time(*v)),
561 property_to_pv: pv_to_time,
562 pa_wrap: PropertyValue::Time,
563 rd_wrap: (|v: &Time| PropertyValue::Time(*v)),
564 copy_type: copy,
565}
566
567define_value_object_commandable! {
568 name: DateTimeValueObject,
569 doc: "BACnet DateTime Value object (type 44).",
570 object_type: ObjectType::DATETIME_VALUE,
571 value_type: (Date, Time),
572 default_value: (
573 Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
574 Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
575 ),
576 pv_to_property: (|v: &(Date, Time)| datetime_to_pv(v)),
577 property_to_pv: pv_to_datetime,
578 pa_wrap: datetime_copy_to_pv,
579 rd_wrap: (|v: &(Date, Time)| datetime_to_pv(v)),
580 copy_type: copy,
581}
582
583define_value_object_commandable! {
588 name: DatePatternValueObject,
589 doc: "BACnet Date Pattern Value object (type 41).",
590 object_type: ObjectType::DATEPATTERN_VALUE,
591 value_type: Date,
592 default_value: Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
593 pv_to_property: (|v: &Date| PropertyValue::Date(*v)),
594 property_to_pv: pv_to_date,
595 pa_wrap: PropertyValue::Date,
596 rd_wrap: (|v: &Date| PropertyValue::Date(*v)),
597 copy_type: copy,
598}
599
600define_value_object_commandable! {
601 name: TimePatternValueObject,
602 doc: "BACnet Time Pattern Value object (type 49).",
603 object_type: ObjectType::TIMEPATTERN_VALUE,
604 value_type: Time,
605 default_value: Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
606 pv_to_property: (|v: &Time| PropertyValue::Time(*v)),
607 property_to_pv: pv_to_time,
608 pa_wrap: PropertyValue::Time,
609 rd_wrap: (|v: &Time| PropertyValue::Time(*v)),
610 copy_type: copy,
611}
612
613define_value_object_commandable! {
614 name: DateTimePatternValueObject,
615 doc: "BACnet DateTime Pattern Value object (type 43).",
616 object_type: ObjectType::DATETIMEPATTERN_VALUE,
617 value_type: (Date, Time),
618 default_value: (
619 Date { year: 0xFF, month: 0xFF, day: 0xFF, day_of_week: 0xFF },
620 Time { hour: 0xFF, minute: 0xFF, second: 0xFF, hundredths: 0xFF },
621 ),
622 pv_to_property: (|v: &(Date, Time)| datetime_to_pv(v)),
623 property_to_pv: pv_to_datetime,
624 pa_wrap: datetime_copy_to_pv,
625 rd_wrap: (|v: &(Date, Time)| datetime_to_pv(v)),
626 copy_type: copy,
627}
628
629#[cfg(test)]
634mod tests;