bacnet_encoding/constructed/
staging.rs1use bacnet_types::constructed::{BACnetDeviceObjectReference, BACnetStageLimitValue};
4use bacnet_types::error::Error;
5use bacnet_types::primitives::{ObjectIdentifier, PropertyValue};
6use bytes::BytesMut;
7
8use crate::primitives;
9use crate::tags;
10
11pub fn encode_stage_limit_value(buf: &mut BytesMut, value: &BACnetStageLimitValue) {
13 primitives::encode_app_real(buf, value.limit);
14 let (unused_bits, data) = pack_bits(&value.values);
15 primitives::encode_app_bit_string(buf, unused_bits, &data);
16 primitives::encode_app_real(buf, value.deadband);
17}
18
19pub fn decode_stage_limit_value(
21 data: &[u8],
22 offset: usize,
23) -> Result<(BACnetStageLimitValue, usize), Error> {
24 let (limit, offset) = primitives::decode_application_value(data, offset)?;
25 let PropertyValue::Real(limit) = limit else {
26 return Err(Error::decoding(
27 offset,
28 "stage limit must be application REAL",
29 ));
30 };
31
32 let (values, offset) = primitives::decode_application_value(data, offset)?;
33 let PropertyValue::BitString {
34 unused_bits,
35 data: packed,
36 } = values
37 else {
38 return Err(Error::decoding(
39 offset,
40 "stage values must be application BIT STRING",
41 ));
42 };
43 let values = unpack_bits(unused_bits, &packed, offset)?;
44
45 let (deadband, offset) = primitives::decode_application_value(data, offset)?;
46 let PropertyValue::Real(deadband) = deadband else {
47 return Err(Error::decoding(
48 offset,
49 "stage deadband must be application REAL",
50 ));
51 };
52
53 Ok((
54 BACnetStageLimitValue {
55 limit,
56 values,
57 deadband,
58 },
59 offset,
60 ))
61}
62
63pub fn encode_device_object_reference(buf: &mut BytesMut, reference: &BACnetDeviceObjectReference) {
65 if let Some(device) = &reference.device_identifier {
66 primitives::encode_ctx_object_id(buf, 0, device);
67 }
68 primitives::encode_ctx_object_id(buf, 1, &reference.object_identifier);
69}
70
71pub fn decode_device_object_reference(
73 data: &[u8],
74 offset: usize,
75) -> Result<(BACnetDeviceObjectReference, usize), Error> {
76 let mut offset = offset;
77 let mut device_identifier = None;
78 let (first, first_content) = tags::decode_tag(data, offset)?;
79 if first.is_context(0) {
80 let end = object_id_end(data, offset, first_content, first.length, 0)?;
81 device_identifier = Some(ObjectIdentifier::decode(&data[first_content..end])?);
82 offset = end;
83 }
84
85 let (object_tag, object_content) = tags::decode_tag(data, offset)?;
86 if !object_tag.is_context(1) || object_tag.length != 4 {
87 return Err(Error::decoding(
88 offset,
89 "device-object reference requires object-identifier [1]",
90 ));
91 }
92 let end = object_id_end(data, offset, object_content, object_tag.length, 1)?;
93 let object_identifier = ObjectIdentifier::decode(&data[object_content..end])?;
94
95 Ok((
96 BACnetDeviceObjectReference {
97 device_identifier,
98 object_identifier,
99 },
100 end,
101 ))
102}
103
104fn object_id_end(
105 data: &[u8],
106 tag_offset: usize,
107 content: usize,
108 length: u32,
109 tag: u8,
110) -> Result<usize, Error> {
111 if length != 4 {
112 return Err(Error::decoding(
113 tag_offset,
114 format!("device-object reference [{tag}] must contain a 4-octet object identifier"),
115 ));
116 }
117 let end = content
118 .checked_add(4)
119 .ok_or_else(|| Error::decoding(content, "device-object reference length overflow"))?;
120 if end > data.len() {
121 return Err(Error::buffer_too_short(end, data.len()));
122 }
123 Ok(end)
124}
125
126fn pack_bits(values: &[bool]) -> (u8, Vec<u8>) {
127 if values.is_empty() {
128 return (0, Vec::new());
129 }
130 let mut data = vec![0; values.len().div_ceil(8)];
131 for (index, value) in values.iter().enumerate() {
132 if *value {
133 data[index / 8] |= 0x80 >> (index % 8);
134 }
135 }
136 ((data.len() * 8 - values.len()) as u8, data)
137}
138
139fn unpack_bits(unused_bits: u8, data: &[u8], offset: usize) -> Result<Vec<bool>, Error> {
140 if data.is_empty() {
141 if unused_bits == 0 {
142 return Ok(Vec::new());
143 }
144 return Err(Error::decoding(
145 offset,
146 "empty stage values BIT STRING must declare zero unused bits",
147 ));
148 }
149 let mask = if unused_bits == 0 {
150 0
151 } else {
152 (1_u8 << unused_bits) - 1
153 };
154 if data.last().copied().unwrap_or_default() & mask != 0 {
155 return Err(Error::decoding(
156 offset,
157 "stage values BIT STRING has nonzero padding bits",
158 ));
159 }
160 let bit_len = data
161 .len()
162 .checked_mul(8)
163 .and_then(|bits| bits.checked_sub(unused_bits as usize))
164 .ok_or_else(|| Error::decoding(offset, "invalid stage values BIT STRING length"))?;
165 Ok((0..bit_len)
166 .map(|index| data[index / 8] & (0x80 >> (index % 8)) != 0)
167 .collect())
168}