bacnet_server/cov/
sample.rs1use bacnet_types::{
3 enums::{ErrorClass, ErrorCode},
4 error::Error,
5 primitives::PropertyValue,
6};
7use std::sync::Arc;
8
9#[derive(Debug, Clone)]
12pub struct CovSample(Arc<PropertyValue>);
13impl CovSample {
14 pub fn new(value: &PropertyValue) -> Result<Self, Error> {
16 let (mut nodes, mut bytes) = (0usize, 0usize);
17 validate(value, 0, &mut nodes, &mut bytes)?;
18 Ok(Self(Arc::new(normalize(value))))
19 }
20 pub fn value(&self) -> &PropertyValue {
22 &self.0
23 }
24
25 pub(crate) fn reports(
26 &self,
27 previous: Option<&Self>,
28 increment: Option<f64>,
29 numeric: bool,
30 ) -> bool {
31 let Some(previous) = previous else {
32 return true;
33 };
34 if !numeric {
35 return self != previous;
36 }
37 use PropertyValue::*;
38 match (self.value(), previous.value()) {
39 (Unsigned(a), Unsigned(b)) => integer_delta(a.abs_diff(*b), increment),
40 (Signed(a), Signed(b)) => integer_delta(u64::from(a.abs_diff(*b)), increment),
41 (Real(a), Real(b)) => {
42 if !a.is_finite() || !b.is_finite() {
43 a.to_bits() != b.to_bits()
44 } else {
45 float_delta((f64::from(*a) - f64::from(*b)).abs(), increment)
46 }
47 }
48 (Double(a), Double(b)) => {
49 if !a.is_finite() || !b.is_finite() {
50 a.to_bits() != b.to_bits()
51 } else {
52 float_delta((a - b).abs(), increment)
53 }
54 }
55 _ => self != previous,
56 }
57 }
58}
59impl PartialEq for CovSample {
60 fn eq(&self, other: &Self) -> bool {
61 equal(self.value(), other.value())
62 }
63}
64impl Eq for CovSample {}
65fn integer_delta(delta: u64, increment: Option<f64>) -> bool {
66 match increment {
67 None => delta != 0,
68 Some(i) if i <= 0.0 => delta != 0,
69 Some(i) if i.is_nan() || i >= 18_446_744_073_709_551_616.0 => false,
70 Some(i) => delta >= i.ceil() as u64,
71 }
72}
73fn float_delta(delta: f64, increment: Option<f64>) -> bool {
74 match increment {
75 None => delta != 0.0,
76 Some(i) if i <= 0.0 => delta != 0.0,
77 Some(i) if !i.is_finite() => false,
78 Some(i) => delta >= i,
79 }
80}
81fn capacity_error() -> Error {
82 Error::Protocol {
83 class: ErrorClass::RESOURCES.to_raw() as u32,
84 code: ErrorCode::NO_SPACE_TO_ADD_LIST_ELEMENT.to_raw() as u32,
85 }
86}
87fn validate(
88 v: &PropertyValue,
89 depth: usize,
90 nodes: &mut usize,
91 bytes: &mut usize,
92) -> Result<(), Error> {
93 *nodes = nodes.checked_add(1).ok_or_else(capacity_error)?;
94 if *nodes > 1024 {
95 return Err(capacity_error());
96 }
97 use PropertyValue::*;
98 let size = match v {
99 List(values) => {
100 if depth == 32 {
101 return Err(capacity_error());
102 }
103 for value in values {
104 validate(value, depth + 1, nodes, bytes)?;
105 }
106 0
107 }
108 OctetString(v) | ApplicationData(v) => v.len(),
109 CharacterString(v) => v.len(),
110 BitString { data, .. } => data.len(),
111 Null => 0,
112 Boolean(_) => 1,
113 Real(_) | Signed(_) | Enumerated(_) | Date(_) | Time(_) | ObjectIdentifier(_) => 4,
114 Unsigned(_) | Double(_) => 8,
115 };
116 *bytes = bytes.checked_add(size).ok_or_else(capacity_error)?;
117 if *bytes > 65536 {
118 return Err(capacity_error());
119 }
120 Ok(())
121}
122fn normalize(v: &PropertyValue) -> PropertyValue {
123 use PropertyValue::*;
124 fn bytes(v: &[u8]) -> Vec<u8> {
125 v.to_vec().into_boxed_slice().into_vec()
126 }
127 match v {
128 List(v) => List(
129 v.iter()
130 .map(normalize)
131 .collect::<Vec<_>>()
132 .into_boxed_slice()
133 .into_vec(),
134 ),
135 OctetString(v) => OctetString(bytes(v)),
136 ApplicationData(v) => ApplicationData(bytes(v)),
137 CharacterString(v) => CharacterString(v.as_str().into()),
138 BitString { unused_bits, data } => BitString {
139 unused_bits: *unused_bits,
140 data: bytes(data),
141 },
142 _ => v.clone(),
143 }
144}
145fn equal(a: &PropertyValue, b: &PropertyValue) -> bool {
146 use PropertyValue::*;
147 match (a, b) {
148 (Real(a), Real(b)) => a.to_bits() == b.to_bits(),
149 (Double(a), Double(b)) => a.to_bits() == b.to_bits(),
150 (List(a), List(b)) => a.len() == b.len() && a.iter().zip(b).all(|(a, b)| equal(a, b)),
151 _ => a == b,
152 }
153}
154
155#[cfg(test)]
156mod tests;