Skip to main content

bacnet_encoding/constructed/event_notification/
mod.rs

1//! ConfirmedEventNotification / UnconfirmedEventNotification request
2//! parameters (Clauses 13.8 and 13.9) and their event values, the
3//! NotificationParameters choice of Clause 21.
4//!
5//! An Event Log record carries the same request parameters as its
6//! notification datum (Clause 12.27.13), so the record codec reuses these
7//! functions.
8
9#[cfg(not(feature = "std"))]
10use alloc::string::ToString;
11
12use bacnet_types::constructed::{
13    BACnetDeviceObjectPropertyReference, BACnetDeviceObjectReference, BACnetPropertyStates,
14    ChangeOfValueChoice, EventNotificationRequest, NotificationParameters,
15};
16use bacnet_types::enums::{
17    AccessEvent, EventState, EventType, LifeSafetyMode, LifeSafetyOperation, LifeSafetyState,
18    NotifyType, Reliability, TimerState, TimerTransition,
19};
20use bacnet_types::error::Error;
21use bacnet_types::primitives::{BACnetTimeStamp, Date, ObjectIdentifier, StatusFlags, Time};
22use bytes::BytesMut;
23
24use super::tagged::{misplaced_tag, unclosed_kind};
25use super::{
26    decode_bacnet_property_value_in_list, encode_bacnet_property_value, validate_tlv_sequence,
27    MAX_FRAMED_ITEMS,
28};
29use crate::{primitives, tags};
30use decode_helpers::{
31    decode_context, decode_context_bool, decode_context_enum, decode_context_u32,
32};
33use property_states::{
34    decode_device_obj_prop_ref, decode_property_states, encode_property_states, extract_raw_context,
35};
36
37mod decode_helpers;
38mod decode_timer;
39mod parameters_decode;
40mod parameters_encode;
41mod property_states;
42mod structured;
43mod tolerant;
44
45pub use parameters_decode::decode_notification_parameters;
46pub use parameters_encode::encode_notification_parameters;
47pub use tolerant::decode_event_notification_tolerant;
48
49/// Append the encoded request parameters to `buf`; `buf` is unchanged if validation fails.
50pub fn encode_event_notification(
51    request: &EventNotificationRequest,
52    buf: &mut BytesMut,
53) -> Result<(), Error> {
54    let mut encoded = BytesMut::new();
55    encode_into(request, &mut encoded)?;
56    validate_tlv_sequence(&encoded, "EventNotification")
57        .map_err(|error| Error::Encoding(error.to_string()))?;
58    buf.extend_from_slice(&encoded);
59    Ok(())
60}
61
62fn encode_into(request: &EventNotificationRequest, buf: &mut BytesMut) -> Result<(), Error> {
63    // [0] processIdentifier
64    primitives::encode_ctx_unsigned(buf, 0, request.process_identifier as u64);
65    // [1] initiatingDeviceIdentifier
66    primitives::encode_ctx_object_id(buf, 1, &request.initiating_device_identifier);
67    // [2] eventObjectIdentifier
68    primitives::encode_ctx_object_id(buf, 2, &request.event_object_identifier);
69    // [3] timeStamp
70    primitives::encode_timestamp(buf, 3, &request.timestamp)?;
71    // [4] notificationClass
72    primitives::encode_ctx_unsigned(buf, 4, request.notification_class as u64);
73    // [5] priority
74    primitives::encode_ctx_unsigned(buf, 5, request.priority as u64);
75    // [6] eventType
76    primitives::encode_ctx_enumerated(buf, 6, request.event_type.to_raw());
77    // [7] messageText (optional)
78    if let Some(ref text) = request.message_text {
79        primitives::encode_ctx_character_string(buf, 7, text)?;
80    }
81    // [8] notifyType
82    primitives::encode_ctx_enumerated(buf, 8, request.notify_type.to_raw());
83    // [9] ackRequired (only for ALARM/EVENT)
84    if request.notify_type != NotifyType::ACK_NOTIFICATION {
85        primitives::encode_ctx_boolean(buf, 9, request.ack_required);
86    }
87    // [10] fromState (only for ALARM/EVENT)
88    if request.notify_type != NotifyType::ACK_NOTIFICATION {
89        primitives::encode_ctx_enumerated(buf, 10, request.from_state.to_raw());
90    }
91    // [11] toState
92    primitives::encode_ctx_enumerated(buf, 11, request.to_state.to_raw());
93    // [12] eventValues — optional
94    if request.notify_type != NotifyType::ACK_NOTIFICATION {
95        if let Some(ref params) = request.event_values {
96            tags::encode_opening_tag(buf, 12);
97            encode_notification_parameters(params, buf)?;
98            tags::encode_closing_tag(buf, 12);
99        }
100    }
101    Ok(())
102}
103
104/// Decode request parameters from `data`; errors on missing, malformed or truncated fields.
105pub fn decode_event_notification(data: &[u8]) -> Result<EventNotificationRequest, Error> {
106    validate_tlv_sequence(data, "EventNotification")?;
107    // [0] processIdentifier
108    let (process_identifier, mut offset) =
109        decode_context_u32(data, 0, 0, "EventNotification processIdentifier")?;
110
111    // [1] initiatingDeviceIdentifier
112    let (content, new_offset) = decode_context(
113        data,
114        offset,
115        1,
116        "EventNotification initiatingDeviceIdentifier",
117    )?;
118    let initiating_device_identifier = ObjectIdentifier::decode(content)?;
119    offset = new_offset;
120
121    // [2] eventObjectIdentifier
122    let (content, new_offset) =
123        decode_context(data, offset, 2, "EventNotification eventObjectIdentifier")?;
124    let event_object_identifier = ObjectIdentifier::decode(content)?;
125    offset = new_offset;
126
127    // [3] timeStamp
128    let (timestamp, new_offset) = primitives::decode_timestamp(data, offset, 3)?;
129    offset = new_offset;
130
131    // [4] notificationClass
132    let (notification_class, new_offset) =
133        decode_context_u32(data, offset, 4, "EventNotification notificationClass")?;
134    offset = new_offset;
135
136    // [5] priority
137    let priority_offset = offset;
138    let (content, new_offset) = decode_context(data, offset, 5, "EventNotification priority")?;
139    let priority = primitives::decode_unsigned(content)?;
140    let priority = u8::try_from(priority).map_err(|_| {
141        Error::out_of_range(priority_offset, "EventNotification priority exceeds u8")
142    })?;
143    offset = new_offset;
144
145    // [6] eventType
146    let (event_type, new_offset) = decode_context_enum(
147        data,
148        offset,
149        6,
150        "EventNotification eventType",
151        EventType::from_raw,
152    )?;
153    offset = new_offset;
154
155    // [7] messageText (optional)
156    let mut message_text = None;
157    if offset < data.len() {
158        let (peek, _) = tags::decode_tag(data, offset)?;
159        if peek.is_context(7) {
160            let (content, new_offset) =
161                decode_context(data, offset, 7, "EventNotification messageText")?;
162            message_text = Some(primitives::decode_character_string(content)?);
163            offset = new_offset;
164        }
165    }
166
167    // [8] notifyType
168    let (notify_type, new_offset) = decode_context_enum(
169        data,
170        offset,
171        8,
172        "EventNotification notifyType",
173        NotifyType::from_raw,
174    )?;
175    offset = new_offset;
176
177    // [9] ackRequired (optional — present for ALARM/EVENT)
178    let mut ack_required = false;
179    if offset < data.len() {
180        let (peek, _) = tags::decode_tag(data, offset)?;
181        if peek.is_context(9) {
182            (ack_required, offset) =
183                decode_context_bool(data, offset, 9, "EventNotification ackRequired")?;
184        }
185    }
186
187    // [10] fromState (absent for ACK_NOTIFICATION)
188    let mut from_state = EventState::NORMAL;
189    if offset < data.len() {
190        let (peek, _) = tags::decode_tag(data, offset)?;
191        if peek.is_context(10) {
192            (from_state, offset) = decode_context_enum(
193                data,
194                offset,
195                10,
196                "EventNotification fromState",
197                EventState::from_raw,
198            )?;
199        } else if notify_type != NotifyType::ACK_NOTIFICATION {
200            return Err(misplaced_tag(
201                data,
202                &peek,
203                Some(10),
204                offset,
205                "EventNotification expected fromState",
206            ));
207        }
208    } else if notify_type != NotifyType::ACK_NOTIFICATION {
209        return Err(Error::missing(
210            offset,
211            "EventNotification missing fromState",
212        ));
213    }
214
215    // [11] toState
216    let (to_state, new_offset) = decode_context_enum(
217        data,
218        offset,
219        11,
220        "EventNotification toState",
221        EventState::from_raw,
222    )?;
223    offset = new_offset;
224
225    // [12] eventValues — optional
226    let mut event_values = None;
227    if offset < data.len() {
228        let (opening, inner_start) = tags::decode_tag(data, offset)?;
229        if !opening.is_opening_tag(12) {
230            return Err(misplaced_tag(
231                data,
232                &opening,
233                Some(12),
234                offset,
235                "EventNotification expected opening tag 12 for eventValues",
236            ));
237        }
238        let closing_offset = data
239            .len()
240            .checked_sub(1)
241            .ok_or_else(|| Error::missing(offset, "EventNotification missing closing tag 12"))?;
242        if closing_offset < inner_start {
243            // The data ends right after the opening tag.
244            return Err(Error::missing(
245                inner_start,
246                "EventNotification eventValues are never closed",
247            ));
248        }
249        let (closing, next) = tags::decode_tag(data, closing_offset)?;
250        if !closing.is_closing_tag(12) || next != data.len() {
251            // Where the frame really ends: past the data (no closing tag) or
252            // before its end (octets after the request).
253            return Err(match tags::extract_context_value(data, inner_start, 12) {
254                Err(error) => error,
255                Ok((_, end)) if end < data.len() => {
256                    Error::trailing(end, "EventNotification has octets after eventValues")
257                }
258                Ok(_) => Error::invalid_tag(
259                    closing_offset,
260                    "EventNotification expected closing tag 12 after eventValues",
261                ),
262            });
263        }
264        event_values = Some(parameters_decode::decode_bounded(
265            data,
266            inner_start,
267            closing_offset,
268        )?);
269        offset = next;
270    }
271    let _ = offset;
272
273    Ok(EventNotificationRequest {
274        process_identifier,
275        initiating_device_identifier,
276        event_object_identifier,
277        timestamp,
278        notification_class,
279        priority,
280        event_type,
281        message_text,
282        notify_type,
283        ack_required,
284        from_state,
285        to_state,
286        event_values,
287    })
288}