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::{DecodeError, DecodingKind, 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(DecodeError::new(
197 DecodingKind::Overflow,
198 pos,
199 "specification count exceeds limit",
200 )
201 .within(what)
202 .into());
203 }
204 let (specification, end) = decode_specification(data, pos, what)?;
205 specifications.push(specification);
206 pos = end;
207 }
208 let pos = expect_closing(data, pos, 4, what)?;
209 Ok((
210 BACnetCOVMultipleSubscription {
211 recipient,
212 issue_confirmed_notifications,
213 time_remaining,
214 max_notification_delay,
215 list_of_cov_subscription_specifications: specifications,
216 },
217 pos,
218 ))
219}
220
221fn decode_specification(
224 data: &[u8],
225 offset: usize,
226 what: &'static str,
227) -> Result<(BACnetCOVSubscriptionSpecification, usize), Error> {
228 let (monitored_object_identifier, pos) = decode_ctx_object_id(data, offset, 0, what)?;
229 let mut pos = expect_opening(data, pos, 1, what)?;
230 let mut references = Vec::new();
231 while !is_closing(data, pos, 1)? {
232 if references.len() >= MAX_FRAMED_ITEMS {
233 return Err(DecodeError::new(
234 DecodingKind::Overflow,
235 pos,
236 "COV reference count exceeds limit",
237 )
238 .within(what)
239 .into());
240 }
241 let (reference, end) = decode_cov_reference(data, pos, what)?;
242 references.push(reference);
243 pos = end;
244 }
245 Ok((
246 BACnetCOVSubscriptionSpecification {
247 monitored_object_identifier,
248 list_of_cov_references: references,
249 },
250 expect_closing(data, pos, 1, what)?,
251 ))
252}
253
254fn decode_cov_reference(
257 data: &[u8],
258 offset: usize,
259 what: &'static str,
260) -> Result<(BACnetCOVReference, usize), Error> {
261 let pos = expect_opening(data, offset, 0, what)?;
262 let (property_identifier, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
263 let (property_array_index, pos) =
264 decode_optional_ctx(data, pos, 1, what, decode_ctx_unsigned::<u32>)?;
265 let pos = expect_closing(data, pos, 0, what)?;
266 let (cov_increment, pos) = decode_optional_ctx(data, pos, 1, what, decode_ctx_real)?;
267 let (timestamped, pos) = decode_ctx_boolean(data, pos, 2, what)?;
268 Ok((
269 BACnetCOVReference {
270 property_identifier: PropertyIdentifier::from_raw(property_identifier),
271 property_array_index,
272 cov_increment,
273 timestamped,
274 },
275 pos,
276 ))
277}
278
279fn decode_recipient_process(
282 data: &[u8],
283 offset: usize,
284 what: &'static str,
285) -> Result<(BACnetRecipientProcess, usize), Error> {
286 let pos = expect_opening(data, offset, 0, what)?;
287 let pos = expect_opening(data, pos, 0, what)?;
288 let (recipient, pos) = decode_recipient(data, pos)?;
289 let pos = expect_closing(data, pos, 0, what)?;
290 let (process_identifier, pos) = decode_ctx_unsigned::<u32>(data, pos, 1, what)?;
291 let pos = expect_closing(data, pos, 0, what)?;
292 Ok((
293 BACnetRecipientProcess {
294 recipient,
295 process_identifier,
296 },
297 pos,
298 ))
299}
300
301fn is_closing(data: &[u8], offset: usize, tag: u8) -> Result<bool, Error> {
304 if offset >= data.len() {
305 return Err(Error::buffer_too_short(offset + 1, data.len()));
306 }
307 next_is_closing(data, offset, tag)
308}