Skip to main content

bacnet_server/handlers/alarm_event/
acknowledge_alarm.rs

1//! AcknowledgeAlarm service handler.
2//!
3//! One file per service so the Batch 4 handler PRs do not serialize
4//! on a single module.
5
6use super::super::*;
7use bacnet_objects::event::EventStateChange;
8
9/// Exact context the bundled server can use for ACK_NOTIFICATION distribution.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct AcknowledgmentNotificationContext {
12    /// Exact historical transition correlated by the request.
13    pub change: EventStateChange,
14    /// Effective Event Type for the acknowledged transition.
15    pub event_type: EventType,
16    /// Current Event_Enable decision for the transition coordinate.
17    pub distribute: bool,
18}
19
20/// A successfully correlated and applied acknowledgment.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct AcceptedAcknowledgeAlarm {
23    /// Object whose acknowledgment bit was accepted.
24    pub event_object_identifier: ObjectIdentifier,
25    /// Exact notification context, when the object can provide it.
26    ///
27    /// Custom objects implementing only the legacy coarse acknowledgment hook
28    /// return `None`; their accepted acknowledgment remains successful.
29    pub notification: Option<AcknowledgmentNotificationContext>,
30}
31
32/// Handle an AcknowledgeAlarm request.
33///
34/// Correlates the request with one committed transition before acknowledging it.
35pub fn handle_acknowledge_alarm(
36    db: &mut ObjectDatabase,
37    service_data: &[u8],
38) -> Result<AcceptedAcknowledgeAlarm, Error> {
39    let request = AcknowledgeAlarmRequest::decode(service_data)?;
40    let event_state = EventState::from_raw(request.event_state_acknowledged);
41
42    let object = db
43        .get_mut(&request.event_object_identifier)
44        .ok_or(Error::Protocol {
45            class: ErrorClass::OBJECT.to_raw() as u32,
46            code: ErrorCode::UNKNOWN_OBJECT.to_raw() as u32,
47        })?;
48
49    let change =
50        object.acknowledge_alarm_correlated_detailed_internal(event_state, &request.timestamp)?;
51
52    let notification = change.and_then(|change| {
53        let algorithm = object.enrollment_summary_capability_internal()?.event_type;
54        let PropertyValue::BitString { unused_bits, data } = object
55            .read_property(PropertyIdentifier::EVENT_ENABLE, None)
56            .ok()?
57        else {
58            return None;
59        };
60        if unused_bits != 5 || data.len() != 1 || data[0] & 0x1f != 0 {
61            return None;
62        }
63        let enabled = bacnet_types::bitstring::unpack_octet(&data, 3);
64        let distribute = enabled & change.transition().bit_mask() != 0;
65        let event_type = change.event_type(algorithm);
66        Some(AcknowledgmentNotificationContext {
67            change,
68            event_type,
69            distribute,
70        })
71    });
72
73    // These request fields are validated by decoding but intentionally are not
74    // persisted. ACK_NOTIFICATION construction uses object history and a fresh
75    // send-time timestamp rather than either caller timestamp.
76    let _ = (
77        request.acknowledging_process_identifier,
78        request.acknowledgment_source,
79        request.time_of_acknowledgment,
80    );
81
82    Ok(AcceptedAcknowledgeAlarm {
83        event_object_identifier: request.event_object_identifier,
84        notification,
85    })
86}