1use bacnet_types::bitstring::EventTransitionBits;
4use bacnet_types::constructed::{
5 BACnetDeviceObjectPropertyReference, BACnetEventParameter, FaultParameters,
6};
7use bacnet_types::enums::{
8 ErrorClass, ErrorCode, EventState, EventType, NotifyType, ObjectType, PropertyIdentifier,
9 Reliability,
10};
11use bacnet_types::error::Error;
12use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, StatusFlags};
13use std::borrow::Cow;
14
15use crate::common::{self, read_common_properties};
16use crate::event::history::EventHistory;
17use crate::event::{
18 EnrollmentSummaryCapability, EventTransitionCommit, EventTransitionCommitError,
19};
20use crate::property_metadata::PropertyMetadata;
21use crate::traits::BACnetObject;
22
23mod alert;
24mod metadata;
25mod parameters;
26mod state;
27mod transition;
28pub use alert::AlertEnrollmentObject;
29pub use state::{EventEnrollmentEvalState, EventEnrollmentMonitoredSource, EventEnrollmentPending};
30pub use transition::EventEnrollmentReliabilityCommit;
31
32pub struct EventEnrollmentObject {
39 oid: ObjectIdentifier,
40 name: String,
41 description: String,
42 event_type: EventType,
43 notify_type: NotifyType,
44 event_parameters: BACnetEventParameter,
45 object_property_reference: Option<BACnetDeviceObjectPropertyReference>,
46 event_state: EventState,
47 event_enable: EventTransitionBits,
48 acked_transitions: EventTransitionBits,
49 event_history: EventHistory,
50 event_detection_enable: bool,
51 notification_class: u32,
52 fault_parameters: Option<FaultParameters>,
53 status_flags: StatusFlags,
54 reliability: Reliability,
55 time_delay_normal: Option<u32>,
62 pending: Option<EventEnrollmentPending>,
64 monitored_reference: Option<EventEnrollmentMonitoredSource>,
66 cov_baseline: Option<PropertyValue>,
68 last_offnormal_value: Option<u64>,
71}
72
73impl EventEnrollmentObject {
74 pub fn new(
76 instance: u32,
77 name: impl Into<String>,
78 event_type: EventType,
79 ) -> Result<Self, Error> {
80 let oid = ObjectIdentifier::new(ObjectType::EVENT_ENROLLMENT, instance)?;
81 Ok(Self {
82 oid,
83 name: name.into(),
84 description: String::new(),
85 event_type,
86 notify_type: NotifyType::ALARM,
87 event_parameters: BACnetEventParameter::Opaque {
88 tag: 0xFF,
89 data: Vec::new(),
90 },
91 object_property_reference: None,
92 event_state: EventState::NORMAL,
93 event_enable: EventTransitionBits::all(),
94 acked_transitions: Self::RESET_ACKED_TRANSITIONS,
99 event_history: EventHistory::default(),
100 event_detection_enable: true,
101 notification_class: 0,
102 fault_parameters: None,
103 status_flags: StatusFlags::empty(),
104 reliability: Reliability::NO_FAULT_DETECTED,
105 time_delay_normal: None,
109 pending: None,
110 monitored_reference: None,
111 cov_baseline: None,
112 last_offnormal_value: None,
113 })
114 }
115
116 const RESET_ACKED_TRANSITIONS: EventTransitionBits = EventTransitionBits::all();
120
121 fn apply_detection_disabled_reset(&mut self) {
138 self.event_state = EventState::NORMAL;
139 self.acked_transitions = Self::RESET_ACKED_TRANSITIONS;
140 self.event_history.reset();
141 self.pending = None;
142 self.monitored_reference = None;
143 self.cov_baseline = None;
144 self.last_offnormal_value = None;
145 }
146
147 pub fn set_description(&mut self, desc: impl Into<String>) {
149 self.description = desc.into();
150 }
151
152 pub fn set_object_property_reference(
162 &mut self,
163 reference: Option<BACnetDeviceObjectPropertyReference>,
164 ) -> Result<(), Error> {
165 if let Some(reference) = &reference {
166 crate::device_reference::check_device_member(reference.device_identifier)?;
167 }
168 self.object_property_reference = reference.and_then(crate::device_reference::set_or_unset);
169 self.pending = None;
170 Ok(())
171 }
172
173 pub fn set_event_parameters(&mut self, params: BACnetEventParameter) {
175 self.event_parameters = params;
176 self.pending = None;
177 }
178
179 pub fn set_fault_parameters(&mut self, fp: Option<FaultParameters>) {
181 self.fault_parameters = fp;
182 }
183
184 pub fn set_event_state(&mut self, state: EventState) {
193 if !self.event_detection_enable && state != EventState::NORMAL {
194 return;
195 }
196 self.event_state = state;
197 }
198
199 pub fn set_notification_class(&mut self, nc: u32) {
201 self.notification_class = nc;
202 }
203
204 pub fn set_event_enable(&mut self, enable: EventTransitionBits) {
208 self.event_enable = enable & EventTransitionBits::all();
209 }
210
211 pub fn set_time_delay_normal(&mut self, delay: Option<u32>) {
215 self.time_delay_normal = delay;
216 self.pending = None;
217 }
218
219 fn event_parameters_time_delay(&self) -> u32 {
226 use BACnetEventParameter as P;
227 match &self.event_parameters {
228 P::ChangeOfBitstring { time_delay, .. }
229 | P::ChangeOfState { time_delay, .. }
230 | P::ChangeOfValue { time_delay, .. }
231 | P::FloatingLimit { time_delay, .. }
232 | P::OutOfRange { time_delay, .. } => *time_delay,
233 _ => 0,
234 }
235 }
236
237 fn effective_fault_parameters(&self) -> &FaultParameters {
238 self.fault_parameters
239 .as_ref()
240 .unwrap_or(&FaultParameters::FaultNone)
241 }
242}
243
244impl BACnetObject for EventEnrollmentObject {
245 fn object_identifier(&self) -> ObjectIdentifier {
246 self.oid
247 }
248
249 fn object_name(&self) -> &str {
250 &self.name
251 }
252
253 fn enrollment_summary_capability_internal(&self) -> Option<EnrollmentSummaryCapability> {
254 let supported_parameters = match &self.event_parameters {
255 BACnetEventParameter::Extended { .. } => false,
256 BACnetEventParameter::Opaque { tag, .. } => *tag == 0xFF,
257 _ => true,
258 } || matches!(
259 self.effective_fault_parameters(),
260 FaultParameters::FaultStatusFlags { .. } | FaultParameters::FaultOutOfRange { .. }
261 );
262 (self.object_property_reference.is_some() && supported_parameters).then_some(
263 EnrollmentSummaryCapability {
264 event_type: self.event_type,
265 last_transition: self.event_history.last_transition(),
266 },
267 )
268 }
269
270 fn read_property(
271 &self,
272 property: PropertyIdentifier,
273 array_index: Option<u32>,
274 ) -> Result<PropertyValue, Error> {
275 if property == PropertyIdentifier::STATUS_FLAGS {
276 return Ok(common::compute_status_flags(
279 StatusFlags::empty(),
280 self.reliability,
281 false,
282 self.event_state,
283 ));
284 }
285 if let Some(result) =
287 read_common_properties!(self, property, array_index, no_out_of_service)
288 {
289 return result;
290 }
291 match property {
292 p if p == PropertyIdentifier::EVENT_TIME_STAMPS => self
293 .event_history
294 .read(p, array_index)
295 .expect("EventHistory handles Event_Time_Stamps"),
296 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
297 ObjectType::EVENT_ENROLLMENT.to_raw(),
298 )),
299 p if p == PropertyIdentifier::EVENT_TYPE => {
300 Ok(PropertyValue::Enumerated(self.event_type.to_raw()))
301 }
302 p if p == PropertyIdentifier::NOTIFY_TYPE => {
303 Ok(PropertyValue::Enumerated(self.notify_type.to_raw()))
304 }
305 p if p == PropertyIdentifier::EVENT_PARAMETERS => {
306 let mut buf = bytes::BytesMut::new();
307 bacnet_encoding::constructed::encode_event_parameter(
308 &mut buf,
309 &self.event_parameters,
310 )?;
311 Ok(PropertyValue::ApplicationData(buf.to_vec()))
312 }
313 p if p == PropertyIdentifier::OBJECT_PROPERTY_REFERENCE => {
317 Ok(crate::device_reference::optional_reference_value(
318 self.object_property_reference.as_ref(),
319 ObjectType::ANALOG_INPUT,
320 ))
321 }
322 p if p == PropertyIdentifier::EVENT_STATE => {
323 Ok(PropertyValue::Enumerated(self.event_state.to_raw()))
324 }
325 p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
326 unused_bits: 5,
327 data: vec![self.event_enable.to_bacnet()],
328 }),
329 p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
330 unused_bits: 5,
331 data: vec![self.acked_transitions.to_bacnet()],
332 }),
333 p if p == PropertyIdentifier::EVENT_DETECTION_ENABLE => {
334 Ok(PropertyValue::Boolean(self.event_detection_enable))
335 }
336 p if p == PropertyIdentifier::NOTIFICATION_CLASS => {
337 Ok(PropertyValue::Unsigned(self.notification_class as u64))
338 }
339 p if p == PropertyIdentifier::FAULT_TYPE => Ok(PropertyValue::Enumerated(
340 self.effective_fault_parameters().fault_type().to_raw(),
341 )),
342 p if p == PropertyIdentifier::FAULT_PARAMETERS => {
343 let mut buf = bytes::BytesMut::new();
344 bacnet_encoding::constructed::encode_fault_parameters(
345 &mut buf,
346 self.effective_fault_parameters(),
347 )?;
348 Ok(PropertyValue::ApplicationData(buf.to_vec()))
349 }
350 p if p == PropertyIdentifier::TIME_DELAY_NORMAL => {
351 Ok(PropertyValue::Unsigned(
357 self.time_delay_normal
358 .unwrap_or_else(|| self.event_parameters_time_delay())
359 as u64,
360 ))
361 }
362 _ => Err(common::unknown_property_error()),
363 }
364 }
365
366 fn write_property(
367 &mut self,
368 property: PropertyIdentifier,
369 _array_index: Option<u32>,
370 value: PropertyValue,
371 _priority: Option<u8>,
372 ) -> Result<(), Error> {
373 if property == PropertyIdentifier::NOTIFY_TYPE {
374 if let PropertyValue::Enumerated(v) = value {
378 let notify_type = NotifyType::from_raw(v);
379 if !NotifyType::ALL_NAMED.iter().any(|&(_, n)| n == notify_type) {
380 return Err(common::value_out_of_range_error());
381 }
382 self.notify_type = notify_type;
383 return Ok(());
384 }
385 return Err(common::invalid_data_type_error());
386 }
387 if property == PropertyIdentifier::NOTIFICATION_CLASS {
388 if let PropertyValue::Unsigned(v) = value {
389 self.notification_class = common::u64_to_u32(v)?;
390 return Ok(());
391 }
392 return Err(common::invalid_data_type_error());
393 }
394 if property == PropertyIdentifier::EVENT_ENABLE {
395 if let PropertyValue::BitString { unused_bits, data } = &value {
398 let byte = common::check_fixed_width_bit_string(*unused_bits, data, 3)?;
399 self.event_enable = EventTransitionBits::from_bacnet(&[byte]);
400 return Ok(());
401 }
402 return Err(common::invalid_data_type_error());
403 }
404 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
405 if let PropertyValue::Boolean(v) = value {
406 self.event_detection_enable = v;
407 if !v {
414 self.apply_detection_disabled_reset();
415 }
416 return Ok(());
417 }
418 return Err(common::invalid_data_type_error());
419 }
420 if property == PropertyIdentifier::EVENT_PARAMETERS {
427 self.set_event_parameters(parameters::decode_event_parameters(value)?);
428 return Ok(());
429 }
430 if property == PropertyIdentifier::FAULT_PARAMETERS {
431 let parameters = parameters::decode_fault_parameters(value)?;
433 self.fault_parameters =
434 (!matches!(parameters, FaultParameters::FaultNone)).then_some(parameters);
435 return Ok(());
436 }
437 if property == PropertyIdentifier::TIME_DELAY_NORMAL {
438 if let PropertyValue::Unsigned(v) = value {
443 self.set_time_delay_normal(Some(common::u64_to_u32(v)?));
444 return Ok(());
445 }
446 return Err(common::invalid_data_type_error());
447 }
448 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
449 return result;
450 }
451 if let Some(result) = common::write_description(&mut self.description, property, &value) {
452 return result;
453 }
454 Err(crate::common::unhandled_write_error(
455 self.property_metadata().as_ref(),
456 property,
457 _array_index,
458 ))
459 }
460
461 fn set_event_state_internal(&mut self, state: EventState) -> Result<(), Error> {
474 if !self.event_detection_enable && state != EventState::NORMAL {
475 return Err(common::write_access_denied_error());
476 }
477 self.event_state = state;
478 Ok(())
479 }
480
481 fn enrollment_eval_state_internal(&self) -> Option<EventEnrollmentEvalState> {
484 Some(EventEnrollmentEvalState {
485 pending: self.pending.clone(),
486 cov_baseline: self.cov_baseline.clone(),
487 last_offnormal_value: self.last_offnormal_value,
488 })
489 }
490
491 fn set_enrollment_eval_state_internal(
497 &mut self,
498 state: EventEnrollmentEvalState,
499 ) -> Result<(), Error> {
500 if !self.event_detection_enable {
501 return Err(common::write_access_denied_error());
502 }
503 self.pending = state.pending;
504 self.cov_baseline = state.cov_baseline;
505 self.last_offnormal_value = state.last_offnormal_value;
506 Ok(())
507 }
508
509 fn enrollment_eval_source_internal(&self) -> Option<Option<EventEnrollmentMonitoredSource>> {
510 Some(self.monitored_reference)
511 }
512
513 fn set_enrollment_eval_source_internal(
514 &mut self,
515 source: Option<EventEnrollmentMonitoredSource>,
516 ) -> Result<(), Error> {
517 if !self.event_detection_enable {
518 return Err(common::write_access_denied_error());
519 }
520 self.monitored_reference = source;
521 Ok(())
522 }
523
524 fn acknowledge_alarm(
535 &mut self,
536 transition_bit: EventTransitionBits,
537 ) -> Result<(), bacnet_types::error::Error> {
538 if !self.event_detection_enable {
539 return Err(bacnet_types::error::Error::Protocol {
540 class: bacnet_types::enums::ErrorClass::OBJECT.to_raw() as u32,
541 code: bacnet_types::enums::ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
542 });
543 }
544 self.acked_transitions |= transition_bit & EventTransitionBits::all();
545 Ok(())
546 }
547
548 fn acknowledge_alarm_correlated_internal(
549 &mut self,
550 event_state: EventState,
551 timestamp: &BACnetTimeStamp,
552 ) -> Result<(), Error> {
553 if !self.event_detection_enable || self.enrollment_summary_capability_internal().is_none() {
554 return Err(Error::Protocol {
555 class: ErrorClass::OBJECT.to_raw() as u32,
556 code: ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
557 });
558 }
559 self.event_history.acknowledge_correlated(
560 &mut self.acked_transitions,
561 event_state,
562 timestamp,
563 )
564 }
565
566 fn acknowledge_alarm_correlated_detailed_internal(
567 &mut self,
568 event_state: EventState,
569 timestamp: &BACnetTimeStamp,
570 ) -> Result<Option<crate::event::EventStateChange>, Error> {
571 if !self.event_detection_enable || self.enrollment_summary_capability_internal().is_none() {
572 return Err(Error::Protocol {
573 class: ErrorClass::OBJECT.to_raw() as u32,
574 code: ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
575 });
576 }
577 self.event_history.acknowledge_correlated_detailed(
578 &mut self.acked_transitions,
579 event_state,
580 timestamp,
581 )
582 }
583
584 fn set_acked_transitions_internal(
591 &mut self,
592 transition_bit: EventTransitionBits,
593 acknowledged: bool,
594 ) -> Result<(), Error> {
595 if !self.event_detection_enable {
596 return Err(common::write_access_denied_error());
597 }
598 self.acked_transitions
599 .set(transition_bit & EventTransitionBits::all(), acknowledged);
600 Ok(())
601 }
602
603 transition::impl_event_enrollment_transition_commit!();
604
605 fn is_writable_property(&self, property: PropertyIdentifier) -> bool {
623 matches!(
626 property,
627 PropertyIdentifier::OBJECT_NAME
628 | PropertyIdentifier::DESCRIPTION
629 | PropertyIdentifier::NOTIFY_TYPE
630 | PropertyIdentifier::NOTIFICATION_CLASS
631 | PropertyIdentifier::EVENT_ENABLE
632 | PropertyIdentifier::EVENT_DETECTION_ENABLE
633 | PropertyIdentifier::EVENT_PARAMETERS
634 | PropertyIdentifier::FAULT_PARAMETERS
635 | PropertyIdentifier::TIME_DELAY_NORMAL
636 )
637 }
638
639 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
640 Cow::Borrowed(metadata::EVENT_ENROLLMENT_PROPERTIES)
641 }
642
643 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
644 crate::property_metadata::property_list_from_metadata(metadata::EVENT_ENROLLMENT_PROPERTIES)
645 }
646}
647
648#[cfg(test)]
649mod tests;