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