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