1#[cfg(not(feature = "std"))]
23use alloc::vec::Vec;
24
25use bacnet_types::constructed::{
26 BACnetDeviceObjectPropertyReference, BACnetEventParameter, ChangeOfValueCriteria,
27};
28use bacnet_types::error::{DecodeError, DecodingKind, EncodeError, Error};
29use bytes::BytesMut;
30
31use crate::primitives;
32use crate::tags;
33
34use super::tagged::{
35 contents, decode_app_bit_string, decode_ctx_bit_string, decode_ctx_constructed,
36 decode_ctx_real, decode_ctx_unsigned, expect_closing, expect_opening, next_is_closing,
37};
38use super::{
39 decode_dopr_body, decode_property_state, encode_dopr_body, encode_property_state,
40 validate_extended_parameters, validate_tlv_sequence, MAX_FRAMED_ITEMS,
41};
42
43const RESERVED_EVENT_TAGS: [u8; 4] = [6, 7, 12, 19];
47
48const LAST_ASSIGNED_TAG: u8 = 22;
51
52pub fn encode_event_parameter(
57 buf: &mut BytesMut,
58 value: &BACnetEventParameter,
59) -> Result<(), Error> {
60 let mut encoded = BytesMut::new();
61 encode_event_parameter_into(&mut encoded, value)?;
62 validate_tlv_sequence(&encoded, "BACnetEventParameter")
63 .map_err(Error::into_encode_refusal)?;
64 buf.extend_from_slice(&encoded);
65 Ok(())
66}
67
68fn encode_event_parameter_into(
69 buf: &mut BytesMut,
70 value: &BACnetEventParameter,
71) -> Result<(), Error> {
72 match value {
73 BACnetEventParameter::ChangeOfBitstring {
74 time_delay,
75 bitmask,
76 list_of_values,
77 } => {
78 tags::encode_opening_tag(buf, 0);
79 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
80 primitives::encode_ctx_bit_string(buf, 1, bitmask.0, &bitmask.1);
81 tags::encode_opening_tag(buf, 2);
82 for (unused_bits, data) in list_of_values {
83 primitives::encode_app_bit_string(buf, *unused_bits, data);
84 }
85 tags::encode_closing_tag(buf, 2);
86 tags::encode_closing_tag(buf, 0);
87 }
88 BACnetEventParameter::ChangeOfState {
89 time_delay,
90 list_of_values,
91 } => {
92 let mut encoded_states = BytesMut::new();
93 for state in list_of_values {
94 encode_property_state(&mut encoded_states, state)?;
95 }
96 tags::encode_opening_tag(buf, 1);
97 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
98 tags::encode_opening_tag(buf, 1);
99 buf.extend_from_slice(&encoded_states);
100 tags::encode_closing_tag(buf, 1);
101 tags::encode_closing_tag(buf, 1);
102 }
103 BACnetEventParameter::ChangeOfValue {
104 time_delay,
105 criteria,
106 } => {
107 tags::encode_opening_tag(buf, 2);
108 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
109 tags::encode_opening_tag(buf, 1);
112 match criteria {
113 ChangeOfValueCriteria::Bitmask { unused_bits, data } => {
114 primitives::encode_ctx_bit_string(buf, 0, *unused_bits, data);
115 }
116 ChangeOfValueCriteria::ReferencedPropertyIncrement(increment) => {
117 primitives::encode_ctx_real(buf, 1, *increment);
118 }
119 }
120 tags::encode_closing_tag(buf, 1);
121 tags::encode_closing_tag(buf, 2);
122 }
123 BACnetEventParameter::FloatingLimit {
124 time_delay,
125 setpoint_reference,
126 low_diff_limit,
127 high_diff_limit,
128 deadband,
129 } => {
130 tags::encode_opening_tag(buf, 4);
131 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
132 tags::encode_opening_tag(buf, 1);
134 encode_dopr_body(buf, setpoint_reference);
135 tags::encode_closing_tag(buf, 1);
136 primitives::encode_ctx_real(buf, 2, *low_diff_limit);
137 primitives::encode_ctx_real(buf, 3, *high_diff_limit);
138 primitives::encode_ctx_real(buf, 4, *deadband);
139 tags::encode_closing_tag(buf, 4);
140 }
141 BACnetEventParameter::OutOfRange {
142 time_delay,
143 low_limit,
144 high_limit,
145 deadband,
146 } => {
147 tags::encode_opening_tag(buf, 5);
148 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
149 primitives::encode_ctx_real(buf, 1, *low_limit);
150 primitives::encode_ctx_real(buf, 2, *high_limit);
151 primitives::encode_ctx_real(buf, 3, *deadband);
152 tags::encode_closing_tag(buf, 5);
153 }
154 BACnetEventParameter::Extended {
155 vendor_id,
156 extended_event_type,
157 parameters,
158 } => {
159 validate_extended_parameters(parameters, "extended")
160 .map_err(Error::into_encode_refusal)?;
161 tags::encode_opening_tag(buf, 9);
162 primitives::encode_ctx_unsigned(buf, 0, *vendor_id as u64);
163 primitives::encode_ctx_unsigned(buf, 1, *extended_event_type as u64);
164 tags::encode_opening_tag(buf, 2);
166 buf.extend_from_slice(parameters);
167 tags::encode_closing_tag(buf, 2);
168 tags::encode_closing_tag(buf, 9);
169 }
170 BACnetEventParameter::None => {
171 tags::encode_tag(buf, 20, tags::TagClass::Context, 0);
173 }
174 BACnetEventParameter::Opaque { tag, data } if *tag == u8::MAX => {
175 primitives::encode_app_octet_string(buf, data);
177 }
178 BACnetEventParameter::Opaque { tag, .. } if *tag <= LAST_ASSIGNED_TAG => {
179 return Err(
180 EncodeError::new("tag [{}] cannot use an opaque constructed body")
181 .within("BACnetEventParameter")
182 .with_value((*tag).into())
183 .into(),
184 );
185 }
186 BACnetEventParameter::Opaque { tag, data } => {
187 validate_tlv_sequence(data, "opaque event parameters")
188 .map_err(Error::into_encode_refusal)?;
189 tags::encode_opening_tag(buf, *tag);
192 buf.extend_from_slice(data);
193 tags::encode_closing_tag(buf, *tag);
194 }
195 BACnetEventParameter::CommandFailure {
196 time_delay,
197 feedback_property_reference,
198 } => {
199 tags::encode_opening_tag(buf, 3);
200 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
201 encode_reference(buf, 1, feedback_property_reference);
202 tags::encode_closing_tag(buf, 3);
203 }
204 BACnetEventParameter::ChangeOfLifeSafety {
205 time_delay,
206 list_of_life_safety_alarm_values,
207 list_of_alarm_values,
208 mode_property_reference,
209 } => {
210 tags::encode_opening_tag(buf, 8);
211 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
212 encode_enumerations(buf, 1, list_of_life_safety_alarm_values.iter().map(|s| s.to_raw()));
213 encode_enumerations(buf, 2, list_of_alarm_values.iter().map(|s| s.to_raw()));
214 encode_reference(buf, 3, mode_property_reference);
215 tags::encode_closing_tag(buf, 8);
216 }
217 BACnetEventParameter::BufferReady {
218 notification_threshold,
219 previous_notification_count,
220 } => {
221 tags::encode_opening_tag(buf, 10);
222 primitives::encode_ctx_unsigned(buf, 0, u64::from(*notification_threshold));
223 primitives::encode_ctx_unsigned(buf, 1, u64::from(*previous_notification_count));
224 tags::encode_closing_tag(buf, 10);
225 }
226 BACnetEventParameter::UnsignedRange {
227 time_delay,
228 low_limit,
229 high_limit,
230 } => {
231 tags::encode_opening_tag(buf, 11);
232 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
233 primitives::encode_ctx_unsigned(buf, 1, *low_limit);
234 primitives::encode_ctx_unsigned(buf, 2, *high_limit);
235 tags::encode_closing_tag(buf, 11);
236 }
237 BACnetEventParameter::AccessEvent {
238 list_of_access_events,
239 access_event_time_reference,
240 } => {
241 tags::encode_opening_tag(buf, 13);
242 encode_enumerations(buf, 0, list_of_access_events.iter().map(|e| e.to_raw()));
243 encode_reference(buf, 1, access_event_time_reference);
244 tags::encode_closing_tag(buf, 13);
245 }
246 BACnetEventParameter::DoubleOutOfRange {
247 time_delay,
248 low_limit,
249 high_limit,
250 deadband,
251 } => {
252 tags::encode_opening_tag(buf, 14);
253 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
254 primitives::encode_ctx_double(buf, 1, *low_limit);
255 primitives::encode_ctx_double(buf, 2, *high_limit);
256 primitives::encode_ctx_double(buf, 3, *deadband);
257 tags::encode_closing_tag(buf, 14);
258 }
259 BACnetEventParameter::SignedOutOfRange {
260 time_delay,
261 low_limit,
262 high_limit,
263 deadband,
264 } => {
265 tags::encode_opening_tag(buf, 15);
266 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
267 primitives::encode_ctx_signed(buf, 1, *low_limit);
268 primitives::encode_ctx_signed(buf, 2, *high_limit);
269 primitives::encode_ctx_unsigned(buf, 3, *deadband);
270 tags::encode_closing_tag(buf, 15);
271 }
272 BACnetEventParameter::UnsignedOutOfRange {
273 time_delay,
274 low_limit,
275 high_limit,
276 deadband,
277 } => {
278 tags::encode_opening_tag(buf, 16);
279 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
280 primitives::encode_ctx_unsigned(buf, 1, *low_limit);
281 primitives::encode_ctx_unsigned(buf, 2, *high_limit);
282 primitives::encode_ctx_unsigned(buf, 3, *deadband);
283 tags::encode_closing_tag(buf, 16);
284 }
285 BACnetEventParameter::ChangeOfCharacterstring {
286 time_delay,
287 list_of_alarm_values,
288 } => {
289 let mut strings = BytesMut::new();
290 for value in list_of_alarm_values {
291 primitives::encode_app_character_string(&mut strings, value)?;
292 }
293 tags::encode_opening_tag(buf, 17);
294 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
295 tags::encode_opening_tag(buf, 1);
296 buf.extend_from_slice(&strings);
297 tags::encode_closing_tag(buf, 1);
298 tags::encode_closing_tag(buf, 17);
299 }
300 BACnetEventParameter::ChangeOfStatusFlags {
301 time_delay,
302 selected_flags,
303 } => {
304 tags::encode_opening_tag(buf, 18);
305 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
306 primitives::encode_ctx_bit_string(buf, 1, 4, &[selected_flags.bits() << 4]);
307 tags::encode_closing_tag(buf, 18);
308 }
309 BACnetEventParameter::ChangeOfDiscreteValue { time_delay } => {
310 tags::encode_opening_tag(buf, 21);
311 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
312 tags::encode_closing_tag(buf, 21);
313 }
314 BACnetEventParameter::ChangeOfTimer {
315 time_delay,
316 alarm_values,
317 update_time_reference,
318 } => {
319 tags::encode_opening_tag(buf, 22);
320 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
321 encode_enumerations(buf, 1, alarm_values.iter().map(|s| s.to_raw()));
322 encode_reference(buf, 2, update_time_reference);
323 tags::encode_closing_tag(buf, 22);
324 }
325 }
326 Ok(())
327}
328
329fn encode_reference(buf: &mut BytesMut, tag: u8, reference: &BACnetDeviceObjectPropertyReference) {
332 tags::encode_opening_tag(buf, tag);
333 encode_dopr_body(buf, reference);
334 tags::encode_closing_tag(buf, tag);
335}
336
337fn encode_enumerations(buf: &mut BytesMut, tag: u8, values: impl Iterator<Item = u32>) {
340 tags::encode_opening_tag(buf, tag);
341 for value in values {
342 primitives::encode_app_enumerated(buf, value);
343 }
344 tags::encode_closing_tag(buf, tag);
345}
346
347pub fn decode_event_parameter(
354 data: &[u8],
355 offset: usize,
356) -> Result<(BACnetEventParameter, usize), Error> {
357 use BACnetEventParameter as EP;
358
359 if data.get(offset..offset.saturating_add(2)) == Some(&[0xFE, 0xFF]) {
363 let body_start = offset + 2;
364 if data.len() < body_start + 2 {
366 return Err(Error::buffer_too_short(body_start + 2, data.len()));
367 }
368 let body_end = data.len() - 2;
369 if data.get(body_end..) != Some(&[0xFF, 0xFF]) {
370 return Err(Error::decoding(
371 offset,
372 "BACnetEventParameter: missing legacy closing tag",
373 ));
374 }
375 return Ok((
376 EP::Opaque {
377 tag: u8::MAX,
378 data: data[body_start..body_end].to_vec(),
379 },
380 data.len(),
381 ));
382 }
383
384 let (tag, pos) = tags::decode_tag(data, offset)?;
385 let what = "BACnetEventParameter";
386
387 if tag.class == tags::TagClass::Application && tag.number == tags::app_tag::OCTET_STRING {
388 let (octets, end) = contents(data, pos, tag.length)?;
389 return Ok((
390 EP::Opaque {
391 tag: u8::MAX,
392 data: octets.to_vec(),
393 },
394 end,
395 ));
396 }
397
398 if tag.class != tags::TagClass::Context {
399 return Err(Error::decoding(
400 offset,
401 "BACnetEventParameter: expected a context tag",
402 ));
403 }
404
405 if RESERVED_EVENT_TAGS.contains(&tag.number) {
406 return Err(DecodeError::new(
407 DecodingKind::InvalidEncoding,
408 offset,
409 "BACnetEventParameter: context tag [{}] is omitted/deprecated/reserved in Clause 21",
410 )
411 .with_value(tag.number.into())
412 .into());
413 }
414
415 if !tag.is_opening && !tag.is_closing {
417 if tag.number == 20 && tag.length == 0 {
418 return Ok((EP::None, pos));
419 }
420 return Err(Error::decoding(
421 offset,
422 "BACnetEventParameter: invalid primitive alternative",
423 ));
424 }
425
426 if !tag.is_opening {
427 return Err(Error::decoding(
428 offset,
429 "BACnetEventParameter: unexpected closing tag",
430 ));
431 }
432 if tag.number == 20 {
433 return Err(Error::decoding(
434 offset,
435 "BACnetEventParameter: none [20] must use primitive NULL form",
436 ));
437 }
438
439 let value = match tag.number {
440 0 => {
441 let what = "BACnetEventParameter change-of-bitstring";
442 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
443 let (bitmask, pos) = decode_ctx_bit_string(data, pos, 1, what)?;
444 let mut pos = expect_opening(data, pos, 2, what)?;
445 let mut list_of_values = Vec::new();
446 while !next_is_closing(data, pos, 2)? {
447 if list_of_values.len() >= MAX_FRAMED_ITEMS {
448 return Err(Error::overflow(
449 pos,
450 "change-of-bitstring: list-of-bitstring-values exceeds limit",
451 ));
452 }
453 let (bits, p) = decode_app_bit_string(data, pos, what)?;
454 list_of_values.push(bits);
455 pos = p;
456 }
457 pos = expect_closing(data, pos, 2, what)?;
458 pos = expect_closing(data, pos, 0, what)?;
459 (
460 EP::ChangeOfBitstring {
461 time_delay,
462 bitmask,
463 list_of_values,
464 },
465 pos,
466 )
467 }
468 1 => {
469 let what = "BACnetEventParameter change-of-state";
470 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
471 let mut pos = expect_opening(data, pos, 1, what)?;
472 let mut list_of_values = Vec::new();
473 while !next_is_closing(data, pos, 1)? {
474 if list_of_values.len() >= MAX_FRAMED_ITEMS {
475 return Err(Error::overflow(
476 pos,
477 "change-of-state: list-of-values exceeds limit",
478 ));
479 }
480 let (state, p) = decode_property_state(data, pos)?;
481 list_of_values.push(state);
482 pos = p;
483 }
484 pos = expect_closing(data, pos, 1, what)?;
485 pos = expect_closing(data, pos, 1, what)?;
486 (
487 EP::ChangeOfState {
488 time_delay,
489 list_of_values,
490 },
491 pos,
492 )
493 }
494 2 => {
495 let what = "BACnetEventParameter change-of-value";
496 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
497 let mut pos = expect_opening(data, pos, 1, what)?;
498 let (inner, _) = tags::decode_tag(data, pos)?;
499 let criteria = if inner.is_context(0) {
500 let (bits, p) = decode_ctx_bit_string(data, pos, 0, what)?;
501 pos = p;
502 ChangeOfValueCriteria::Bitmask {
503 unused_bits: bits.0,
504 data: bits.1,
505 }
506 } else if inner.is_context(1) {
507 let (increment, p) = decode_ctx_real(data, pos, 1, what)?;
508 pos = p;
509 ChangeOfValueCriteria::ReferencedPropertyIncrement(increment)
510 } else {
511 return Err(DecodeError::new(
512 DecodingKind::InvalidEncoding,
513 pos,
514 "change-of-value: unexpected cov-criteria tag [{}]",
515 )
516 .with_value(inner.number.into())
517 .into());
518 };
519 pos = expect_closing(data, pos, 1, what)?;
520 pos = expect_closing(data, pos, 2, what)?;
521 (
522 EP::ChangeOfValue {
523 time_delay,
524 criteria,
525 },
526 pos,
527 )
528 }
529 4 => {
530 let what = "BACnetEventParameter floating-limit";
531 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
532 let pos = expect_opening(data, pos, 1, what)?;
533 let (setpoint_reference, pos) = decode_dopr_body(data, pos, what)?;
534 let pos = expect_closing(data, pos, 1, what)?;
535 let (low_diff_limit, pos) = decode_ctx_real(data, pos, 2, what)?;
536 let (high_diff_limit, pos) = decode_ctx_real(data, pos, 3, what)?;
537 let (deadband, pos) = decode_ctx_real(data, pos, 4, what)?;
538 let pos = expect_closing(data, pos, 4, what)?;
539 (
540 EP::FloatingLimit {
541 time_delay,
542 setpoint_reference,
543 low_diff_limit,
544 high_diff_limit,
545 deadband,
546 },
547 pos,
548 )
549 }
550 5 => {
551 let what = "BACnetEventParameter out-of-range";
552 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
553 let (low_limit, pos) = decode_ctx_real(data, pos, 1, what)?;
554 let (high_limit, pos) = decode_ctx_real(data, pos, 2, what)?;
555 let (deadband, pos) = decode_ctx_real(data, pos, 3, what)?;
556 let pos = expect_closing(data, pos, 5, what)?;
557 (
558 EP::OutOfRange {
559 time_delay,
560 low_limit,
561 high_limit,
562 deadband,
563 },
564 pos,
565 )
566 }
567 9 => {
568 let what = "BACnetEventParameter extended";
569 let (vendor_id, pos) = decode_ctx_unsigned::<u16>(data, pos, 0, what)?;
570 let (extended_event_type, pos) = decode_ctx_unsigned::<u32>(data, pos, 1, what)?;
571 let (raw_params, pos) = decode_ctx_constructed(data, pos, 2, what)?;
572 validate_extended_parameters(raw_params, "extended")?;
573 let pos = expect_closing(data, pos, 9, what)?;
574 (
575 EP::Extended {
576 vendor_id,
577 extended_event_type,
578 parameters: raw_params.to_vec(),
579 },
580 pos,
581 )
582 }
583 n @ (3 | 8 | 10 | 11 | 13..=18 | 21 | 22) => members::decode(data, pos, n)?,
584 n => {
585 let (body, p) = tags::extract_context_value(data, pos, n)?;
588 validate_tlv_sequence(body, "opaque event parameters")?;
589 (
590 EP::Opaque {
591 tag: n,
592 data: body.to_vec(),
593 },
594 p,
595 )
596 }
597 };
598 let (value, end) = value;
599 validate_tlv_sequence(&data[offset..end], what)?;
600 Ok((value, end))
601}
602
603mod members;