Skip to main content

bacnet_objects/database/
averaging_sampling.rs

1//! Server-taken Averaging samples (Clauses 12.5.13 to 12.5.15, #1144). Each
2//! object keeps its own schedule; the database reads the property a due
3//! sample names, because only it can reach the other object.
4
5use crate::prelude::*;
6use core::time::Duration;
7
8use bacnet_types::enums::PropertyIdentifier;
9use bacnet_types::primitives::ObjectIdentifier;
10
11use super::ObjectDatabase;
12
13impl ObjectDatabase {
14    /// Take every Averaging sample due at the monotonic instant `now` and
15    /// return the objects that recorded one, for COV processing.
16    ///
17    /// The caller must hold exclusive database access for the whole call. A
18    /// due object names the local property it averages
19    /// (`take_due_averaging_sample_internal`); this reads it and records the
20    /// value. An attempt that yields nothing usable is recorded as a miss,
21    /// which counts toward Attempted_Samples but not Valid_Samples: a
22    /// referenced object or property that doesn't exist, an array index on a
23    /// property that isn't an array, any other read error, and a value the
24    /// object refuses to average (a datatype outside the five Clause 12.5
25    /// accepts, or a REAL that isn't finite).
26    ///
27    /// Without a bound monotonic clock nothing is ever due. Reads go through
28    /// [`BACnetObject::read_property`](crate::traits::BACnetObject::read_property),
29    /// so they must stay bounded, as for Trend Log polling.
30    pub fn sample_due_averaging_objects(&mut self, now: Duration) -> Vec<ObjectIdentifier> {
31        let mut due = Vec::new();
32        self.for_each_object_mut(|oid, object| {
33            if let Some(reference) = object.take_due_averaging_sample_internal(now) {
34                due.push((oid, reference));
35            }
36        });
37        let mut sampled = Vec::with_capacity(due.len());
38        for (oid, reference) in due {
39            let property = PropertyIdentifier::from_raw(reference.property_identifier);
40            let index = reference.property_array_index;
41            let value = self
42                .get(&reference.object_identifier)
43                // An index on anything but an array fails, as ReadProperty's
44                // gate makes it; object read arms may ignore the index.
45                .filter(|target| index.is_none() || target.is_array_property(property))
46                .and_then(|target| target.read_property(property, index).ok());
47            let Some(object) = self.get_mut(&oid) else {
48                continue;
49            };
50            let recorded = value
51                .and_then(|value| object.add_averaging_sample_internal(Some(value)).ok())
52                .is_some()
53                || object.add_averaging_sample_internal(None).is_ok();
54            if recorded {
55                sampled.push(oid);
56            }
57        }
58        sampled
59    }
60}
61
62#[cfg(test)]
63#[path = "averaging_sampling_tests.rs"]
64mod tests;