Skip to main content

bacnet_objects/trend/
multiple_durable.rs

1//! A Trend Log Multiple kept in storage (#1541); see
2//! [`crate::log_persistence`].
3
4use std::sync::Arc;
5
6use bacnet_types::constructed::BACnetLogMultipleRecord;
7use bacnet_types::error::Error;
8use bacnet_types::primitives::{Date, Time};
9
10use super::TrendLogMultipleObject;
11use crate::log_buffer::LogRecordBuffer;
12use crate::log_persistence::store::{
13    fit_to_buffer_size, impl_durable_log, DurableLog, LogState, LogStore,
14};
15use crate::log_persistence::{LogCommitCadence, LogPersistence};
16
17impl TrendLogMultipleObject {
18    /// Open a Trend Log Multiple kept in `persistence`, or start one there
19    /// when it holds none yet, as
20    /// [`TrendLogObject::with_persistence`](crate::trend::TrendLogObject::with_persistence)
21    /// does. Its snapshot holds the whole Log_DeviceObjectProperty array, up
22    /// to [`MAX_LOG_DEVICE_OBJECT_PROPERTIES`](crate::trend::MAX_LOG_DEVICE_OBJECT_PROPERTIES)
23    /// references, and its Logging_Type is POLLED or TRIGGERED.
24    pub fn with_persistence(
25        instance: u32,
26        name: impl Into<String>,
27        buffer_size: u32,
28        persistence: Arc<dyn LogPersistence<BACnetLogMultipleRecord>>,
29    ) -> Result<Self, Error> {
30        let mut log = Self::new(instance, name, buffer_size)?;
31        let (mut store, loaded) = LogStore::open(log.oid, buffer_size, persistence)?;
32        if let Some(mut snapshot) = loaded {
33            let resized = fit_to_buffer_size(&mut snapshot, buffer_size);
34            log.lifecycle().reopen(&mut snapshot)?;
35            if let Some(acquisition) = snapshot.acquisition {
36                log.acquisition.restore(acquisition);
37            }
38            log.log_device_object_property = snapshot.log_device_object_property;
39            store.reopened(log.log_state(), resized);
40        }
41        log.store = Some(store);
42        Ok(log)
43    }
44
45    /// Whether the log came back from a snapshot its storage held, rather
46    /// than starting empty. Always false without storage.
47    pub fn reopened(&self) -> bool {
48        self.store.as_ref().is_some_and(LogStore::is_reopened)
49    }
50
51    /// When the log commits to its storage; `None` without storage.
52    pub fn commit_cadence(&self) -> Option<LogCommitCadence> {
53        self.store.as_ref().map(LogStore::cadence)
54    }
55
56    /// Set when the log commits to its storage. Does nothing without
57    /// storage.
58    pub fn set_commit_cadence(&mut self, cadence: LogCommitCadence) {
59        if let Some(store) = &mut self.store {
60            store.set_cadence(cadence);
61        }
62    }
63
64    /// The generation of the last snapshot storage took, as
65    /// [`TrendLogObject::storage_generation`](crate::trend::TrendLogObject::storage_generation)
66    /// gives it.
67    pub fn storage_generation(&self) -> Option<u64> {
68        self.store.as_ref().map(LogStore::generation)
69    }
70}
71
72impl DurableLog for TrendLogMultipleObject {
73    type Record = BACnetLogMultipleRecord;
74
75    fn log_state(&self) -> LogState {
76        LogState::of(
77            &self.log_buffer,
78            self.log_enable,
79            self.stop_when_full,
80            &self.window,
81            &self.reporting,
82        )
83        .trend(
84            self.acquisition.settings(),
85            self.log_device_object_property.clone(),
86        )
87    }
88
89    fn durable_parts(
90        &mut self,
91    ) -> (
92        Option<&mut LogStore<BACnetLogMultipleRecord>>,
93        &LogRecordBuffer<BACnetLogMultipleRecord>,
94    ) {
95        (self.store.as_mut(), &self.log_buffer)
96    }
97
98    fn record_owed_interruption(&mut self, fallback: Option<(Date, Time)>) -> bool {
99        self.lifecycle().record_interruption_now(fallback)
100    }
101}
102
103impl_durable_log!(TrendLogMultipleObject);