rstango 0.4.0

Implementation of the Tango distributed control system
Documentation
//! Implementation of the histories of attributes and commands.

use std::collections::VecDeque;
use std::time::{Duration, Instant, SystemTime};

use crate::dev::{Recorded, RecordedResult};
use crate::error::Result;

use super::DEFAULT_HISTORY_DEPTH;


/// The results recorded of an attribute or a command, a ring of the latest ones, which is
/// what a request from the cache is answered with, see [`crate::dev::DevSource`], and what
/// a client asking for the history is given. It is filled by the polling of the device, or
/// by the device itself, polled or not, see [`super::Attr::push_history`] and
/// [`super::Command::push_history`].
pub(super) struct History<T> {
    /// The results, oldest first.
    entries: VecDeque<RecordedResult<T>>,
    /// How many results are kept at most, the oldest being dropped beyond that.
    depth: usize,
    /// Until when the latest result answers a request from the cache, always when absent.
    expire: Option<Instant>,
}

impl<T: Clone> History<T> {

    /// An empty history, of the default depth.
    pub fn new() -> Self {
        Self {
            entries: VecDeque::new(),
            depth: DEFAULT_HISTORY_DEPTH,
            expire: None,
        }
    }

    /// Record that result, recorded at that time, the oldest one being dropped when the
    /// history is full. The latest result answers a request from the cache for that long
    /// when given a validity, and until the next is recorded without one.
    pub fn push(&mut self, time: SystemTime, result: Result<T>, valid_for: Option<Duration>) {
        self.entries.push_back(Recorded::new(time, result));
        while self.entries.len() > self.depth {
            self.entries.pop_front();
        }
        self.expire = valid_for.map(|valid_for| Instant::now() + valid_for);
    }

    /// Whether nothing is recorded.
    pub fn is_empty(&self) -> bool {
        self.entries.is_empty()
    }

    /// The latest result, if there is one and it has not expired yet: what a request from
    /// the cache is answered with.
    pub fn latest(&self) -> Option<&Result<T>> {
        let entry = self.entries.back()?;
        match self.expire {
            Some(expire) if expire <= Instant::now() => None,
            _ => Some(&entry.value),
        }
    }

    /// The latest results, oldest first and at most `max` of them.
    pub fn entries(&self, max: usize) -> Vec<RecordedResult<T>> {
        let skip = self.entries.len().saturating_sub(max);
        self.entries.iter().skip(skip).cloned().collect()
    }

    /// Forget every result recorded.
    pub fn clear(&mut self) {
        self.entries.clear();
        self.expire = None;
    }

    /// How many results are kept at most.
    pub fn depth(&self) -> usize {
        self.depth
    }

    /// Set how many results are kept at most, the oldest ones beyond that being dropped
    /// now. A depth of zero records nothing.
    pub fn set_depth(&mut self, depth: usize) {
        self.depth = depth;
        while self.entries.len() > depth {
            self.entries.pop_front();
        }
    }

}