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