embedded_bacnet/application_protocol/services/
change_of_value.rs

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