Skip to main content

bacnet_encoding/constructed/
read_access.rs

1//! `BACnetPropertyReference` and `ReadAccessSpecification` (ASHRAE 135-2020
2//! Clause 21), shared by the ReadPropertyMultiple request, the COV and Audit
3//! services and a Group object's List_Of_Group_Members (Clause 12.14.5).
4//!
5//! A property reference is the property identifier in context tag `[0]`,
6//! then an optional Unsigned array index in `[1]`. A specification is the
7//! object identifier in `[0]`, then its references back to back inside an
8//! opening/closing `[1]` pair.
9
10#[cfg(not(feature = "std"))]
11use alloc::vec::Vec;
12
13use bacnet_types::constructed::{PropertyReference, ReadAccessSpecification};
14use bacnet_types::enums::PropertyIdentifier;
15use bacnet_types::error::Error;
16use bytes::BytesMut;
17
18use super::tagged::{
19    decode_ctx_object_id, decode_ctx_unsigned, decode_optional_ctx, expect_opening,
20};
21use super::MAX_FRAMED_ITEMS;
22use crate::{primitives, tags};
23
24/// Encode one `BACnetPropertyReference`, appending to `buf`.
25pub fn encode_property_reference(buf: &mut BytesMut, reference: &PropertyReference) {
26    primitives::encode_ctx_unsigned(buf, 0, u64::from(reference.property_identifier.to_raw()));
27    if let Some(index) = reference.property_array_index {
28        primitives::encode_ctx_unsigned(buf, 1, u64::from(index));
29    }
30}
31
32/// Decode one `BACnetPropertyReference` at `offset`; returns it and the
33/// offset just past it. The array index is read only when the next tag is a
34/// primitive context tag `[1]`, so a reference may end the input or be
35/// followed by anything else.
36///
37/// A member whose contents run past the end of `data` fails with
38/// [`Error::BufferTooShort`]; any other malformed input with
39/// [`Error::Decoding`].
40pub fn decode_property_reference(
41    data: &[u8],
42    offset: usize,
43) -> Result<(PropertyReference, usize), Error> {
44    const PROPERTY: &str = "PropertyReference property-id";
45    const INDEX: &str = "PropertyReference array-index";
46    let (property, next) = decode_ctx_unsigned::<u32>(data, offset, 0, PROPERTY)?;
47    let (property_array_index, offset) =
48        decode_optional_ctx(data, next, 1, INDEX, decode_ctx_unsigned::<u32>)?;
49    Ok((
50        PropertyReference {
51            property_identifier: PropertyIdentifier::from_raw(property),
52            property_array_index,
53        },
54        offset,
55    ))
56}
57
58/// Encode one `ReadAccessSpecification`, appending to `buf`. An empty
59/// reference list encodes as the bare `[1]` pair; the ReadPropertyMultiple
60/// request encoder is what refuses one.
61pub fn encode_read_access_specification(buf: &mut BytesMut, spec: &ReadAccessSpecification) {
62    primitives::encode_ctx_object_id(buf, 0, &spec.object_identifier);
63    tags::encode_opening_tag(buf, 1);
64    for reference in &spec.list_of_property_references {
65        encode_property_reference(buf, reference);
66    }
67    tags::encode_closing_tag(buf, 1);
68}
69
70/// Decode one `ReadAccessSpecification` at `offset`; returns it and the
71/// offset just past its closing `[1]` tag, so a sequence of them is walked
72/// by calling this at each element's start. Errors are as for
73/// [`decode_property_reference`].
74pub fn decode_read_access_specification(
75    data: &[u8],
76    offset: usize,
77) -> Result<(ReadAccessSpecification, usize), Error> {
78    const WHAT: &str = "ReadAccessSpecification";
79    let (object_identifier, end) = decode_ctx_object_id(data, offset, 0, WHAT)?;
80    let mut offset = expect_opening(data, end, 1, WHAT)?;
81    let mut list_of_property_references = Vec::new();
82    loop {
83        if offset >= data.len() {
84            return Err(Error::missing(
85                offset,
86                format!("{WHAT} missing closing tag 1"),
87            ));
88        }
89        if list_of_property_references.len() >= MAX_FRAMED_ITEMS {
90            return Err(Error::overflow(
91                offset,
92                format!("{WHAT} property references exceed the item limit"),
93            ));
94        }
95        let (tag, tag_end) = tags::decode_tag(data, offset)?;
96        if tag.is_closing_tag(1) {
97            offset = tag_end;
98            break;
99        }
100        let (reference, next) = decode_property_reference(data, offset)?;
101        list_of_property_references.push(reference);
102        offset = next;
103    }
104    Ok((
105        ReadAccessSpecification {
106            object_identifier,
107            list_of_property_references,
108        },
109        offset,
110    ))
111}