1use 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
46pub 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 primitives::encode_ctx_unsigned(buf, 0, request.process_identifier as u64);
62 primitives::encode_ctx_object_id(buf, 1, &request.initiating_device_identifier);
64 primitives::encode_ctx_object_id(buf, 2, &request.event_object_identifier);
66 primitives::encode_timestamp(buf, 3, &request.timestamp)?;
68 primitives::encode_ctx_unsigned(buf, 4, request.notification_class as u64);
70 primitives::encode_ctx_unsigned(buf, 5, request.priority as u64);
72 primitives::encode_ctx_enumerated(buf, 6, request.event_type.to_raw());
74 if let Some(ref text) = request.message_text {
76 primitives::encode_ctx_character_string(buf, 7, text)?;
77 }
78 primitives::encode_ctx_enumerated(buf, 8, request.notify_type.to_raw());
80 if request.notify_type != NotifyType::ACK_NOTIFICATION {
82 primitives::encode_ctx_boolean(buf, 9, request.ack_required);
83 }
84 if request.notify_type != NotifyType::ACK_NOTIFICATION {
86 primitives::encode_ctx_enumerated(buf, 10, request.from_state.to_raw());
87 }
88 primitives::encode_ctx_enumerated(buf, 11, request.to_state.to_raw());
90 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
100pub fn decode_event_notification(data: &[u8]) -> Result<EventNotificationRequest, Error> {
102 validate_tlv_sequence(data, "EventNotification")?;
103 let (process_identifier, mut offset) =
105 decode_context_u32(data, 0, 0, "EventNotification processIdentifier")?;
106
107 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 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 let (timestamp, new_offset) = primitives::decode_timestamp(data, offset, 3)?;
125 offset = new_offset;
126
127 let (notification_class, new_offset) =
129 decode_context_u32(data, offset, 4, "EventNotification notificationClass")?;
130 offset = new_offset;
131
132 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 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 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 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 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 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 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 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 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 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}