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