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