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