bacnet_server/cov/
observation.rs1use super::CovSample;
3use bacnet_types::{error::Error, primitives::PropertyValue};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct CovObservation {
14 sample: CovSample,
15 flags: Option<u8>,
16 source_companions: Option<(CovSample, Option<CovSample>)>,
17 triggers: Box<[CovSample]>,
18}
19impl CovObservation {
20 pub fn new(sample: CovSample, flags: Option<&PropertyValue>) -> Result<Self, Error> {
23 Ok(Self {
24 sample,
25 source_companions: None,
26 flags: flags.map(validate_flags).transpose()?,
27 triggers: Box::default(),
28 })
29 }
30 pub(crate) fn with_triggers(mut self, triggers: Box<[CovSample]>) -> Self {
32 self.triggers = triggers;
33 self
34 }
35 pub(crate) fn triggers_changed(&self, previous: Option<&Self>) -> bool {
38 !self.triggers.is_empty() && previous.map(|p| &p.triggers) != Some(&self.triggers)
39 }
40 pub(crate) fn with_source_companions(
42 mut self,
43 pv: CovSample,
44 priority: Option<CovSample>,
45 ) -> Self {
46 self.source_companions = Some((pv, priority));
47 self
48 }
49 pub(crate) fn source_companions(&self) -> Option<&(CovSample, Option<CovSample>)> {
50 self.source_companions.as_ref()
51 }
52 pub fn sample(&self) -> &CovSample {
54 &self.sample
55 }
56 pub fn status_flags(&self) -> Option<u8> {
58 self.flags
59 }
60 pub(crate) fn flags_changed(&self, previous: Option<&Self>) -> bool {
62 self.flags.is_some() && previous.map(|p| p.flags) != Some(self.flags)
63 }
64}
65pub(crate) fn validate_flags(value: &PropertyValue) -> Result<u8, Error> {
66 match value {
67 PropertyValue::BitString {
68 unused_bits: 4,
69 data,
70 } if data.len() == 1 && data[0] & 0x0f == 0 => Ok(data[0] >> 4),
71 _ => Err(Error::Encoding(
72 "COV Status_Flags must be a canonical four-bit BitString".into(),
73 )),
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 #[test]
81 fn cov_status_observation_validation_and_presence_semantics() {
82 let sample = CovSample::new(&PropertyValue::Real(1.0)).unwrap();
83 let absent = CovObservation::new(sample.clone(), None).unwrap();
84 let flags = PropertyValue::BitString {
85 unused_bits: 4,
86 data: vec![0x80],
87 };
88 let present = CovObservation::new(sample.clone(), Some(&flags)).unwrap();
89 assert!(present.flags_changed(None));
90 assert!(present.flags_changed(Some(&absent)));
91 assert!(!present.flags_changed(Some(&present)));
92 assert!(!absent.flags_changed(Some(&present)));
93 assert_eq!(present.status_flags(), Some(8));
94 assert_eq!(absent.status_flags(), None);
95 for invalid in [
96 PropertyValue::Null,
97 PropertyValue::BitString {
98 unused_bits: 0,
99 data: vec![0],
100 },
101 PropertyValue::BitString {
102 unused_bits: 4,
103 data: vec![],
104 },
105 PropertyValue::BitString {
106 unused_bits: 4,
107 data: vec![0, 0],
108 },
109 PropertyValue::BitString {
110 unused_bits: 4,
111 data: vec![0x81],
112 },
113 ] {
114 assert!(CovObservation::new(sample.clone(), Some(&invalid)).is_err());
115 }
116 }
117}