1#[cfg(not(feature = "std"))]
49use alloc::vec::Vec;
50
51use bacnet_types::constructed::{BACnetEventParameter, ChangeOfValueCriteria};
52use bacnet_types::error::{DecodeError, DecodingKind, EncodeError, Error};
53use bytes::BytesMut;
54
55use crate::primitives;
56use crate::tags;
57
58use super::tagged::{
59 contents, decode_app_bit_string, decode_ctx_bit_string, decode_ctx_constructed,
60 decode_ctx_real, decode_ctx_unsigned, expect_closing, expect_opening, next_is_closing,
61};
62use super::{
63 decode_dopr_body, decode_property_state, encode_dopr_body, encode_property_state,
64 validate_extended_parameters, validate_tlv_sequence, MAX_FRAMED_ITEMS,
65};
66
67const RESERVED_EVENT_TAGS: [u8; 4] = [6, 7, 12, 19];
71const MODELED_EVENT_TAGS: [u8; 6] = [0, 1, 2, 4, 5, 9];
72
73pub fn encode_event_parameter(
78 buf: &mut BytesMut,
79 value: &BACnetEventParameter,
80) -> Result<(), Error> {
81 let mut encoded = BytesMut::new();
82 encode_event_parameter_into(&mut encoded, value)?;
83 validate_tlv_sequence(&encoded, "BACnetEventParameter")
84 .map_err(Error::into_encode_refusal)?;
85 buf.extend_from_slice(&encoded);
86 Ok(())
87}
88
89fn encode_event_parameter_into(
90 buf: &mut BytesMut,
91 value: &BACnetEventParameter,
92) -> Result<(), Error> {
93 match value {
94 BACnetEventParameter::ChangeOfBitstring {
95 time_delay,
96 bitmask,
97 list_of_values,
98 } => {
99 tags::encode_opening_tag(buf, 0);
100 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
101 primitives::encode_ctx_bit_string(buf, 1, bitmask.0, &bitmask.1);
102 tags::encode_opening_tag(buf, 2);
103 for (unused_bits, data) in list_of_values {
104 primitives::encode_app_bit_string(buf, *unused_bits, data);
105 }
106 tags::encode_closing_tag(buf, 2);
107 tags::encode_closing_tag(buf, 0);
108 }
109 BACnetEventParameter::ChangeOfState {
110 time_delay,
111 list_of_values,
112 } => {
113 let mut encoded_states = BytesMut::new();
114 for state in list_of_values {
115 encode_property_state(&mut encoded_states, state)?;
116 }
117 tags::encode_opening_tag(buf, 1);
118 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
119 tags::encode_opening_tag(buf, 1);
120 buf.extend_from_slice(&encoded_states);
121 tags::encode_closing_tag(buf, 1);
122 tags::encode_closing_tag(buf, 1);
123 }
124 BACnetEventParameter::ChangeOfValue {
125 time_delay,
126 criteria,
127 } => {
128 tags::encode_opening_tag(buf, 2);
129 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
130 tags::encode_opening_tag(buf, 1);
133 match criteria {
134 ChangeOfValueCriteria::Bitmask { unused_bits, data } => {
135 primitives::encode_ctx_bit_string(buf, 0, *unused_bits, data);
136 }
137 ChangeOfValueCriteria::ReferencedPropertyIncrement(increment) => {
138 primitives::encode_ctx_real(buf, 1, *increment);
139 }
140 }
141 tags::encode_closing_tag(buf, 1);
142 tags::encode_closing_tag(buf, 2);
143 }
144 BACnetEventParameter::FloatingLimit {
145 time_delay,
146 setpoint_reference,
147 low_diff_limit,
148 high_diff_limit,
149 deadband,
150 } => {
151 tags::encode_opening_tag(buf, 4);
152 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
153 tags::encode_opening_tag(buf, 1);
155 encode_dopr_body(buf, setpoint_reference);
156 tags::encode_closing_tag(buf, 1);
157 primitives::encode_ctx_real(buf, 2, *low_diff_limit);
158 primitives::encode_ctx_real(buf, 3, *high_diff_limit);
159 primitives::encode_ctx_real(buf, 4, *deadband);
160 tags::encode_closing_tag(buf, 4);
161 }
162 BACnetEventParameter::OutOfRange {
163 time_delay,
164 low_limit,
165 high_limit,
166 deadband,
167 } => {
168 tags::encode_opening_tag(buf, 5);
169 primitives::encode_ctx_unsigned(buf, 0, *time_delay as u64);
170 primitives::encode_ctx_real(buf, 1, *low_limit);
171 primitives::encode_ctx_real(buf, 2, *high_limit);
172 primitives::encode_ctx_real(buf, 3, *deadband);
173 tags::encode_closing_tag(buf, 5);
174 }
175 BACnetEventParameter::Extended {
176 vendor_id,
177 extended_event_type,
178 parameters,
179 } => {
180 validate_extended_parameters(parameters, "extended")
181 .map_err(Error::into_encode_refusal)?;
182 tags::encode_opening_tag(buf, 9);
183 primitives::encode_ctx_unsigned(buf, 0, *vendor_id as u64);
184 primitives::encode_ctx_unsigned(buf, 1, *extended_event_type as u64);
185 tags::encode_opening_tag(buf, 2);
187 buf.extend_from_slice(parameters);
188 tags::encode_closing_tag(buf, 2);
189 tags::encode_closing_tag(buf, 9);
190 }
191 BACnetEventParameter::Opaque { tag, data } if *tag == u8::MAX => {
192 primitives::encode_app_octet_string(buf, data);
194 }
195 BACnetEventParameter::Opaque { tag: 20, data } if data.is_empty() => {
196 tags::encode_tag(buf, 20, tags::TagClass::Context, 0);
198 }
199 BACnetEventParameter::Opaque { tag, .. }
200 if *tag == 20
201 || MODELED_EVENT_TAGS.contains(tag)
202 || RESERVED_EVENT_TAGS.contains(tag) =>
203 {
204 return Err(
205 EncodeError::new("tag [{}] cannot use an opaque constructed body")
206 .within("BACnetEventParameter")
207 .with_value((*tag).into())
208 .into(),
209 );
210 }
211 BACnetEventParameter::Opaque { tag, data } => {
212 validate_tlv_sequence(data, "opaque event parameters")
213 .map_err(Error::into_encode_refusal)?;
214 tags::encode_opening_tag(buf, *tag);
217 buf.extend_from_slice(data);
218 tags::encode_closing_tag(buf, *tag);
219 }
220 }
221 Ok(())
222}
223
224pub fn decode_event_parameter(
230 data: &[u8],
231 offset: usize,
232) -> Result<(BACnetEventParameter, usize), Error> {
233 use BACnetEventParameter as EP;
234
235 if data.get(offset..offset.saturating_add(2)) == Some(&[0xFE, 0xFF]) {
239 let body_start = offset + 2;
240 if data.len() < body_start + 2 {
242 return Err(Error::buffer_too_short(body_start + 2, data.len()));
243 }
244 let body_end = data.len() - 2;
245 if data.get(body_end..) != Some(&[0xFF, 0xFF]) {
246 return Err(Error::decoding(
247 offset,
248 "BACnetEventParameter: missing legacy closing tag",
249 ));
250 }
251 return Ok((
252 EP::Opaque {
253 tag: u8::MAX,
254 data: data[body_start..body_end].to_vec(),
255 },
256 data.len(),
257 ));
258 }
259
260 let (tag, pos) = tags::decode_tag(data, offset)?;
261 let what = "BACnetEventParameter";
262
263 if tag.class == tags::TagClass::Application && tag.number == tags::app_tag::OCTET_STRING {
264 let (octets, end) = contents(data, pos, tag.length)?;
265 return Ok((
266 EP::Opaque {
267 tag: u8::MAX,
268 data: octets.to_vec(),
269 },
270 end,
271 ));
272 }
273
274 if tag.class != tags::TagClass::Context {
275 return Err(Error::decoding(
276 offset,
277 "BACnetEventParameter: expected a context tag",
278 ));
279 }
280
281 if RESERVED_EVENT_TAGS.contains(&tag.number) {
282 return Err(DecodeError::new(
283 DecodingKind::InvalidEncoding,
284 offset,
285 "BACnetEventParameter: context tag [{}] is omitted/deprecated/reserved in Clause 21",
286 )
287 .with_value(tag.number.into())
288 .into());
289 }
290
291 if !tag.is_opening && !tag.is_closing {
293 if tag.number == 20 && tag.length == 0 {
294 return Ok((
295 EP::Opaque {
296 tag: 20,
297 data: Vec::new(),
298 },
299 pos,
300 ));
301 }
302 return Err(Error::decoding(
303 offset,
304 "BACnetEventParameter: invalid primitive alternative",
305 ));
306 }
307
308 if !tag.is_opening {
309 return Err(Error::decoding(
310 offset,
311 "BACnetEventParameter: unexpected closing tag",
312 ));
313 }
314 if tag.number == 20 {
315 return Err(Error::decoding(
316 offset,
317 "BACnetEventParameter: none [20] must use primitive NULL form",
318 ));
319 }
320
321 let value = match tag.number {
322 0 => {
323 let what = "BACnetEventParameter change-of-bitstring";
324 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
325 let (bitmask, pos) = decode_ctx_bit_string(data, pos, 1, what)?;
326 let mut pos = expect_opening(data, pos, 2, what)?;
327 let mut list_of_values = Vec::new();
328 while !next_is_closing(data, pos, 2)? {
329 if list_of_values.len() >= MAX_FRAMED_ITEMS {
330 return Err(Error::overflow(
331 pos,
332 "change-of-bitstring: list-of-bitstring-values exceeds limit",
333 ));
334 }
335 let (bits, p) = decode_app_bit_string(data, pos, what)?;
336 list_of_values.push(bits);
337 pos = p;
338 }
339 pos = expect_closing(data, pos, 2, what)?;
340 pos = expect_closing(data, pos, 0, what)?;
341 (
342 EP::ChangeOfBitstring {
343 time_delay,
344 bitmask,
345 list_of_values,
346 },
347 pos,
348 )
349 }
350 1 => {
351 let what = "BACnetEventParameter change-of-state";
352 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
353 let mut pos = expect_opening(data, pos, 1, what)?;
354 let mut list_of_values = Vec::new();
355 while !next_is_closing(data, pos, 1)? {
356 if list_of_values.len() >= MAX_FRAMED_ITEMS {
357 return Err(Error::overflow(
358 pos,
359 "change-of-state: list-of-values exceeds limit",
360 ));
361 }
362 let (state, p) = decode_property_state(data, pos)?;
363 list_of_values.push(state);
364 pos = p;
365 }
366 pos = expect_closing(data, pos, 1, what)?;
367 pos = expect_closing(data, pos, 1, what)?;
368 (
369 EP::ChangeOfState {
370 time_delay,
371 list_of_values,
372 },
373 pos,
374 )
375 }
376 2 => {
377 let what = "BACnetEventParameter change-of-value";
378 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
379 let mut pos = expect_opening(data, pos, 1, what)?;
380 let (inner, _) = tags::decode_tag(data, pos)?;
381 let criteria = if inner.is_context(0) {
382 let (bits, p) = decode_ctx_bit_string(data, pos, 0, what)?;
383 pos = p;
384 ChangeOfValueCriteria::Bitmask {
385 unused_bits: bits.0,
386 data: bits.1,
387 }
388 } else if inner.is_context(1) {
389 let (increment, p) = decode_ctx_real(data, pos, 1, what)?;
390 pos = p;
391 ChangeOfValueCriteria::ReferencedPropertyIncrement(increment)
392 } else {
393 return Err(DecodeError::new(
394 DecodingKind::InvalidEncoding,
395 pos,
396 "change-of-value: unexpected cov-criteria tag [{}]",
397 )
398 .with_value(inner.number.into())
399 .into());
400 };
401 pos = expect_closing(data, pos, 1, what)?;
402 pos = expect_closing(data, pos, 2, what)?;
403 (
404 EP::ChangeOfValue {
405 time_delay,
406 criteria,
407 },
408 pos,
409 )
410 }
411 4 => {
412 let what = "BACnetEventParameter floating-limit";
413 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
414 let pos = expect_opening(data, pos, 1, what)?;
415 let (setpoint_reference, pos) = decode_dopr_body(data, pos, what)?;
416 let pos = expect_closing(data, pos, 1, what)?;
417 let (low_diff_limit, pos) = decode_ctx_real(data, pos, 2, what)?;
418 let (high_diff_limit, pos) = decode_ctx_real(data, pos, 3, what)?;
419 let (deadband, pos) = decode_ctx_real(data, pos, 4, what)?;
420 let pos = expect_closing(data, pos, 4, what)?;
421 (
422 EP::FloatingLimit {
423 time_delay,
424 setpoint_reference,
425 low_diff_limit,
426 high_diff_limit,
427 deadband,
428 },
429 pos,
430 )
431 }
432 5 => {
433 let what = "BACnetEventParameter out-of-range";
434 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
435 let (low_limit, pos) = decode_ctx_real(data, pos, 1, what)?;
436 let (high_limit, pos) = decode_ctx_real(data, pos, 2, what)?;
437 let (deadband, pos) = decode_ctx_real(data, pos, 3, what)?;
438 let pos = expect_closing(data, pos, 5, what)?;
439 (
440 EP::OutOfRange {
441 time_delay,
442 low_limit,
443 high_limit,
444 deadband,
445 },
446 pos,
447 )
448 }
449 9 => {
450 let what = "BACnetEventParameter extended";
451 let (vendor_id, pos) = decode_ctx_unsigned::<u16>(data, pos, 0, what)?;
452 let (extended_event_type, pos) = decode_ctx_unsigned::<u32>(data, pos, 1, what)?;
453 let (raw_params, pos) = decode_ctx_constructed(data, pos, 2, what)?;
454 validate_extended_parameters(raw_params, "extended")?;
455 let pos = expect_closing(data, pos, 9, what)?;
456 (
457 EP::Extended {
458 vendor_id,
459 extended_event_type,
460 parameters: raw_params.to_vec(),
461 },
462 pos,
463 )
464 }
465 n => {
466 let (body, p) = tags::extract_context_value(data, pos, n)?;
469 validate_tlv_sequence(body, "opaque event parameters")?;
470 (
471 EP::Opaque {
472 tag: n,
473 data: body.to_vec(),
474 },
475 p,
476 )
477 }
478 };
479 let (value, end) = value;
480 validate_tlv_sequence(&data[offset..end], what)?;
481 Ok((value, end))
482}