1#[cfg(not(feature = "std"))]
49use alloc::{string::ToString, vec::Vec};
50
51use bacnet_types::constructed::{BACnetEventParameter, ChangeOfValueCriteria};
52use bacnet_types::error::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| Error::Encoding(error.to_string()))?;
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| Error::Encoding(error.to_string()))?;
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(Error::Encoding(format!(
205 "BACnetEventParameter: tag [{tag}] cannot use an opaque constructed body"
206 )));
207 }
208 BACnetEventParameter::Opaque { tag, data } => {
209 validate_tlv_sequence(data, "opaque event parameters")
210 .map_err(|error| Error::Encoding(error.to_string()))?;
211 tags::encode_opening_tag(buf, *tag);
214 buf.extend_from_slice(data);
215 tags::encode_closing_tag(buf, *tag);
216 }
217 }
218 Ok(())
219}
220
221pub fn decode_event_parameter(
227 data: &[u8],
228 offset: usize,
229) -> Result<(BACnetEventParameter, usize), Error> {
230 use BACnetEventParameter as EP;
231
232 if data.get(offset..offset.saturating_add(2)) == Some(&[0xFE, 0xFF]) {
236 let body_start = offset + 2;
237 if data.len() < body_start + 2 {
239 return Err(Error::buffer_too_short(body_start + 2, data.len()));
240 }
241 let body_end = data.len() - 2;
242 if data.get(body_end..) != Some(&[0xFF, 0xFF]) {
243 return Err(Error::decoding(
244 offset,
245 "BACnetEventParameter: missing legacy closing tag",
246 ));
247 }
248 return Ok((
249 EP::Opaque {
250 tag: u8::MAX,
251 data: data[body_start..body_end].to_vec(),
252 },
253 data.len(),
254 ));
255 }
256
257 let (tag, pos) = tags::decode_tag(data, offset)?;
258 let what = "BACnetEventParameter";
259
260 if tag.class == tags::TagClass::Application && tag.number == tags::app_tag::OCTET_STRING {
261 let (octets, end) = contents(data, pos, tag.length)?;
262 return Ok((
263 EP::Opaque {
264 tag: u8::MAX,
265 data: octets.to_vec(),
266 },
267 end,
268 ));
269 }
270
271 if tag.class != tags::TagClass::Context {
272 return Err(Error::decoding(
273 offset,
274 "BACnetEventParameter: expected a context tag",
275 ));
276 }
277
278 if RESERVED_EVENT_TAGS.contains(&tag.number) {
279 return Err(Error::decoding(
280 offset,
281 format!(
282 "BACnetEventParameter: context tag [{}] is omitted/deprecated/reserved in Clause 21",
283 tag.number
284 ),
285 ));
286 }
287
288 if !tag.is_opening && !tag.is_closing {
290 if tag.number == 20 && tag.length == 0 {
291 return Ok((
292 EP::Opaque {
293 tag: 20,
294 data: Vec::new(),
295 },
296 pos,
297 ));
298 }
299 return Err(Error::decoding(
300 offset,
301 "BACnetEventParameter: invalid primitive alternative",
302 ));
303 }
304
305 if !tag.is_opening {
306 return Err(Error::decoding(
307 offset,
308 "BACnetEventParameter: unexpected closing tag",
309 ));
310 }
311 if tag.number == 20 {
312 return Err(Error::decoding(
313 offset,
314 "BACnetEventParameter: none [20] must use primitive NULL form",
315 ));
316 }
317
318 let value = match tag.number {
319 0 => {
320 let what = "BACnetEventParameter change-of-bitstring";
321 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
322 let (bitmask, pos) = decode_ctx_bit_string(data, pos, 1, what)?;
323 let mut pos = expect_opening(data, pos, 2, what)?;
324 let mut list_of_values = Vec::new();
325 while !next_is_closing(data, pos, 2)? {
326 if list_of_values.len() >= MAX_FRAMED_ITEMS {
327 return Err(Error::overflow(
328 pos,
329 "change-of-bitstring: list-of-bitstring-values exceeds limit",
330 ));
331 }
332 let (bits, p) = decode_app_bit_string(data, pos, what)?;
333 list_of_values.push(bits);
334 pos = p;
335 }
336 pos = expect_closing(data, pos, 2, what)?;
337 pos = expect_closing(data, pos, 0, what)?;
338 (
339 EP::ChangeOfBitstring {
340 time_delay,
341 bitmask,
342 list_of_values,
343 },
344 pos,
345 )
346 }
347 1 => {
348 let what = "BACnetEventParameter change-of-state";
349 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
350 let mut pos = expect_opening(data, pos, 1, what)?;
351 let mut list_of_values = Vec::new();
352 while !next_is_closing(data, pos, 1)? {
353 if list_of_values.len() >= MAX_FRAMED_ITEMS {
354 return Err(Error::overflow(
355 pos,
356 "change-of-state: list-of-values exceeds limit",
357 ));
358 }
359 let (state, p) = decode_property_state(data, pos)?;
360 list_of_values.push(state);
361 pos = p;
362 }
363 pos = expect_closing(data, pos, 1, what)?;
364 pos = expect_closing(data, pos, 1, what)?;
365 (
366 EP::ChangeOfState {
367 time_delay,
368 list_of_values,
369 },
370 pos,
371 )
372 }
373 2 => {
374 let what = "BACnetEventParameter change-of-value";
375 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
376 let mut pos = expect_opening(data, pos, 1, what)?;
377 let (inner, _) = tags::decode_tag(data, pos)?;
378 let criteria = if inner.is_context(0) {
379 let (bits, p) = decode_ctx_bit_string(data, pos, 0, what)?;
380 pos = p;
381 ChangeOfValueCriteria::Bitmask {
382 unused_bits: bits.0,
383 data: bits.1,
384 }
385 } else if inner.is_context(1) {
386 let (increment, p) = decode_ctx_real(data, pos, 1, what)?;
387 pos = p;
388 ChangeOfValueCriteria::ReferencedPropertyIncrement(increment)
389 } else {
390 return Err(Error::decoding(
391 pos,
392 format!(
393 "change-of-value: unexpected cov-criteria tag [{}]",
394 inner.number
395 ),
396 ));
397 };
398 pos = expect_closing(data, pos, 1, what)?;
399 pos = expect_closing(data, pos, 2, what)?;
400 (
401 EP::ChangeOfValue {
402 time_delay,
403 criteria,
404 },
405 pos,
406 )
407 }
408 4 => {
409 let what = "BACnetEventParameter floating-limit";
410 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
411 let pos = expect_opening(data, pos, 1, what)?;
412 let (setpoint_reference, pos) = decode_dopr_body(data, pos, what)?;
413 let pos = expect_closing(data, pos, 1, what)?;
414 let (low_diff_limit, pos) = decode_ctx_real(data, pos, 2, what)?;
415 let (high_diff_limit, pos) = decode_ctx_real(data, pos, 3, what)?;
416 let (deadband, pos) = decode_ctx_real(data, pos, 4, what)?;
417 let pos = expect_closing(data, pos, 4, what)?;
418 (
419 EP::FloatingLimit {
420 time_delay,
421 setpoint_reference,
422 low_diff_limit,
423 high_diff_limit,
424 deadband,
425 },
426 pos,
427 )
428 }
429 5 => {
430 let what = "BACnetEventParameter out-of-range";
431 let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
432 let (low_limit, pos) = decode_ctx_real(data, pos, 1, what)?;
433 let (high_limit, pos) = decode_ctx_real(data, pos, 2, what)?;
434 let (deadband, pos) = decode_ctx_real(data, pos, 3, what)?;
435 let pos = expect_closing(data, pos, 5, what)?;
436 (
437 EP::OutOfRange {
438 time_delay,
439 low_limit,
440 high_limit,
441 deadband,
442 },
443 pos,
444 )
445 }
446 9 => {
447 let what = "BACnetEventParameter extended";
448 let (vendor_id, pos) = decode_ctx_unsigned::<u16>(data, pos, 0, what)?;
449 let (extended_event_type, pos) = decode_ctx_unsigned::<u32>(data, pos, 1, what)?;
450 let (raw_params, pos) = decode_ctx_constructed(data, pos, 2, what)?;
451 validate_extended_parameters(raw_params, "extended")?;
452 let pos = expect_closing(data, pos, 9, what)?;
453 (
454 EP::Extended {
455 vendor_id,
456 extended_event_type,
457 parameters: raw_params.to_vec(),
458 },
459 pos,
460 )
461 }
462 n => {
463 let (body, p) = tags::extract_context_value(data, pos, n)?;
466 validate_tlv_sequence(body, "opaque event parameters")?;
467 (
468 EP::Opaque {
469 tag: n,
470 data: body.to_vec(),
471 },
472 p,
473 )
474 }
475 };
476 let (value, end) = value;
477 validate_tlv_sequence(&data[offset..end], what)?;
478 Ok((value, end))
479}