1use bacnet_types::constructed::{BACnetObjectPropertyReference, BACnetPrescale, BACnetScale};
6use bacnet_types::enums::{ObjectType, PropertyIdentifier};
7use bacnet_types::error::Error;
8use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
9use std::borrow::Cow;
10
11use crate::common::{self, read_common_properties};
12use crate::traits::BACnetObject;
13
14pub struct AccumulatorObject {
20 oid: ObjectIdentifier,
21 name: String,
22 description: String,
23 present_value: u64,
24 max_pres_value: u64,
25 scale: BACnetScale,
26 prescale: Option<BACnetPrescale>,
27 pulse_rate: f32,
28 units: u32,
29 limit_monitoring_interval: u32,
30 status_flags: StatusFlags,
31 event_state: u32,
32 out_of_service: bool,
33 reliability: u32,
34 value_before_change: u64,
35 value_set: u64,
36}
37
38impl AccumulatorObject {
39 pub fn new(instance: u32, name: impl Into<String>, units: u32) -> Result<Self, Error> {
41 let oid = ObjectIdentifier::new(ObjectType::ACCUMULATOR, instance)?;
42 Ok(Self {
43 oid,
44 name: name.into(),
45 description: String::new(),
46 present_value: 0,
47 max_pres_value: u64::MAX,
48 scale: BACnetScale::FloatScale(1.0),
49 prescale: None,
50 pulse_rate: 0.0,
51 units,
52 limit_monitoring_interval: 0,
53 status_flags: StatusFlags::empty(),
54 event_state: 0,
55 out_of_service: false,
56 reliability: 0,
57 value_before_change: 0,
58 value_set: 0,
59 })
60 }
61
62 pub fn set_present_value(&mut self, value: u64) {
64 self.present_value = value;
65 }
66
67 pub fn set_description(&mut self, desc: impl Into<String>) {
69 self.description = desc.into();
70 }
71
72 pub fn set_scale(&mut self, scale: BACnetScale) {
74 self.scale = scale;
75 }
76
77 pub fn set_prescale(&mut self, prescale: BACnetPrescale) {
79 self.prescale = Some(prescale);
80 }
81}
82
83impl BACnetObject for AccumulatorObject {
84 fn object_identifier(&self) -> ObjectIdentifier {
85 self.oid
86 }
87
88 fn object_name(&self) -> &str {
89 &self.name
90 }
91
92 fn read_property(
93 &self,
94 property: PropertyIdentifier,
95 array_index: Option<u32>,
96 ) -> Result<PropertyValue, Error> {
97 if let Some(result) = read_common_properties!(self, property, array_index) {
98 return result;
99 }
100 match property {
101 p if p == PropertyIdentifier::OBJECT_TYPE => {
102 Ok(PropertyValue::Enumerated(ObjectType::ACCUMULATOR.to_raw()))
103 }
104 p if p == PropertyIdentifier::PRESENT_VALUE => {
105 Ok(PropertyValue::Unsigned(self.present_value))
106 }
107 p if p == PropertyIdentifier::MAX_PRES_VALUE => {
108 Ok(PropertyValue::Unsigned(self.max_pres_value))
109 }
110 p if p == PropertyIdentifier::SCALE => match &self.scale {
111 BACnetScale::FloatScale(v) => {
112 Ok(PropertyValue::List(vec![PropertyValue::Real(*v)]))
113 }
114 BACnetScale::IntegerScale(v) => {
115 Ok(PropertyValue::List(vec![PropertyValue::Signed(*v)]))
116 }
117 },
118 p if p == PropertyIdentifier::PRESCALE => match &self.prescale {
119 Some(ps) => Ok(PropertyValue::List(vec![
120 PropertyValue::Unsigned(ps.multiplier as u64),
121 PropertyValue::Unsigned(ps.modulo_divide as u64),
122 ])),
123 None => Ok(PropertyValue::Null),
124 },
125 p if p == PropertyIdentifier::PULSE_RATE => Ok(PropertyValue::Real(self.pulse_rate)),
126 p if p == PropertyIdentifier::UNITS => Ok(PropertyValue::Enumerated(self.units)),
127 p if p == PropertyIdentifier::LIMIT_MONITORING_INTERVAL => Ok(PropertyValue::Unsigned(
128 self.limit_monitoring_interval as u64,
129 )),
130 p if p == PropertyIdentifier::EVENT_STATE => {
131 Ok(PropertyValue::Enumerated(self.event_state))
132 }
133 p if p == PropertyIdentifier::VALUE_BEFORE_CHANGE => {
134 Ok(PropertyValue::Unsigned(self.value_before_change))
135 }
136 p if p == PropertyIdentifier::VALUE_SET => Ok(PropertyValue::Unsigned(self.value_set)),
137 _ => Err(common::unknown_property_error()),
138 }
139 }
140
141 fn write_property(
142 &mut self,
143 property: PropertyIdentifier,
144 _array_index: Option<u32>,
145 value: PropertyValue,
146 _priority: Option<u8>,
147 ) -> Result<(), Error> {
148 if property == PropertyIdentifier::PRESENT_VALUE {
150 return Err(common::write_access_denied_error());
151 }
152 if let Some(result) =
153 common::write_out_of_service(&mut self.out_of_service, property, &value)
154 {
155 return result;
156 }
157 if let Some(result) = common::write_description(&mut self.description, property, &value) {
158 return result;
159 }
160 match property {
161 p if p == PropertyIdentifier::MAX_PRES_VALUE => {
162 if let PropertyValue::Unsigned(v) = value {
163 self.max_pres_value = v;
164 Ok(())
165 } else {
166 Err(common::invalid_data_type_error())
167 }
168 }
169 p if p == PropertyIdentifier::PULSE_RATE => {
170 if let PropertyValue::Real(v) = value {
171 common::reject_non_finite(v)?;
172 self.pulse_rate = v;
173 Ok(())
174 } else {
175 Err(common::invalid_data_type_error())
176 }
177 }
178 p if p == PropertyIdentifier::LIMIT_MONITORING_INTERVAL => {
179 if let PropertyValue::Unsigned(v) = value {
180 self.limit_monitoring_interval = common::u64_to_u32(v)?;
181 Ok(())
182 } else {
183 Err(common::invalid_data_type_error())
184 }
185 }
186 _ => Err(common::write_access_denied_error()),
187 }
188 }
189
190 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
191 static PROPS: &[PropertyIdentifier] = &[
192 PropertyIdentifier::OBJECT_IDENTIFIER,
193 PropertyIdentifier::OBJECT_NAME,
194 PropertyIdentifier::DESCRIPTION,
195 PropertyIdentifier::OBJECT_TYPE,
196 PropertyIdentifier::PRESENT_VALUE,
197 PropertyIdentifier::MAX_PRES_VALUE,
198 PropertyIdentifier::SCALE,
199 PropertyIdentifier::PRESCALE,
200 PropertyIdentifier::PULSE_RATE,
201 PropertyIdentifier::UNITS,
202 PropertyIdentifier::LIMIT_MONITORING_INTERVAL,
203 PropertyIdentifier::STATUS_FLAGS,
204 PropertyIdentifier::EVENT_STATE,
205 PropertyIdentifier::OUT_OF_SERVICE,
206 PropertyIdentifier::RELIABILITY,
207 PropertyIdentifier::VALUE_BEFORE_CHANGE,
208 PropertyIdentifier::VALUE_SET,
209 ];
210 Cow::Borrowed(PROPS)
211 }
212
213 fn supports_cov(&self) -> bool {
214 true
215 }
216}
217
218pub struct PulseConverterObject {
224 oid: ObjectIdentifier,
225 name: String,
226 description: String,
227 present_value: f32,
228 units: u32,
229 scale_factor: f32,
230 adjust_value: f32,
231 cov_increment: f32,
232 input_reference: Option<BACnetObjectPropertyReference>,
233 status_flags: StatusFlags,
234 event_state: u32,
235 out_of_service: bool,
236 reliability: u32,
237}
238
239impl PulseConverterObject {
240 pub fn new(instance: u32, name: impl Into<String>, units: u32) -> Result<Self, Error> {
242 let oid = ObjectIdentifier::new(ObjectType::PULSE_CONVERTER, instance)?;
243 Ok(Self {
244 oid,
245 name: name.into(),
246 description: String::new(),
247 present_value: 0.0,
248 units,
249 scale_factor: 1.0,
250 adjust_value: 0.0,
251 cov_increment: 0.0,
252 input_reference: None,
253 status_flags: StatusFlags::empty(),
254 event_state: 0,
255 out_of_service: false,
256 reliability: 0,
257 })
258 }
259
260 pub fn set_description(&mut self, desc: impl Into<String>) {
262 self.description = desc.into();
263 }
264
265 pub fn set_input_reference(&mut self, r: BACnetObjectPropertyReference) {
267 self.input_reference = Some(r);
268 }
269}
270
271impl BACnetObject for PulseConverterObject {
272 fn object_identifier(&self) -> ObjectIdentifier {
273 self.oid
274 }
275
276 fn object_name(&self) -> &str {
277 &self.name
278 }
279
280 fn read_property(
281 &self,
282 property: PropertyIdentifier,
283 array_index: Option<u32>,
284 ) -> Result<PropertyValue, Error> {
285 if let Some(result) = read_common_properties!(self, property, array_index) {
286 return result;
287 }
288 match property {
289 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
290 ObjectType::PULSE_CONVERTER.to_raw(),
291 )),
292 p if p == PropertyIdentifier::PRESENT_VALUE => {
293 Ok(PropertyValue::Real(self.present_value))
294 }
295 p if p == PropertyIdentifier::UNITS => Ok(PropertyValue::Enumerated(self.units)),
296 p if p == PropertyIdentifier::SCALE_FACTOR => {
297 Ok(PropertyValue::Real(self.scale_factor))
298 }
299 p if p == PropertyIdentifier::ADJUST_VALUE => {
300 Ok(PropertyValue::Real(self.adjust_value))
301 }
302 p if p == PropertyIdentifier::COV_INCREMENT => {
303 Ok(PropertyValue::Real(self.cov_increment))
304 }
305 p if p == PropertyIdentifier::INPUT_REFERENCE => Ok(
306 crate::reference::reference_read_value(&self.input_reference),
307 ),
308 p if p == PropertyIdentifier::EVENT_STATE => {
309 Ok(PropertyValue::Enumerated(self.event_state))
310 }
311 _ => Err(common::unknown_property_error()),
312 }
313 }
314
315 fn write_property(
316 &mut self,
317 property: PropertyIdentifier,
318 _array_index: Option<u32>,
319 value: PropertyValue,
320 _priority: Option<u8>,
321 ) -> Result<(), Error> {
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) = common::write_description(&mut self.description, property, &value) {
328 return result;
329 }
330 if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
331 {
332 return result;
333 }
334 match property {
335 p if p == PropertyIdentifier::PRESENT_VALUE => {
336 if !self.out_of_service {
337 return Err(common::write_access_denied_error());
338 }
339 if let PropertyValue::Real(v) = value {
340 common::reject_non_finite(v)?;
341 self.present_value = v;
342 Ok(())
343 } else {
344 Err(common::invalid_data_type_error())
345 }
346 }
347 p if p == PropertyIdentifier::SCALE_FACTOR => {
348 if let PropertyValue::Real(v) = value {
349 common::reject_non_finite(v)?;
350 self.scale_factor = v;
351 Ok(())
352 } else {
353 Err(common::invalid_data_type_error())
354 }
355 }
356 p if p == PropertyIdentifier::ADJUST_VALUE => {
357 if let PropertyValue::Real(v) = value {
358 common::reject_non_finite(v)?;
359 self.adjust_value = v;
360 Ok(())
361 } else {
362 Err(common::invalid_data_type_error())
363 }
364 }
365 p if p == PropertyIdentifier::INPUT_REFERENCE => {
368 self.input_reference = crate::reference::decode_reference_write(
369 &value,
370 crate::reference::ReferenceFrame::Bare,
371 )?;
372 Ok(())
373 }
374 _ => Err(common::write_access_denied_error()),
375 }
376 }
377
378 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
379 static PROPS: &[PropertyIdentifier] = &[
380 PropertyIdentifier::OBJECT_IDENTIFIER,
381 PropertyIdentifier::OBJECT_NAME,
382 PropertyIdentifier::DESCRIPTION,
383 PropertyIdentifier::OBJECT_TYPE,
384 PropertyIdentifier::PRESENT_VALUE,
385 PropertyIdentifier::UNITS,
386 PropertyIdentifier::SCALE_FACTOR,
387 PropertyIdentifier::ADJUST_VALUE,
388 PropertyIdentifier::COV_INCREMENT,
389 PropertyIdentifier::INPUT_REFERENCE,
390 PropertyIdentifier::STATUS_FLAGS,
391 PropertyIdentifier::EVENT_STATE,
392 PropertyIdentifier::OUT_OF_SERVICE,
393 PropertyIdentifier::RELIABILITY,
394 ];
395 Cow::Borrowed(PROPS)
396 }
397
398 fn supports_cov(&self) -> bool {
399 true
400 }
401
402 fn cov_increment(&self) -> Option<f32> {
403 Some(self.cov_increment)
404 }
405
406 fn is_writable_property(&self, property: PropertyIdentifier) -> bool {
411 matches!(
412 property,
413 PropertyIdentifier::PRESENT_VALUE
414 | PropertyIdentifier::SCALE_FACTOR
415 | PropertyIdentifier::ADJUST_VALUE
416 | PropertyIdentifier::INPUT_REFERENCE
417 | PropertyIdentifier::DESCRIPTION
418 | PropertyIdentifier::OUT_OF_SERVICE
419 | PropertyIdentifier::COV_INCREMENT
420 )
421 }
422}
423
424#[cfg(test)]
429#[path = "pulse_converter_policy_tests.rs"]
430mod pulse_converter_policy_tests;
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435 use bacnet_types::constructed::BACnetPrescale;
436
437 #[test]
440 fn accumulator_create_and_read_defaults() {
441 let acc = AccumulatorObject::new(1, "ACC-1", 95).unwrap();
442 assert_eq!(acc.object_name(), "ACC-1");
443 assert_eq!(
444 acc.read_property(PropertyIdentifier::PRESENT_VALUE, None)
445 .unwrap(),
446 PropertyValue::Unsigned(0)
447 );
448 assert_eq!(
449 acc.read_property(PropertyIdentifier::UNITS, None).unwrap(),
450 PropertyValue::Enumerated(95)
451 );
452 }
453
454 #[test]
455 fn accumulator_read_present_value() {
456 let mut acc = AccumulatorObject::new(1, "ACC-1", 95).unwrap();
457 acc.set_present_value(42);
458 assert_eq!(
459 acc.read_property(PropertyIdentifier::PRESENT_VALUE, None)
460 .unwrap(),
461 PropertyValue::Unsigned(42)
462 );
463 }
464
465 #[test]
466 fn accumulator_present_value_read_only_from_network() {
467 let mut acc = AccumulatorObject::new(1, "ACC-1", 95).unwrap();
468 let result = acc.write_property(
469 PropertyIdentifier::PRESENT_VALUE,
470 None,
471 PropertyValue::Unsigned(10),
472 None,
473 );
474 assert!(result.is_err());
475 }
476
477 #[test]
478 fn accumulator_read_scale_float() {
479 let acc = AccumulatorObject::new(1, "ACC-1", 95).unwrap();
480 let val = acc.read_property(PropertyIdentifier::SCALE, None).unwrap();
481 assert_eq!(val, PropertyValue::List(vec![PropertyValue::Real(1.0)]));
482 }
483
484 #[test]
485 fn accumulator_read_scale_integer() {
486 let mut acc = AccumulatorObject::new(1, "ACC-1", 95).unwrap();
487 acc.set_scale(BACnetScale::IntegerScale(10));
488 let val = acc.read_property(PropertyIdentifier::SCALE, None).unwrap();
489 assert_eq!(val, PropertyValue::List(vec![PropertyValue::Signed(10)]));
490 }
491
492 #[test]
493 fn accumulator_read_prescale_none() {
494 let acc = AccumulatorObject::new(1, "ACC-1", 95).unwrap();
495 let val = acc
496 .read_property(PropertyIdentifier::PRESCALE, None)
497 .unwrap();
498 assert_eq!(val, PropertyValue::Null);
499 }
500
501 #[test]
502 fn accumulator_read_prescale_set() {
503 let mut acc = AccumulatorObject::new(1, "ACC-1", 95).unwrap();
504 acc.set_prescale(BACnetPrescale {
505 multiplier: 5,
506 modulo_divide: 100,
507 });
508 let val = acc
509 .read_property(PropertyIdentifier::PRESCALE, None)
510 .unwrap();
511 assert_eq!(
512 val,
513 PropertyValue::List(vec![
514 PropertyValue::Unsigned(5),
515 PropertyValue::Unsigned(100),
516 ])
517 );
518 }
519
520 #[test]
521 fn accumulator_object_type() {
522 let acc = AccumulatorObject::new(1, "ACC-1", 95).unwrap();
523 let val = acc
524 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
525 .unwrap();
526 assert_eq!(
527 val,
528 PropertyValue::Enumerated(ObjectType::ACCUMULATOR.to_raw())
529 );
530 }
531
532 #[test]
533 fn accumulator_property_list() {
534 let acc = AccumulatorObject::new(1, "ACC-1", 95).unwrap();
535 let list = acc.property_list();
536 assert!(list.contains(&PropertyIdentifier::PRESENT_VALUE));
537 assert!(list.contains(&PropertyIdentifier::SCALE));
538 assert!(list.contains(&PropertyIdentifier::PRESCALE));
539 assert!(list.contains(&PropertyIdentifier::MAX_PRES_VALUE));
540 assert!(list.contains(&PropertyIdentifier::PULSE_RATE));
541 }
542
543 #[test]
546 fn pulse_converter_create_and_read_defaults() {
547 let pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
548 assert_eq!(pc.object_name(), "PC-1");
549 assert_eq!(
550 pc.read_property(PropertyIdentifier::PRESENT_VALUE, None)
551 .unwrap(),
552 PropertyValue::Real(0.0)
553 );
554 assert_eq!(
555 pc.read_property(PropertyIdentifier::UNITS, None).unwrap(),
556 PropertyValue::Enumerated(62)
557 );
558 }
559
560 #[test]
561 fn pulse_converter_read_write_present_value_while_out_of_service() {
562 let mut pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
563 pc.write_property(
564 PropertyIdentifier::OUT_OF_SERVICE,
565 None,
566 PropertyValue::Boolean(true),
567 None,
568 )
569 .unwrap();
570 pc.write_property(
571 PropertyIdentifier::PRESENT_VALUE,
572 None,
573 PropertyValue::Real(123.45),
574 None,
575 )
576 .unwrap();
577 assert_eq!(
578 pc.read_property(PropertyIdentifier::PRESENT_VALUE, None)
579 .unwrap(),
580 PropertyValue::Real(123.45)
581 );
582 }
583
584 #[test]
585 fn pulse_converter_read_scale_factor() {
586 let pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
587 let val = pc
588 .read_property(PropertyIdentifier::SCALE_FACTOR, None)
589 .unwrap();
590 assert_eq!(val, PropertyValue::Real(1.0));
591 }
592
593 #[test]
594 fn pulse_converter_write_scale_factor() {
595 let mut pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
596 pc.write_property(
597 PropertyIdentifier::SCALE_FACTOR,
598 None,
599 PropertyValue::Real(2.5),
600 None,
601 )
602 .unwrap();
603 assert_eq!(
604 pc.read_property(PropertyIdentifier::SCALE_FACTOR, None)
605 .unwrap(),
606 PropertyValue::Real(2.5)
607 );
608 }
609
610 #[test]
611 fn pulse_converter_cov_increment() {
612 let mut pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
613 assert_eq!(pc.cov_increment(), Some(0.0));
614 pc.write_property(
615 PropertyIdentifier::COV_INCREMENT,
616 None,
617 PropertyValue::Real(1.5),
618 None,
619 )
620 .unwrap();
621 assert_eq!(
622 pc.read_property(PropertyIdentifier::COV_INCREMENT, None)
623 .unwrap(),
624 PropertyValue::Real(1.5)
625 );
626 assert_eq!(pc.cov_increment(), Some(1.5));
627 }
628
629 #[test]
630 fn pulse_converter_object_type() {
631 let pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
632 let val = pc
633 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
634 .unwrap();
635 assert_eq!(
636 val,
637 PropertyValue::Enumerated(ObjectType::PULSE_CONVERTER.to_raw())
638 );
639 }
640
641 #[test]
642 fn pulse_converter_write_wrong_type_rejected() {
643 let mut pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
644 let result = pc.write_property(
645 PropertyIdentifier::PRESENT_VALUE,
646 None,
647 PropertyValue::Unsigned(42),
648 None,
649 );
650 assert!(result.is_err());
651 }
652
653 #[test]
654 fn pulse_converter_input_reference_defaults_null() {
655 let pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
656 assert_eq!(
657 pc.read_property(PropertyIdentifier::INPUT_REFERENCE, None)
658 .unwrap(),
659 PropertyValue::Null
660 );
661 }
662
663 #[test]
664 fn pulse_converter_set_input_reference() {
665 let mut pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
666 let oid = ObjectIdentifier::new(ObjectType::ACCUMULATOR, 1).unwrap();
667 let prop_raw = PropertyIdentifier::PRESENT_VALUE.to_raw();
668 pc.set_input_reference(BACnetObjectPropertyReference::new(oid, prop_raw));
669 let val = pc
670 .read_property(PropertyIdentifier::INPUT_REFERENCE, None)
671 .unwrap();
672 assert_eq!(
673 val,
674 PropertyValue::List(vec![
675 PropertyValue::ObjectIdentifier(oid),
676 PropertyValue::Enumerated(prop_raw),
677 ])
678 );
679 }
680
681 #[test]
682 fn pulse_converter_property_list() {
683 let pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
684 let list = pc.property_list();
685 assert!(list.contains(&PropertyIdentifier::PRESENT_VALUE));
686 assert!(list.contains(&PropertyIdentifier::SCALE_FACTOR));
687 assert!(list.contains(&PropertyIdentifier::ADJUST_VALUE));
688 assert!(list.contains(&PropertyIdentifier::COV_INCREMENT));
689 assert!(list.contains(&PropertyIdentifier::INPUT_REFERENCE));
690 }
691
692 #[test]
695 fn pulse_converter_write_framed_indexed_input_reference_lands() {
696 let mut pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
697 let oid = ObjectIdentifier::new(ObjectType::ACCUMULATOR, 1).unwrap();
698 let prop_raw = PropertyIdentifier::PRESENT_VALUE.to_raw();
699 let r = BACnetObjectPropertyReference::new_indexed(oid, prop_raw, 4);
700 let mut buf = bytes::BytesMut::new();
701 bacnet_encoding::constructed::encode_object_property_reference(&mut buf, &r);
702 pc.write_property(
703 PropertyIdentifier::INPUT_REFERENCE,
704 None,
705 PropertyValue::ApplicationData(buf.to_vec()),
706 None,
707 )
708 .unwrap();
709 assert_eq!(
710 pc.read_property(PropertyIdentifier::INPUT_REFERENCE, None)
711 .unwrap(),
712 PropertyValue::List(vec![
713 PropertyValue::ObjectIdentifier(oid),
714 PropertyValue::Enumerated(prop_raw),
715 PropertyValue::Unsigned(4),
716 ])
717 );
718 pc.write_property(
720 PropertyIdentifier::INPUT_REFERENCE,
721 None,
722 PropertyValue::Null,
723 None,
724 )
725 .unwrap();
726 assert_eq!(
727 pc.read_property(PropertyIdentifier::INPUT_REFERENCE, None)
728 .unwrap(),
729 PropertyValue::Null
730 );
731 }
732
733 #[test]
734 fn pulse_converter_write_malformed_framed_input_reference_rejected() {
735 let mut pc = PulseConverterObject::new(1, "PC-1", 62).unwrap();
736 let mut buf = bytes::BytesMut::new();
738 bacnet_encoding::primitives::encode_ctx_object_id(
739 &mut buf,
740 0,
741 &ObjectIdentifier::new(ObjectType::ACCUMULATOR, 1).unwrap(),
742 );
743 match pc
744 .write_property(
745 PropertyIdentifier::INPUT_REFERENCE,
746 None,
747 PropertyValue::ApplicationData(buf.to_vec()),
748 None,
749 )
750 .unwrap_err()
751 {
752 Error::Protocol { class, code } => {
753 assert_eq!(
754 class,
755 bacnet_types::enums::ErrorClass::PROPERTY.to_raw() as u32
756 );
757 assert_eq!(
758 code,
759 bacnet_types::enums::ErrorCode::INVALID_DATA_ENCODING.to_raw() as u32
760 );
761 }
762 other => panic!("expected PROPERTY/INVALID_DATA_ENCODING, got {other:?}"),
763 }
764 assert_eq!(
765 pc.read_property(PropertyIdentifier::INPUT_REFERENCE, None)
766 .unwrap(),
767 PropertyValue::Null
768 );
769 }
770}