bacnet_emb/application_protocol/services/
change_of_value.rs

1// change of value
2
3#[cfg(feature = "alloc")]
4use alloc::vec::Vec;
5
6use crate::{
7    application_protocol::primitives::data_value::ApplicationDataValue,
8    common::{
9        error::Error,
10        helper::{
11            decode_unsigned, encode_context_bool, encode_context_object_id,
12            encode_context_unsigned, get_tagged_body_for_tag,
13        },
14        io::{Reader, Writer},
15        object_id::{ObjectId, ObjectType},
16        property_id::PropertyId,
17        tag::{Tag, TagNumber},
18    },
19};
20
21#[derive(Debug, Clone)]
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23pub struct CovNotification<'a> {
24    pub process_id: u32,
25    pub device_id: ObjectId,
26    pub object_id: ObjectId,
27    pub time_remaining_seconds: u32,
28    pub values: CovNotificationValues<'a>,
29}
30
31#[cfg(not(feature = "alloc"))]
32#[derive(Debug, Clone)]
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34pub struct CovNotificationValues<'a> {
35    _property_results: &'a [PropertyResult<'a>], // use this when encoding is implemented
36    object_id: ObjectId,
37    buf: &'a [u8],
38}
39
40#[cfg(feature = "alloc")]
41#[derive(Debug, Clone)]
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43pub struct CovNotificationValues<'a> {
44    pub property_results: Vec<PropertyResult<'a>>, // use this when encoding is implemented
45    pub object_id: ObjectId,
46}
47
48impl<'a> CovNotificationValues<'a> {
49    #[cfg(not(feature = "alloc"))]
50    pub fn decode(_reader: &mut Reader, buf: &'a [u8], object_id: ObjectId) -> Result<Self, Error> {
51        Ok(CovNotificationValues {
52            buf,
53            _property_results: &[],
54            object_id,
55        })
56    }
57
58    #[cfg(feature = "alloc")]
59    pub fn decode(reader: &mut Reader, buf: &[u8], object_id: ObjectId) -> Result<Self, Error> {
60        let mut property_results = Vec::new();
61
62        while !reader.eof() {
63            let result = PropertyResult::decode(reader, buf, &object_id)?;
64            property_results.push(result);
65        }
66
67        Ok(Self {
68            object_id,
69            property_results,
70        })
71    }
72}
73
74#[derive(Debug, Clone)]
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76pub struct PropertyResult<'a> {
77    pub id: PropertyId,
78    pub value: ApplicationDataValue<'a>,
79}
80
81impl<'a> PropertyResult<'a> {
82    #[cfg_attr(feature = "alloc", bacnet_macros::remove_lifetimes_from_fn_args)]
83    pub fn decode(reader: &mut Reader, buf: &'a [u8], object_id: &ObjectId) -> Result<Self, Error> {
84        // property id
85        let tag = Tag::decode_expected(
86            reader,
87            buf,
88            TagNumber::ContextSpecific(0),
89            "CovNotification next property_id",
90        )?;
91        let property_id: PropertyId = (decode_unsigned(tag.value, reader, buf)? as u32).into();
92
93        // value
94        Tag::decode_expected(
95            reader,
96            buf,
97            TagNumber::ContextSpecificOpening(2),
98            "CovNotification next expected value opening tag",
99        )?;
100        let tag = Tag::decode(reader, buf)?;
101        let value = ApplicationDataValue::decode(&tag, object_id, &property_id, reader, buf)?;
102        Tag::decode_expected(
103            reader,
104            buf,
105            TagNumber::ContextSpecificClosing(2),
106            "CovNotification next expected value closing tag",
107        )?;
108
109        Ok(PropertyResult {
110            id: property_id,
111            value,
112        })
113    }
114}
115
116impl<'a> CovNotification<'a> {
117    const TAG_PROCESS_ID: u8 = 0;
118    const TAG_DEVICE_ID: u8 = 1;
119    const TAG_OBJECT_ID: u8 = 2;
120    const TAG_LIFETIME: u8 = 3;
121    const TAG_LIST_OF_VALUES: u8 = 4;
122
123    #[cfg_attr(feature = "alloc", bacnet_macros::remove_lifetimes_from_fn_args)]
124    pub fn decode(reader: &mut Reader, buf: &'a [u8]) -> Result<Self, Error> {
125        // parse a tag, starting from after the pdu type and service choice
126
127        // process_id
128        let tag = Tag::decode_expected(
129            reader,
130            buf,
131            TagNumber::ContextSpecific(Self::TAG_PROCESS_ID),
132            "CovNotification process_id",
133        )?;
134        let process_id = decode_unsigned(tag.value, reader, buf)? as u32;
135
136        // device_id
137        let tag = Tag::decode_expected(
138            reader,
139            buf,
140            TagNumber::ContextSpecific(Self::TAG_DEVICE_ID),
141            "CovNotification device_id tag",
142        )?;
143        let device_id = ObjectId::decode(tag.value, reader, buf)?;
144        if device_id.object_type != ObjectType::ObjectDevice {
145            return Err(Error::InvalidValue(
146                "expected device object type for CovNotification device_id field",
147            ));
148        }
149
150        // object_id
151        let tag = Tag::decode_expected(
152            reader,
153            buf,
154            TagNumber::ContextSpecific(Self::TAG_OBJECT_ID),
155            "CovNotification object_id",
156        )?;
157        let object_id = ObjectId::decode(tag.value, reader, buf)?;
158
159        // lifetime
160        let tag = Tag::decode_expected(
161            reader,
162            buf,
163            TagNumber::ContextSpecific(Self::TAG_LIFETIME),
164            "CovNotification lifetime",
165        )?;
166        let time_remaining_seconds = decode_unsigned(tag.value, reader, buf)? as u32;
167
168        // values
169        let inner_buf = get_tagged_body_for_tag(
170            reader,
171            buf,
172            Self::TAG_LIST_OF_VALUES,
173            "CovNotification decode list of values",
174        )?;
175        let mut inner_reader = Reader::new_with_len(inner_buf.len());
176        let values = CovNotificationValues::decode(&mut inner_reader, inner_buf, object_id)?;
177
178        Ok(Self {
179            process_id,
180            device_id,
181            object_id,
182            time_remaining_seconds,
183            values,
184        })
185    }
186}
187
188#[cfg(not(feature = "alloc"))]
189impl<'a> IntoIterator for &'_ CovNotificationValues<'a> {
190    type Item = Result<PropertyResult<'a>, Error>;
191    type IntoIter = CovNotificationIter<'a>;
192
193    fn into_iter(self) -> Self::IntoIter {
194        CovNotificationIter {
195            buf: self.buf,
196            reader: Reader::new_with_len(self.buf.len()),
197            object_id: self.object_id,
198        }
199    }
200}
201
202pub struct CovNotificationIter<'a> {
203    object_id: ObjectId,
204    reader: Reader,
205    buf: &'a [u8],
206}
207
208impl<'a> Iterator for CovNotificationIter<'a> {
209    type Item = Result<PropertyResult<'a>, Error>;
210
211    fn next(&mut self) -> Option<Self::Item> {
212        if self.reader.eof() {
213            return None;
214        }
215
216        let result = PropertyResult::decode(&mut self.reader, self.buf, &self.object_id);
217        Some(result)
218    }
219}
220
221#[derive(Debug, Clone)]
222#[cfg_attr(feature = "defmt", derive(defmt::Format))]
223pub struct SubscribeCov {
224    process_id: u32,
225    object_id: ObjectId,
226    issue_confirmed_notifications: bool,
227    lifetime_seconds: u32, // zero for indefinite
228}
229
230impl SubscribeCov {
231    const TAG_PROCESS_ID: u8 = 0;
232    const TAG_OBJECT_ID: u8 = 1;
233    const TAG_CONFIRMED: u8 = 2;
234    const TAG_LIFETIME: u8 = 3;
235
236    pub fn new(
237        process_id: u32,
238        object_id: ObjectId,
239        issue_confirmed_notifications: bool,
240        lifetime_seconds: u32,
241    ) -> Self {
242        Self {
243            process_id,
244            object_id,
245            issue_confirmed_notifications,
246            lifetime_seconds,
247        }
248    }
249
250    pub fn encode(&self, writer: &mut Writer) {
251        // subscriber process_id
252        encode_context_unsigned(writer, Self::TAG_PROCESS_ID, self.process_id);
253
254        // object_id
255        encode_context_object_id(writer, Self::TAG_OBJECT_ID, &self.object_id);
256
257        // issue confirmed notifications
258        encode_context_bool(
259            writer,
260            Self::TAG_CONFIRMED,
261            self.issue_confirmed_notifications,
262        );
263
264        // lifetime of subscription
265        encode_context_unsigned(writer, Self::TAG_LIFETIME, self.lifetime_seconds);
266    }
267}