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}