Skip to main content

bacnet_objects/event_enrollment/
state.rs

1use bacnet_types::constructed::{BACnetEventParameter, FaultParameters};
2use bacnet_types::enums::{EventState, PropertyIdentifier};
3use bacnet_types::primitives::{ObjectIdentifier, PropertyValue};
4
5use crate::event::history::EventHistory;
6
7pub(super) struct AlertEnrollmentWriteRollback {
8    pub(super) enabled: bool,
9    pub(super) event_state: u32,
10    pub(super) acked_transitions: u8,
11    pub(super) event_history: EventHistory,
12}
13
14/// Effective object, property, and optional array index that own an Event
15/// Enrollment object's private evaluation state.
16pub type EventEnrollmentMonitoredSource = (ObjectIdentifier, PropertyIdentifier, Option<u32>);
17
18/// A delayed Event Enrollment transition, counting down its delay.
19///
20/// The enrollment counterpart of the intrinsic detectors'
21/// [`PendingTransition`](crate::event::PendingTransition), kept as a distinct
22/// type because the driving mechanism differs: the server evaluator advances
23/// `remaining` once per *evaluation pass* (the `event_enrollment_task`
24/// interval, configurable via #133), whereas the intrinsic detectors tick on
25/// a fixed one-second task and seed from per-write probes. Clause 13.2.4
26/// semantics are shared — the observable `Event_State` holds at the confirmed
27/// state while the countdown runs, a reverted condition cancels without
28/// firing, and a redundant qualifying observation never re-seeds — but the
29/// two implementations do not share code across the objects/server boundary.
30///
31/// In-memory only: like the intrinsic detectors' pending state and baselines,
32/// this is not persisted; a device restart re-evaluation starts from the
33/// confirmed `Event_State`, which is the same restart semantics the
34/// intrinsic-reporting path ships.
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct EventEnrollmentPending {
37    /// The event state the algorithm indicated and will enter when the
38    /// countdown elapses.
39    pub state: EventState,
40    /// Evaluation passes remaining before the transition fires; seeded with
41    /// the direction-appropriate delay (pTimeDelay for offnormal targets,
42    /// pTimeDelayNormal — else pTimeDelay — for NORMAL), converted from
43    /// seconds by the evaluator as `ceil(delay_secs / interval_secs)`.
44    pub remaining: u32,
45    /// Identity of the indicating condition, per algorithm. CHANGE_OF_STATE
46    /// uses one identity for condition (a)'s "any" alarm value and a
47    /// value-specific identity for condition (c)'s "that" value.
48    /// CHANGE_OF_BITSTRING uses the masked monitored bytes. Algorithms whose
49    /// delay applies to the threshold condition itself (OUT_OF_RANGE,
50    /// FLOATING_LIMIT, CHANGE_OF_VALUE) use `0`; the target identifies them.
51    pub condition: u64,
52    /// Fingerprint of the `Event_Parameters` (framed encoding) plus the
53    /// effective `Time_Delay_Normal` in force when this countdown was seeded.
54    /// The evaluator re-reads its parameters every pass; a mismatch cancels
55    /// the in-flight countdown and re-gates from the current parameters —
56    /// no partial countdown is resumed across a parameter change.
57    pub params_fingerprint: u64,
58}
59
60/// Algorithm-side evaluation state owned by an Event Enrollment object.
61///
62/// Not BACnet properties: none of the three slots maps to a Clause 12.12
63/// property (nor to the Table 12-14 `Time_Delay_Normal`, which is
64/// configuration and lives on the object directly). Clause 13.3 assigns the
65/// baseline's initialization and the countdown's existence to local matters,
66/// so they are reachable only through the internal trait channel
67/// ([`BACnetObject::enrollment_eval_state_internal`](crate::traits::BACnetObject::enrollment_eval_state_internal) /
68/// [`BACnetObject::set_enrollment_eval_state_internal`](crate::traits::BACnetObject::set_enrollment_eval_state_internal)),
69/// mirroring the `set_event_state_internal` precedent (issue #130).
70#[derive(Debug, Clone, Default, PartialEq)]
71pub struct EventEnrollmentEvalState {
72    /// Delayed transition in flight, if any.
73    pub pending: Option<EventEnrollmentPending>,
74    /// CHANGE_OF_VALUE detection baseline (Clause 13.3.3: "the value of the
75    /// monitored value when a transition to NORMAL is indicated shall be used
76    /// in evaluation of the conditions until the next transition to NORMAL is
77    /// indicated"). `None` before the first sample; the first observed value
78    /// initializes it without indicating a transition ("the initialization of
79    /// the value used in evaluation before the first transition to NORMAL is
80    /// indicated is a local matter" — the policy chosen here).
81    pub cov_baseline: Option<PropertyValue>,
82    /// Domain-tagged identity of the monitored value that caused the last
83    /// transition to OFFNORMAL. CHANGE_OF_STATE condition (c) requires a
84    /// re-indication only for a different alarm value; retaining the BACnet
85    /// datatype keeps equal numeric values from different domains distinct.
86    pub last_offnormal_value: Option<u64>,
87}
88
89pub(super) enum EventEnrollmentWriteRollback {
90    Detection {
91        enabled: bool,
92        event_state: u32,
93        acked_transitions: u8,
94        event_history: EventHistory,
95        monitored_reference: Option<EventEnrollmentMonitoredSource>,
96        evaluation: EventEnrollmentEvalState,
97    },
98    EventParameters {
99        value: BACnetEventParameter,
100        pending: Option<EventEnrollmentPending>,
101    },
102    FaultParameters(Option<FaultParameters>),
103    TimeDelayNormal {
104        value: Option<u32>,
105        pending: Option<EventEnrollmentPending>,
106    },
107}