1#[cfg(not(feature = "std"))]
35use alloc::vec::Vec;
36
37use bacnet_types::constructed::FaultParameters;
38use bacnet_types::enums::LifeSafetyState;
39use bacnet_types::error::{DecodeError, DecodingKind, Error};
40use bytes::BytesMut;
41
42use crate::primitives;
43use crate::tags::{self, TagClass};
44
45use super::tagged::{
46 contents, decode_app_character_string, decode_app_enumerated, decode_ctx_constructed,
47 decode_ctx_unsigned, expect_closing, expect_opening, next_is_closing,
48};
49use super::{
50 decode_dopr_body, decode_property_state, encode_dopr_body, encode_property_state,
51 validate_extended_parameters, MAX_FRAMED_ITEMS,
52};
53
54pub fn encode_fault_parameters(buf: &mut BytesMut, value: &FaultParameters) -> Result<(), Error> {
56 let mut encoded = BytesMut::new();
57 encode_fault_parameters_into(&mut encoded, value)?;
58 super::validate_tlv_sequence(&encoded, "FaultParameters")
59 .map_err(Error::into_encode_refusal)?;
60 buf.extend_from_slice(&encoded);
61 Ok(())
62}
63
64fn encode_fault_parameters_into(buf: &mut BytesMut, value: &FaultParameters) -> Result<(), Error> {
65 use FaultParameters as F;
66 match value {
67 F::FaultNone => {
68 tags::encode_tag(buf, 0, TagClass::Context, 0);
70 }
71 F::FaultCharacterString { fault_values } => {
72 tags::encode_opening_tag(buf, 1);
73 tags::encode_opening_tag(buf, 0);
74 for s in fault_values {
75 primitives::encode_app_character_string(buf, s)?;
76 }
77 tags::encode_closing_tag(buf, 0);
78 tags::encode_closing_tag(buf, 1);
79 }
80 F::FaultExtended {
81 vendor_id,
82 extended_fault_type,
83 parameters,
84 } => {
85 validate_extended_parameters(parameters, "fault-extended")
86 .map_err(Error::into_encode_refusal)?;
87 tags::encode_opening_tag(buf, 2);
88 primitives::encode_ctx_unsigned(buf, 0, *vendor_id as u64);
89 primitives::encode_ctx_unsigned(buf, 1, *extended_fault_type as u64);
90 tags::encode_opening_tag(buf, 2);
91 buf.extend_from_slice(parameters);
92 tags::encode_closing_tag(buf, 2);
93 tags::encode_closing_tag(buf, 2);
94 }
95 F::FaultLifeSafety {
96 fault_values,
97 mode_property_reference,
98 } => {
99 tags::encode_opening_tag(buf, 3);
100 tags::encode_opening_tag(buf, 0);
101 for v in fault_values {
102 primitives::encode_app_enumerated(buf, v.to_raw());
103 }
104 tags::encode_closing_tag(buf, 0);
105 tags::encode_opening_tag(buf, 1);
107 encode_dopr_body(buf, mode_property_reference);
108 tags::encode_closing_tag(buf, 1);
109 tags::encode_closing_tag(buf, 3);
110 }
111 F::FaultState { fault_values } => {
112 let mut encoded_states = BytesMut::new();
113 for state in fault_values {
114 encode_property_state(&mut encoded_states, state)?;
115 }
116 tags::encode_opening_tag(buf, 4);
117 tags::encode_opening_tag(buf, 0);
118 buf.extend_from_slice(&encoded_states);
119 tags::encode_closing_tag(buf, 0);
120 tags::encode_closing_tag(buf, 4);
121 }
122 F::FaultStatusFlags { reference } => {
123 tags::encode_opening_tag(buf, 5);
124 tags::encode_opening_tag(buf, 0);
125 encode_dopr_body(buf, reference);
126 tags::encode_closing_tag(buf, 0);
127 tags::encode_closing_tag(buf, 5);
128 }
129 F::FaultOutOfRange {
130 min_normal,
131 max_normal,
132 } => {
133 tags::encode_opening_tag(buf, 6);
134 tags::encode_opening_tag(buf, 0);
138 primitives::encode_app_double(buf, *min_normal);
139 tags::encode_closing_tag(buf, 0);
140 tags::encode_opening_tag(buf, 1);
141 primitives::encode_app_double(buf, *max_normal);
142 tags::encode_closing_tag(buf, 1);
143 tags::encode_closing_tag(buf, 6);
144 }
145 F::FaultListed { reference } => {
146 tags::encode_opening_tag(buf, 7);
147 tags::encode_opening_tag(buf, 0);
148 encode_dopr_body(buf, reference);
149 tags::encode_closing_tag(buf, 0);
150 tags::encode_closing_tag(buf, 7);
151 }
152 }
153 Ok(())
154}
155
156fn decode_fault_normal_value(data: &[u8], offset: usize) -> Result<(f64, usize), Error> {
159 let (t, pos) = tags::decode_tag(data, offset)?;
160 if t.class != TagClass::Application || t.is_opening || t.is_closing {
161 return Err(Error::decoding(
162 offset,
163 "fault-out-of-range: min/max-normal-value must be application-tagged",
164 ));
165 }
166 let what = "fault-out-of-range min/max-normal-value";
167 if t.number == tags::app_tag::BOOLEAN {
168 return Err(DecodeError::new(
169 DecodingKind::InvalidEncoding,
170 offset,
171 "BOOLEAN is not a valid alternative",
172 )
173 .within(what)
174 .into());
175 }
176 let (content, end) = contents(data, pos, t.length)?;
177 let value = match t.number {
178 tags::app_tag::REAL => primitives::decode_real(content)? as f64,
179 tags::app_tag::UNSIGNED => primitives::decode_unsigned(content)? as f64,
180 tags::app_tag::DOUBLE => primitives::decode_double(content)?,
181 tags::app_tag::SIGNED => primitives::decode_signed(content)? as f64,
182 other => {
183 return Err(DecodeError::new(
184 DecodingKind::InvalidEncoding,
185 offset,
186 "unexpected application tag {}",
187 )
188 .within(what)
189 .with_value(other.into())
190 .into())
191 }
192 };
193 Ok((value, end))
194}
195
196pub fn decode_fault_parameters(
200 data: &[u8],
201 offset: usize,
202) -> Result<(FaultParameters, usize), Error> {
203 use FaultParameters as F;
204 let (tag, pos) = tags::decode_tag(data, offset)?;
205 let what = "BACnetFaultParameter";
206
207 if tag.is_context(0) {
209 if tag.length != 0 {
210 return Err(Error::decoding(
211 offset,
212 "BACnetFaultParameter: none [0] NULL must have no contents",
213 ));
214 }
215 return Ok((F::FaultNone, pos));
216 }
217 if !tag.is_opening {
218 return Err(Error::decoding(
219 offset,
220 "BACnetFaultParameter: expected an opening context tag or [0] NULL",
221 ));
222 }
223
224 let value = match tag.number {
225 1 => {
226 let mut pos = expect_opening(data, pos, 0, what)?;
227 let mut fault_values = Vec::new();
228 while !next_is_closing(data, pos, 0)? {
229 if fault_values.len() >= MAX_FRAMED_ITEMS {
230 return Err(Error::overflow(
231 pos,
232 "fault-characterstring: list-of-fault-values exceeds limit",
233 ));
234 }
235 let (s, p) = decode_app_character_string(data, pos, what)?;
236 fault_values.push(s);
237 pos = p;
238 }
239 pos = expect_closing(data, pos, 0, what)?;
240 pos = expect_closing(data, pos, 1, what)?;
241 (F::FaultCharacterString { fault_values }, pos)
242 }
243 2 => {
244 let what = "BACnetFaultParameter fault-extended";
245 let (vendor_id, pos) = decode_ctx_unsigned::<u16>(data, pos, 0, what)?;
246 let (extended_fault_type, pos) = decode_ctx_unsigned::<u32>(data, pos, 1, what)?;
247 let (raw_params, pos) = decode_ctx_constructed(data, pos, 2, what)?;
248 validate_extended_parameters(raw_params, "fault-extended")?;
249 let pos = expect_closing(data, pos, 2, what)?;
250 (
251 F::FaultExtended {
252 vendor_id,
253 extended_fault_type,
254 parameters: raw_params.to_vec(),
255 },
256 pos,
257 )
258 }
259 3 => {
260 let mut pos = expect_opening(data, pos, 0, what)?;
261 let mut fault_values = Vec::new();
262 while !next_is_closing(data, pos, 0)? {
263 if fault_values.len() >= MAX_FRAMED_ITEMS {
264 return Err(Error::overflow(
265 pos,
266 "fault-life-safety: list-of-fault-values exceeds limit",
267 ));
268 }
269 let (v, p) = decode_app_enumerated::<u32>(data, pos, what)?;
270 fault_values.push(LifeSafetyState::from_raw(v));
271 pos = p;
272 }
273 pos = expect_closing(data, pos, 0, what)?;
274 pos = expect_opening(data, pos, 1, what)?;
275 let (mode_property_reference, p) = decode_dopr_body(data, pos, what)?;
276 pos = p;
277 pos = expect_closing(data, pos, 1, what)?;
278 pos = expect_closing(data, pos, 3, what)?;
279 (
280 F::FaultLifeSafety {
281 fault_values,
282 mode_property_reference,
283 },
284 pos,
285 )
286 }
287 4 => {
288 let mut pos = expect_opening(data, pos, 0, what)?;
289 let mut fault_values = Vec::new();
290 while !next_is_closing(data, pos, 0)? {
291 if fault_values.len() >= MAX_FRAMED_ITEMS {
292 return Err(Error::overflow(
293 pos,
294 "fault-state: list-of-fault-values exceeds limit",
295 ));
296 }
297 let (state, p) = decode_property_state(data, pos)?;
298 fault_values.push(state);
299 pos = p;
300 }
301 pos = expect_closing(data, pos, 0, what)?;
302 pos = expect_closing(data, pos, 4, what)?;
303 (F::FaultState { fault_values }, pos)
304 }
305 5 => {
306 let mut pos = expect_opening(data, pos, 0, what)?;
307 let (reference, p) = decode_dopr_body(data, pos, what)?;
308 pos = p;
309 pos = expect_closing(data, pos, 0, what)?;
310 pos = expect_closing(data, pos, 5, what)?;
311 (F::FaultStatusFlags { reference }, pos)
312 }
313 6 => {
314 let mut pos = expect_opening(data, pos, 0, what)?;
315 let (min_normal, p) = decode_fault_normal_value(data, pos)?;
316 pos = p;
317 pos = expect_closing(data, pos, 0, what)?;
318 pos = expect_opening(data, pos, 1, what)?;
319 let (max_normal, p) = decode_fault_normal_value(data, pos)?;
320 pos = p;
321 pos = expect_closing(data, pos, 1, what)?;
322 pos = expect_closing(data, pos, 6, what)?;
323 (
324 F::FaultOutOfRange {
325 min_normal,
326 max_normal,
327 },
328 pos,
329 )
330 }
331 7 => {
332 let mut pos = expect_opening(data, pos, 0, what)?;
333 let (reference, p) = decode_dopr_body(data, pos, what)?;
334 pos = p;
335 pos = expect_closing(data, pos, 0, what)?;
336 pos = expect_closing(data, pos, 7, what)?;
337 (F::FaultListed { reference }, pos)
338 }
339 n => {
340 return Err(DecodeError::new(
341 DecodingKind::InvalidEncoding,
342 offset,
343 "BACnetFaultParameter: unknown CHOICE tag [{}]",
344 )
345 .with_value(n.into())
346 .into());
347 }
348 };
349 let (value, end) = value;
350 super::validate_tlv_sequence(&data[offset..end], what)?;
351 Ok((value, end))
352}