1#[cfg(not(feature = "std"))]
10use alloc::vec::Vec;
11
12use bacnet_types::constructed::{
13 BACnetCOVMultipleSubscription, BACnetCOVReference, BACnetCOVSubscription,
14 BACnetCOVSubscriptionSpecification, BACnetRecipientProcess,
15};
16use bacnet_types::enums::PropertyIdentifier;
17use bacnet_types::error::Error;
18use bytes::BytesMut;
19
20use crate::{primitives, tags};
21
22use super::recipient::{check_encoded_recipient, write_recipient};
23use super::tagged::{
24 decode_ctx_boolean, decode_ctx_constructed, decode_ctx_object_id, decode_ctx_real,
25 decode_ctx_unsigned, decode_optional_ctx, expect_closing, expect_opening, next_is_closing,
26};
27use super::{
28 decode_object_property_reference, decode_recipient, encode_object_property_reference,
29 MAX_FRAMED_ITEMS,
30};
31
32pub fn encode_cov_subscription(
36 buf: &mut BytesMut,
37 subscription: &BACnetCOVSubscription,
38) -> Result<(), Error> {
39 check_encoded_recipient(&subscription.recipient.recipient)?;
40 write_cov_subscription(buf, subscription);
41 Ok(())
42}
43
44fn write_cov_subscription(buf: &mut BytesMut, subscription: &BACnetCOVSubscription) {
46 tags::encode_opening_tag(buf, 0);
47 tags::encode_opening_tag(buf, 0);
48 write_recipient(buf, &subscription.recipient.recipient);
49 tags::encode_closing_tag(buf, 0);
50 primitives::encode_ctx_unsigned(buf, 1, subscription.recipient.process_identifier as u64);
51 tags::encode_closing_tag(buf, 0);
52
53 tags::encode_opening_tag(buf, 1);
54 encode_object_property_reference(buf, &subscription.monitored_property_reference);
55 tags::encode_closing_tag(buf, 1);
56
57 primitives::encode_ctx_boolean(buf, 2, subscription.issue_confirmed_notifications);
58 primitives::encode_ctx_unsigned(buf, 3, subscription.time_remaining as u64);
59 if let Some(increment) = subscription.cov_increment {
60 primitives::encode_ctx_real(buf, 4, increment);
61 }
62}
63
64pub fn encode_cov_subscription_list(
67 buf: &mut BytesMut,
68 subscriptions: &[BACnetCOVSubscription],
69) -> Result<(), Error> {
70 for subscription in subscriptions {
71 check_encoded_recipient(&subscription.recipient.recipient)?;
72 }
73 for subscription in subscriptions {
74 write_cov_subscription(buf, subscription);
75 }
76 Ok(())
77}
78
79pub fn encode_cov_multiple_subscription(
87 buf: &mut BytesMut,
88 subscription: &BACnetCOVMultipleSubscription,
89) -> Result<(), Error> {
90 check_encoded_recipient(&subscription.recipient.recipient)?;
91 write_cov_multiple_subscription(buf, subscription);
92 Ok(())
93}
94
95fn write_cov_multiple_subscription(
97 buf: &mut BytesMut,
98 subscription: &BACnetCOVMultipleSubscription,
99) {
100 tags::encode_opening_tag(buf, 0);
101 tags::encode_opening_tag(buf, 0);
102 write_recipient(buf, &subscription.recipient.recipient);
103 tags::encode_closing_tag(buf, 0);
104 primitives::encode_ctx_unsigned(buf, 1, subscription.recipient.process_identifier as u64);
105 tags::encode_closing_tag(buf, 0);
106
107 primitives::encode_ctx_boolean(buf, 1, subscription.issue_confirmed_notifications);
108 primitives::encode_ctx_unsigned(buf, 2, subscription.time_remaining as u64);
109 primitives::encode_ctx_unsigned(buf, 3, subscription.max_notification_delay as u64);
110
111 tags::encode_opening_tag(buf, 4);
112 for specification in &subscription.list_of_cov_subscription_specifications {
113 primitives::encode_ctx_object_id(buf, 0, &specification.monitored_object_identifier);
114 tags::encode_opening_tag(buf, 1);
115 for reference in &specification.list_of_cov_references {
116 tags::encode_opening_tag(buf, 0);
117 primitives::encode_ctx_unsigned(buf, 0, reference.property_identifier.to_raw() as u64);
118 if let Some(index) = reference.property_array_index {
119 primitives::encode_ctx_unsigned(buf, 1, index as u64);
120 }
121 tags::encode_closing_tag(buf, 0);
122 if let Some(increment) = reference.cov_increment {
123 primitives::encode_ctx_real(buf, 1, increment);
124 }
125 primitives::encode_ctx_boolean(buf, 2, reference.timestamped);
126 }
127 tags::encode_closing_tag(buf, 1);
128 }
129 tags::encode_closing_tag(buf, 4);
130}
131
132pub fn encode_cov_multiple_subscription_list(
136 buf: &mut BytesMut,
137 subscriptions: &[BACnetCOVMultipleSubscription],
138) -> Result<(), Error> {
139 for subscription in subscriptions {
140 check_encoded_recipient(&subscription.recipient.recipient)?;
141 }
142 for subscription in subscriptions {
143 write_cov_multiple_subscription(buf, subscription);
144 }
145 Ok(())
146}
147
148pub fn decode_cov_subscription(
157 data: &[u8],
158 offset: usize,
159) -> Result<(BACnetCOVSubscription, usize), Error> {
160 let what = "BACnetCOVSubscription";
161 let (recipient, pos) = decode_recipient_process(data, offset, what)?;
162 let (reference, pos) = decode_ctx_constructed(data, pos, 1, what)?;
163 let monitored_property_reference = decode_object_property_reference(reference)?;
164 let (issue_confirmed_notifications, pos) = decode_ctx_boolean(data, pos, 2, what)?;
165 let (time_remaining, pos) = decode_ctx_unsigned::<u32>(data, pos, 3, what)?;
166 let (cov_increment, pos) = decode_optional_ctx(data, pos, 4, what, decode_ctx_real)?;
167 Ok((
168 BACnetCOVSubscription {
169 recipient,
170 monitored_property_reference,
171 issue_confirmed_notifications,
172 time_remaining,
173 cov_increment,
174 },
175 pos,
176 ))
177}
178
179pub fn decode_cov_multiple_subscription(
184 data: &[u8],
185 offset: usize,
186) -> Result<(BACnetCOVMultipleSubscription, usize), Error> {
187 let what = "BACnetCOVMultipleSubscription";
188 let (recipient, pos) = decode_recipient_process(data, offset, what)?;
189 let (issue_confirmed_notifications, pos) = decode_ctx_boolean(data, pos, 1, what)?;
190 let (time_remaining, pos) = decode_ctx_unsigned::<u32>(data, pos, 2, what)?;
191 let (max_notification_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 3, what)?;
192 let mut pos = expect_opening(data, pos, 4, what)?;
193 let mut specifications = Vec::new();
194 while !is_closing(data, pos, 4)? {
195 if specifications.len() >= MAX_FRAMED_ITEMS {
196 return Err(Error::overflow(
197 pos,
198 format!("{what}: specification count exceeds limit"),
199 ));
200 }
201 let (specification, end) = decode_specification(data, pos, what)?;
202 specifications.push(specification);
203 pos = end;
204 }
205 let pos = expect_closing(data, pos, 4, what)?;
206 Ok((
207 BACnetCOVMultipleSubscription {
208 recipient,
209 issue_confirmed_notifications,
210 time_remaining,
211 max_notification_delay,
212 list_of_cov_subscription_specifications: specifications,
213 },
214 pos,
215 ))
216}
217
218fn decode_specification(
221 data: &[u8],
222 offset: usize,
223 what: &str,
224) -> Result<(BACnetCOVSubscriptionSpecification, usize), Error> {
225 let (monitored_object_identifier, pos) = decode_ctx_object_id(data, offset, 0, what)?;
226 let mut pos = expect_opening(data, pos, 1, what)?;
227 let mut references = Vec::new();
228 while !is_closing(data, pos, 1)? {
229 if references.len() >= MAX_FRAMED_ITEMS {
230 return Err(Error::overflow(
231 pos,
232 format!("{what}: COV reference count exceeds limit"),
233 ));
234 }
235 let (reference, end) = decode_cov_reference(data, pos, what)?;
236 references.push(reference);
237 pos = end;
238 }
239 Ok((
240 BACnetCOVSubscriptionSpecification {
241 monitored_object_identifier,
242 list_of_cov_references: references,
243 },
244 expect_closing(data, pos, 1, what)?,
245 ))
246}
247
248fn decode_cov_reference(
251 data: &[u8],
252 offset: usize,
253 what: &str,
254) -> Result<(BACnetCOVReference, usize), Error> {
255 let pos = expect_opening(data, offset, 0, what)?;
256 let (property_identifier, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
257 let (property_array_index, pos) =
258 decode_optional_ctx(data, pos, 1, what, decode_ctx_unsigned::<u32>)?;
259 let pos = expect_closing(data, pos, 0, what)?;
260 let (cov_increment, pos) = decode_optional_ctx(data, pos, 1, what, decode_ctx_real)?;
261 let (timestamped, pos) = decode_ctx_boolean(data, pos, 2, what)?;
262 Ok((
263 BACnetCOVReference {
264 property_identifier: PropertyIdentifier::from_raw(property_identifier),
265 property_array_index,
266 cov_increment,
267 timestamped,
268 },
269 pos,
270 ))
271}
272
273fn decode_recipient_process(
276 data: &[u8],
277 offset: usize,
278 what: &str,
279) -> Result<(BACnetRecipientProcess, usize), Error> {
280 let pos = expect_opening(data, offset, 0, what)?;
281 let pos = expect_opening(data, pos, 0, what)?;
282 let (recipient, pos) = decode_recipient(data, pos)?;
283 let pos = expect_closing(data, pos, 0, what)?;
284 let (process_identifier, pos) = decode_ctx_unsigned::<u32>(data, pos, 1, what)?;
285 let pos = expect_closing(data, pos, 0, what)?;
286 Ok((
287 BACnetRecipientProcess {
288 recipient,
289 process_identifier,
290 },
291 pos,
292 ))
293}
294
295fn is_closing(data: &[u8], offset: usize, tag: u8) -> Result<bool, Error> {
298 if offset >= data.len() {
299 return Err(Error::buffer_too_short(offset + 1, data.len()));
300 }
301 next_is_closing(data, offset, tag)
302}