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