1use bacnet_encoding::primitives;
4use bacnet_encoding::tags::{self, TagClass};
5use bacnet_types::enums::PropertyIdentifier;
6use bacnet_types::error::Error;
7use bacnet_types::primitives::ObjectIdentifier;
8use bytes::BytesMut;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct ReadPropertyRequest {
25 pub object_identifier: ObjectIdentifier,
26 pub property_identifier: PropertyIdentifier,
27 pub property_array_index: Option<u32>,
28}
29
30impl ReadPropertyRequest {
31 pub fn encode(&self, buf: &mut BytesMut) {
32 primitives::encode_ctx_object_id(buf, 0, &self.object_identifier);
33 primitives::encode_ctx_unsigned(buf, 1, self.property_identifier.to_raw() as u64);
34 if let Some(idx) = self.property_array_index {
35 primitives::encode_ctx_unsigned(buf, 2, idx as u64);
36 }
37 }
38
39 pub fn decode(data: &[u8]) -> Result<Self, Error> {
40 let mut offset = 0;
41
42 let (tag, pos) = tags::decode_tag(data, offset)?;
44 let end = pos + tag.length as usize;
45 if end > data.len() {
46 return Err(Error::decoding(
47 pos,
48 "ReadProperty request truncated at object-id",
49 ));
50 }
51 let object_identifier = ObjectIdentifier::decode(&data[pos..end])?;
52 offset = end;
53
54 let (tag, pos) = tags::decode_tag(data, offset)?;
56 let end = pos + tag.length as usize;
57 if end > data.len() {
58 return Err(Error::decoding(
59 pos,
60 "ReadProperty request truncated at property-id",
61 ));
62 }
63 let prop_raw = primitives::decode_unsigned(&data[pos..end])? as u32;
64 let property_identifier = PropertyIdentifier::from_raw(prop_raw);
65 offset = end;
66
67 let mut property_array_index = None;
69 if offset < data.len() {
70 let (tag, pos) = tags::decode_tag(data, offset)?;
71 if tag.is_context(2) {
72 let end = pos + tag.length as usize;
73 if end > data.len() {
74 return Err(Error::decoding(
75 pos,
76 "ReadProperty request truncated at array-index",
77 ));
78 }
79 property_array_index = Some(primitives::decode_unsigned(&data[pos..end])? as u32);
80 }
81 }
82
83 Ok(Self {
84 object_identifier,
85 property_identifier,
86 property_array_index,
87 })
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct ReadPropertyACK {
109 pub object_identifier: ObjectIdentifier,
110 pub property_identifier: PropertyIdentifier,
111 pub property_array_index: Option<u32>,
112 pub property_value: Vec<u8>,
113}
114
115impl ReadPropertyACK {
116 pub fn encode(&self, buf: &mut BytesMut) {
117 primitives::encode_ctx_object_id(buf, 0, &self.object_identifier);
118 primitives::encode_ctx_unsigned(buf, 1, self.property_identifier.to_raw() as u64);
119 if let Some(idx) = self.property_array_index {
120 primitives::encode_ctx_unsigned(buf, 2, idx as u64);
121 }
122 tags::encode_opening_tag(buf, 3);
123 buf.extend_from_slice(&self.property_value);
124 tags::encode_closing_tag(buf, 3);
125 }
126
127 pub fn decode(data: &[u8]) -> Result<Self, Error> {
128 let mut offset = 0;
129
130 let (tag, pos) = tags::decode_tag(data, offset)?;
132 let end = pos + tag.length as usize;
133 if end > data.len() {
134 return Err(Error::decoding(
135 pos,
136 "ReadPropertyACK truncated at object-id",
137 ));
138 }
139 let object_identifier = ObjectIdentifier::decode(&data[pos..end])?;
140 offset = end;
141
142 let (tag, pos) = tags::decode_tag(data, offset)?;
144 let end = pos + tag.length as usize;
145 if end > data.len() {
146 return Err(Error::decoding(
147 pos,
148 "ReadPropertyACK truncated at property-id",
149 ));
150 }
151 let prop_raw = primitives::decode_unsigned(&data[pos..end])? as u32;
152 let property_identifier = PropertyIdentifier::from_raw(prop_raw);
153 offset = end;
154
155 let mut property_array_index = None;
157 let (tag, tag_end) = tags::decode_tag(data, offset)?;
158 if tag.class == TagClass::Context && tag.number == 2 && !tag.is_opening && !tag.is_closing {
159 let end = tag_end + tag.length as usize;
160 if end > data.len() {
161 return Err(Error::decoding(
162 tag_end,
163 "ReadPropertyACK truncated at array-index",
164 ));
165 }
166 property_array_index = Some(primitives::decode_unsigned(&data[tag_end..end])? as u32);
167 offset = end;
168 let (tag, tag_end) = tags::decode_tag(data, offset)?;
169 if !tag.is_opening_tag(3) {
170 return Err(Error::decoding(
171 offset,
172 "ReadPropertyACK expected opening tag 3",
173 ));
174 }
175 let (value_bytes, _end) = tags::extract_context_value(data, tag_end, 3)?;
176 return Ok(Self {
177 object_identifier,
178 property_identifier,
179 property_array_index,
180 property_value: value_bytes.to_vec(),
181 });
182 }
183
184 if !tag.is_opening_tag(3) {
185 return Err(Error::decoding(
186 offset,
187 "ReadPropertyACK expected opening tag 3",
188 ));
189 }
190 let (value_bytes, _) = tags::extract_context_value(data, tag_end, 3)?;
191
192 Ok(Self {
193 object_identifier,
194 property_identifier,
195 property_array_index,
196 property_value: value_bytes.to_vec(),
197 })
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204 use bacnet_types::enums::ObjectType;
205
206 #[test]
207 fn request_round_trip() {
208 let req = ReadPropertyRequest {
209 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
210 property_identifier: PropertyIdentifier::PRESENT_VALUE,
211 property_array_index: None,
212 };
213 let mut buf = BytesMut::new();
214 req.encode(&mut buf);
215 let decoded = ReadPropertyRequest::decode(&buf).unwrap();
216 assert_eq!(req, decoded);
217 }
218
219 #[test]
220 fn request_with_index_round_trip() {
221 let req = ReadPropertyRequest {
222 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_OUTPUT, 5).unwrap(),
223 property_identifier: PropertyIdentifier::PRIORITY_ARRAY,
224 property_array_index: Some(8),
225 };
226 let mut buf = BytesMut::new();
227 req.encode(&mut buf);
228 let decoded = ReadPropertyRequest::decode(&buf).unwrap();
229 assert_eq!(req, decoded);
230 }
231
232 #[test]
233 fn ack_round_trip() {
234 let ack = ReadPropertyACK {
235 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
236 property_identifier: PropertyIdentifier::PRESENT_VALUE,
237 property_array_index: None,
238 property_value: vec![0x44, 0x42, 0x90, 0x00, 0x00], };
240 let mut buf = BytesMut::new();
241 ack.encode(&mut buf);
242 let decoded = ReadPropertyACK::decode(&buf).unwrap();
243 assert_eq!(ack, decoded);
244 }
245
246 #[test]
247 fn ack_with_index_round_trip() {
248 let ack = ReadPropertyACK {
249 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_OUTPUT, 3).unwrap(),
250 property_identifier: PropertyIdentifier::PRIORITY_ARRAY,
251 property_array_index: Some(8),
252 property_value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
253 };
254 let mut buf = BytesMut::new();
255 ack.encode(&mut buf);
256 let decoded = ReadPropertyACK::decode(&buf).unwrap();
257 assert_eq!(ack, decoded);
258 }
259
260 #[test]
265 fn test_decode_read_property_request_empty_input() {
266 assert!(ReadPropertyRequest::decode(&[]).is_err());
267 }
268
269 #[test]
270 fn test_decode_read_property_request_truncated_1_byte() {
271 let req = ReadPropertyRequest {
273 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
274 property_identifier: PropertyIdentifier::PRESENT_VALUE,
275 property_array_index: None,
276 };
277 let mut buf = BytesMut::new();
278 req.encode(&mut buf);
279 assert!(ReadPropertyRequest::decode(&buf[..1]).is_err());
280 }
281
282 #[test]
283 fn test_decode_read_property_request_truncated_2_bytes() {
284 let req = ReadPropertyRequest {
285 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
286 property_identifier: PropertyIdentifier::PRESENT_VALUE,
287 property_array_index: None,
288 };
289 let mut buf = BytesMut::new();
290 req.encode(&mut buf);
291 assert!(ReadPropertyRequest::decode(&buf[..2]).is_err());
292 }
293
294 #[test]
295 fn test_decode_read_property_request_truncated_3_bytes() {
296 let req = ReadPropertyRequest {
297 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
298 property_identifier: PropertyIdentifier::PRESENT_VALUE,
299 property_array_index: None,
300 };
301 let mut buf = BytesMut::new();
302 req.encode(&mut buf);
303 assert!(ReadPropertyRequest::decode(&buf[..3]).is_err());
304 }
305
306 #[test]
307 fn test_decode_read_property_request_invalid_tag() {
308 assert!(ReadPropertyRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
310 }
311
312 #[test]
313 fn test_decode_read_property_request_oversized_length() {
314 assert!(ReadPropertyRequest::decode(&[0x05, 0xFF]).is_err());
317 }
318
319 #[test]
320 fn test_decode_read_property_ack_empty_input() {
321 assert!(ReadPropertyACK::decode(&[]).is_err());
322 }
323
324 #[test]
325 fn test_decode_read_property_ack_truncated_1_byte() {
326 let ack = ReadPropertyACK {
327 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
328 property_identifier: PropertyIdentifier::PRESENT_VALUE,
329 property_array_index: None,
330 property_value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
331 };
332 let mut buf = BytesMut::new();
333 ack.encode(&mut buf);
334 assert!(ReadPropertyACK::decode(&buf[..1]).is_err());
335 }
336
337 #[test]
338 fn test_decode_read_property_ack_truncated_3_bytes() {
339 let ack = ReadPropertyACK {
340 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
341 property_identifier: PropertyIdentifier::PRESENT_VALUE,
342 property_array_index: None,
343 property_value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
344 };
345 let mut buf = BytesMut::new();
346 ack.encode(&mut buf);
347 assert!(ReadPropertyACK::decode(&buf[..3]).is_err());
348 }
349
350 #[test]
351 fn test_decode_read_property_ack_truncated_half() {
352 let ack = ReadPropertyACK {
353 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
354 property_identifier: PropertyIdentifier::PRESENT_VALUE,
355 property_array_index: None,
356 property_value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
357 };
358 let mut buf = BytesMut::new();
359 ack.encode(&mut buf);
360 let half = buf.len() / 2;
361 assert!(ReadPropertyACK::decode(&buf[..half]).is_err());
362 }
363
364 #[test]
365 fn test_decode_read_property_ack_invalid_tag() {
366 assert!(ReadPropertyACK::decode(&[0xFF, 0xFF, 0xFF]).is_err());
367 }
368}