129 lines
3.6 KiB
Rust
129 lines
3.6 KiB
Rust
use crate::app_config::AppConfig;
|
|
use crate::energy::consumption::EnergyConsumption;
|
|
use crate::utils::time_utils::day_number;
|
|
|
|
const TIME_INTERVAL: usize = 10;
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub enum ConsumptionHistoryError {
|
|
#[error("Given time is out of file bounds!")]
|
|
TimeOutOfFileBound,
|
|
}
|
|
|
|
/// # ConsumptionHistoryFile
|
|
///
|
|
/// Stores the history of house consumption
|
|
pub struct ConsumptionHistoryFile {
|
|
day: u64,
|
|
buff: Vec<EnergyConsumption>,
|
|
}
|
|
|
|
impl ConsumptionHistoryFile {
|
|
/// Open consumption history file, if it exists, or create an empty one
|
|
pub fn open(time: u64) -> anyhow::Result<Self> {
|
|
let day = day_number(time);
|
|
let path = AppConfig::get().energy_consumption_history_day(day);
|
|
|
|
if path.exists() {
|
|
Ok(Self {
|
|
day,
|
|
buff: bincode::decode_from_slice(
|
|
&std::fs::read(path)?,
|
|
bincode::config::standard(),
|
|
)?
|
|
.0,
|
|
})
|
|
} else {
|
|
log::debug!(
|
|
"Energy consumption stats for day {day} does not exists yet, creating memory buffer"
|
|
);
|
|
Ok(Self::new_memory(day))
|
|
}
|
|
}
|
|
|
|
/// Create a new in memory consumption history
|
|
fn new_memory(day: u64) -> Self {
|
|
Self {
|
|
day,
|
|
buff: vec![0; 3600 * 24 / TIME_INTERVAL],
|
|
}
|
|
}
|
|
|
|
/// Resolve time offset of a given time in buffer
|
|
fn resolve_offset(&self, time: u64) -> anyhow::Result<usize> {
|
|
let start_of_day = self.day * 3600 * 24;
|
|
|
|
if time < start_of_day || time >= start_of_day + 3600 * 24 {
|
|
return Err(ConsumptionHistoryError::TimeOutOfFileBound.into());
|
|
}
|
|
|
|
let relative_time = (time - start_of_day) / TIME_INTERVAL as u64;
|
|
|
|
Ok(relative_time as usize)
|
|
}
|
|
|
|
/// Check if a time is contained in this history
|
|
pub fn contains_time(&self, time: u64) -> bool {
|
|
self.resolve_offset(time).is_ok()
|
|
}
|
|
|
|
/// Set new state of relay
|
|
pub fn set_consumption(
|
|
&mut self,
|
|
time: u64,
|
|
consumption: EnergyConsumption,
|
|
) -> anyhow::Result<()> {
|
|
let idx = self.resolve_offset(time)?;
|
|
self.buff[idx] = consumption;
|
|
Ok(())
|
|
}
|
|
|
|
/// Get the consumption recorded at a given time
|
|
pub fn get_consumption(&self, time: u64) -> anyhow::Result<EnergyConsumption> {
|
|
let idx = self.resolve_offset(time)?;
|
|
|
|
Ok(self.buff[idx])
|
|
}
|
|
|
|
/// Persist device relay state history
|
|
pub fn save(&self) -> anyhow::Result<()> {
|
|
let path = AppConfig::get().energy_consumption_history_day(self.day);
|
|
std::fs::write(
|
|
path,
|
|
bincode::encode_to_vec(&self.buff, bincode::config::standard())?,
|
|
)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::energy::consumption::EnergyConsumption;
|
|
use crate::energy::consumption_history_file::{ConsumptionHistoryFile, TIME_INTERVAL};
|
|
|
|
#[test]
|
|
fn test_consumption_history() {
|
|
let mut history = ConsumptionHistoryFile::new_memory(0);
|
|
|
|
for i in 0..50 {
|
|
assert_eq!(
|
|
history.get_consumption(i * TIME_INTERVAL as u64).unwrap(),
|
|
0
|
|
);
|
|
}
|
|
|
|
for i in 0..50 {
|
|
history
|
|
.set_consumption(i * TIME_INTERVAL as u64, i as EnergyConsumption * 2)
|
|
.unwrap();
|
|
}
|
|
|
|
for i in 0..50 {
|
|
assert_eq!(
|
|
history.get_consumption(i * TIME_INTERVAL as u64).unwrap(),
|
|
i as EnergyConsumption * 2
|
|
);
|
|
}
|
|
}
|
|
}
|