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)?;
170 if !tag.is_opening_tag(3) {
171 return Err(Error::decoding(
172 offset,
173 "ReadPropertyACK expected opening tag 3",
174 ));
175 }
176 let (value_bytes, _end) = tags::extract_context_value(data, tag_end, 3)?;
177 return Ok(Self {
178 object_identifier,
179 property_identifier,
180 property_array_index,
181 property_value: value_bytes.to_vec(),
182 });
183 }
184
185 if !tag.is_opening_tag(3) {
187 return Err(Error::decoding(
188 offset,
189 "ReadPropertyACK expected opening tag 3",
190 ));
191 }
192 let (value_bytes, _) = tags::extract_context_value(data, tag_end, 3)?;
193
194 Ok(Self {
195 object_identifier,
196 property_identifier,
197 property_array_index,
198 property_value: value_bytes.to_vec(),
199 })
200 }
201}
202
203#[cfg(test)]
208mod tests {
209 use super::*;
210 use bacnet_types::enums::ObjectType;
211
212 #[test]
213 fn request_round_trip() {
214 let req = ReadPropertyRequest {
215 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
216 property_identifier: PropertyIdentifier::PRESENT_VALUE,
217 property_array_index: None,
218 };
219 let mut buf = BytesMut::new();
220 req.encode(&mut buf);
221 let decoded = ReadPropertyRequest::decode(&buf).unwrap();
222 assert_eq!(req, decoded);
223 }
224
225 #[test]
226 fn request_with_index_round_trip() {
227 let req = ReadPropertyRequest {
228 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_OUTPUT, 5).unwrap(),
229 property_identifier: PropertyIdentifier::PRIORITY_ARRAY,
230 property_array_index: Some(8),
231 };
232 let mut buf = BytesMut::new();
233 req.encode(&mut buf);
234 let decoded = ReadPropertyRequest::decode(&buf).unwrap();
235 assert_eq!(req, decoded);
236 }
237
238 #[test]
239 fn ack_round_trip() {
240 let ack = ReadPropertyACK {
241 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
242 property_identifier: PropertyIdentifier::PRESENT_VALUE,
243 property_array_index: None,
244 property_value: vec![0x44, 0x42, 0x90, 0x00, 0x00], };
246 let mut buf = BytesMut::new();
247 ack.encode(&mut buf);
248 let decoded = ReadPropertyACK::decode(&buf).unwrap();
249 assert_eq!(ack, decoded);
250 }
251
252 #[test]
253 fn ack_with_index_round_trip() {
254 let ack = ReadPropertyACK {
255 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_OUTPUT, 3).unwrap(),
256 property_identifier: PropertyIdentifier::PRIORITY_ARRAY,
257 property_array_index: Some(8),
258 property_value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
259 };
260 let mut buf = BytesMut::new();
261 ack.encode(&mut buf);
262 let decoded = ReadPropertyACK::decode(&buf).unwrap();
263 assert_eq!(ack, decoded);
264 }
265
266 #[test]
271 fn test_decode_read_property_request_empty_input() {
272 assert!(ReadPropertyRequest::decode(&[]).is_err());
273 }
274
275 #[test]
276 fn test_decode_read_property_request_truncated_1_byte() {
277 let req = ReadPropertyRequest {
279 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
280 property_identifier: PropertyIdentifier::PRESENT_VALUE,
281 property_array_index: None,
282 };
283 let mut buf = BytesMut::new();
284 req.encode(&mut buf);
285 assert!(ReadPropertyRequest::decode(&buf[..1]).is_err());
286 }
287
288 #[test]
289 fn test_decode_read_property_request_truncated_2_bytes() {
290 let req = ReadPropertyRequest {
291 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
292 property_identifier: PropertyIdentifier::PRESENT_VALUE,
293 property_array_index: None,
294 };
295 let mut buf = BytesMut::new();
296 req.encode(&mut buf);
297 assert!(ReadPropertyRequest::decode(&buf[..2]).is_err());
298 }
299
300 #[test]
301 fn test_decode_read_property_request_truncated_3_bytes() {
302 let req = ReadPropertyRequest {
303 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
304 property_identifier: PropertyIdentifier::PRESENT_VALUE,
305 property_array_index: None,
306 };
307 let mut buf = BytesMut::new();
308 req.encode(&mut buf);
309 assert!(ReadPropertyRequest::decode(&buf[..3]).is_err());
310 }
311
312 #[test]
313 fn test_decode_read_property_request_invalid_tag() {
314 assert!(ReadPropertyRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
316 }
317
318 #[test]
319 fn test_decode_read_property_request_oversized_length() {
320 assert!(ReadPropertyRequest::decode(&[0x05, 0xFF]).is_err());
323 }
324
325 #[test]
326 fn test_decode_read_property_ack_empty_input() {
327 assert!(ReadPropertyACK::decode(&[]).is_err());
328 }
329
330 #[test]
331 fn test_decode_read_property_ack_truncated_1_byte() {
332 let ack = ReadPropertyACK {
333 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
334 property_identifier: PropertyIdentifier::PRESENT_VALUE,
335 property_array_index: None,
336 property_value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
337 };
338 let mut buf = BytesMut::new();
339 ack.encode(&mut buf);
340 assert!(ReadPropertyACK::decode(&buf[..1]).is_err());
341 }
342
343 #[test]
344 fn test_decode_read_property_ack_truncated_3_bytes() {
345 let ack = ReadPropertyACK {
346 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
347 property_identifier: PropertyIdentifier::PRESENT_VALUE,
348 property_array_index: None,
349 property_value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
350 };
351 let mut buf = BytesMut::new();
352 ack.encode(&mut buf);
353 assert!(ReadPropertyACK::decode(&buf[..3]).is_err());
354 }
355
356 #[test]
357 fn test_decode_read_property_ack_truncated_half() {
358 let ack = ReadPropertyACK {
359 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
360 property_identifier: PropertyIdentifier::PRESENT_VALUE,
361 property_array_index: None,
362 property_value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
363 };
364 let mut buf = BytesMut::new();
365 ack.encode(&mut buf);
366 let half = buf.len() / 2;
367 assert!(ReadPropertyACK::decode(&buf[..half]).is_err());
368 }
369
370 #[test]
371 fn test_decode_read_property_ack_invalid_tag() {
372 assert!(ReadPropertyACK::decode(&[0xFF, 0xFF, 0xFF]).is_err());
373 }
374}