1use bacnet_encoding::constructed::{ConstructedElement, ElementShape};
10use bacnet_types::enums::{
11 PropertyIdentifier, ResolvedBits, ResolvedEnum, resolve_bits, resolve_enum,
12};
13use bacnet_types::primitives::PropertyValue;
14use serde::Serialize;
15
16use super::primitive::{date_text, hex, plain_bits, time_text};
17use super::{ValueFormat, constructed, decode_all, event_parameter, holds_state, is_binary};
18use crate::label::kebab;
19
20#[derive(Debug, Clone, PartialEq, Serialize)]
23#[serde(untagged)]
24#[non_exhaustive]
25pub enum Datum {
26 Null,
28 Boolean(bool),
30 Unsigned(u64),
32 Signed(i64),
34 Real(f32),
36 Double(f64),
38 NonFinite {
41 float: String,
43 },
44 Text(String),
46 Octets {
48 octets: String,
50 },
51 Enumerated {
54 value: u32,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 name: Option<String>,
59 },
60 Bits {
64 bits: String,
66 #[serde(skip_serializing_if = "Option::is_none")]
68 names: Option<Vec<String>>,
69 },
70 Date {
72 date: String,
74 },
75 Time {
77 time: String,
79 },
80 Object {
83 #[serde(rename = "type")]
85 object_type: String,
86 type_number: u32,
88 instance: u32,
90 },
91 List(Vec<Datum>),
93 Constructed {
99 element: String,
101 text: String,
103 #[serde(skip_serializing_if = "Option::is_none")]
105 fields: Option<Fields>,
106 },
107 Raw {
109 raw: String,
111 },
112}
113
114#[derive(Debug, Clone, Default, PartialEq)]
118pub struct Fields(Vec<(String, Datum)>);
119
120impl Fields {
121 pub fn new(fields: Vec<(String, Datum)>) -> Self {
123 Self(fields)
124 }
125
126 pub fn get(&self, name: &str) -> Option<&Datum> {
128 self.0
129 .iter()
130 .find(|(key, _)| key == name)
131 .map(|(_, value)| value)
132 }
133
134 pub fn iter(&self) -> impl Iterator<Item = (&str, &Datum)> {
136 self.0.iter().map(|(key, value)| (key.as_str(), value))
137 }
138
139 pub fn len(&self) -> usize {
141 self.0.len()
142 }
143
144 pub fn is_empty(&self) -> bool {
146 self.0.is_empty()
147 }
148}
149
150pub const PLAIN_ELEMENTS: &[&str] = &[
154 "destination",
155 "port-permission",
156 "read-access-specification",
157 "device-object-reference",
158 "authentication-factor-format",
159 "stage-limit-value",
160 "device-object-property-reference",
161 "object-property-reference",
162 "recipient",
163 "calendar-entry",
164 "date-range",
165 "date-time",
166 "time-stamp",
167 "cov-subscription",
168 "value-source",
169 "scale",
170 "prescale",
171 "address-binding",
172 "shed-level",
173];
174
175impl Datum {
176 pub fn may_hold_device_text(&self) -> bool {
181 match self {
182 Self::Text(_) => true,
183 Self::List(items) => items.iter().any(Self::may_hold_device_text),
184 Self::Constructed {
185 element, fields, ..
186 } => {
187 !PLAIN_ELEMENTS.contains(&element.as_str())
188 || fields
189 .iter()
190 .flat_map(Fields::iter)
191 .any(|(_, datum)| datum.may_hold_device_text())
192 }
193 Self::Null
195 | Self::Boolean(_)
196 | Self::Unsigned(_)
197 | Self::Signed(_)
198 | Self::Real(_)
199 | Self::Double(_)
200 | Self::NonFinite { .. }
201 | Self::Octets { .. }
202 | Self::Enumerated { .. }
203 | Self::Bits { .. }
204 | Self::Date { .. }
205 | Self::Time { .. }
206 | Self::Object { .. }
207 | Self::Raw { .. } => false,
208 }
209 }
210}
211
212impl Serialize for Fields {
213 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
214 use serde::ser::SerializeMap;
215 let mut map = serializer.serialize_map(Some(self.0.len()))?;
216 for (key, value) in &self.0 {
217 map.serialize_entry(key, value)?;
218 }
219 map.end()
220 }
221}
222
223impl ValueFormat<'_> {
224 pub fn data(&self, property: PropertyIdentifier, data: &[u8]) -> Datum {
228 self.data_at(property, None, data)
229 }
230
231 pub fn data_at(&self, property: PropertyIdentifier, index: Option<u32>, data: &[u8]) -> Datum {
234 if let Some((ConstructedElement::EventParameter, shape, _)) =
235 self.element_at(property, index)
236 && let Some(mut items) = event_parameter::data(self, data)
237 {
238 return match shape {
239 ElementShape::Single if items.len() == 1 => items.remove(0),
240 _ => Datum::List(items),
241 };
242 }
243 if let Some((element, shape, texts)) = self.elements_at(property, index, data) {
244 let kind = constructed::element_name(element);
245 let mut items: Vec<Datum> = texts
246 .into_iter()
247 .map(|text| Datum::Constructed {
248 element: kind.into(),
249 text,
250 fields: None,
251 })
252 .collect();
253 return match shape {
254 ElementShape::Single if items.len() == 1 => items.remove(0),
255 _ => Datum::List(items),
256 };
257 }
258 let Some(values) = decode_all(data) else {
259 return Datum::Raw { raw: hex(data, "") };
260 };
261 match values.as_slice() {
262 [one] => self.element_data(property, one),
263 many => Datum::List(
264 many.iter()
265 .map(|value| self.element_data(property, value))
266 .collect(),
267 ),
268 }
269 }
270
271 pub fn element_data(&self, property: PropertyIdentifier, value: &PropertyValue) -> Datum {
273 match value {
274 PropertyValue::Null => Datum::Null,
275 PropertyValue::Boolean(b) => Datum::Boolean(*b),
276 PropertyValue::Unsigned(n) => Datum::Unsigned(*n),
277 PropertyValue::Signed(n) => Datum::Signed(i64::from(*n)),
278 PropertyValue::Real(v) if v.is_finite() => Datum::Real(*v),
279 PropertyValue::Double(v) if v.is_finite() => Datum::Double(*v),
280 PropertyValue::Real(v) => non_finite(f64::from(*v)),
281 PropertyValue::Double(v) => non_finite(*v),
282 PropertyValue::CharacterString(text) => Datum::Text(text.clone()),
283 PropertyValue::OctetString(bytes) => Datum::Octets {
284 octets: hex(bytes, ""),
285 },
286 PropertyValue::Enumerated(raw) => Datum::Enumerated {
287 value: *raw,
288 name: self.enum_name(property, value),
289 },
290 PropertyValue::BitString { .. } => Datum::Bits {
291 bits: bit_digits(value),
292 names: bit_names(property, value),
293 },
294 PropertyValue::Date(date) => Datum::Date {
295 date: date_text(date),
296 },
297 PropertyValue::Time(time) => Datum::Time {
298 time: time_text(time),
299 },
300 PropertyValue::ObjectIdentifier(object) => Datum::Object {
301 object_type: kebab(object.object_type()),
302 type_number: object.object_type().to_raw(),
303 instance: object.instance_number(),
304 },
305 PropertyValue::List(items) => Datum::List(
306 items
307 .iter()
308 .map(|item| self.element_data(property, item))
309 .collect(),
310 ),
311 PropertyValue::ApplicationData(bytes) => match self.priority_date_time(property, value)
312 {
313 Some((date, time)) => Datum::List(vec![
315 Datum::Date {
316 date: date_text(&date),
317 },
318 Datum::Time {
319 time: time_text(&time),
320 },
321 ]),
322 None => Datum::Raw {
323 raw: hex(bytes, ""),
324 },
325 },
326 }
327 }
328
329 fn enum_name(&self, property: PropertyIdentifier, value: &PropertyValue) -> Option<String> {
332 if let PropertyValue::Enumerated(raw @ (0 | 1)) = value
333 && holds_state(property)
334 && is_binary(self.object_type)
335 {
336 return Some(if *raw == 1 { "active" } else { "inactive" }.into());
337 }
338 match resolve_enum(property, value)? {
339 ResolvedEnum::Unknown(_) => None,
340 named => Some(kebab(named)).filter(|name| name.parse::<u64>().is_err()),
342 }
343 }
344}
345
346fn non_finite(value: f64) -> Datum {
347 Datum::NonFinite {
348 float: value.to_string(),
349 }
350}
351
352fn bit_digits(value: &PropertyValue) -> String {
354 let text = plain_bits(value);
355 text.trim_start_matches("bits(")
356 .trim_end_matches(')')
357 .to_string()
358}
359
360fn bit_names(property: PropertyIdentifier, value: &PropertyValue) -> Option<Vec<String>> {
362 fn names<'a>(flags: impl Iterator<Item = (&'a str, impl Sized)>) -> Vec<String> {
363 flags.map(|(name, _)| kebab(name)).collect()
364 }
365 Some(match resolve_bits(property, value)? {
366 ResolvedBits::StatusFlags(flags) => names(flags.iter_names()),
367 ResolvedBits::EventTransitionBits(bits) => names(bits.iter_names()),
368 ResolvedBits::LimitEnable(bits) => names(bits.iter_names()),
369 ResolvedBits::ServicesSupported(services) => services.iter().map(kebab).collect(),
370 ResolvedBits::ObjectTypesSupported(types) => types.iter().map(kebab).collect(),
371 ResolvedBits::Unknown { .. } => return None,
372 })
373}