bacnet_encoding/constructed/
staging.rs1#[cfg(not(feature = "std"))]
4use alloc::vec::Vec;
5
6use bacnet_types::constructed::{BACnetDeviceObjectReference, BACnetStageLimitValue};
7use bacnet_types::error::Error;
8use bacnet_types::primitives::PropertyValue;
9use bytes::BytesMut;
10
11use super::tagged::{decode_ctx_object_id, decode_optional_ctx};
12use crate::primitives;
13
14pub fn encode_stage_limit_value(buf: &mut BytesMut, value: &BACnetStageLimitValue) {
16 primitives::encode_app_real(buf, value.limit);
17 let (unused_bits, data) = pack_bits(&value.values);
18 primitives::encode_app_bit_string(buf, unused_bits, &data);
19 primitives::encode_app_real(buf, value.deadband);
20}
21
22pub fn decode_stage_limit_value(
24 data: &[u8],
25 offset: usize,
26) -> Result<(BACnetStageLimitValue, usize), Error> {
27 let (limit, offset) = primitives::decode_application_value(data, offset)?;
28 let PropertyValue::Real(limit) = limit else {
29 return Err(Error::decoding(
30 offset,
31 "stage limit must be application REAL",
32 ));
33 };
34
35 let (values, offset) = primitives::decode_application_value(data, offset)?;
36 let PropertyValue::BitString {
37 unused_bits,
38 data: packed,
39 } = values
40 else {
41 return Err(Error::decoding(
42 offset,
43 "stage values must be application BIT STRING",
44 ));
45 };
46 let values = unpack_bits(unused_bits, &packed, offset)?;
47
48 let (deadband, offset) = primitives::decode_application_value(data, offset)?;
49 let PropertyValue::Real(deadband) = deadband else {
50 return Err(Error::decoding(
51 offset,
52 "stage deadband must be application REAL",
53 ));
54 };
55
56 Ok((
57 BACnetStageLimitValue {
58 limit,
59 values,
60 deadband,
61 },
62 offset,
63 ))
64}
65
66pub fn encode_device_object_reference(buf: &mut BytesMut, reference: &BACnetDeviceObjectReference) {
68 if let Some(device) = &reference.device_identifier {
69 primitives::encode_ctx_object_id(buf, 0, device);
70 }
71 primitives::encode_ctx_object_id(buf, 1, &reference.object_identifier);
72}
73
74pub fn decode_device_object_reference(
76 data: &[u8],
77 offset: usize,
78) -> Result<(BACnetDeviceObjectReference, usize), Error> {
79 const WHAT: &str = "BACnetDeviceObjectReference";
80 let (device_identifier, offset) =
81 decode_optional_ctx(data, offset, 0, WHAT, decode_ctx_object_id)?;
82 let (object_identifier, end) = decode_ctx_object_id(data, offset, 1, WHAT)?;
83 Ok((
84 BACnetDeviceObjectReference {
85 device_identifier,
86 object_identifier,
87 },
88 end,
89 ))
90}
91
92fn pack_bits(values: &[bool]) -> (u8, Vec<u8>) {
93 if values.is_empty() {
94 return (0, Vec::new());
95 }
96 let mut data = vec![0; values.len().div_ceil(8)];
97 for (index, value) in values.iter().enumerate() {
98 if *value {
99 data[index / 8] |= 0x80 >> (index % 8);
100 }
101 }
102 ((data.len() * 8 - values.len()) as u8, data)
103}
104
105fn unpack_bits(unused_bits: u8, data: &[u8], offset: usize) -> Result<Vec<bool>, Error> {
106 if data.is_empty() {
107 if unused_bits == 0 {
108 return Ok(Vec::new());
109 }
110 return Err(Error::decoding(
111 offset,
112 "empty stage values BIT STRING must declare zero unused bits",
113 ));
114 }
115 let mask = if unused_bits == 0 {
116 0
117 } else {
118 (1_u8 << unused_bits) - 1
119 };
120 if data.last().copied().unwrap_or_default() & mask != 0 {
121 return Err(Error::decoding(
122 offset,
123 "stage values BIT STRING has nonzero padding bits",
124 ));
125 }
126 let bit_len = data
127 .len()
128 .checked_mul(8)
129 .and_then(|bits| bits.checked_sub(unused_bits as usize))
130 .ok_or_else(|| Error::decoding(offset, "invalid stage values BIT STRING length"))?;
131 Ok((0..bit_len)
132 .map(|index| data[index / 8] & (0x80 >> (index % 8)) != 0)
133 .collect())
134}