1use bacnet_types::constructed::{
4 BACnetDeviceObjectPropertyReference, BACnetEventParameter, FaultParameters,
5};
6use bacnet_types::enums::{ErrorClass, ErrorCode, EventState, ObjectType, PropertyIdentifier};
7use bacnet_types::error::Error;
8use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, StatusFlags};
9use std::borrow::Cow;
10
11use crate::common::{self, read_common_properties};
12use crate::event::history::EventHistory;
13use crate::event::{
14 EnrollmentSummaryCapability, EventTransitionCommit, EventTransitionCommitError,
15};
16use crate::property_metadata::PropertyMetadata;
17use crate::traits::{BACnetObject, WritePropertyRollback};
18
19mod alert;
20mod metadata;
21mod parameters;
22mod state;
23mod transition;
24pub use alert::AlertEnrollmentObject;
25use state::EventEnrollmentWriteRollback;
26pub use state::{EventEnrollmentEvalState, EventEnrollmentMonitoredSource, EventEnrollmentPending};
27pub use transition::EventEnrollmentReliabilityCommit;
28
29pub struct EventEnrollmentObject {
36 oid: ObjectIdentifier,
37 name: String,
38 description: String,
39 event_type: u32,
40 notify_type: u32,
41 event_parameters: BACnetEventParameter,
42 object_property_reference: Option<BACnetDeviceObjectPropertyReference>,
43 event_state: u32,
44 event_enable: u8,
45 acked_transitions: u8,
46 event_history: EventHistory,
47 event_detection_enable: bool,
48 notification_class: u32,
49 fault_parameters: Option<FaultParameters>,
50 status_flags: StatusFlags,
51 out_of_service: bool,
52 reliability: u32,
53 time_delay_normal: Option<u32>,
61 pending: Option<EventEnrollmentPending>,
63 monitored_reference: Option<EventEnrollmentMonitoredSource>,
65 cov_baseline: Option<PropertyValue>,
67 last_offnormal_value: Option<u64>,
70}
71
72impl EventEnrollmentObject {
73 pub fn new(instance: u32, name: impl Into<String>, event_type: u32) -> Result<Self, Error> {
77 let oid = ObjectIdentifier::new(ObjectType::EVENT_ENROLLMENT, instance)?;
78 Ok(Self {
79 oid,
80 name: name.into(),
81 description: String::new(),
82 event_type,
83 notify_type: 0,
84 event_parameters: BACnetEventParameter::Opaque {
85 tag: 0xFF,
86 data: Vec::new(),
87 },
88 object_property_reference: None,
89 event_state: 0,
90 event_enable: 0b111,
91 acked_transitions: Self::RESET_ACKED_TRANSITIONS,
96 event_history: EventHistory::default(),
97 event_detection_enable: true,
98 notification_class: 0,
99 fault_parameters: None,
100 status_flags: StatusFlags::empty(),
101 out_of_service: false,
102 reliability: 0,
103 time_delay_normal: None,
107 pending: None,
108 monitored_reference: None,
109 cov_baseline: None,
110 last_offnormal_value: None,
111 })
112 }
113
114 const RESET_ACKED_TRANSITIONS: u8 = 0b111;
118
119 fn apply_detection_disabled_reset(&mut self) {
136 self.event_state = EventState::NORMAL.to_raw();
137 self.acked_transitions = Self::RESET_ACKED_TRANSITIONS;
138 self.event_history.reset();
139 self.pending = None;
140 self.monitored_reference = None;
141 self.cov_baseline = None;
142 self.last_offnormal_value = None;
143 }
144
145 pub fn set_description(&mut self, desc: impl Into<String>) {
147 self.description = desc.into();
148 }
149
150 pub fn set_object_property_reference(
152 &mut self,
153 reference: Option<BACnetDeviceObjectPropertyReference>,
154 ) {
155 self.object_property_reference = reference;
156 self.pending = None;
157 }
158
159 pub fn set_event_parameters(&mut self, params: BACnetEventParameter) {
161 self.event_parameters = params;
162 self.pending = None;
163 }
164
165 pub fn set_fault_parameters(&mut self, fp: Option<FaultParameters>) {
167 self.fault_parameters = fp;
168 }
169
170 pub fn set_event_state(&mut self, state: u32) {
179 if !self.event_detection_enable && state != EventState::NORMAL.to_raw() {
180 return;
181 }
182 self.event_state = state;
183 }
184
185 pub fn set_notification_class(&mut self, nc: u32) {
187 self.notification_class = nc;
188 }
189
190 pub fn set_event_enable(&mut self, enable: u8) {
192 self.event_enable = enable & 0x07;
193 }
194
195 pub fn set_time_delay_normal(&mut self, delay: Option<u32>) {
199 self.time_delay_normal = delay;
200 self.pending = None;
201 }
202
203 fn event_parameters_time_delay(&self) -> u32 {
210 use BACnetEventParameter as P;
211 match &self.event_parameters {
212 P::ChangeOfBitstring { time_delay, .. }
213 | P::ChangeOfState { time_delay, .. }
214 | P::ChangeOfValue { time_delay, .. }
215 | P::FloatingLimit { time_delay, .. }
216 | P::OutOfRange { time_delay, .. } => *time_delay,
217 _ => 0,
218 }
219 }
220
221 fn effective_fault_parameters(&self) -> &FaultParameters {
222 self.fault_parameters
223 .as_ref()
224 .unwrap_or(&FaultParameters::FaultNone)
225 }
226}
227
228impl BACnetObject for EventEnrollmentObject {
229 fn object_identifier(&self) -> ObjectIdentifier {
230 self.oid
231 }
232
233 fn object_name(&self) -> &str {
234 &self.name
235 }
236
237 fn enrollment_summary_capability_internal(&self) -> Option<EnrollmentSummaryCapability> {
238 let supported_parameters = match &self.event_parameters {
239 BACnetEventParameter::Extended { .. } => false,
240 BACnetEventParameter::Opaque { tag, .. } => *tag == 0xFF,
241 _ => true,
242 } || matches!(
243 self.effective_fault_parameters(),
244 FaultParameters::FaultStatusFlags { .. } | FaultParameters::FaultOutOfRange { .. }
245 );
246 (self.object_property_reference.is_some() && supported_parameters).then_some(
247 EnrollmentSummaryCapability {
248 event_type: bacnet_types::enums::EventType::from_raw(self.event_type),
249 last_transition: self.event_history.last_transition(),
250 },
251 )
252 }
253
254 fn read_property(
255 &self,
256 property: PropertyIdentifier,
257 array_index: Option<u32>,
258 ) -> Result<PropertyValue, Error> {
259 if property == PropertyIdentifier::STATUS_FLAGS {
260 return Ok(common::compute_status_flags(
263 StatusFlags::empty(),
264 self.reliability,
265 false,
266 self.event_state,
267 ));
268 }
269 if let Some(result) = read_common_properties!(self, property, array_index) {
270 return result;
271 }
272 match property {
273 p if p == PropertyIdentifier::EVENT_TIME_STAMPS => self
274 .event_history
275 .read(p, array_index)
276 .expect("EventHistory handles Event_Time_Stamps"),
277 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
278 ObjectType::EVENT_ENROLLMENT.to_raw(),
279 )),
280 p if p == PropertyIdentifier::EVENT_TYPE => {
281 Ok(PropertyValue::Enumerated(self.event_type))
282 }
283 p if p == PropertyIdentifier::NOTIFY_TYPE => {
284 Ok(PropertyValue::Enumerated(self.notify_type))
285 }
286 p if p == PropertyIdentifier::EVENT_PARAMETERS => {
287 let mut buf = bytes::BytesMut::new();
288 bacnet_encoding::constructed::encode_event_parameter(
289 &mut buf,
290 &self.event_parameters,
291 )?;
292 Ok(PropertyValue::ApplicationData(buf.to_vec()))
293 }
294 p if p == PropertyIdentifier::OBJECT_PROPERTY_REFERENCE => {
295 match &self.object_property_reference {
296 None => Ok(PropertyValue::Null),
297 Some(r) => Ok(PropertyValue::List(vec![
298 PropertyValue::ObjectIdentifier(r.object_identifier),
299 PropertyValue::Unsigned(r.property_identifier as u64),
300 match r.property_array_index {
301 Some(idx) => PropertyValue::Unsigned(idx as u64),
302 None => PropertyValue::Null,
303 },
304 match r.device_identifier {
305 Some(dev) => PropertyValue::ObjectIdentifier(dev),
306 None => PropertyValue::Null,
307 },
308 ])),
309 }
310 }
311 p if p == PropertyIdentifier::EVENT_STATE => {
312 Ok(PropertyValue::Enumerated(self.event_state))
313 }
314 p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
315 unused_bits: 5,
316 data: vec![bacnet_types::bitstring::pack_octet(self.event_enable)],
317 }),
318 p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
319 unused_bits: 5,
320 data: vec![bacnet_types::bitstring::pack_octet(self.acked_transitions)],
321 }),
322 p if p == PropertyIdentifier::EVENT_DETECTION_ENABLE => {
323 Ok(PropertyValue::Boolean(self.event_detection_enable))
324 }
325 p if p == PropertyIdentifier::NOTIFICATION_CLASS => {
326 Ok(PropertyValue::Unsigned(self.notification_class as u64))
327 }
328 p if p == PropertyIdentifier::FAULT_TYPE => Ok(PropertyValue::Enumerated(
329 self.effective_fault_parameters().fault_type().to_raw(),
330 )),
331 p if p == PropertyIdentifier::FAULT_PARAMETERS => {
332 let mut buf = bytes::BytesMut::new();
333 bacnet_encoding::constructed::encode_fault_parameters(
334 &mut buf,
335 self.effective_fault_parameters(),
336 )?;
337 Ok(PropertyValue::ApplicationData(buf.to_vec()))
338 }
339 p if p == PropertyIdentifier::TIME_DELAY_NORMAL => {
340 Ok(PropertyValue::Unsigned(
346 self.time_delay_normal
347 .unwrap_or_else(|| self.event_parameters_time_delay())
348 as u64,
349 ))
350 }
351 _ => Err(common::unknown_property_error()),
352 }
353 }
354
355 fn write_property(
356 &mut self,
357 property: PropertyIdentifier,
358 _array_index: Option<u32>,
359 value: PropertyValue,
360 _priority: Option<u8>,
361 ) -> Result<(), Error> {
362 if property == PropertyIdentifier::NOTIFY_TYPE {
363 if let PropertyValue::Enumerated(v) = value {
367 let named = bacnet_types::enums::NotifyType::ALL_NAMED
368 .iter()
369 .any(|&(_, n)| n.to_raw() == v);
370 if !named {
371 return Err(common::value_out_of_range_error());
372 }
373 self.notify_type = v;
374 return Ok(());
375 }
376 return Err(common::invalid_data_type_error());
377 }
378 if property == PropertyIdentifier::NOTIFICATION_CLASS {
379 if let PropertyValue::Unsigned(v) = value {
380 self.notification_class = common::u64_to_u32(v)?;
381 return Ok(());
382 }
383 return Err(common::invalid_data_type_error());
384 }
385 if property == PropertyIdentifier::EVENT_ENABLE {
386 if let PropertyValue::BitString { unused_bits, data } = &value {
389 let byte = common::check_fixed_width_bit_string(*unused_bits, data, 3)?;
390 self.event_enable = bacnet_types::bitstring::unpack_octet(&[byte], 3);
391 return Ok(());
392 }
393 return Err(common::invalid_data_type_error());
394 }
395 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
396 if let PropertyValue::Boolean(v) = value {
397 self.event_detection_enable = v;
398 if !v {
405 self.apply_detection_disabled_reset();
406 }
407 return Ok(());
408 }
409 return Err(common::invalid_data_type_error());
410 }
411 if property == PropertyIdentifier::EVENT_PARAMETERS {
418 self.set_event_parameters(parameters::decode_event_parameters(value)?);
419 return Ok(());
420 }
421 if property == PropertyIdentifier::FAULT_PARAMETERS {
422 self.fault_parameters = parameters::decode_fault_parameters(value)?;
423 return Ok(());
424 }
425 if property == PropertyIdentifier::TIME_DELAY_NORMAL {
426 if let PropertyValue::Unsigned(v) = value {
431 self.set_time_delay_normal(Some(common::u64_to_u32(v)?));
432 return Ok(());
433 }
434 return Err(common::invalid_data_type_error());
435 }
436 if let Some(result) =
437 common::write_out_of_service(&mut self.out_of_service, property, &value)
438 {
439 return result;
440 }
441 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
442 return result;
443 }
444 if let Some(result) = common::write_description(&mut self.description, property, &value) {
445 return result;
446 }
447 Err(common::write_access_denied_error())
448 }
449
450 fn set_event_state_internal(&mut self, state: EventState) -> Result<(), Error> {
463 if !self.event_detection_enable && state != EventState::NORMAL {
464 return Err(common::write_access_denied_error());
465 }
466 self.event_state = state.to_raw();
467 Ok(())
468 }
469
470 fn enrollment_eval_state_internal(&self) -> Option<EventEnrollmentEvalState> {
473 Some(EventEnrollmentEvalState {
474 pending: self.pending.clone(),
475 cov_baseline: self.cov_baseline.clone(),
476 last_offnormal_value: self.last_offnormal_value,
477 })
478 }
479
480 fn set_enrollment_eval_state_internal(
486 &mut self,
487 state: EventEnrollmentEvalState,
488 ) -> Result<(), Error> {
489 if !self.event_detection_enable {
490 return Err(common::write_access_denied_error());
491 }
492 self.pending = state.pending;
493 self.cov_baseline = state.cov_baseline;
494 self.last_offnormal_value = state.last_offnormal_value;
495 Ok(())
496 }
497
498 fn enrollment_eval_source_internal(&self) -> Option<Option<EventEnrollmentMonitoredSource>> {
499 Some(self.monitored_reference)
500 }
501
502 fn set_enrollment_eval_source_internal(
503 &mut self,
504 source: Option<EventEnrollmentMonitoredSource>,
505 ) -> Result<(), Error> {
506 if !self.event_detection_enable {
507 return Err(common::write_access_denied_error());
508 }
509 self.monitored_reference = source;
510 Ok(())
511 }
512
513 fn acknowledge_alarm(&mut self, transition_bit: u8) -> Result<(), bacnet_types::error::Error> {
524 if !self.event_detection_enable {
525 return Err(bacnet_types::error::Error::Protocol {
526 class: bacnet_types::enums::ErrorClass::OBJECT.to_raw() as u32,
527 code: bacnet_types::enums::ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
528 });
529 }
530 self.acked_transitions |= transition_bit & 0x07;
531 Ok(())
532 }
533
534 fn acknowledge_alarm_correlated_internal(
535 &mut self,
536 event_state: EventState,
537 timestamp: &BACnetTimeStamp,
538 ) -> Result<(), Error> {
539 if !self.event_detection_enable || self.enrollment_summary_capability_internal().is_none() {
540 return Err(Error::Protocol {
541 class: ErrorClass::OBJECT.to_raw() as u32,
542 code: ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
543 });
544 }
545 self.event_history.acknowledge_correlated(
546 &mut self.acked_transitions,
547 event_state,
548 timestamp,
549 )
550 }
551
552 fn acknowledge_alarm_correlated_detailed_internal(
553 &mut self,
554 event_state: EventState,
555 timestamp: &BACnetTimeStamp,
556 ) -> Result<Option<crate::event::EventStateChange>, Error> {
557 if !self.event_detection_enable || self.enrollment_summary_capability_internal().is_none() {
558 return Err(Error::Protocol {
559 class: ErrorClass::OBJECT.to_raw() as u32,
560 code: ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
561 });
562 }
563 self.event_history.acknowledge_correlated_detailed(
564 &mut self.acked_transitions,
565 event_state,
566 timestamp,
567 )
568 }
569
570 fn set_acked_transitions_internal(
577 &mut self,
578 transition_bit: u8,
579 acknowledged: bool,
580 ) -> Result<(), Error> {
581 if !self.event_detection_enable {
582 return Err(common::write_access_denied_error());
583 }
584 if acknowledged {
585 self.acked_transitions |= transition_bit & 0x07;
586 } else {
587 self.acked_transitions &= !(transition_bit & 0x07);
588 }
589 Ok(())
590 }
591
592 transition::impl_event_enrollment_transition_commit!();
593
594 fn capture_write_property_rollback(
595 &mut self,
596 property: PropertyIdentifier,
597 _value: &PropertyValue,
598 ) -> Option<WritePropertyRollback> {
599 match property {
600 PropertyIdentifier::EVENT_DETECTION_ENABLE => Some(WritePropertyRollback::new(
601 EventEnrollmentWriteRollback::Detection {
602 enabled: self.event_detection_enable,
603 event_state: self.event_state,
604 acked_transitions: self.acked_transitions,
605 event_history: self.event_history.clone(),
606 monitored_reference: self.monitored_reference,
607 evaluation: EventEnrollmentEvalState {
608 pending: self.pending.clone(),
609 cov_baseline: self.cov_baseline.clone(),
610 last_offnormal_value: self.last_offnormal_value,
611 },
612 },
613 )),
614 PropertyIdentifier::EVENT_PARAMETERS => Some(WritePropertyRollback::new(
615 EventEnrollmentWriteRollback::EventParameters {
616 value: self.event_parameters.clone(),
617 pending: self.pending.clone(),
618 },
619 )),
620 PropertyIdentifier::FAULT_PARAMETERS => Some(WritePropertyRollback::new(
621 EventEnrollmentWriteRollback::FaultParameters(self.fault_parameters.clone()),
622 )),
623 PropertyIdentifier::TIME_DELAY_NORMAL => Some(WritePropertyRollback::new(
624 EventEnrollmentWriteRollback::TimeDelayNormal {
625 value: self.time_delay_normal,
626 pending: self.pending.clone(),
627 },
628 )),
629 _ => None,
630 }
631 }
632
633 fn restore_write_property_rollback(
634 &mut self,
635 rollback: WritePropertyRollback,
636 ) -> Result<(), Error> {
637 match rollback.downcast::<EventEnrollmentWriteRollback>()? {
638 EventEnrollmentWriteRollback::Detection {
639 enabled,
640 event_state,
641 acked_transitions,
642 event_history,
643 monitored_reference,
644 evaluation,
645 } => {
646 self.event_detection_enable = enabled;
647 self.event_state = event_state;
648 self.acked_transitions = acked_transitions;
649 self.event_history = event_history;
650 self.monitored_reference = monitored_reference;
651 self.pending = evaluation.pending;
652 self.cov_baseline = evaluation.cov_baseline;
653 self.last_offnormal_value = evaluation.last_offnormal_value;
654 Ok(())
655 }
656 EventEnrollmentWriteRollback::EventParameters { value, pending } => {
657 self.event_parameters = value;
658 self.pending = pending;
659 Ok(())
660 }
661 EventEnrollmentWriteRollback::FaultParameters(value) => {
662 self.fault_parameters = value;
663 Ok(())
664 }
665 EventEnrollmentWriteRollback::TimeDelayNormal { value, pending } => {
666 self.time_delay_normal = value;
667 self.pending = pending;
668 Ok(())
669 }
670 }
671 }
672
673 fn is_writable_property(&self, property: PropertyIdentifier) -> bool {
694 common::is_common_writable(property)
695 || matches!(
696 property,
697 PropertyIdentifier::NOTIFY_TYPE
698 | PropertyIdentifier::NOTIFICATION_CLASS
699 | PropertyIdentifier::EVENT_ENABLE
700 | PropertyIdentifier::EVENT_DETECTION_ENABLE
701 | PropertyIdentifier::EVENT_PARAMETERS
702 | PropertyIdentifier::FAULT_PARAMETERS
703 | PropertyIdentifier::TIME_DELAY_NORMAL
704 )
705 }
706
707 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
708 Cow::Borrowed(metadata::EVENT_ENROLLMENT_PROPERTIES)
709 }
710
711 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
712 crate::property_metadata::property_list_from_metadata(metadata::EVENT_ENROLLMENT_PROPERTIES)
713 }
714}
715
716#[cfg(test)]
717mod tests;