SolarEnergy/central_backend/src/energy/consumption_history_file.rs

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
);
}
}
}