bacnet_encoding/constructed/
staging.rs1use bacnet_types::constructed::{BACnetDeviceObjectReference, BACnetStageLimitValue};
4use bacnet_types::error::Error;
5use bacnet_types::primitives::PropertyValue;
6use bytes::BytesMut;
7
8use super::tagged::{decode_ctx_object_id, decode_optional_ctx};
9use crate::primitives;
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 const WHAT: &str = "BACnetDeviceObjectReference";
77 let (device_identifier, offset) =
78 decode_optional_ctx(data, offset, 0, WHAT, decode_ctx_object_id)?;
79 let (object_identifier, end) = decode_ctx_object_id(data, offset, 1, WHAT)?;
80 Ok((
81 BACnetDeviceObjectReference {
82 device_identifier,
83 object_identifier,
84 },
85 end,
86 ))
87}
88
89fn pack_bits(values: &[bool]) -> (u8, Vec<u8>) {
90 if values.is_empty() {
91 return (0, Vec::new());
92 }
93 let mut data = vec![0; values.len().div_ceil(8)];
94 for (index, value) in values.iter().enumerate() {
95 if *value {
96 data[index / 8] |= 0x80 >> (index % 8);
97 }
98 }
99 ((data.len() * 8 - values.len()) as u8, data)
100}
101
102fn unpack_bits(unused_bits: u8, data: &[u8], offset: usize) -> Result<Vec<bool>, Error> {
103 if data.is_empty() {
104 if unused_bits == 0 {
105 return Ok(Vec::new());
106 }
107 return Err(Error::decoding(
108 offset,
109 "empty stage values BIT STRING must declare zero unused bits",
110 ));
111 }
112 let mask = if unused_bits == 0 {
113 0
114 } else {
115 (1_u8 << unused_bits) - 1
116 };
117 if data.last().copied().unwrap_or_default() & mask != 0 {
118 return Err(Error::decoding(
119 offset,
120 "stage values BIT STRING has nonzero padding bits",
121 ));
122 }
123 let bit_len = data
124 .len()
125 .checked_mul(8)
126 .and_then(|bits| bits.checked_sub(unused_bits as usize))
127 .ok_or_else(|| Error::decoding(offset, "invalid stage values BIT STRING length"))?;
128 Ok((0..bit_len)
129 .map(|index| data[index / 8] & (0x80 >> (index % 8)) != 0)
130 .collect())
131}