1use bacnet_types::enums::{ObjectType, PropertyIdentifier};
5use bacnet_types::error::Error;
6use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, StatusFlags};
7use std::borrow::Cow;
8
9use crate::common::{self, read_common_properties};
10use crate::event::{ChangeOfStateDetector, EventStateChange};
11use crate::traits::BACnetObject;
12
13pub struct BinaryInputObject {
22 oid: ObjectIdentifier,
23 name: String,
24 description: String,
25 present_value: u32,
26 out_of_service: bool,
27 status_flags: StatusFlags,
28 polarity: u32,
30 reliability: u32,
32 active_text: String,
33 inactive_text: String,
34 event_detector: ChangeOfStateDetector,
36}
37
38impl BinaryInputObject {
39 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
40 let oid = ObjectIdentifier::new(ObjectType::BINARY_INPUT, instance)?;
41 Ok(Self {
42 oid,
43 name: name.into(),
44 description: String::new(),
45 present_value: 0,
46 out_of_service: false,
47 status_flags: StatusFlags::empty(),
48 polarity: 0,
49 reliability: 0,
50 active_text: "Active".into(),
51 inactive_text: "Inactive".into(),
52 event_detector: ChangeOfStateDetector::default(),
53 })
54 }
55
56 pub fn set_present_value(&mut self, value: u32) {
58 self.present_value = value;
59 }
60
61 pub fn set_description(&mut self, desc: impl Into<String>) {
63 self.description = desc.into();
64 }
65}
66
67impl BACnetObject for BinaryInputObject {
68 fn object_identifier(&self) -> ObjectIdentifier {
69 self.oid
70 }
71
72 fn object_name(&self) -> &str {
73 &self.name
74 }
75
76 fn supports_cov(&self) -> bool {
77 true
78 }
79
80 fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
81 self.event_detector.evaluate(self.present_value)
82 }
83
84 fn read_property(
85 &self,
86 property: PropertyIdentifier,
87 array_index: Option<u32>,
88 ) -> Result<PropertyValue, Error> {
89 if property == PropertyIdentifier::STATUS_FLAGS {
90 return Ok(common::compute_status_flags(
91 self.status_flags,
92 self.reliability,
93 self.out_of_service,
94 self.event_detector.event_state.to_raw(),
95 ));
96 }
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::BINARY_INPUT.to_raw()))
103 }
104 p if p == PropertyIdentifier::PRESENT_VALUE => {
105 Ok(PropertyValue::Enumerated(self.present_value))
106 }
107 p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
108 self.event_detector.event_state.to_raw(),
109 )),
110 p if p == PropertyIdentifier::POLARITY => Ok(PropertyValue::Enumerated(self.polarity)),
111 p if p == PropertyIdentifier::ACTIVE_TEXT => {
112 Ok(PropertyValue::CharacterString(self.active_text.clone()))
113 }
114 p if p == PropertyIdentifier::INACTIVE_TEXT => {
115 Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
116 }
117 p if p == PropertyIdentifier::ALARM_VALUES => Ok(PropertyValue::List(
118 self.event_detector
119 .alarm_values
120 .iter()
121 .map(|v| PropertyValue::Enumerated(*v))
122 .collect(),
123 )),
124 p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
125 unused_bits: 5,
126 data: vec![self.event_detector.event_enable << 5],
127 }),
128 p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
129 unused_bits: 5,
130 data: vec![self.event_detector.acked_transitions << 5],
131 }),
132 p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
133 self.event_detector.notification_class as u64,
134 )),
135 p if p == PropertyIdentifier::EVENT_TIME_STAMPS => Ok(PropertyValue::List(vec![
136 PropertyValue::Unsigned(0),
137 PropertyValue::Unsigned(0),
138 PropertyValue::Unsigned(0),
139 ])),
140 _ => Err(common::unknown_property_error()),
141 }
142 }
143
144 fn write_property(
145 &mut self,
146 property: PropertyIdentifier,
147 _array_index: Option<u32>,
148 value: PropertyValue,
149 _priority: Option<u8>,
150 ) -> Result<(), Error> {
151 if property == PropertyIdentifier::PRESENT_VALUE {
152 if !self.out_of_service {
153 return Err(common::write_access_denied_error());
154 }
155 if let PropertyValue::Enumerated(v) = value {
156 if v > 1 {
157 return Err(common::value_out_of_range_error());
158 }
159 self.present_value = v;
160 return Ok(());
161 }
162 return Err(common::invalid_data_type_error());
163 }
164 if property == PropertyIdentifier::ACTIVE_TEXT {
165 if let PropertyValue::CharacterString(s) = value {
166 self.active_text = s;
167 return Ok(());
168 }
169 return Err(common::invalid_data_type_error());
170 }
171 if property == PropertyIdentifier::INACTIVE_TEXT {
172 if let PropertyValue::CharacterString(s) = value {
173 self.inactive_text = s;
174 return Ok(());
175 }
176 return Err(common::invalid_data_type_error());
177 }
178 if let Some(result) =
179 common::write_out_of_service(&mut self.out_of_service, property, &value)
180 {
181 return result;
182 }
183 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
184 return result;
185 }
186 if let Some(result) = common::write_description(&mut self.description, property, &value) {
187 return result;
188 }
189 Err(common::write_access_denied_error())
190 }
191
192 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
193 static PROPS: &[PropertyIdentifier] = &[
194 PropertyIdentifier::OBJECT_IDENTIFIER,
195 PropertyIdentifier::OBJECT_NAME,
196 PropertyIdentifier::DESCRIPTION,
197 PropertyIdentifier::OBJECT_TYPE,
198 PropertyIdentifier::PRESENT_VALUE,
199 PropertyIdentifier::STATUS_FLAGS,
200 PropertyIdentifier::EVENT_STATE,
201 PropertyIdentifier::OUT_OF_SERVICE,
202 PropertyIdentifier::POLARITY,
203 PropertyIdentifier::RELIABILITY,
204 PropertyIdentifier::ACTIVE_TEXT,
205 PropertyIdentifier::INACTIVE_TEXT,
206 ];
207 Cow::Borrowed(PROPS)
208 }
209}
210
211pub struct BinaryOutputObject {
220 oid: ObjectIdentifier,
221 name: String,
222 description: String,
223 present_value: u32,
224 out_of_service: bool,
225 status_flags: StatusFlags,
226 priority_array: [Option<u32>; 16],
227 relinquish_default: u32,
228 polarity: u32,
230 reliability: u32,
232 active_text: String,
233 inactive_text: String,
234 event_detector: ChangeOfStateDetector,
236 value_source: common::ValueSourceTracking,
238}
239
240impl BinaryOutputObject {
241 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
242 let oid = ObjectIdentifier::new(ObjectType::BINARY_OUTPUT, instance)?;
243 Ok(Self {
244 oid,
245 name: name.into(),
246 description: String::new(),
247 present_value: 0,
248 out_of_service: false,
249 status_flags: StatusFlags::empty(),
250 priority_array: [None; 16],
251 relinquish_default: 0,
252 polarity: 0,
253 reliability: 0,
254 active_text: "Active".into(),
255 inactive_text: "Inactive".into(),
256 event_detector: ChangeOfStateDetector::default(),
257 value_source: common::ValueSourceTracking::default(),
258 })
259 }
260
261 pub fn set_description(&mut self, desc: impl Into<String>) {
263 self.description = desc.into();
264 }
265
266 fn recalculate_present_value(&mut self) {
267 self.present_value =
268 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
269 }
270}
271
272impl BACnetObject for BinaryOutputObject {
273 fn object_identifier(&self) -> ObjectIdentifier {
274 self.oid
275 }
276
277 fn object_name(&self) -> &str {
278 &self.name
279 }
280
281 fn supports_cov(&self) -> bool {
282 true
283 }
284
285 fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
286 self.event_detector.evaluate(self.present_value)
287 }
288
289 fn read_property(
290 &self,
291 property: PropertyIdentifier,
292 array_index: Option<u32>,
293 ) -> Result<PropertyValue, Error> {
294 if property == PropertyIdentifier::STATUS_FLAGS {
295 return Ok(common::compute_status_flags(
296 self.status_flags,
297 self.reliability,
298 self.out_of_service,
299 self.event_detector.event_state.to_raw(),
300 ));
301 }
302 if let Some(result) = read_common_properties!(self, property, array_index) {
303 return result;
304 }
305 match property {
306 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
307 ObjectType::BINARY_OUTPUT.to_raw(),
308 )),
309 p if p == PropertyIdentifier::PRESENT_VALUE => {
310 Ok(PropertyValue::Enumerated(self.present_value))
311 }
312 p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
313 self.event_detector.event_state.to_raw(),
314 )),
315 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
316 common::read_priority_array!(self, array_index, PropertyValue::Enumerated)
317 }
318 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
319 Ok(PropertyValue::Enumerated(self.relinquish_default))
320 }
321 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
322 Ok(common::current_command_priority(&self.priority_array))
323 }
324 p if p == PropertyIdentifier::VALUE_SOURCE => {
325 Ok(self.value_source.value_source.clone())
326 }
327 p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
328 match self.value_source.last_command_time {
329 BACnetTimeStamp::SequenceNumber(n) => n,
330 _ => 0,
331 },
332 )),
333 p if p == PropertyIdentifier::POLARITY => Ok(PropertyValue::Enumerated(self.polarity)),
334 p if p == PropertyIdentifier::ACTIVE_TEXT => {
335 Ok(PropertyValue::CharacterString(self.active_text.clone()))
336 }
337 p if p == PropertyIdentifier::INACTIVE_TEXT => {
338 Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
339 }
340 p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
341 unused_bits: 5,
342 data: vec![self.event_detector.event_enable << 5],
343 }),
344 p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
345 unused_bits: 5,
346 data: vec![self.event_detector.acked_transitions << 5],
347 }),
348 p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
349 self.event_detector.notification_class as u64,
350 )),
351 p if p == PropertyIdentifier::EVENT_TIME_STAMPS => Ok(PropertyValue::List(vec![
352 PropertyValue::Unsigned(0),
353 PropertyValue::Unsigned(0),
354 PropertyValue::Unsigned(0),
355 ])),
356 _ => Err(common::unknown_property_error()),
357 }
358 }
359
360 fn write_property(
361 &mut self,
362 property: PropertyIdentifier,
363 array_index: Option<u32>,
364 value: PropertyValue,
365 priority: Option<u8>,
366 ) -> Result<(), Error> {
367 common::write_priority_array_direct!(self, property, array_index, value, |v| {
368 if let PropertyValue::Enumerated(e) = v {
369 if e > 1 {
370 Err(common::value_out_of_range_error())
371 } else {
372 Ok(e)
373 }
374 } else {
375 Err(common::invalid_data_type_error())
376 }
377 });
378 if property == PropertyIdentifier::PRESENT_VALUE {
379 return common::write_priority_array!(self, value, priority, |v| {
380 if let PropertyValue::Enumerated(e) = v {
381 if e > 1 {
382 Err(common::value_out_of_range_error())
383 } else {
384 Ok(e)
385 }
386 } else {
387 Err(common::invalid_data_type_error())
388 }
389 });
390 }
391 if property == PropertyIdentifier::ACTIVE_TEXT {
392 if let PropertyValue::CharacterString(s) = value {
393 self.active_text = s;
394 return Ok(());
395 }
396 return Err(common::invalid_data_type_error());
397 }
398 if property == PropertyIdentifier::INACTIVE_TEXT {
399 if let PropertyValue::CharacterString(s) = value {
400 self.inactive_text = s;
401 return Ok(());
402 }
403 return Err(common::invalid_data_type_error());
404 }
405 if let Some(result) =
406 common::write_out_of_service(&mut self.out_of_service, property, &value)
407 {
408 return result;
409 }
410 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
411 return result;
412 }
413 if let Some(result) = common::write_description(&mut self.description, property, &value) {
414 return result;
415 }
416 Err(common::write_access_denied_error())
417 }
418
419 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
420 static PROPS: &[PropertyIdentifier] = &[
421 PropertyIdentifier::OBJECT_IDENTIFIER,
422 PropertyIdentifier::OBJECT_NAME,
423 PropertyIdentifier::DESCRIPTION,
424 PropertyIdentifier::OBJECT_TYPE,
425 PropertyIdentifier::PRESENT_VALUE,
426 PropertyIdentifier::STATUS_FLAGS,
427 PropertyIdentifier::EVENT_STATE,
428 PropertyIdentifier::OUT_OF_SERVICE,
429 PropertyIdentifier::PRIORITY_ARRAY,
430 PropertyIdentifier::RELINQUISH_DEFAULT,
431 PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
432 PropertyIdentifier::POLARITY,
433 PropertyIdentifier::RELIABILITY,
434 PropertyIdentifier::ACTIVE_TEXT,
435 PropertyIdentifier::INACTIVE_TEXT,
436 ];
437 Cow::Borrowed(PROPS)
438 }
439}
440
441pub struct BinaryValueObject {
450 oid: ObjectIdentifier,
451 name: String,
452 description: String,
453 present_value: u32, out_of_service: bool,
455 status_flags: StatusFlags,
456 priority_array: [Option<u32>; 16],
457 relinquish_default: u32,
458 reliability: u32,
460 active_text: String,
461 inactive_text: String,
462 event_detector: ChangeOfStateDetector,
464 value_source: common::ValueSourceTracking,
466}
467
468impl BinaryValueObject {
469 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
471 let oid = ObjectIdentifier::new(ObjectType::BINARY_VALUE, instance)?;
472 Ok(Self {
473 oid,
474 name: name.into(),
475 description: String::new(),
476 present_value: 0, out_of_service: false,
478 status_flags: StatusFlags::empty(),
479 priority_array: [None; 16],
480 relinquish_default: 0,
481 reliability: 0,
482 active_text: "Active".into(),
483 inactive_text: "Inactive".into(),
484 event_detector: ChangeOfStateDetector::default(),
485 value_source: common::ValueSourceTracking::default(),
486 })
487 }
488
489 pub fn set_description(&mut self, desc: impl Into<String>) {
491 self.description = desc.into();
492 }
493
494 fn recalculate_present_value(&mut self) {
495 self.present_value =
496 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
497 }
498}
499
500impl BACnetObject for BinaryValueObject {
501 fn object_identifier(&self) -> ObjectIdentifier {
502 self.oid
503 }
504
505 fn object_name(&self) -> &str {
506 &self.name
507 }
508
509 fn supports_cov(&self) -> bool {
510 true
511 }
512
513 fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
514 self.event_detector.evaluate(self.present_value)
515 }
516
517 fn read_property(
518 &self,
519 property: PropertyIdentifier,
520 array_index: Option<u32>,
521 ) -> Result<PropertyValue, Error> {
522 if property == PropertyIdentifier::STATUS_FLAGS {
523 return Ok(common::compute_status_flags(
524 self.status_flags,
525 self.reliability,
526 self.out_of_service,
527 self.event_detector.event_state.to_raw(),
528 ));
529 }
530 if let Some(result) = read_common_properties!(self, property, array_index) {
531 return result;
532 }
533 match property {
534 p if p == PropertyIdentifier::OBJECT_TYPE => {
535 Ok(PropertyValue::Enumerated(ObjectType::BINARY_VALUE.to_raw()))
536 }
537 p if p == PropertyIdentifier::PRESENT_VALUE => {
538 Ok(PropertyValue::Enumerated(self.present_value))
539 }
540 p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
541 self.event_detector.event_state.to_raw(),
542 )),
543 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
544 common::read_priority_array!(self, array_index, PropertyValue::Enumerated)
545 }
546 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
547 Ok(PropertyValue::Enumerated(self.relinquish_default))
548 }
549 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
550 Ok(common::current_command_priority(&self.priority_array))
551 }
552 p if p == PropertyIdentifier::VALUE_SOURCE => {
553 Ok(self.value_source.value_source.clone())
554 }
555 p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
556 match self.value_source.last_command_time {
557 BACnetTimeStamp::SequenceNumber(n) => n,
558 _ => 0,
559 },
560 )),
561 p if p == PropertyIdentifier::ACTIVE_TEXT => {
562 Ok(PropertyValue::CharacterString(self.active_text.clone()))
563 }
564 p if p == PropertyIdentifier::INACTIVE_TEXT => {
565 Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
566 }
567 p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
568 unused_bits: 5,
569 data: vec![self.event_detector.event_enable << 5],
570 }),
571 p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
572 unused_bits: 5,
573 data: vec![self.event_detector.acked_transitions << 5],
574 }),
575 p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
576 self.event_detector.notification_class as u64,
577 )),
578 p if p == PropertyIdentifier::EVENT_TIME_STAMPS => Ok(PropertyValue::List(vec![
579 PropertyValue::Unsigned(0),
580 PropertyValue::Unsigned(0),
581 PropertyValue::Unsigned(0),
582 ])),
583 _ => Err(common::unknown_property_error()),
584 }
585 }
586
587 fn write_property(
588 &mut self,
589 property: PropertyIdentifier,
590 array_index: Option<u32>,
591 value: PropertyValue,
592 priority: Option<u8>,
593 ) -> Result<(), Error> {
594 common::write_priority_array_direct!(self, property, array_index, value, |v| {
595 if let PropertyValue::Enumerated(e) = v {
596 if e > 1 {
597 Err(common::value_out_of_range_error())
598 } else {
599 Ok(e)
600 }
601 } else {
602 Err(common::invalid_data_type_error())
603 }
604 });
605 if property == PropertyIdentifier::PRESENT_VALUE {
606 return common::write_priority_array!(self, value, priority, |v| {
607 if let PropertyValue::Enumerated(e) = v {
608 if e > 1 {
609 Err(common::value_out_of_range_error())
610 } else {
611 Ok(e)
612 }
613 } else {
614 Err(common::invalid_data_type_error())
615 }
616 });
617 }
618 if property == PropertyIdentifier::ACTIVE_TEXT {
619 if let PropertyValue::CharacterString(s) = value {
620 self.active_text = s;
621 return Ok(());
622 }
623 return Err(common::invalid_data_type_error());
624 }
625 if property == PropertyIdentifier::INACTIVE_TEXT {
626 if let PropertyValue::CharacterString(s) = value {
627 self.inactive_text = s;
628 return Ok(());
629 }
630 return Err(common::invalid_data_type_error());
631 }
632 if let Some(result) =
633 common::write_out_of_service(&mut self.out_of_service, property, &value)
634 {
635 return result;
636 }
637 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
638 return result;
639 }
640 if let Some(result) = common::write_description(&mut self.description, property, &value) {
641 return result;
642 }
643 Err(common::write_access_denied_error())
644 }
645
646 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
647 static PROPS: &[PropertyIdentifier] = &[
648 PropertyIdentifier::OBJECT_IDENTIFIER,
649 PropertyIdentifier::OBJECT_NAME,
650 PropertyIdentifier::DESCRIPTION,
651 PropertyIdentifier::OBJECT_TYPE,
652 PropertyIdentifier::PRESENT_VALUE,
653 PropertyIdentifier::STATUS_FLAGS,
654 PropertyIdentifier::EVENT_STATE,
655 PropertyIdentifier::OUT_OF_SERVICE,
656 PropertyIdentifier::PRIORITY_ARRAY,
657 PropertyIdentifier::RELINQUISH_DEFAULT,
658 PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
659 PropertyIdentifier::RELIABILITY,
660 PropertyIdentifier::ACTIVE_TEXT,
661 PropertyIdentifier::INACTIVE_TEXT,
662 ];
663 Cow::Borrowed(PROPS)
664 }
665}
666
667#[cfg(test)]
668mod tests;