1use bacnet_encoding::primitives;
4use bacnet_encoding::tags;
5use bacnet_types::enums::{ErrorClass, ErrorCode, PropertyIdentifier};
6use bacnet_types::error::Error;
7use bacnet_types::primitives::ObjectIdentifier;
8use bytes::BytesMut;
9
10use crate::common::{PropertyReference, MAX_DECODED_ITEMS};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct ReadAccessSpecification {
19 pub object_identifier: ObjectIdentifier,
20 pub list_of_property_references: Vec<PropertyReference>,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct ReadPropertyMultipleRequest {
26 pub list_of_read_access_specs: Vec<ReadAccessSpecification>,
27}
28
29impl ReadPropertyMultipleRequest {
30 pub fn encode(&self, buf: &mut BytesMut) {
31 for spec in &self.list_of_read_access_specs {
32 primitives::encode_ctx_object_id(buf, 0, &spec.object_identifier);
34 tags::encode_opening_tag(buf, 1);
36 for prop_ref in &spec.list_of_property_references {
37 prop_ref.encode(buf);
38 }
39 tags::encode_closing_tag(buf, 1);
40 }
41 }
42
43 pub fn decode(data: &[u8]) -> Result<Self, Error> {
44 let mut offset = 0;
45 let mut specs = Vec::new();
46
47 while offset < data.len() {
48 if specs.len() >= MAX_DECODED_ITEMS {
49 return Err(Error::decoding(
50 offset,
51 "RPM request exceeds max decoded items",
52 ));
53 }
54
55 let (tag, pos) = tags::decode_tag(data, offset)?;
57 let end = pos + tag.length as usize;
58 if end > data.len() {
59 return Err(Error::decoding(pos, "RPM request truncated at object-id"));
60 }
61 let object_identifier = ObjectIdentifier::decode(&data[pos..end])?;
62 offset = end;
63
64 let (tag, tag_end) = tags::decode_tag(data, offset)?;
66 if !tag.is_opening_tag(1) {
67 return Err(Error::decoding(
68 offset,
69 "RPM request expected opening tag 1",
70 ));
71 }
72 offset = tag_end;
73
74 let mut prop_refs = Vec::new();
75 loop {
76 if offset >= data.len() {
77 return Err(Error::decoding(offset, "RPM request missing closing tag 1"));
78 }
79 if prop_refs.len() >= MAX_DECODED_ITEMS {
80 return Err(Error::decoding(offset, "RPM property refs exceeds max"));
81 }
82 let (tag, tag_end) = tags::decode_tag(data, offset)?;
84 if tag.is_closing_tag(1) {
85 offset = tag_end;
86 break;
87 }
88 let (pr, new_offset) = PropertyReference::decode(data, offset)?;
90 prop_refs.push(pr);
91 offset = new_offset;
92 }
93
94 specs.push(ReadAccessSpecification {
95 object_identifier,
96 list_of_property_references: prop_refs,
97 });
98 }
99
100 Ok(Self {
101 list_of_read_access_specs: specs,
102 })
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct ReadResultElement {
113 pub property_identifier: PropertyIdentifier,
114 pub property_array_index: Option<u32>,
115 pub property_value: Option<Vec<u8>>,
117 pub error: Option<(ErrorClass, ErrorCode)>,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
123pub struct ReadAccessResult {
124 pub object_identifier: ObjectIdentifier,
125 pub list_of_results: Vec<ReadResultElement>,
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct ReadPropertyMultipleACK {
131 pub list_of_read_access_results: Vec<ReadAccessResult>,
132}
133
134impl ReadPropertyMultipleACK {
135 pub fn encode(&self, buf: &mut BytesMut) {
136 for result in &self.list_of_read_access_results {
137 primitives::encode_ctx_object_id(buf, 0, &result.object_identifier);
139 tags::encode_opening_tag(buf, 1);
141 for elem in &result.list_of_results {
142 primitives::encode_ctx_unsigned(buf, 2, elem.property_identifier.to_raw() as u64);
144 if let Some(idx) = elem.property_array_index {
146 primitives::encode_ctx_unsigned(buf, 3, idx as u64);
147 }
148 if let Some(ref value) = elem.property_value {
149 tags::encode_opening_tag(buf, 4);
151 buf.extend_from_slice(value);
152 tags::encode_closing_tag(buf, 4);
153 } else if let Some((class, code)) = elem.error {
154 tags::encode_opening_tag(buf, 5);
156 primitives::encode_app_enumerated(buf, class.to_raw() as u32);
157 primitives::encode_app_enumerated(buf, code.to_raw() as u32);
158 tags::encode_closing_tag(buf, 5);
159 }
160 }
161 tags::encode_closing_tag(buf, 1);
162 }
163 }
164
165 pub fn decode(data: &[u8]) -> Result<Self, Error> {
166 let mut offset = 0;
167 let mut results = Vec::new();
168
169 while offset < data.len() {
170 if results.len() >= MAX_DECODED_ITEMS {
171 return Err(Error::decoding(offset, "RPM ACK exceeds max decoded items"));
172 }
173
174 let (tag, pos) = tags::decode_tag(data, offset)?;
176 let end = pos + tag.length as usize;
177 if end > data.len() {
178 return Err(Error::decoding(pos, "RPM ACK truncated at object-id"));
179 }
180 let object_identifier = ObjectIdentifier::decode(&data[pos..end])?;
181 offset = end;
182
183 let (tag, tag_end) = tags::decode_tag(data, offset)?;
185 if !tag.is_opening_tag(1) {
186 return Err(Error::decoding(offset, "RPM ACK expected opening tag 1"));
187 }
188 offset = tag_end;
189
190 let mut elements = Vec::new();
191 loop {
192 if offset >= data.len() {
193 return Err(Error::decoding(offset, "RPM ACK missing closing tag 1"));
194 }
195 if elements.len() >= MAX_DECODED_ITEMS {
196 return Err(Error::decoding(offset, "RPM ACK results exceeds max"));
197 }
198 let (tag, tag_end) = tags::decode_tag(data, offset)?;
199 if tag.is_closing_tag(1) {
200 offset = tag_end;
201 break;
202 }
203
204 if !tag.is_context(2) {
206 return Err(Error::decoding(offset, "RPM ACK expected context tag 2"));
207 }
208 let end = tag_end + tag.length as usize;
209 if end > data.len() {
210 return Err(Error::decoding(tag_end, "RPM ACK truncated at property-id"));
211 }
212 let prop_raw = primitives::decode_unsigned(&data[tag_end..end])? as u32;
213 let property_identifier = PropertyIdentifier::from_raw(prop_raw);
214 offset = end;
215
216 let mut array_index = None;
218 let (tag, tag_end) = tags::decode_tag(data, offset)?;
219 if tag.is_context(3) {
220 let end = tag_end + tag.length as usize;
221 if end > data.len() {
222 return Err(Error::decoding(tag_end, "RPM ACK truncated at array-index"));
223 }
224 array_index = Some(primitives::decode_unsigned(&data[tag_end..end])? as u32);
225 offset = end;
226 let (tag, tag_end) = tags::decode_tag(data, offset)?;
227 if tag.is_opening_tag(4) {
228 let (value_bytes, new_offset) =
229 tags::extract_context_value(data, tag_end, 4)?;
230 elements.push(ReadResultElement {
231 property_identifier,
232 property_array_index: array_index,
233 property_value: Some(value_bytes.to_vec()),
234 error: None,
235 });
236 offset = new_offset;
237 } else if tag.is_opening_tag(5) {
238 let (error_class, error_code, new_offset) =
239 decode_error_pair(data, tag_end)?;
240 elements.push(ReadResultElement {
241 property_identifier,
242 property_array_index: array_index,
243 property_value: None,
244 error: Some((error_class, error_code)),
245 });
246 offset = new_offset;
247 } else {
248 return Err(Error::decoding(offset, "RPM ACK expected tag 4 or 5"));
249 }
250 } else if tag.is_opening_tag(4) {
251 let (value_bytes, new_offset) = tags::extract_context_value(data, tag_end, 4)?;
253 elements.push(ReadResultElement {
254 property_identifier,
255 property_array_index: array_index,
256 property_value: Some(value_bytes.to_vec()),
257 error: None,
258 });
259 offset = new_offset;
260 } else if tag.is_opening_tag(5) {
261 let (error_class, error_code, new_offset) = decode_error_pair(data, tag_end)?;
263 elements.push(ReadResultElement {
264 property_identifier,
265 property_array_index: array_index,
266 property_value: None,
267 error: Some((error_class, error_code)),
268 });
269 offset = new_offset;
270 } else {
271 return Err(Error::decoding(offset, "RPM ACK expected tag 3, 4, or 5"));
272 }
273 }
274
275 results.push(ReadAccessResult {
276 object_identifier,
277 list_of_results: elements,
278 });
279 }
280
281 Ok(Self {
282 list_of_read_access_results: results,
283 })
284 }
285}
286
287fn decode_error_pair(data: &[u8], offset: usize) -> Result<(ErrorClass, ErrorCode, usize), Error> {
290 let (tag, pos) = tags::decode_tag(data, offset)?;
292 let end = pos + tag.length as usize;
293 if end > data.len() {
294 return Err(Error::decoding(pos, "RPM error truncated at error-class"));
295 }
296 let error_class = ErrorClass::from_raw(primitives::decode_unsigned(&data[pos..end])? as u16);
297 let mut offset = end;
298
299 let (tag, pos) = tags::decode_tag(data, offset)?;
301 let end = pos + tag.length as usize;
302 if end > data.len() {
303 return Err(Error::decoding(pos, "RPM error truncated at error-code"));
304 }
305 let error_code = ErrorCode::from_raw(primitives::decode_unsigned(&data[pos..end])? as u16);
306 offset = end;
307
308 let (tag, tag_end) = tags::decode_tag(data, offset)?;
310 if !tag.is_closing_tag(5) {
311 return Err(Error::decoding(offset, "RPM error expected closing tag 5"));
312 }
313
314 Ok((error_class, error_code, tag_end))
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320 use bacnet_types::enums::ObjectType;
321
322 #[test]
323 fn request_single_object_round_trip() {
324 let req = ReadPropertyMultipleRequest {
325 list_of_read_access_specs: vec![ReadAccessSpecification {
326 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
327 list_of_property_references: vec![
328 PropertyReference {
329 property_identifier: PropertyIdentifier::PRESENT_VALUE,
330 property_array_index: None,
331 },
332 PropertyReference {
333 property_identifier: PropertyIdentifier::OBJECT_NAME,
334 property_array_index: None,
335 },
336 ],
337 }],
338 };
339 let mut buf = BytesMut::new();
340 req.encode(&mut buf);
341 let decoded = ReadPropertyMultipleRequest::decode(&buf).unwrap();
342 assert_eq!(req, decoded);
343 }
344
345 #[test]
346 fn request_multi_object_round_trip() {
347 let req = ReadPropertyMultipleRequest {
348 list_of_read_access_specs: vec![
349 ReadAccessSpecification {
350 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
351 list_of_property_references: vec![PropertyReference {
352 property_identifier: PropertyIdentifier::PRESENT_VALUE,
353 property_array_index: None,
354 }],
355 },
356 ReadAccessSpecification {
357 object_identifier: ObjectIdentifier::new(ObjectType::BINARY_OUTPUT, 3).unwrap(),
358 list_of_property_references: vec![PropertyReference {
359 property_identifier: PropertyIdentifier::PRESENT_VALUE,
360 property_array_index: None,
361 }],
362 },
363 ],
364 };
365 let mut buf = BytesMut::new();
366 req.encode(&mut buf);
367 let decoded = ReadPropertyMultipleRequest::decode(&buf).unwrap();
368 assert_eq!(req, decoded);
369 }
370
371 #[test]
372 fn ack_success_round_trip() {
373 let ack = ReadPropertyMultipleACK {
374 list_of_read_access_results: vec![ReadAccessResult {
375 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
376 list_of_results: vec![ReadResultElement {
377 property_identifier: PropertyIdentifier::PRESENT_VALUE,
378 property_array_index: None,
379 property_value: Some(vec![0x44, 0x42, 0x90, 0x00, 0x00]),
380 error: None,
381 }],
382 }],
383 };
384 let mut buf = BytesMut::new();
385 ack.encode(&mut buf);
386 let decoded = ReadPropertyMultipleACK::decode(&buf).unwrap();
387 assert_eq!(ack, decoded);
388 }
389
390 #[test]
391 fn ack_mixed_success_error_round_trip() {
392 let ack = ReadPropertyMultipleACK {
393 list_of_read_access_results: vec![ReadAccessResult {
394 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
395 list_of_results: vec![
396 ReadResultElement {
397 property_identifier: PropertyIdentifier::PRESENT_VALUE,
398 property_array_index: None,
399 property_value: Some(vec![0x44, 0x42, 0x90, 0x00, 0x00]),
400 error: None,
401 },
402 ReadResultElement {
403 property_identifier: PropertyIdentifier::from_raw(9999),
404 property_array_index: None,
405 property_value: None,
406 error: Some((ErrorClass::PROPERTY, ErrorCode::UNKNOWN_PROPERTY)),
407 },
408 ],
409 }],
410 };
411 let mut buf = BytesMut::new();
412 ack.encode(&mut buf);
413 let decoded = ReadPropertyMultipleACK::decode(&buf).unwrap();
414 assert_eq!(ack, decoded);
415 }
416
417 #[test]
422 fn test_decode_rpm_request_truncated_1_byte() {
423 let req = ReadPropertyMultipleRequest {
424 list_of_read_access_specs: vec![ReadAccessSpecification {
425 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
426 list_of_property_references: vec![PropertyReference {
427 property_identifier: PropertyIdentifier::PRESENT_VALUE,
428 property_array_index: None,
429 }],
430 }],
431 };
432 let mut buf = BytesMut::new();
433 req.encode(&mut buf);
434 assert!(ReadPropertyMultipleRequest::decode(&buf[..1]).is_err());
435 }
436
437 #[test]
438 fn test_decode_rpm_request_truncated_3_bytes() {
439 let req = ReadPropertyMultipleRequest {
440 list_of_read_access_specs: vec![ReadAccessSpecification {
441 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
442 list_of_property_references: vec![PropertyReference {
443 property_identifier: PropertyIdentifier::PRESENT_VALUE,
444 property_array_index: None,
445 }],
446 }],
447 };
448 let mut buf = BytesMut::new();
449 req.encode(&mut buf);
450 assert!(ReadPropertyMultipleRequest::decode(&buf[..3]).is_err());
451 }
452
453 #[test]
454 fn test_decode_rpm_request_truncated_half() {
455 let req = ReadPropertyMultipleRequest {
456 list_of_read_access_specs: vec![ReadAccessSpecification {
457 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
458 list_of_property_references: vec![PropertyReference {
459 property_identifier: PropertyIdentifier::PRESENT_VALUE,
460 property_array_index: None,
461 }],
462 }],
463 };
464 let mut buf = BytesMut::new();
465 req.encode(&mut buf);
466 let half = buf.len() / 2;
467 assert!(ReadPropertyMultipleRequest::decode(&buf[..half]).is_err());
468 }
469
470 #[test]
471 fn test_decode_rpm_request_invalid_tag() {
472 assert!(ReadPropertyMultipleRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
473 }
474
475 #[test]
476 fn test_decode_rpm_ack_truncated_1_byte() {
477 let ack = ReadPropertyMultipleACK {
478 list_of_read_access_results: vec![ReadAccessResult {
479 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
480 list_of_results: vec![ReadResultElement {
481 property_identifier: PropertyIdentifier::PRESENT_VALUE,
482 property_array_index: None,
483 property_value: Some(vec![0x44, 0x42, 0x90, 0x00, 0x00]),
484 error: None,
485 }],
486 }],
487 };
488 let mut buf = BytesMut::new();
489 ack.encode(&mut buf);
490 assert!(ReadPropertyMultipleACK::decode(&buf[..1]).is_err());
491 }
492
493 #[test]
494 fn test_decode_rpm_ack_truncated_3_bytes() {
495 let ack = ReadPropertyMultipleACK {
496 list_of_read_access_results: vec![ReadAccessResult {
497 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
498 list_of_results: vec![ReadResultElement {
499 property_identifier: PropertyIdentifier::PRESENT_VALUE,
500 property_array_index: None,
501 property_value: Some(vec![0x44, 0x42, 0x90, 0x00, 0x00]),
502 error: None,
503 }],
504 }],
505 };
506 let mut buf = BytesMut::new();
507 ack.encode(&mut buf);
508 assert!(ReadPropertyMultipleACK::decode(&buf[..3]).is_err());
509 }
510
511 #[test]
512 fn test_decode_rpm_ack_truncated_half() {
513 let ack = ReadPropertyMultipleACK {
514 list_of_read_access_results: vec![ReadAccessResult {
515 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
516 list_of_results: vec![ReadResultElement {
517 property_identifier: PropertyIdentifier::PRESENT_VALUE,
518 property_array_index: None,
519 property_value: Some(vec![0x44, 0x42, 0x90, 0x00, 0x00]),
520 error: None,
521 }],
522 }],
523 };
524 let mut buf = BytesMut::new();
525 ack.encode(&mut buf);
526 let half = buf.len() / 2;
527 assert!(ReadPropertyMultipleACK::decode(&buf[..half]).is_err());
528 }
529
530 #[test]
531 fn test_decode_rpm_ack_invalid_tag() {
532 assert!(ReadPropertyMultipleACK::decode(&[0xFF, 0xFF, 0xFF]).is_err());
533 }
534}