1use super::*;
2use crate::constructed::log_fields::check_bit_string;
3use bacnet_types::error::EncodeError;
4
5fn encode_status_flags(buf: &mut BytesMut, tag: u8, flags: StatusFlags) {
8 primitives::encode_ctx_bit_string(buf, tag, 4, &[flags.bits() << 4]);
9}
10
11pub fn encode_notification_parameters(
16 params: &NotificationParameters,
17 buf: &mut BytesMut,
18) -> Result<(), Error> {
19 let mut encoded = BytesMut::new();
20 encode_into(params, &mut encoded)?;
21 if matches!(params, NotificationParameters::ComplexEventType { .. }) {
22 let (opening, content_start) =
23 tags::decode_tag(&encoded, 0).map_err(Error::into_encode_refusal)?;
24 if !opening.is_opening_tag(6) {
25 return Err(Error::encode("ComplexEventType missing opening tag [6]"));
26 }
27 let (_, next) = tags::extract_context_value(&encoded, content_start, 6)
28 .map_err(Error::into_encode_refusal)?;
29 if next != encoded.len() {
30 return Err(Error::encode("ComplexEventType has trailing encoded fields"));
31 }
32 } else {
33 validate_tlv_sequence(&encoded, "NotificationParameters")
34 .map_err(Error::into_encode_refusal)?;
35 }
36 buf.extend_from_slice(&encoded);
37 Ok(())
38}
39
40fn encode_into(params: &NotificationParameters, buf: &mut BytesMut) -> Result<(), Error> {
41 match params {
42 NotificationParameters::ChangeOfBitstring {
43 referenced_bitstring,
44 status_flags,
45 } => {
46 let (unused_bits, data) = referenced_bitstring;
47 check_bit_string(
48 *unused_bits,
49 data,
50 "ChangeOfBitstring referenced-bitstring has {} unused bits over {} octets",
51 )?;
52 tags::encode_opening_tag(buf, 0);
53 primitives::encode_ctx_bit_string(buf, 0, *unused_bits, data);
54 encode_status_flags(buf, 1, *status_flags);
55 tags::encode_closing_tag(buf, 0);
56 }
57 NotificationParameters::ChangeOfState {
58 new_state,
59 status_flags,
60 } => {
61 tags::encode_opening_tag(buf, 1);
62 tags::encode_opening_tag(buf, 0);
64 encode_property_states(buf, new_state)?;
65 tags::encode_closing_tag(buf, 0);
66 encode_status_flags(buf, 1, *status_flags);
68 tags::encode_closing_tag(buf, 1);
69 }
70 NotificationParameters::ChangeOfValue {
71 new_value,
72 status_flags,
73 } => {
74 tags::encode_opening_tag(buf, 2);
75 tags::encode_opening_tag(buf, 0);
77 match new_value {
78 ChangeOfValueChoice::ChangedBits { unused_bits, data } => {
79 check_bit_string(
80 *unused_bits,
81 data,
82 "ChangeOfValue changed-bits has {} unused bits over {} octets",
83 )?;
84 primitives::encode_ctx_bit_string(buf, 0, *unused_bits, data);
85 }
86 ChangeOfValueChoice::ChangedValue(v) => {
87 primitives::encode_ctx_real(buf, 1, *v);
88 }
89 }
90 tags::encode_closing_tag(buf, 0);
91 encode_status_flags(buf, 1, *status_flags);
93 tags::encode_closing_tag(buf, 2);
94 }
95 NotificationParameters::CommandFailure {
96 command_value,
97 status_flags,
98 feedback_value,
99 } => {
100 tags::encode_opening_tag(buf, 3);
101 tags::encode_opening_tag(buf, 0);
103 buf.extend_from_slice(command_value);
104 tags::encode_closing_tag(buf, 0);
105 encode_status_flags(buf, 1, *status_flags);
107 tags::encode_opening_tag(buf, 2);
109 buf.extend_from_slice(feedback_value);
110 tags::encode_closing_tag(buf, 2);
111 tags::encode_closing_tag(buf, 3);
112 }
113 NotificationParameters::FloatingLimit {
114 reference_value,
115 status_flags,
116 setpoint_value,
117 error_limit,
118 } => {
119 tags::encode_opening_tag(buf, 4);
120 primitives::encode_ctx_real(buf, 0, *reference_value);
121 encode_status_flags(buf, 1, *status_flags);
122 primitives::encode_ctx_real(buf, 2, *setpoint_value);
123 primitives::encode_ctx_real(buf, 3, *error_limit);
124 tags::encode_closing_tag(buf, 4);
125 }
126 NotificationParameters::OutOfRange {
127 exceeding_value,
128 status_flags,
129 deadband,
130 exceeded_limit,
131 } => {
132 tags::encode_opening_tag(buf, 5);
133 primitives::encode_ctx_real(buf, 0, *exceeding_value);
134 encode_status_flags(buf, 1, *status_flags);
135 primitives::encode_ctx_real(buf, 2, *deadband);
136 primitives::encode_ctx_real(buf, 3, *exceeded_limit);
137 tags::encode_closing_tag(buf, 5);
138 }
139 NotificationParameters::ComplexEventType { property_values } => {
140 if property_values.len() > MAX_FRAMED_ITEMS {
141 return Err(EncodeError::new("ComplexEventType exceeds {} property values")
142 .with_value(MAX_FRAMED_ITEMS as u32)
143 .into());
144 }
145 tags::encode_opening_tag(buf, 6);
146 for property_value in property_values {
147 if property_value
148 .priority
149 .is_some_and(|priority| !(1..=16).contains(&priority))
150 {
151 return Err(Error::encode("ComplexEventType property priority must be in 1..=16"));
152 }
153 validate_tlv_sequence(&property_value.value, "ComplexEventType property value")
154 .map_err(Error::into_encode_refusal)?;
155 encode_bacnet_property_value(property_value, buf);
156 }
157 tags::encode_closing_tag(buf, 6);
158 }
159 NotificationParameters::ChangeOfLifeSafety {
160 new_state,
161 new_mode,
162 status_flags,
163 operation_expected,
164 } => {
165 tags::encode_opening_tag(buf, 8);
166 primitives::encode_ctx_enumerated(buf, 0, new_state.to_raw());
167 primitives::encode_ctx_enumerated(buf, 1, new_mode.to_raw());
168 encode_status_flags(buf, 2, *status_flags);
169 primitives::encode_ctx_enumerated(buf, 3, operation_expected.to_raw());
170 tags::encode_closing_tag(buf, 8);
171 }
172 NotificationParameters::Extended {
173 vendor_id,
174 extended_event_type,
175 parameters,
176 } => {
177 tags::encode_opening_tag(buf, 9);
178 primitives::encode_ctx_unsigned(buf, 0, *vendor_id as u64);
179 primitives::encode_ctx_unsigned(buf, 1, *extended_event_type as u64);
180 tags::encode_opening_tag(buf, 2);
182 buf.extend_from_slice(parameters);
183 tags::encode_closing_tag(buf, 2);
184 tags::encode_closing_tag(buf, 9);
185 }
186 NotificationParameters::BufferReady {
187 buffer_property,
188 previous_notification,
189 current_notification,
190 } => {
191 tags::encode_opening_tag(buf, 10);
192 tags::encode_opening_tag(buf, 0);
194 primitives::encode_ctx_object_id(buf, 0, &buffer_property.object_identifier);
195 primitives::encode_ctx_unsigned(buf, 1, buffer_property.property_identifier as u64);
196 if let Some(idx) = buffer_property.property_array_index {
197 primitives::encode_ctx_unsigned(buf, 2, idx as u64);
198 }
199 if let Some(ref dev) = buffer_property.device_identifier {
200 primitives::encode_ctx_object_id(buf, 3, dev);
201 }
202 tags::encode_closing_tag(buf, 0);
203 primitives::encode_ctx_unsigned(buf, 1, *previous_notification as u64);
205 primitives::encode_ctx_unsigned(buf, 2, *current_notification as u64);
207 tags::encode_closing_tag(buf, 10);
208 }
209 NotificationParameters::UnsignedRange {
210 exceeding_value,
211 status_flags,
212 exceeded_limit,
213 } => {
214 tags::encode_opening_tag(buf, 11);
215 primitives::encode_ctx_unsigned(buf, 0, *exceeding_value);
216 encode_status_flags(buf, 1, *status_flags);
217 primitives::encode_ctx_unsigned(buf, 2, *exceeded_limit);
218 tags::encode_closing_tag(buf, 11);
219 }
220 NotificationParameters::AccessEvent {
221 access_event,
222 status_flags,
223 access_event_tag,
224 access_event_time,
225 access_credential,
226 authentication_factor,
227 } => {
228 if let Some(authentication_factor) = authentication_factor {
229 structured::validate_authentication_factor(authentication_factor)
230 .map_err(Error::into_encode_refusal)?;
231 }
232 tags::encode_opening_tag(buf, 13);
233 primitives::encode_ctx_enumerated(buf, 0, access_event.to_raw());
234 encode_status_flags(buf, 1, *status_flags);
235 primitives::encode_ctx_unsigned(buf, 2, *access_event_tag as u64);
236 primitives::encode_timestamp(
238 buf,
239 3,
240 &BACnetTimeStamp::DateTime {
241 date: access_event_time.0,
242 time: access_event_time.1,
243 },
244 )?;
245 tags::encode_opening_tag(buf, 4);
247 if let Some(ref dev) = access_credential.device_identifier {
248 primitives::encode_ctx_object_id(buf, 0, dev);
249 }
250 primitives::encode_ctx_object_id(buf, 1, &access_credential.object_identifier);
251 tags::encode_closing_tag(buf, 4);
252 if let Some(authentication_factor) = authentication_factor {
253 tags::encode_opening_tag(buf, 5);
255 buf.extend_from_slice(authentication_factor);
256 tags::encode_closing_tag(buf, 5);
257 }
258 tags::encode_closing_tag(buf, 13);
259 }
260 NotificationParameters::DoubleOutOfRange {
261 exceeding_value,
262 status_flags,
263 deadband,
264 exceeded_limit,
265 } => {
266 tags::encode_opening_tag(buf, 14);
267 primitives::encode_ctx_double(buf, 0, *exceeding_value);
268 encode_status_flags(buf, 1, *status_flags);
269 primitives::encode_ctx_double(buf, 2, *deadband);
270 primitives::encode_ctx_double(buf, 3, *exceeded_limit);
271 tags::encode_closing_tag(buf, 14);
272 }
273 NotificationParameters::SignedOutOfRange {
274 exceeding_value,
275 status_flags,
276 deadband,
277 exceeded_limit,
278 } => {
279 tags::encode_opening_tag(buf, 15);
280 primitives::encode_ctx_signed(buf, 0, *exceeding_value);
281 encode_status_flags(buf, 1, *status_flags);
282 primitives::encode_ctx_unsigned(buf, 2, *deadband);
283 primitives::encode_ctx_signed(buf, 3, *exceeded_limit);
284 tags::encode_closing_tag(buf, 15);
285 }
286 NotificationParameters::UnsignedOutOfRange {
287 exceeding_value,
288 status_flags,
289 deadband,
290 exceeded_limit,
291 } => {
292 tags::encode_opening_tag(buf, 16);
293 primitives::encode_ctx_unsigned(buf, 0, *exceeding_value);
294 encode_status_flags(buf, 1, *status_flags);
295 primitives::encode_ctx_unsigned(buf, 2, *deadband);
296 primitives::encode_ctx_unsigned(buf, 3, *exceeded_limit);
297 tags::encode_closing_tag(buf, 16);
298 }
299 NotificationParameters::ChangeOfCharacterstring {
300 changed_value,
301 status_flags,
302 alarm_value,
303 } => {
304 tags::encode_opening_tag(buf, 17);
305 primitives::encode_ctx_character_string(buf, 0, changed_value)?;
306 encode_status_flags(buf, 1, *status_flags);
307 primitives::encode_ctx_character_string(buf, 2, alarm_value)?;
308 tags::encode_closing_tag(buf, 17);
309 }
310 NotificationParameters::ChangeOfStatusFlags {
311 present_value,
312 referenced_flags,
313 } => {
314 tags::encode_opening_tag(buf, 18);
315 if let Some(present_value) = present_value {
316 tags::encode_opening_tag(buf, 0);
318 buf.extend_from_slice(present_value);
319 tags::encode_closing_tag(buf, 0);
320 }
321 encode_status_flags(buf, 1, *referenced_flags);
323 tags::encode_closing_tag(buf, 18);
324 }
325 NotificationParameters::ChangeOfReliability {
326 reliability,
327 status_flags,
328 property_values,
329 } => {
330 tags::encode_opening_tag(buf, 19);
331 primitives::encode_ctx_enumerated(buf, 0, reliability.to_raw());
332 encode_status_flags(buf, 1, *status_flags);
333 tags::encode_opening_tag(buf, 2);
335 buf.extend_from_slice(property_values);
336 tags::encode_closing_tag(buf, 2);
337 tags::encode_closing_tag(buf, 19);
338 }
339 NotificationParameters::ChangeOfDiscreteValue {
340 new_value,
341 status_flags,
342 } => {
343 tags::encode_opening_tag(buf, 21);
344 tags::encode_opening_tag(buf, 0);
346 buf.extend_from_slice(new_value);
347 tags::encode_closing_tag(buf, 0);
348 encode_status_flags(buf, 1, *status_flags);
350 tags::encode_closing_tag(buf, 21);
351 }
352 NotificationParameters::ChangeOfTimer {
353 new_state,
354 status_flags,
355 update_time,
356 last_state_change,
357 initial_timeout,
358 expiration_time,
359 } => {
360 tags::encode_opening_tag(buf, 22);
361 primitives::encode_ctx_enumerated(buf, 0, new_state.to_raw());
362 encode_status_flags(buf, 1, *status_flags);
363 tags::encode_opening_tag(buf, 2);
365 primitives::encode_app_date(buf, &update_time.0);
366 primitives::encode_app_time(buf, &update_time.1);
367 tags::encode_closing_tag(buf, 2);
368 if let Some(last_state_change) = last_state_change {
369 primitives::encode_ctx_enumerated(buf, 3, last_state_change.to_raw());
370 }
371 if let Some(initial_timeout) = initial_timeout {
372 primitives::encode_ctx_unsigned(buf, 4, *initial_timeout as u64);
373 }
374 if let Some(expiration_time) = expiration_time {
375 tags::encode_opening_tag(buf, 5);
376 primitives::encode_app_date(buf, &expiration_time.0);
377 primitives::encode_app_time(buf, &expiration_time.1);
378 tags::encode_closing_tag(buf, 5);
379 }
380 tags::encode_closing_tag(buf, 22);
381 }
382 }
383 Ok(())
384}