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::Error;
18use bacnet_types::primitives::{BACnetTimeStamp, Date, ObjectIdentifier, StatusFlags, Time};
19use bytes::BytesMut;
20
21use super::tagged::{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| Error::Encoding(error.to_string()))?;
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        if 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    }
98    Ok(())
99}
100
101/// Decode request parameters from `data`; errors on missing, malformed or truncated fields.
102pub fn decode_event_notification(data: &[u8]) -> Result<EventNotificationRequest, Error> {
103    validate_tlv_sequence(data, "EventNotification")?;
104    // [0] processIdentifier
105    let (process_identifier, mut offset) =
106        decode_context_u32(data, 0, 0, "EventNotification processIdentifier")?;
107
108    // [1] initiatingDeviceIdentifier
109    let (content, new_offset) = decode_context(
110        data,
111        offset,
112        1,
113        "EventNotification initiatingDeviceIdentifier",
114    )?;
115    let initiating_device_identifier = ObjectIdentifier::decode(content)?;
116    offset = new_offset;
117
118    // [2] eventObjectIdentifier
119    let (content, new_offset) =
120        decode_context(data, offset, 2, "EventNotification eventObjectIdentifier")?;
121    let event_object_identifier = ObjectIdentifier::decode(content)?;
122    offset = new_offset;
123
124    // [3] timeStamp
125    let (timestamp, new_offset) = primitives::decode_timestamp(data, offset, 3)?;
126    offset = new_offset;
127
128    // [4] notificationClass
129    let (notification_class, new_offset) =
130        decode_context_u32(data, offset, 4, "EventNotification notificationClass")?;
131    offset = new_offset;
132
133    // [5] priority
134    let priority_offset = offset;
135    let (content, new_offset) = decode_context(data, offset, 5, "EventNotification priority")?;
136    let priority = primitives::decode_unsigned(content)?;
137    let priority = u8::try_from(priority).map_err(|_| {
138        Error::out_of_range(priority_offset, "EventNotification priority exceeds u8")
139    })?;
140    offset = new_offset;
141
142    // [6] eventType
143    let (event_type, new_offset) = decode_context_enum(
144        data,
145        offset,
146        6,
147        "EventNotification eventType",
148        EventType::from_raw,
149    )?;
150    offset = new_offset;
151
152    // [7] messageText (optional)
153    let mut message_text = None;
154    if offset < data.len() {
155        let (peek, _) = tags::decode_tag(data, offset)?;
156        if peek.is_context(7) {
157            let (content, new_offset) =
158                decode_context(data, offset, 7, "EventNotification messageText")?;
159            message_text = Some(primitives::decode_character_string(content)?);
160            offset = new_offset;
161        }
162    }
163
164    // [8] notifyType
165    let (notify_type, new_offset) = decode_context_enum(
166        data,
167        offset,
168        8,
169        "EventNotification notifyType",
170        NotifyType::from_raw,
171    )?;
172    offset = new_offset;
173
174    // [9] ackRequired (optional — present for ALARM/EVENT)
175    let mut ack_required = false;
176    if offset < data.len() {
177        let (peek, _) = tags::decode_tag(data, offset)?;
178        if peek.is_context(9) {
179            (ack_required, offset) =
180                decode_context_bool(data, offset, 9, "EventNotification ackRequired")?;
181        }
182    }
183
184    // [10] fromState (absent for ACK_NOTIFICATION)
185    let mut from_state = EventState::NORMAL;
186    if offset < data.len() {
187        let (peek, _) = tags::decode_tag(data, offset)?;
188        if peek.is_context(10) {
189            (from_state, offset) = decode_context_enum(
190                data,
191                offset,
192                10,
193                "EventNotification fromState",
194                EventState::from_raw,
195            )?;
196        } else if notify_type != NotifyType::ACK_NOTIFICATION {
197            return Err(misplaced_tag(
198                data,
199                &peek,
200                Some(10),
201                offset,
202                "EventNotification expected fromState",
203            ));
204        }
205    } else if notify_type != NotifyType::ACK_NOTIFICATION {
206        return Err(Error::missing(
207            offset,
208            "EventNotification missing fromState",
209        ));
210    }
211
212    // [11] toState
213    let (to_state, new_offset) = decode_context_enum(
214        data,
215        offset,
216        11,
217        "EventNotification toState",
218        EventState::from_raw,
219    )?;
220    offset = new_offset;
221
222    // [12] eventValues — optional
223    let mut event_values = None;
224    if offset < data.len() {
225        let (opening, inner_start) = tags::decode_tag(data, offset)?;
226        if !opening.is_opening_tag(12) {
227            return Err(misplaced_tag(
228                data,
229                &opening,
230                Some(12),
231                offset,
232                "EventNotification expected opening tag 12 for eventValues",
233            ));
234        }
235        let closing_offset = data
236            .len()
237            .checked_sub(1)
238            .ok_or_else(|| Error::missing(offset, "EventNotification missing closing tag 12"))?;
239        if closing_offset < inner_start {
240            // The data ends right after the opening tag.
241            return Err(Error::missing(
242                inner_start,
243                "EventNotification eventValues are never closed",
244            ));
245        }
246        let (closing, next) = tags::decode_tag(data, closing_offset)?;
247        if !closing.is_closing_tag(12) || next != data.len() {
248            // Where the frame really ends: past the data (no closing tag) or
249            // before its end (octets after the request).
250            return Err(match tags::extract_context_value(data, inner_start, 12) {
251                Err(error) => error,
252                Ok((_, end)) if end < data.len() => {
253                    Error::trailing(end, "EventNotification has octets after eventValues")
254                }
255                Ok(_) => Error::invalid_tag(
256                    closing_offset,
257                    "EventNotification expected closing tag 12 after eventValues",
258                ),
259            });
260        }
261        event_values = Some(parameters_decode::decode_bounded(
262            data,
263            inner_start,
264            closing_offset,
265        )?);
266        offset = next;
267    }
268    let _ = offset;
269
270    Ok(EventNotificationRequest {
271        process_identifier,
272        initiating_device_identifier,
273        event_object_identifier,
274        timestamp,
275        notification_class,
276        priority,
277        event_type,
278        message_text,
279        notify_type,
280        ack_required,
281        from_state,
282        to_state,
283        event_values,
284    })
285}