1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
//! Stores the most recent [`Update`] for any given source/destination pair.
use crate::hardware_data_manager::{HardwareDataManager, Id, Update};
use std::{
collections::{HashMap, VecDeque},
sync::{Arc, Mutex},
};
const BUFFER_SIZE: usize = 5;
/// The `UpdateAccumulator` consumes updates from a [`HardwareDataManager`], and
/// accumulates them. It can be queried for the most recent updates using [`get_status`](UpdateAccumulator::get_status).
// The <Hdm> means that we are allowed to use `Hdm` as a type within `UpdateAccumulator`.
#[derive(Debug)]
pub struct UpdateAccumulator<Hdm>
where
// Then this binding ensures that `Hdm` implements `HardwareDataManager`.
Hdm: HardwareDataManager,
{
/// A handle to communicate with the [`Hdm`]
// `Rc` means this is a "reference-counted" smart pointer, and `RefCell` means we
// are going to enforce the borrow checking rules at runtime instead of
// compile time. This way we can keep references to the HDM in several scopes.
hdm_handle: Arc<Mutex<Hdm>>,
/// A HashMap mapping `(Id, Id)` pairs to `Update`s.
accumulated_updates: HashMap<(Id, Id), VecDeque<Update>>,
}
// We see `Hdm` in three places here. First, it is declared as a type for use
// in the impl block. Second, we say that the impl block applies to `UpdateAccumulator`s
// that use the `Hdm` type. Finally, we say that `Hdm` must implement the
// `HardwareDataManager` trait.
impl<Hdm> UpdateAccumulator<Hdm>
where
Hdm: HardwareDataManager,
{
/// Instantiates a new [`UpdateAccumulator`] attached to a [`Hdm`]
pub fn new(hdm_handle: Arc<Mutex<Hdm>>) -> Self {
Self {
hdm_handle,
accumulated_updates: HashMap::new(),
}
}
/// Returns a vec contatining the most recent [`Update`]s for all pairs
/// of blocks. Essentially, the most updated data available.
pub fn get_status(&mut self) -> Vec<Update> {
for update in self.hdm_handle.lock().unwrap().by_ref() {
self.accumulated_updates
.entry((update.src, update.dst))
.and_modify(|v| v.push_back(update.clone()))
.or_insert_with(|| VecDeque::from(vec![update.clone()]));
}
// Return a copy of the most recent updates, in a Vec rather than a HashMap
let res = self
.accumulated_updates
.values()
.map(|v| {
let taken = v.iter().rev().take(BUFFER_SIZE);
let len = taken.len() as f64;
let sum = taken
.cloned()
.reduce(|l, r| Update {
elv: l.elv + r.elv,
azm: l.azm + r.azm,
..l
})
.expect("There should be some elements here");
Update {
elv: sum.elv / len,
azm: sum.azm / len,
..sum
}
})
.collect();
for v in self.accumulated_updates.values_mut() {
if v.len() > BUFFER_SIZE {
v.pop_front();
}
}
res
}
}