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)?;
228 if tag.is_opening_tag(4) {
229 let (value_bytes, new_offset) =
230 tags::extract_context_value(data, tag_end, 4)?;
231 elements.push(ReadResultElement {
232 property_identifier,
233 property_array_index: array_index,
234 property_value: Some(value_bytes.to_vec()),
235 error: None,
236 });
237 offset = new_offset;
238 } else if tag.is_opening_tag(5) {
239 let (error_class, error_code, new_offset) =
240 decode_error_pair(data, tag_end)?;
241 elements.push(ReadResultElement {
242 property_identifier,
243 property_array_index: array_index,
244 property_value: None,
245 error: Some((error_class, error_code)),
246 });
247 offset = new_offset;
248 } else {
249 return Err(Error::decoding(offset, "RPM ACK expected tag 4 or 5"));
250 }
251 } else if tag.is_opening_tag(4) {
252 let (value_bytes, new_offset) = tags::extract_context_value(data, tag_end, 4)?;
254 elements.push(ReadResultElement {
255 property_identifier,
256 property_array_index: array_index,
257 property_value: Some(value_bytes.to_vec()),
258 error: None,
259 });
260 offset = new_offset;
261 } else if tag.is_opening_tag(5) {
262 let (error_class, error_code, new_offset) = decode_error_pair(data, tag_end)?;
264 elements.push(ReadResultElement {
265 property_identifier,
266 property_array_index: array_index,
267 property_value: None,
268 error: Some((error_class, error_code)),
269 });
270 offset = new_offset;
271 } else {
272 return Err(Error::decoding(offset, "RPM ACK expected tag 3, 4, or 5"));
273 }
274 }
275
276 results.push(ReadAccessResult {
277 object_identifier,
278 list_of_results: elements,
279 });
280 }
281
282 Ok(Self {
283 list_of_read_access_results: results,
284 })
285 }
286}
287
288fn decode_error_pair(data: &[u8], offset: usize) -> Result<(ErrorClass, ErrorCode, usize), Error> {
291 let (tag, pos) = tags::decode_tag(data, offset)?;
293 let end = pos + tag.length as usize;
294 if end > data.len() {
295 return Err(Error::decoding(pos, "RPM error truncated at error-class"));
296 }
297 let error_class = ErrorClass::from_raw(primitives::decode_unsigned(&data[pos..end])? as u16);
298 let mut offset = end;
299
300 let (tag, pos) = tags::decode_tag(data, offset)?;
302 let end = pos + tag.length as usize;
303 if end > data.len() {
304 return Err(Error::decoding(pos, "RPM error truncated at error-code"));
305 }
306 let error_code = ErrorCode::from_raw(primitives::decode_unsigned(&data[pos..end])? as u16);
307 offset = end;
308
309 let (tag, tag_end) = tags::decode_tag(data, offset)?;
311 if !tag.is_closing_tag(5) {
312 return Err(Error::decoding(offset, "RPM error expected closing tag 5"));
313 }
314
315 Ok((error_class, error_code, tag_end))
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321 use bacnet_types::enums::ObjectType;
322
323 #[test]
324 fn request_single_object_round_trip() {
325 let req = ReadPropertyMultipleRequest {
326 list_of_read_access_specs: vec![ReadAccessSpecification {
327 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
328 list_of_property_references: vec![
329 PropertyReference {
330 property_identifier: PropertyIdentifier::PRESENT_VALUE,
331 property_array_index: None,
332 },
333 PropertyReference {
334 property_identifier: PropertyIdentifier::OBJECT_NAME,
335 property_array_index: None,
336 },
337 ],
338 }],
339 };
340 let mut buf = BytesMut::new();
341 req.encode(&mut buf);
342 let decoded = ReadPropertyMultipleRequest::decode(&buf).unwrap();
343 assert_eq!(req, decoded);
344 }
345
346 #[test]
347 fn request_multi_object_round_trip() {
348 let req = ReadPropertyMultipleRequest {
349 list_of_read_access_specs: vec![
350 ReadAccessSpecification {
351 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
352 list_of_property_references: vec![PropertyReference {
353 property_identifier: PropertyIdentifier::PRESENT_VALUE,
354 property_array_index: None,
355 }],
356 },
357 ReadAccessSpecification {
358 object_identifier: ObjectIdentifier::new(ObjectType::BINARY_OUTPUT, 3).unwrap(),
359 list_of_property_references: vec![PropertyReference {
360 property_identifier: PropertyIdentifier::PRESENT_VALUE,
361 property_array_index: None,
362 }],
363 },
364 ],
365 };
366 let mut buf = BytesMut::new();
367 req.encode(&mut buf);
368 let decoded = ReadPropertyMultipleRequest::decode(&buf).unwrap();
369 assert_eq!(req, decoded);
370 }
371
372 #[test]
373 fn ack_success_round_trip() {
374 let ack = ReadPropertyMultipleACK {
375 list_of_read_access_results: vec![ReadAccessResult {
376 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
377 list_of_results: vec![ReadResultElement {
378 property_identifier: PropertyIdentifier::PRESENT_VALUE,
379 property_array_index: None,
380 property_value: Some(vec![0x44, 0x42, 0x90, 0x00, 0x00]),
381 error: None,
382 }],
383 }],
384 };
385 let mut buf = BytesMut::new();
386 ack.encode(&mut buf);
387 let decoded = ReadPropertyMultipleACK::decode(&buf).unwrap();
388 assert_eq!(ack, decoded);
389 }
390
391 #[test]
392 fn ack_mixed_success_error_round_trip() {
393 let ack = ReadPropertyMultipleACK {
394 list_of_read_access_results: vec![ReadAccessResult {
395 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
396 list_of_results: vec![
397 ReadResultElement {
398 property_identifier: PropertyIdentifier::PRESENT_VALUE,
399 property_array_index: None,
400 property_value: Some(vec![0x44, 0x42, 0x90, 0x00, 0x00]),
401 error: None,
402 },
403 ReadResultElement {
404 property_identifier: PropertyIdentifier::from_raw(9999),
405 property_array_index: None,
406 property_value: None,
407 error: Some((ErrorClass::PROPERTY, ErrorCode::UNKNOWN_PROPERTY)),
408 },
409 ],
410 }],
411 };
412 let mut buf = BytesMut::new();
413 ack.encode(&mut buf);
414 let decoded = ReadPropertyMultipleACK::decode(&buf).unwrap();
415 assert_eq!(ack, decoded);
416 }
417
418 #[test]
423 fn test_decode_rpm_request_truncated_1_byte() {
424 let req = ReadPropertyMultipleRequest {
425 list_of_read_access_specs: vec![ReadAccessSpecification {
426 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
427 list_of_property_references: vec![PropertyReference {
428 property_identifier: PropertyIdentifier::PRESENT_VALUE,
429 property_array_index: None,
430 }],
431 }],
432 };
433 let mut buf = BytesMut::new();
434 req.encode(&mut buf);
435 assert!(ReadPropertyMultipleRequest::decode(&buf[..1]).is_err());
436 }
437
438 #[test]
439 fn test_decode_rpm_request_truncated_3_bytes() {
440 let req = ReadPropertyMultipleRequest {
441 list_of_read_access_specs: vec![ReadAccessSpecification {
442 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
443 list_of_property_references: vec![PropertyReference {
444 property_identifier: PropertyIdentifier::PRESENT_VALUE,
445 property_array_index: None,
446 }],
447 }],
448 };
449 let mut buf = BytesMut::new();
450 req.encode(&mut buf);
451 assert!(ReadPropertyMultipleRequest::decode(&buf[..3]).is_err());
452 }
453
454 #[test]
455 fn test_decode_rpm_request_truncated_half() {
456 let req = ReadPropertyMultipleRequest {
457 list_of_read_access_specs: vec![ReadAccessSpecification {
458 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
459 list_of_property_references: vec![PropertyReference {
460 property_identifier: PropertyIdentifier::PRESENT_VALUE,
461 property_array_index: None,
462 }],
463 }],
464 };
465 let mut buf = BytesMut::new();
466 req.encode(&mut buf);
467 let half = buf.len() / 2;
468 assert!(ReadPropertyMultipleRequest::decode(&buf[..half]).is_err());
469 }
470
471 #[test]
472 fn test_decode_rpm_request_invalid_tag() {
473 assert!(ReadPropertyMultipleRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
474 }
475
476 #[test]
477 fn test_decode_rpm_ack_truncated_1_byte() {
478 let ack = ReadPropertyMultipleACK {
479 list_of_read_access_results: vec![ReadAccessResult {
480 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
481 list_of_results: vec![ReadResultElement {
482 property_identifier: PropertyIdentifier::PRESENT_VALUE,
483 property_array_index: None,
484 property_value: Some(vec![0x44, 0x42, 0x90, 0x00, 0x00]),
485 error: None,
486 }],
487 }],
488 };
489 let mut buf = BytesMut::new();
490 ack.encode(&mut buf);
491 assert!(ReadPropertyMultipleACK::decode(&buf[..1]).is_err());
492 }
493
494 #[test]
495 fn test_decode_rpm_ack_truncated_3_bytes() {
496 let ack = ReadPropertyMultipleACK {
497 list_of_read_access_results: vec![ReadAccessResult {
498 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
499 list_of_results: vec![ReadResultElement {
500 property_identifier: PropertyIdentifier::PRESENT_VALUE,
501 property_array_index: None,
502 property_value: Some(vec![0x44, 0x42, 0x90, 0x00, 0x00]),
503 error: None,
504 }],
505 }],
506 };
507 let mut buf = BytesMut::new();
508 ack.encode(&mut buf);
509 assert!(ReadPropertyMultipleACK::decode(&buf[..3]).is_err());
510 }
511
512 #[test]
513 fn test_decode_rpm_ack_truncated_half() {
514 let ack = ReadPropertyMultipleACK {
515 list_of_read_access_results: vec![ReadAccessResult {
516 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
517 list_of_results: vec![ReadResultElement {
518 property_identifier: PropertyIdentifier::PRESENT_VALUE,
519 property_array_index: None,
520 property_value: Some(vec![0x44, 0x42, 0x90, 0x00, 0x00]),
521 error: None,
522 }],
523 }],
524 };
525 let mut buf = BytesMut::new();
526 ack.encode(&mut buf);
527 let half = buf.len() / 2;
528 assert!(ReadPropertyMultipleACK::decode(&buf[..half]).is_err());
529 }
530
531 #[test]
532 fn test_decode_rpm_ack_invalid_tag() {
533 assert!(ReadPropertyMultipleACK::decode(&[0xFF, 0xFF, 0xFF]).is_err());
534 }
535}