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::{DecodeError, DecodingKind, Error};
16use bytes::BytesMut;
17
18pub use super::reference_sinks::encode_property_reference_to_sink;
19use super::tagged::{
20    decode_ctx_object_id, decode_ctx_unsigned, decode_optional_ctx, expect_opening,
21};
22use super::MAX_FRAMED_ITEMS;
23use crate::{primitives, tags};
24
25/// Encode one `BACnetPropertyReference`, appending to `buf`.
26pub fn encode_property_reference(buf: &mut BytesMut, reference: &PropertyReference) {
27    encode_property_reference_to_sink(buf, reference).expect("BytesMut accepts each reference");
28}
29
30/// Decode one `BACnetPropertyReference` at `offset`; returns it and the
31/// offset just past it. The array index is read only when the next tag is a
32/// primitive context tag `[1]`, so a reference may end the input or be
33/// followed by anything else.
34///
35/// A member whose contents run past the end of `data` fails with
36/// [`Error::BufferTooShort`]; any other malformed input with
37/// [`Error::Decoding`].
38pub fn decode_property_reference(
39    data: &[u8],
40    offset: usize,
41) -> Result<(PropertyReference, usize), Error> {
42    const PROPERTY: &str = "PropertyReference property-id";
43    const INDEX: &str = "PropertyReference array-index";
44    let (property, next) = decode_ctx_unsigned::<u32>(data, offset, 0, PROPERTY)?;
45    let (property_array_index, offset) =
46        decode_optional_ctx(data, next, 1, INDEX, decode_ctx_unsigned::<u32>)?;
47    Ok((
48        PropertyReference {
49            property_identifier: PropertyIdentifier::from_raw(property),
50            property_array_index,
51        },
52        offset,
53    ))
54}
55
56/// Encode one `ReadAccessSpecification`, appending to `buf`. An empty
57/// reference list encodes as the bare `[1]` pair; the ReadPropertyMultiple
58/// request encoder is what refuses one.
59pub fn encode_read_access_specification(buf: &mut BytesMut, spec: &ReadAccessSpecification) {
60    primitives::encode_ctx_object_id(buf, 0, &spec.object_identifier);
61    tags::encode_opening_tag(buf, 1);
62    for reference in &spec.list_of_property_references {
63        encode_property_reference(buf, reference);
64    }
65    tags::encode_closing_tag(buf, 1);
66}
67
68/// Decode one `ReadAccessSpecification` at `offset`; returns it and the
69/// offset just past its closing `[1]` tag, so a sequence of them is walked
70/// by calling this at each element's start. Errors are as for
71/// [`decode_property_reference`].
72pub fn decode_read_access_specification(
73    data: &[u8],
74    offset: usize,
75) -> Result<(ReadAccessSpecification, usize), Error> {
76    const WHAT: &str = "ReadAccessSpecification";
77    let (object_identifier, end) = decode_ctx_object_id(data, offset, 0, WHAT)?;
78    let mut offset = expect_opening(data, end, 1, WHAT)?;
79    let mut list_of_property_references = Vec::new();
80    loop {
81        if offset >= data.len() {
82            return Err(DecodeError::new(
83                DecodingKind::Missing,
84                offset,
85                "missing closing tag 1",
86            )
87            .within(WHAT)
88            .into());
89        }
90        if list_of_property_references.len() >= MAX_FRAMED_ITEMS {
91            return Err(DecodeError::new(
92                DecodingKind::Overflow,
93                offset,
94                "property references exceed the item limit",
95            )
96            .within(WHAT)
97            .into());
98        }
99        let (tag, tag_end) = tags::decode_tag(data, offset)?;
100        if tag.is_closing_tag(1) {
101            offset = tag_end;
102            break;
103        }
104        let (reference, next) = decode_property_reference(data, offset)?;
105        list_of_property_references.push(reference);
106        offset = next;
107    }
108    Ok((
109        ReadAccessSpecification {
110            object_identifier,
111            list_of_property_references,
112        },
113        offset,
114    ))
115}