1use super::decode_helpers::{
2 closing_tag_start, decode_context_status_flags, decode_context_u16, decode_context_value,
3 finish_variant as check_variant_end,
4};
5use super::decode_timer::{decode_change_of_discrete_value, decode_change_of_timer};
6use super::structured::{decode_access_event, decode_complex_event_type};
7use super::*;
8use crate::constructed::tagged::decode_ctx_character_string;
9
10pub fn decode_notification_parameters(
12 data: &[u8],
13 offset: usize,
14) -> Result<NotificationParameters, Error> {
15 let end = closing_tag_start(data, data.len(), 12, "NotificationParameters event-values")
16 .unwrap_or(data.len());
17 decode_bounded(data, offset, end)
18}
19
20pub(super) fn decode_bounded(
22 data: &[u8],
23 offset: usize,
24 end: usize,
25) -> Result<NotificationParameters, Error> {
26 let data = data
27 .get(..end)
28 .ok_or_else(|| Error::decoding(end, "NotificationParameters boundary exceeds input"))?;
29 let framed = data
30 .get(offset..)
31 .ok_or_else(|| Error::decoding(offset, "NotificationParameters offset exceeds input"))?;
32 if offset >= data.len() {
34 return Err(Error::missing(
35 offset,
36 "NotificationParameters: empty payload",
37 ));
38 }
39 let (inner_tag, inner_start) = tags::decode_tag(data, offset)?;
40 if !inner_tag.is_opening {
41 return Err(misplaced_tag(
42 data,
43 &inner_tag,
44 None,
45 offset,
46 "NotificationParameters: expected opening tag for variant",
47 ));
48 }
49 let variant_tag = inner_tag.number;
50 if variant_tag == 6 {
51 let (_, next) = tags::extract_context_value(data, inner_start, 6)?;
52 if next != data.len() {
53 return Err(Error::trailing(
54 next,
55 "ComplexEventType has trailing encoded fields",
56 ));
57 }
58 } else {
59 validate_tlv_sequence(framed, "NotificationParameters")?;
60 }
61 let variant_body_end = closing_tag_start(
62 data,
63 data.len(),
64 variant_tag,
65 "NotificationParameters variant",
66 )?;
67 let finish_variant =
68 |value, consumed| check_variant_end(value, consumed, variant_body_end, variant_tag);
69
70 match variant_tag {
71 1 => {
73 let mut pos = inner_start;
74 let (t, p) = tags::decode_tag(data, pos)?;
76 if !t.is_opening || t.number != 0 {
77 return Err(misplaced_tag(
78 data,
79 &t,
80 Some(0),
81 pos,
82 "ChangeOfState: expected opening tag [0] for new-state",
83 ));
84 }
85 pos = p;
86 let new_state = decode_property_states(data, &mut pos)?;
87 let (ct, cp) = tags::decode_tag(data, pos)?;
89 if !ct.is_closing || ct.number != 0 {
90 return Err(Error::decoding_kind(
91 unclosed_kind(&ct),
92 pos,
93 "ChangeOfState: expected closing [0]",
94 ));
95 }
96 pos = cp;
97 let (status_flags, pos) =
99 decode_context_status_flags(data, pos, 1, "ChangeOfState status-flags")?;
100 finish_variant(
101 NotificationParameters::ChangeOfState {
102 new_state,
103 status_flags,
104 },
105 pos,
106 )
107 }
108 2 => {
110 let mut pos = inner_start;
111 let (t, p) = tags::decode_tag(data, pos)?;
113 if !t.is_opening || t.number != 0 {
114 return Err(misplaced_tag(
115 data,
116 &t,
117 Some(0),
118 pos,
119 "ChangeOfValue: expected opening [0] for new-value",
120 ));
121 }
122 pos = p;
123 let (choice_tag, _) = tags::decode_tag(data, pos)?;
125 let new_value = match choice_tag.number {
126 0 if choice_tag.is_context(0) => {
127 let ((unused_bits, data), end) = decode_context_value(
128 data,
129 pos,
130 0,
131 "ChangeOfValue changed-bits",
132 primitives::decode_bit_string,
133 )?;
134 pos = end;
135 ChangeOfValueChoice::ChangedBits { unused_bits, data }
136 }
137 1 if choice_tag.is_context(1) => {
138 let (value, end) = decode_context_value(
139 data,
140 pos,
141 1,
142 "ChangeOfValue changed-value",
143 primitives::decode_real,
144 )?;
145 pos = end;
146 ChangeOfValueChoice::ChangedValue(value)
147 }
148 _ => {
149 return Err(misplaced_tag(
150 data,
151 &choice_tag,
152 None,
153 pos,
154 "ChangeOfValue: expected context choice [0] or [1]",
155 ));
156 }
157 };
158 let (ct, cp) = tags::decode_tag(data, pos)?;
160 if !ct.is_closing || ct.number != 0 {
161 return Err(Error::decoding_kind(
162 unclosed_kind(&ct),
163 pos,
164 "ChangeOfValue: expected closing [0]",
165 ));
166 }
167 pos = cp;
168 let (status_flags, pos) =
170 decode_context_status_flags(data, pos, 1, "ChangeOfValue status-flags")?;
171 finish_variant(
172 NotificationParameters::ChangeOfValue {
173 new_value,
174 status_flags,
175 },
176 pos,
177 )
178 }
179 5 => {
181 let (exceeding_value, pos) = decode_context_value(
183 data,
184 inner_start,
185 0,
186 "OutOfRange exceeding-value",
187 primitives::decode_real,
188 )?;
189 let (status_flags, pos) =
191 decode_context_status_flags(data, pos, 1, "OutOfRange status-flags")?;
192 let (deadband, pos) =
194 decode_context_value(data, pos, 2, "OutOfRange deadband", primitives::decode_real)?;
195 let (exceeded_limit, pos) = decode_context_value(
197 data,
198 pos,
199 3,
200 "OutOfRange exceeded-limit",
201 primitives::decode_real,
202 )?;
203 finish_variant(
204 NotificationParameters::OutOfRange {
205 exceeding_value,
206 status_flags,
207 deadband,
208 exceeded_limit,
209 },
210 pos,
211 )
212 }
213 6 => decode_complex_event_type(data, inner_start, variant_body_end),
215 10 => {
217 let mut pos = inner_start;
218 let (t, p) = tags::decode_tag(data, pos)?;
220 if !t.is_opening || t.number != 0 {
221 return Err(misplaced_tag(
222 data,
223 &t,
224 Some(0),
225 pos,
226 "BufferReady: expected opening [0] for buffer-property",
227 ));
228 }
229 pos = p;
230 let buffer_property = decode_device_obj_prop_ref(data, &mut pos)?;
231 let (ct, cp) = tags::decode_tag(data, pos)?;
233 if !ct.is_closing || ct.number != 0 {
234 return Err(Error::decoding_kind(
235 unclosed_kind(&ct),
236 pos,
237 "BufferReady: expected closing [0]",
238 ));
239 }
240 pos = cp;
241 let (previous_notification, pos) =
243 decode_context_u32(data, pos, 1, "BufferReady previous-notification")?;
244 let (current_notification, end) =
246 decode_context_u32(data, pos, 2, "BufferReady current-notification")?;
247 finish_variant(
248 NotificationParameters::BufferReady {
249 buffer_property,
250 previous_notification,
251 current_notification,
252 },
253 end,
254 )
255 }
256 11 => {
258 let (exceeding_value, pos) = decode_context_value(
260 data,
261 inner_start,
262 0,
263 "UnsignedRange exceeding-value",
264 primitives::decode_unsigned,
265 )?;
266 let (status_flags, pos) =
268 decode_context_status_flags(data, pos, 1, "UnsignedRange status-flags")?;
269 let (exceeded_limit, pos) = decode_context_value(
271 data,
272 pos,
273 2,
274 "UnsignedRange exceeded-limit",
275 primitives::decode_unsigned,
276 )?;
277 finish_variant(
278 NotificationParameters::UnsignedRange {
279 exceeding_value,
280 status_flags,
281 exceeded_limit,
282 },
283 pos,
284 )
285 }
286 0 => {
288 let ((unused, bits), pos) = decode_context_value(
290 data,
291 inner_start,
292 0,
293 "ChangeOfBitstring referenced-bitstring",
294 primitives::decode_bit_string,
295 )?;
296 let (status_flags, pos) =
298 decode_context_status_flags(data, pos, 1, "ChangeOfBitstring status-flags")?;
299 finish_variant(
300 NotificationParameters::ChangeOfBitstring {
301 referenced_bitstring: (unused, bits),
302 status_flags,
303 },
304 pos,
305 )
306 }
307 3 => {
309 let mut pos = inner_start;
310 let (t, p) = tags::decode_tag(data, pos)?;
312 if !t.is_opening || t.number != 0 {
313 return Err(misplaced_tag(
314 data,
315 &t,
316 Some(0),
317 pos,
318 "CommandFailure: expected opening [0]",
319 ));
320 }
321 let (command_value, after) = extract_raw_context(data, p, 0)?;
322 pos = after;
323 let (status_flags, pos) =
325 decode_context_status_flags(data, pos, 1, "CommandFailure status-flags")?;
326 let (t, p) = tags::decode_tag(data, pos)?;
328 if !t.is_opening || t.number != 2 {
329 return Err(misplaced_tag(
330 data,
331 &t,
332 Some(2),
333 pos,
334 "CommandFailure: expected opening [2]",
335 ));
336 }
337 let (feedback_value, after) = extract_raw_context(data, p, 2)?;
338 finish_variant(
339 NotificationParameters::CommandFailure {
340 command_value,
341 status_flags,
342 feedback_value,
343 },
344 after,
345 )
346 }
347 4 => {
349 let (reference_value, pos) = decode_context_value(
351 data,
352 inner_start,
353 0,
354 "FloatingLimit reference-value",
355 primitives::decode_real,
356 )?;
357 let (status_flags, pos) =
359 decode_context_status_flags(data, pos, 1, "FloatingLimit status-flags")?;
360 let (setpoint_value, pos) = decode_context_value(
362 data,
363 pos,
364 2,
365 "FloatingLimit setpoint-value",
366 primitives::decode_real,
367 )?;
368 let (error_limit, pos) = decode_context_value(
370 data,
371 pos,
372 3,
373 "FloatingLimit error-limit",
374 primitives::decode_real,
375 )?;
376 finish_variant(
377 NotificationParameters::FloatingLimit {
378 reference_value,
379 status_flags,
380 setpoint_value,
381 error_limit,
382 },
383 pos,
384 )
385 }
386 8 => {
388 let (new_state, pos) = decode_context_enum(
389 data,
390 inner_start,
391 0,
392 "ChangeOfLifeSafety new-state",
393 LifeSafetyState::from_raw,
394 )?;
395 let (new_mode, pos) = decode_context_enum(
396 data,
397 pos,
398 1,
399 "ChangeOfLifeSafety new-mode",
400 LifeSafetyMode::from_raw,
401 )?;
402 let (status_flags, pos) =
403 decode_context_status_flags(data, pos, 2, "ChangeOfLifeSafety status-flags")?;
404 let (operation_expected, pos) = decode_context_enum(
405 data,
406 pos,
407 3,
408 "ChangeOfLifeSafety operation-expected",
409 LifeSafetyOperation::from_raw,
410 )?;
411 finish_variant(
412 NotificationParameters::ChangeOfLifeSafety {
413 new_state,
414 new_mode,
415 status_flags,
416 operation_expected,
417 },
418 pos,
419 )
420 }
421 9 => {
423 let (vendor_id, pos) = decode_context_u16(data, inner_start, 0, "Extended vendor-id")?;
425 let (extended_event_type, pos) =
427 decode_context_u32(data, pos, 1, "Extended extended-event-type")?;
428 let (t, p) = tags::decode_tag(data, pos)?;
430 if !t.is_opening_tag(2) {
431 return Err(misplaced_tag(
432 data,
433 &t,
434 Some(2),
435 pos,
436 "Extended: expected opening [2]",
437 ));
438 }
439 let (parameters, after) = extract_raw_context(data, p, 2)?;
440 finish_variant(
441 NotificationParameters::Extended {
442 vendor_id,
443 extended_event_type,
444 parameters,
445 },
446 after,
447 )
448 }
449 13 => decode_access_event(data, inner_start, variant_body_end),
451 14 => {
453 let (exceeding_value, pos) = decode_context_value(
455 data,
456 inner_start,
457 0,
458 "DoubleOutOfRange exceeding-value",
459 primitives::decode_double,
460 )?;
461 let (status_flags, pos) =
463 decode_context_status_flags(data, pos, 1, "DoubleOutOfRange status-flags")?;
464 let (deadband, pos) = decode_context_value(
466 data,
467 pos,
468 2,
469 "DoubleOutOfRange deadband",
470 primitives::decode_double,
471 )?;
472 let (exceeded_limit, pos) = decode_context_value(
474 data,
475 pos,
476 3,
477 "DoubleOutOfRange exceeded-limit",
478 primitives::decode_double,
479 )?;
480 finish_variant(
481 NotificationParameters::DoubleOutOfRange {
482 exceeding_value,
483 status_flags,
484 deadband,
485 exceeded_limit,
486 },
487 pos,
488 )
489 }
490 15 => {
492 let (exceeding_value, pos) = decode_context_value(
494 data,
495 inner_start,
496 0,
497 "SignedOutOfRange exceeding-value",
498 primitives::decode_signed,
499 )?;
500 let (status_flags, pos) =
502 decode_context_status_flags(data, pos, 1, "SignedOutOfRange status-flags")?;
503 let (deadband, pos) = decode_context_value(
505 data,
506 pos,
507 2,
508 "SignedOutOfRange deadband",
509 primitives::decode_unsigned,
510 )?;
511 let (exceeded_limit, pos) = decode_context_value(
513 data,
514 pos,
515 3,
516 "SignedOutOfRange exceeded-limit",
517 primitives::decode_signed,
518 )?;
519 finish_variant(
520 NotificationParameters::SignedOutOfRange {
521 exceeding_value,
522 status_flags,
523 deadband,
524 exceeded_limit,
525 },
526 pos,
527 )
528 }
529 16 => {
531 let (exceeding_value, pos) = decode_context_value(
533 data,
534 inner_start,
535 0,
536 "UnsignedOutOfRange exceeding-value",
537 primitives::decode_unsigned,
538 )?;
539 let (status_flags, pos) =
541 decode_context_status_flags(data, pos, 1, "UnsignedOutOfRange status-flags")?;
542 let (deadband, pos) = decode_context_value(
544 data,
545 pos,
546 2,
547 "UnsignedOutOfRange deadband",
548 primitives::decode_unsigned,
549 )?;
550 let (exceeded_limit, pos) = decode_context_value(
552 data,
553 pos,
554 3,
555 "UnsignedOutOfRange exceeded-limit",
556 primitives::decode_unsigned,
557 )?;
558 finish_variant(
559 NotificationParameters::UnsignedOutOfRange {
560 exceeding_value,
561 status_flags,
562 deadband,
563 exceeded_limit,
564 },
565 pos,
566 )
567 }
568 17 => {
570 let (changed_value, pos) = decode_ctx_character_string(
572 data,
573 inner_start,
574 0,
575 "ChangeOfCharacterstring changed-value",
576 )?;
577 let (status_flags, pos) =
579 decode_context_status_flags(data, pos, 1, "ChangeOfCharacterstring status-flags")?;
580 let (alarm_value, new_pos) =
582 decode_ctx_character_string(data, pos, 2, "ChangeOfCharacterstring alarm-value")?;
583 finish_variant(
584 NotificationParameters::ChangeOfCharacterstring {
585 changed_value,
586 status_flags,
587 alarm_value,
588 },
589 new_pos,
590 )
591 }
592 18 => {
594 let mut pos = inner_start;
595 let (t, p) = tags::decode_tag(data, pos)?;
597 let present_value = if t.is_opening_tag(0) {
598 let (value, after) = extract_raw_context(data, p, 0)?;
599 pos = after;
600 Some(value)
601 } else {
602 None
603 };
604 let (referenced_flags, pos) =
606 decode_context_status_flags(data, pos, 1, "ChangeOfStatusFlags referenced-flags")?;
607 finish_variant(
608 NotificationParameters::ChangeOfStatusFlags {
609 present_value,
610 referenced_flags,
611 },
612 pos,
613 )
614 }
615 19 => {
617 let (reliability, pos) = decode_context_enum(
619 data,
620 inner_start,
621 0,
622 "ChangeOfReliability reliability",
623 Reliability::from_raw,
624 )?;
625 let (status_flags, pos) =
627 decode_context_status_flags(data, pos, 1, "ChangeOfReliability status-flags")?;
628 let (t, p) = tags::decode_tag(data, pos)?;
630 if !t.is_opening_tag(2) {
631 return Err(misplaced_tag(
632 data,
633 &t,
634 Some(2),
635 pos,
636 "ChangeOfReliability: expected opening [2]",
637 ));
638 }
639 let (property_values, after) = extract_raw_context(data, p, 2)?;
640 finish_variant(
641 NotificationParameters::ChangeOfReliability {
642 reliability,
643 status_flags,
644 property_values,
645 },
646 after,
647 )
648 }
649 21 => decode_change_of_discrete_value(data, inner_start, variant_body_end),
651 22 => decode_change_of_timer(data, inner_start, variant_body_end),
653 other => Err(Error::invalid_tag(
654 offset,
655 format!("NotificationParameters variant [{other}] unknown"),
656 )),
657 }
658}