1use crate::prelude::*;
61use alloc::borrow::Cow;
62use bacnet_types::constructed::BACnetDeviceObjectReference;
63use bacnet_types::enums::{
64 ErrorClass, ErrorCode, EventState, LifeSafetyMode, LifeSafetyOperation, LifeSafetyState,
65 ObjectType, PropertyIdentifier, Reliability, SilencedState,
66};
67use bacnet_types::error::Error;
68use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
69
70use crate::common::{self, read_common_properties};
71use crate::device_reference::reference_list;
72use crate::traits::{BACnetObject, LifeSafetyOperationEffect, LifeSafetyOperationOutcome};
73
74mod application;
75mod membership;
76mod metadata;
77mod out_of_service;
78mod reset;
79
80use out_of_service::{DeviceValues, Simulation};
81
82pub use reset::{
83 LifeSafetyPointResetCommit, LifeSafetyPointResetContext, LifeSafetyPointResetExecutor,
84 LifeSafetyResetError, LifeSafetyZoneResetCommit, LifeSafetyZoneResetContext,
85 LifeSafetyZoneResetExecutor,
86};
87
88fn valid_life_safety_state(state: LifeSafetyState) -> bool {
91 LifeSafetyState::ALL_NAMED
92 .iter()
93 .any(|&(_, named)| named == state)
94 || (256..=65_535).contains(&state.to_raw())
95}
96
97fn life_safety_error(code: ErrorCode) -> Error {
98 Error::protocol(ErrorClass::OBJECT, code)
99}
100
101fn apply_silenced_operation(
102 silenced: &mut SilencedState,
103 operation_expected: &mut LifeSafetyOperation,
104 operation: LifeSafetyOperation,
105) -> Result<LifeSafetyOperationEffect, Error> {
106 let current = silenced.to_raw();
110 if current > SilencedState::ALL_SILENCED.to_raw() {
111 return Err(life_safety_error(
112 ErrorCode::INVALID_OPERATION_IN_THIS_STATE,
113 ));
114 }
115
116 let desired = if operation == LifeSafetyOperation::SILENCE {
117 SilencedState::ALL_SILENCED
118 } else if operation == LifeSafetyOperation::SILENCE_AUDIBLE {
119 SilencedState::from_raw(current | SilencedState::AUDIBLE_SILENCED.to_raw())
120 } else if operation == LifeSafetyOperation::SILENCE_VISUAL {
121 SilencedState::from_raw(current | SilencedState::VISIBLE_SILENCED.to_raw())
122 } else if operation == LifeSafetyOperation::UNSILENCE {
123 SilencedState::UNSILENCED
124 } else if operation == LifeSafetyOperation::UNSILENCE_AUDIBLE {
125 SilencedState::from_raw(current & !SilencedState::AUDIBLE_SILENCED.to_raw())
126 } else if operation == LifeSafetyOperation::UNSILENCE_VISUAL {
127 SilencedState::from_raw(current & !SilencedState::VISIBLE_SILENCED.to_raw())
128 } else {
129 return Err(life_safety_error(ErrorCode::VALUE_OUT_OF_RANGE));
130 };
131
132 if *operation_expected != operation {
133 return Err(life_safety_error(
134 ErrorCode::INVALID_OPERATION_IN_THIS_STATE,
135 ));
136 }
137
138 *silenced = desired;
139 *operation_expected = LifeSafetyOperation::NONE;
140 Ok(LifeSafetyOperationEffect::Applied)
141}
142
143const COV_PROPERTIES: [PropertyIdentifier; 5] = [
146 PropertyIdentifier::PRESENT_VALUE,
147 PropertyIdentifier::TRACKING_VALUE,
148 PropertyIdentifier::SILENCED,
149 PropertyIdentifier::OPERATION_EXPECTED,
150 PropertyIdentifier::STATUS_FLAGS,
151];
152
153fn standard_life_safety_modes() -> Vec<LifeSafetyMode> {
155 LifeSafetyMode::ALL_NAMED
156 .iter()
157 .map(|&(_, mode)| mode)
158 .collect()
159}
160
161fn distinct_modes(modes: impl IntoIterator<Item = LifeSafetyMode>) -> Vec<LifeSafetyMode> {
163 let mut distinct = Vec::new();
164 for mode in modes {
165 if !distinct.contains(&mode) {
166 distinct.push(mode);
167 }
168 }
169 distinct
170}
171
172fn read_accepted_modes(accepted_modes: &[LifeSafetyMode]) -> PropertyValue {
173 PropertyValue::List(
174 accepted_modes
175 .iter()
176 .map(|mode| PropertyValue::Enumerated(mode.to_raw()))
177 .collect(),
178 )
179}
180
181fn write_mode(
185 mode: &mut LifeSafetyMode,
186 accepted_modes: &[LifeSafetyMode],
187 value: &PropertyValue,
188) -> Result<(), Error> {
189 let PropertyValue::Enumerated(raw) = value else {
190 return Err(common::invalid_data_type_error());
191 };
192 let requested = LifeSafetyMode::from_raw(*raw);
193 if !accepted_modes.contains(&requested) {
194 return Err(common::value_out_of_range_error());
195 }
196 *mode = requested;
197 Ok(())
198}
199
200fn operation_outcome(
201 object: &dyn BACnetObject,
202 before: Vec<(PropertyIdentifier, PropertyValue)>,
203 effect: LifeSafetyOperationEffect,
204) -> LifeSafetyOperationOutcome {
205 let changed_properties = before
206 .into_iter()
207 .filter_map(|(property, previous)| {
208 object
209 .read_property(property, None)
210 .is_ok_and(|current| current != previous)
211 .then_some(property)
212 })
213 .collect();
214 LifeSafetyOperationOutcome {
215 effect,
216 changed_properties,
217 }
218}
219
220pub struct LifeSafetyPointObject {
231 oid: ObjectIdentifier,
232 name: String,
233 description: String,
234 present_value: LifeSafetyState,
236 mode: LifeSafetyMode,
238 accepted_modes: Vec<LifeSafetyMode>,
240 silenced: SilencedState,
242 operation_expected: LifeSafetyOperation,
244 tracking_value: LifeSafetyState,
246 member_of: Vec<BACnetDeviceObjectReference>,
248 direct_reading: f32,
250 maintenance_required: bool,
252 event_state: EventState,
254 status_flags: StatusFlags,
255 out_of_service: bool,
256 reliability: Reliability,
258 device_values: DeviceValues,
261 reset_executor: Option<LifeSafetyPointResetExecutor>,
263}
264
265impl LifeSafetyPointObject {
266 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
272 let oid = ObjectIdentifier::new(ObjectType::LIFE_SAFETY_POINT, instance)?;
273 Ok(Self {
274 oid,
275 name: name.into(),
276 description: String::new(),
277 present_value: LifeSafetyState::QUIET,
278 mode: LifeSafetyMode::OFF,
279 accepted_modes: standard_life_safety_modes(),
280 silenced: SilencedState::UNSILENCED,
281 operation_expected: LifeSafetyOperation::NONE,
282 tracking_value: LifeSafetyState::QUIET,
283 member_of: Vec::new(),
284 direct_reading: 0.0,
285 maintenance_required: false,
286 event_state: EventState::NORMAL,
287 status_flags: StatusFlags::empty(),
288 out_of_service: false,
289 reliability: Reliability::NO_FAULT_DETECTED,
290 device_values: DeviceValues::default(),
291 reset_executor: None,
292 })
293 }
294
295 pub fn set_present_value(&mut self, state: LifeSafetyState) {
297 self.present_value = state;
298 }
299
300 pub fn set_mode(&mut self, mode: LifeSafetyMode) {
303 self.mode = mode;
304 }
305
306 pub fn set_accepted_modes(&mut self, modes: impl IntoIterator<Item = LifeSafetyMode>) {
311 self.accepted_modes = distinct_modes(modes);
312 }
313
314 pub fn set_tracking_value(&mut self, state: LifeSafetyState) {
319 self.simulation().track(state);
320 }
321
322 fn simulation(&mut self) -> Simulation<'_> {
323 Simulation {
324 out_of_service: &mut self.out_of_service,
325 reliability: &mut self.reliability,
326 tracking_value: &mut self.tracking_value,
327 device_values: &mut self.device_values,
328 }
329 }
330
331 pub fn set_silenced(&mut self, state: SilencedState) {
333 self.silenced = state;
334 }
335
336 pub fn set_operation_expected(&mut self, operation: LifeSafetyOperation) {
338 self.operation_expected = operation;
339 }
340
341 pub fn set_reset_executor(&mut self, executor: LifeSafetyPointResetExecutor) {
346 self.reset_executor = Some(executor);
347 }
348
349 pub fn with_reset_executor(mut self, executor: LifeSafetyPointResetExecutor) -> Self {
351 self.set_reset_executor(executor);
352 self
353 }
354
355 pub fn set_direct_reading(&mut self, value: f32) {
357 self.direct_reading = value;
358 }
359
360 pub fn set_description(&mut self, desc: impl Into<String>) {
362 self.description = desc.into();
363 }
364
365 pub fn add_member(
372 &mut self,
373 zone: impl Into<BACnetDeviceObjectReference>,
374 ) -> Result<(), Error> {
375 membership::add(&mut self.member_of, zone.into(), membership::ZONES)
376 }
377}
378
379impl BACnetObject for LifeSafetyPointObject {
380 fn object_identifier(&self) -> ObjectIdentifier {
381 self.oid
382 }
383
384 fn object_name(&self) -> &str {
385 &self.name
386 }
387
388 fn read_property(
389 &self,
390 property: PropertyIdentifier,
391 array_index: Option<u32>,
392 ) -> Result<PropertyValue, Error> {
393 if let Some(result) = read_common_properties!(self, property, array_index) {
394 return result;
395 }
396 match property {
397 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
398 ObjectType::LIFE_SAFETY_POINT.to_raw(),
399 )),
400 p if p == PropertyIdentifier::PRESENT_VALUE => {
401 Ok(PropertyValue::Enumerated(self.present_value.to_raw()))
402 }
403 p if p == PropertyIdentifier::MODE => Ok(PropertyValue::Enumerated(self.mode.to_raw())),
404 p if p == PropertyIdentifier::ACCEPTED_MODES => {
405 Ok(read_accepted_modes(&self.accepted_modes))
406 }
407 p if p == PropertyIdentifier::SILENCED => {
408 Ok(PropertyValue::Enumerated(self.silenced.to_raw()))
409 }
410 p if p == PropertyIdentifier::OPERATION_EXPECTED => {
411 Ok(PropertyValue::Enumerated(self.operation_expected.to_raw()))
412 }
413 p if p == PropertyIdentifier::TRACKING_VALUE => {
414 Ok(PropertyValue::Enumerated(self.tracking_value.to_raw()))
415 }
416 p if p == PropertyIdentifier::MEMBER_OF => Ok(reference_list(&self.member_of)),
417 p if p == PropertyIdentifier::DIRECT_READING => {
418 Ok(PropertyValue::Real(self.direct_reading))
419 }
420 p if p == PropertyIdentifier::MAINTENANCE_REQUIRED => {
421 Ok(PropertyValue::Boolean(self.maintenance_required))
422 }
423 p if p == PropertyIdentifier::EVENT_STATE => {
424 Ok(PropertyValue::Enumerated(self.event_state.to_raw()))
425 }
426 _ => Err(common::unknown_property_error()),
427 }
428 }
429
430 fn write_property(
431 &mut self,
432 property: PropertyIdentifier,
433 _array_index: Option<u32>,
434 value: PropertyValue,
435 _priority: Option<u8>,
436 ) -> Result<(), Error> {
437 if property == PropertyIdentifier::PRESENT_VALUE {
439 return Err(common::write_access_denied_error());
440 }
441 if property == PropertyIdentifier::MODE {
442 return write_mode(&mut self.mode, &self.accepted_modes, &value);
443 }
444 if property == PropertyIdentifier::SILENCED
445 || property == PropertyIdentifier::OPERATION_EXPECTED
446 {
447 return Err(common::write_access_denied_error());
448 }
449 if property == PropertyIdentifier::DIRECT_READING {
450 if let PropertyValue::Real(v) = value {
451 common::reject_non_finite(v)?;
452 self.direct_reading = v;
453 return Ok(());
454 }
455 return Err(common::invalid_data_type_error());
456 }
457 if property == PropertyIdentifier::MAINTENANCE_REQUIRED {
458 if let PropertyValue::Boolean(v) = value {
459 self.maintenance_required = v;
460 return Ok(());
461 }
462 return Err(common::invalid_data_type_error());
463 }
464 if let Some(result) = self.simulation().write(property, &value) {
465 return result;
466 }
467 if let Some(result) = common::write_description(&mut self.description, property, &value) {
468 return result;
469 }
470 Err(crate::common::unhandled_write_error(
471 self.property_metadata().as_ref(),
472 property,
473 _array_index,
474 ))
475 }
476
477 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
478 metadata::for_point(self)
479 }
480
481 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
482 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
483 }
484
485 fn supports_cov(&self) -> bool {
486 true
487 }
488
489 fn supports_cov_property(&self, property: PropertyIdentifier) -> bool {
490 COV_PROPERTIES.contains(&property)
491 }
492
493 fn apply_life_safety_operation(
494 &mut self,
495 operation: LifeSafetyOperation,
496 ) -> Result<LifeSafetyOperationOutcome, Error> {
497 let before = COV_PROPERTIES
498 .into_iter()
499 .filter_map(|property| {
500 self.read_property(property, None)
501 .ok()
502 .map(|value| (property, value))
503 })
504 .collect();
505 let effect = if reset::is_reset_operation(operation) {
506 self.apply_reset_operation(operation)?
507 } else {
508 apply_silenced_operation(&mut self.silenced, &mut self.operation_expected, operation)?
509 };
510 Ok(operation_outcome(self, before, effect))
511 }
512
513 fn set_life_safety_operation_expected_internal(
514 &mut self,
515 operation: LifeSafetyOperation,
516 ) -> Result<(), Error> {
517 self.set_operation_expected(operation);
518 Ok(())
519 }
520
521 fn set_reliability_internal(&mut self, reliability: Reliability) -> Result<(), Error> {
522 self.simulation().set_reliability(reliability)
523 }
524
525 fn set_present_value_internal(&mut self, value: PropertyValue) -> Result<(), Error> {
526 self.present_value = application::life_safety_state(&value)?;
527 Ok(())
528 }
529
530 fn set_tracking_value_internal(&mut self, value: PropertyValue) -> Result<(), Error> {
531 let state = application::life_safety_state(&value)?;
532 self.set_tracking_value(state);
533 Ok(())
534 }
535}
536
537pub struct LifeSafetyZoneObject {
549 oid: ObjectIdentifier,
550 name: String,
551 description: String,
552 present_value: LifeSafetyState,
554 mode: LifeSafetyMode,
556 accepted_modes: Vec<LifeSafetyMode>,
558 silenced: SilencedState,
560 operation_expected: LifeSafetyOperation,
562 tracking_value: LifeSafetyState,
564 zone_members: Vec<BACnetDeviceObjectReference>,
566 member_of: Vec<BACnetDeviceObjectReference>,
568 event_state: EventState,
570 status_flags: StatusFlags,
571 out_of_service: bool,
572 reliability: Reliability,
574 device_values: DeviceValues,
577 reset_executor: Option<LifeSafetyZoneResetExecutor>,
579}
580
581impl LifeSafetyZoneObject {
582 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
588 let oid = ObjectIdentifier::new(ObjectType::LIFE_SAFETY_ZONE, instance)?;
589 Ok(Self {
590 oid,
591 name: name.into(),
592 description: String::new(),
593 present_value: LifeSafetyState::QUIET,
594 mode: LifeSafetyMode::OFF,
595 accepted_modes: standard_life_safety_modes(),
596 silenced: SilencedState::UNSILENCED,
597 operation_expected: LifeSafetyOperation::NONE,
598 tracking_value: LifeSafetyState::QUIET,
599 zone_members: Vec::new(),
600 member_of: Vec::new(),
601 event_state: EventState::NORMAL,
602 status_flags: StatusFlags::empty(),
603 out_of_service: false,
604 reliability: Reliability::NO_FAULT_DETECTED,
605 device_values: DeviceValues::default(),
606 reset_executor: None,
607 })
608 }
609
610 pub fn set_present_value(&mut self, state: LifeSafetyState) {
612 self.present_value = state;
613 }
614
615 pub fn set_mode(&mut self, mode: LifeSafetyMode) {
618 self.mode = mode;
619 }
620
621 pub fn set_accepted_modes(&mut self, modes: impl IntoIterator<Item = LifeSafetyMode>) {
626 self.accepted_modes = distinct_modes(modes);
627 }
628
629 pub fn set_tracking_value(&mut self, state: LifeSafetyState) {
634 self.simulation().track(state);
635 }
636
637 fn simulation(&mut self) -> Simulation<'_> {
638 Simulation {
639 out_of_service: &mut self.out_of_service,
640 reliability: &mut self.reliability,
641 tracking_value: &mut self.tracking_value,
642 device_values: &mut self.device_values,
643 }
644 }
645
646 pub fn set_silenced(&mut self, state: SilencedState) {
648 self.silenced = state;
649 }
650
651 pub fn set_operation_expected(&mut self, operation: LifeSafetyOperation) {
653 self.operation_expected = operation;
654 }
655
656 pub fn set_reset_executor(&mut self, executor: LifeSafetyZoneResetExecutor) {
661 self.reset_executor = Some(executor);
662 }
663
664 pub fn with_reset_executor(mut self, executor: LifeSafetyZoneResetExecutor) -> Self {
666 self.set_reset_executor(executor);
667 self
668 }
669
670 pub fn set_description(&mut self, desc: impl Into<String>) {
672 self.description = desc.into();
673 }
674
675 pub fn add_zone_member(
683 &mut self,
684 member: impl Into<BACnetDeviceObjectReference>,
685 ) -> Result<(), Error> {
686 membership::add(
687 &mut self.zone_members,
688 member.into(),
689 membership::ZONE_MEMBERS,
690 )
691 }
692
693 pub fn add_member(
696 &mut self,
697 zone: impl Into<BACnetDeviceObjectReference>,
698 ) -> Result<(), Error> {
699 membership::add(&mut self.member_of, zone.into(), membership::ZONES)
700 }
701}
702
703impl BACnetObject for LifeSafetyZoneObject {
704 fn object_identifier(&self) -> ObjectIdentifier {
705 self.oid
706 }
707
708 fn object_name(&self) -> &str {
709 &self.name
710 }
711
712 fn read_property(
713 &self,
714 property: PropertyIdentifier,
715 array_index: Option<u32>,
716 ) -> Result<PropertyValue, Error> {
717 if let Some(result) = read_common_properties!(self, property, array_index) {
718 return result;
719 }
720 match property {
721 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
722 ObjectType::LIFE_SAFETY_ZONE.to_raw(),
723 )),
724 p if p == PropertyIdentifier::PRESENT_VALUE => {
725 Ok(PropertyValue::Enumerated(self.present_value.to_raw()))
726 }
727 p if p == PropertyIdentifier::MODE => Ok(PropertyValue::Enumerated(self.mode.to_raw())),
728 p if p == PropertyIdentifier::ACCEPTED_MODES => {
729 Ok(read_accepted_modes(&self.accepted_modes))
730 }
731 p if p == PropertyIdentifier::SILENCED => {
732 Ok(PropertyValue::Enumerated(self.silenced.to_raw()))
733 }
734 p if p == PropertyIdentifier::OPERATION_EXPECTED => {
735 Ok(PropertyValue::Enumerated(self.operation_expected.to_raw()))
736 }
737 p if p == PropertyIdentifier::TRACKING_VALUE => {
738 Ok(PropertyValue::Enumerated(self.tracking_value.to_raw()))
739 }
740 p if p == PropertyIdentifier::ZONE_MEMBERS => Ok(reference_list(&self.zone_members)),
741 p if p == PropertyIdentifier::MEMBER_OF => Ok(reference_list(&self.member_of)),
742 p if p == PropertyIdentifier::EVENT_STATE => {
743 Ok(PropertyValue::Enumerated(self.event_state.to_raw()))
744 }
745 _ => Err(common::unknown_property_error()),
746 }
747 }
748
749 fn write_property(
750 &mut self,
751 property: PropertyIdentifier,
752 _array_index: Option<u32>,
753 value: PropertyValue,
754 _priority: Option<u8>,
755 ) -> Result<(), Error> {
756 if property == PropertyIdentifier::PRESENT_VALUE {
758 return Err(common::write_access_denied_error());
759 }
760 if property == PropertyIdentifier::MODE {
761 return write_mode(&mut self.mode, &self.accepted_modes, &value);
762 }
763 if property == PropertyIdentifier::SILENCED
764 || property == PropertyIdentifier::OPERATION_EXPECTED
765 {
766 return Err(common::write_access_denied_error());
767 }
768 if let Some(result) = self.simulation().write(property, &value) {
769 return result;
770 }
771 if let Some(result) = common::write_description(&mut self.description, property, &value) {
772 return result;
773 }
774 Err(crate::common::unhandled_write_error(
775 self.property_metadata().as_ref(),
776 property,
777 _array_index,
778 ))
779 }
780
781 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
782 metadata::for_zone(self)
783 }
784
785 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
786 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
787 }
788
789 fn supports_cov(&self) -> bool {
790 true
791 }
792
793 fn supports_cov_property(&self, property: PropertyIdentifier) -> bool {
794 COV_PROPERTIES.contains(&property)
795 }
796
797 fn apply_life_safety_operation(
798 &mut self,
799 operation: LifeSafetyOperation,
800 ) -> Result<LifeSafetyOperationOutcome, Error> {
801 let before = COV_PROPERTIES
802 .into_iter()
803 .filter_map(|property| {
804 self.read_property(property, None)
805 .ok()
806 .map(|value| (property, value))
807 })
808 .collect();
809 let effect = if reset::is_reset_operation(operation) {
810 self.apply_reset_operation(operation)?
811 } else {
812 apply_silenced_operation(&mut self.silenced, &mut self.operation_expected, operation)?
813 };
814 Ok(operation_outcome(self, before, effect))
815 }
816
817 fn set_life_safety_operation_expected_internal(
818 &mut self,
819 operation: LifeSafetyOperation,
820 ) -> Result<(), Error> {
821 self.set_operation_expected(operation);
822 Ok(())
823 }
824
825 fn set_reliability_internal(&mut self, reliability: Reliability) -> Result<(), Error> {
826 self.simulation().set_reliability(reliability)
827 }
828
829 fn set_present_value_internal(&mut self, value: PropertyValue) -> Result<(), Error> {
830 self.present_value = application::life_safety_state(&value)?;
831 Ok(())
832 }
833
834 fn set_tracking_value_internal(&mut self, value: PropertyValue) -> Result<(), Error> {
835 let state = application::life_safety_state(&value)?;
836 self.set_tracking_value(state);
837 Ok(())
838 }
839}
840
841#[cfg(test)]
846mod tests;
847
848#[cfg(test)]
849mod reset_tests;
850
851#[cfg(test)]
852mod event_state_tests;
853
854#[cfg(test)]
855mod accepted_modes_tests;
856
857#[cfg(test)]
858mod out_of_service_tests;
859
860#[cfg(test)]
861mod application_tests;
862
863#[cfg(test)]
864mod membership_tests;