1#[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
49pub 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 primitives::encode_ctx_unsigned(buf, 0, request.process_identifier as u64);
65 primitives::encode_ctx_object_id(buf, 1, &request.initiating_device_identifier);
67 primitives::encode_ctx_object_id(buf, 2, &request.event_object_identifier);
69 primitives::encode_timestamp(buf, 3, &request.timestamp)?;
71 primitives::encode_ctx_unsigned(buf, 4, request.notification_class as u64);
73 primitives::encode_ctx_unsigned(buf, 5, request.priority as u64);
75 primitives::encode_ctx_enumerated(buf, 6, request.event_type.to_raw());
77 if let Some(ref text) = request.message_text {
79 primitives::encode_ctx_character_string(buf, 7, text)?;
80 }
81 primitives::encode_ctx_enumerated(buf, 8, request.notify_type.to_raw());
83 if request.notify_type != NotifyType::ACK_NOTIFICATION {
85 primitives::encode_ctx_boolean(buf, 9, request.ack_required);
86 }
87 if request.notify_type != NotifyType::ACK_NOTIFICATION {
89 primitives::encode_ctx_enumerated(buf, 10, request.from_state.to_raw());
90 }
91 primitives::encode_ctx_enumerated(buf, 11, request.to_state.to_raw());
93 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
104pub fn decode_event_notification(data: &[u8]) -> Result<EventNotificationRequest, Error> {
106 validate_tlv_sequence(data, "EventNotification")?;
107 let (process_identifier, mut offset) =
109 decode_context_u32(data, 0, 0, "EventNotification processIdentifier")?;
110
111 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 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 let (timestamp, new_offset) = primitives::decode_timestamp(data, offset, 3)?;
129 offset = new_offset;
130
131 let (notification_class, new_offset) =
133 decode_context_u32(data, offset, 4, "EventNotification notificationClass")?;
134 offset = new_offset;
135
136 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 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 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 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 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 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 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 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 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 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}