1use super::*;
2use crate::constructed::log_fields::check_bit_string;
3
4fn encode_status_flags(buf: &mut BytesMut, tag: u8, flags: StatusFlags) {
7 primitives::encode_ctx_bit_string(buf, tag, 4, &[flags.bits() << 4]);
8}
9
10pub fn encode_notification_parameters(
15 params: &NotificationParameters,
16 buf: &mut BytesMut,
17) -> Result<(), Error> {
18 let mut encoded = BytesMut::new();
19 encode_into(params, &mut encoded)?;
20 if matches!(params, NotificationParameters::ComplexEventType { .. }) {
21 let (opening, content_start) =
22 tags::decode_tag(&encoded, 0).map_err(|error| Error::Encoding(error.to_string()))?;
23 if !opening.is_opening_tag(6) {
24 return Err(Error::Encoding(
25 "ComplexEventType missing opening tag [6]".into(),
26 ));
27 }
28 let (_, next) = tags::extract_context_value(&encoded, content_start, 6)
29 .map_err(|error| Error::Encoding(error.to_string()))?;
30 if next != encoded.len() {
31 return Err(Error::Encoding(
32 "ComplexEventType has trailing encoded fields".into(),
33 ));
34 }
35 } else {
36 validate_tlv_sequence(&encoded, "NotificationParameters")
37 .map_err(|error| Error::Encoding(error.to_string()))?;
38 }
39 buf.extend_from_slice(&encoded);
40 Ok(())
41}
42
43fn encode_into(params: &NotificationParameters, buf: &mut BytesMut) -> Result<(), Error> {
44 match params {
45 NotificationParameters::ChangeOfBitstring {
46 referenced_bitstring,
47 status_flags,
48 } => {
49 let (unused_bits, data) = referenced_bitstring;
50 check_bit_string(*unused_bits, data, "ChangeOfBitstring referenced-bitstring")?;
51 tags::encode_opening_tag(buf, 0);
52 primitives::encode_ctx_bit_string(buf, 0, *unused_bits, data);
53 encode_status_flags(buf, 1, *status_flags);
54 tags::encode_closing_tag(buf, 0);
55 }
56 NotificationParameters::ChangeOfState {
57 new_state,
58 status_flags,
59 } => {
60 tags::encode_opening_tag(buf, 1);
61 tags::encode_opening_tag(buf, 0);
63 encode_property_states(buf, new_state)?;
64 tags::encode_closing_tag(buf, 0);
65 encode_status_flags(buf, 1, *status_flags);
67 tags::encode_closing_tag(buf, 1);
68 }
69 NotificationParameters::ChangeOfValue {
70 new_value,
71 status_flags,
72 } => {
73 tags::encode_opening_tag(buf, 2);
74 tags::encode_opening_tag(buf, 0);
76 match new_value {
77 ChangeOfValueChoice::ChangedBits { unused_bits, data } => {
78 check_bit_string(*unused_bits, data, "ChangeOfValue changed-bits")?;
79 primitives::encode_ctx_bit_string(buf, 0, *unused_bits, data);
80 }
81 ChangeOfValueChoice::ChangedValue(v) => {
82 primitives::encode_ctx_real(buf, 1, *v);
83 }
84 }
85 tags::encode_closing_tag(buf, 0);
86 encode_status_flags(buf, 1, *status_flags);
88 tags::encode_closing_tag(buf, 2);
89 }
90 NotificationParameters::CommandFailure {
91 command_value,
92 status_flags,
93 feedback_value,
94 } => {
95 tags::encode_opening_tag(buf, 3);
96 tags::encode_opening_tag(buf, 0);
98 buf.extend_from_slice(command_value);
99 tags::encode_closing_tag(buf, 0);
100 encode_status_flags(buf, 1, *status_flags);
102 tags::encode_opening_tag(buf, 2);
104 buf.extend_from_slice(feedback_value);
105 tags::encode_closing_tag(buf, 2);
106 tags::encode_closing_tag(buf, 3);
107 }
108 NotificationParameters::FloatingLimit {
109 reference_value,
110 status_flags,
111 setpoint_value,
112 error_limit,
113 } => {
114 tags::encode_opening_tag(buf, 4);
115 primitives::encode_ctx_real(buf, 0, *reference_value);
116 encode_status_flags(buf, 1, *status_flags);
117 primitives::encode_ctx_real(buf, 2, *setpoint_value);
118 primitives::encode_ctx_real(buf, 3, *error_limit);
119 tags::encode_closing_tag(buf, 4);
120 }
121 NotificationParameters::OutOfRange {
122 exceeding_value,
123 status_flags,
124 deadband,
125 exceeded_limit,
126 } => {
127 tags::encode_opening_tag(buf, 5);
128 primitives::encode_ctx_real(buf, 0, *exceeding_value);
129 encode_status_flags(buf, 1, *status_flags);
130 primitives::encode_ctx_real(buf, 2, *deadband);
131 primitives::encode_ctx_real(buf, 3, *exceeded_limit);
132 tags::encode_closing_tag(buf, 5);
133 }
134 NotificationParameters::ComplexEventType { property_values } => {
135 if property_values.len() > MAX_FRAMED_ITEMS {
136 return Err(Error::Encoding(format!(
137 "ComplexEventType exceeds {MAX_FRAMED_ITEMS} property values"
138 )));
139 }
140 tags::encode_opening_tag(buf, 6);
141 for property_value in property_values {
142 if property_value
143 .priority
144 .is_some_and(|priority| !(1..=16).contains(&priority))
145 {
146 return Err(Error::Encoding(
147 "ComplexEventType property priority must be in 1..=16".into(),
148 ));
149 }
150 validate_tlv_sequence(&property_value.value, "ComplexEventType property value")
151 .map_err(|error| Error::Encoding(error.to_string()))?;
152 encode_bacnet_property_value(property_value, buf);
153 }
154 tags::encode_closing_tag(buf, 6);
155 }
156 NotificationParameters::ChangeOfLifeSafety {
157 new_state,
158 new_mode,
159 status_flags,
160 operation_expected,
161 } => {
162 tags::encode_opening_tag(buf, 8);
163 primitives::encode_ctx_enumerated(buf, 0, new_state.to_raw());
164 primitives::encode_ctx_enumerated(buf, 1, new_mode.to_raw());
165 encode_status_flags(buf, 2, *status_flags);
166 primitives::encode_ctx_enumerated(buf, 3, operation_expected.to_raw());
167 tags::encode_closing_tag(buf, 8);
168 }
169 NotificationParameters::Extended {
170 vendor_id,
171 extended_event_type,
172 parameters,
173 } => {
174 tags::encode_opening_tag(buf, 9);
175 primitives::encode_ctx_unsigned(buf, 0, *vendor_id as u64);
176 primitives::encode_ctx_unsigned(buf, 1, *extended_event_type as u64);
177 tags::encode_opening_tag(buf, 2);
179 buf.extend_from_slice(parameters);
180 tags::encode_closing_tag(buf, 2);
181 tags::encode_closing_tag(buf, 9);
182 }
183 NotificationParameters::BufferReady {
184 buffer_property,
185 previous_notification,
186 current_notification,
187 } => {
188 tags::encode_opening_tag(buf, 10);
189 tags::encode_opening_tag(buf, 0);
191 primitives::encode_ctx_object_id(buf, 0, &buffer_property.object_identifier);
192 primitives::encode_ctx_unsigned(buf, 1, buffer_property.property_identifier as u64);
193 if let Some(idx) = buffer_property.property_array_index {
194 primitives::encode_ctx_unsigned(buf, 2, idx as u64);
195 }
196 if let Some(ref dev) = buffer_property.device_identifier {
197 primitives::encode_ctx_object_id(buf, 3, dev);
198 }
199 tags::encode_closing_tag(buf, 0);
200 primitives::encode_ctx_unsigned(buf, 1, *previous_notification as u64);
202 primitives::encode_ctx_unsigned(buf, 2, *current_notification as u64);
204 tags::encode_closing_tag(buf, 10);
205 }
206 NotificationParameters::UnsignedRange {
207 exceeding_value,
208 status_flags,
209 exceeded_limit,
210 } => {
211 tags::encode_opening_tag(buf, 11);
212 primitives::encode_ctx_unsigned(buf, 0, *exceeding_value);
213 encode_status_flags(buf, 1, *status_flags);
214 primitives::encode_ctx_unsigned(buf, 2, *exceeded_limit);
215 tags::encode_closing_tag(buf, 11);
216 }
217 NotificationParameters::AccessEvent {
218 access_event,
219 status_flags,
220 access_event_tag,
221 access_event_time,
222 access_credential,
223 authentication_factor,
224 } => {
225 if let Some(authentication_factor) = authentication_factor {
226 structured::validate_authentication_factor(authentication_factor)
227 .map_err(|error| Error::Encoding(error.to_string()))?;
228 }
229 tags::encode_opening_tag(buf, 13);
230 primitives::encode_ctx_enumerated(buf, 0, access_event.to_raw());
231 encode_status_flags(buf, 1, *status_flags);
232 primitives::encode_ctx_unsigned(buf, 2, *access_event_tag as u64);
233 primitives::encode_timestamp(
235 buf,
236 3,
237 &BACnetTimeStamp::DateTime {
238 date: access_event_time.0,
239 time: access_event_time.1,
240 },
241 )?;
242 tags::encode_opening_tag(buf, 4);
244 if let Some(ref dev) = access_credential.device_identifier {
245 primitives::encode_ctx_object_id(buf, 0, dev);
246 }
247 primitives::encode_ctx_object_id(buf, 1, &access_credential.object_identifier);
248 tags::encode_closing_tag(buf, 4);
249 if let Some(authentication_factor) = authentication_factor {
250 tags::encode_opening_tag(buf, 5);
252 buf.extend_from_slice(authentication_factor);
253 tags::encode_closing_tag(buf, 5);
254 }
255 tags::encode_closing_tag(buf, 13);
256 }
257 NotificationParameters::DoubleOutOfRange {
258 exceeding_value,
259 status_flags,
260 deadband,
261 exceeded_limit,
262 } => {
263 tags::encode_opening_tag(buf, 14);
264 primitives::encode_ctx_double(buf, 0, *exceeding_value);
265 encode_status_flags(buf, 1, *status_flags);
266 primitives::encode_ctx_double(buf, 2, *deadband);
267 primitives::encode_ctx_double(buf, 3, *exceeded_limit);
268 tags::encode_closing_tag(buf, 14);
269 }
270 NotificationParameters::SignedOutOfRange {
271 exceeding_value,
272 status_flags,
273 deadband,
274 exceeded_limit,
275 } => {
276 tags::encode_opening_tag(buf, 15);
277 primitives::encode_ctx_signed(buf, 0, *exceeding_value);
278 encode_status_flags(buf, 1, *status_flags);
279 primitives::encode_ctx_unsigned(buf, 2, *deadband);
280 primitives::encode_ctx_signed(buf, 3, *exceeded_limit);
281 tags::encode_closing_tag(buf, 15);
282 }
283 NotificationParameters::UnsignedOutOfRange {
284 exceeding_value,
285 status_flags,
286 deadband,
287 exceeded_limit,
288 } => {
289 tags::encode_opening_tag(buf, 16);
290 primitives::encode_ctx_unsigned(buf, 0, *exceeding_value);
291 encode_status_flags(buf, 1, *status_flags);
292 primitives::encode_ctx_unsigned(buf, 2, *deadband);
293 primitives::encode_ctx_unsigned(buf, 3, *exceeded_limit);
294 tags::encode_closing_tag(buf, 16);
295 }
296 NotificationParameters::ChangeOfCharacterstring {
297 changed_value,
298 status_flags,
299 alarm_value,
300 } => {
301 tags::encode_opening_tag(buf, 17);
302 primitives::encode_ctx_character_string(buf, 0, changed_value)?;
303 encode_status_flags(buf, 1, *status_flags);
304 primitives::encode_ctx_character_string(buf, 2, alarm_value)?;
305 tags::encode_closing_tag(buf, 17);
306 }
307 NotificationParameters::ChangeOfStatusFlags {
308 present_value,
309 referenced_flags,
310 } => {
311 tags::encode_opening_tag(buf, 18);
312 if let Some(present_value) = present_value {
313 tags::encode_opening_tag(buf, 0);
315 buf.extend_from_slice(present_value);
316 tags::encode_closing_tag(buf, 0);
317 }
318 encode_status_flags(buf, 1, *referenced_flags);
320 tags::encode_closing_tag(buf, 18);
321 }
322 NotificationParameters::ChangeOfReliability {
323 reliability,
324 status_flags,
325 property_values,
326 } => {
327 tags::encode_opening_tag(buf, 19);
328 primitives::encode_ctx_enumerated(buf, 0, reliability.to_raw());
329 encode_status_flags(buf, 1, *status_flags);
330 tags::encode_opening_tag(buf, 2);
332 buf.extend_from_slice(property_values);
333 tags::encode_closing_tag(buf, 2);
334 tags::encode_closing_tag(buf, 19);
335 }
336 NotificationParameters::ChangeOfDiscreteValue {
337 new_value,
338 status_flags,
339 } => {
340 tags::encode_opening_tag(buf, 21);
341 tags::encode_opening_tag(buf, 0);
343 buf.extend_from_slice(new_value);
344 tags::encode_closing_tag(buf, 0);
345 encode_status_flags(buf, 1, *status_flags);
347 tags::encode_closing_tag(buf, 21);
348 }
349 NotificationParameters::ChangeOfTimer {
350 new_state,
351 status_flags,
352 update_time,
353 last_state_change,
354 initial_timeout,
355 expiration_time,
356 } => {
357 tags::encode_opening_tag(buf, 22);
358 primitives::encode_ctx_enumerated(buf, 0, new_state.to_raw());
359 encode_status_flags(buf, 1, *status_flags);
360 tags::encode_opening_tag(buf, 2);
362 primitives::encode_app_date(buf, &update_time.0);
363 primitives::encode_app_time(buf, &update_time.1);
364 tags::encode_closing_tag(buf, 2);
365 if let Some(last_state_change) = last_state_change {
366 primitives::encode_ctx_enumerated(buf, 3, last_state_change.to_raw());
367 }
368 if let Some(initial_timeout) = initial_timeout {
369 primitives::encode_ctx_unsigned(buf, 4, *initial_timeout as u64);
370 }
371 if let Some(expiration_time) = expiration_time {
372 tags::encode_opening_tag(buf, 5);
373 primitives::encode_app_date(buf, &expiration_time.0);
374 primitives::encode_app_time(buf, &expiration_time.1);
375 tags::encode_closing_tag(buf, 5);
376 }
377 tags::encode_closing_tag(buf, 22);
378 }
379 }
380 Ok(())
381}