Record received consumption from inverter

This commit is contained in:
Pierre HUBERT 2024-09-23 21:47:25 +02:00
parent 0e0da14fde
commit 228e1a7293
9 changed files with 183 additions and 12 deletions

View File

@ -523,6 +523,25 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bincode"
version = "2.0.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95"
dependencies = [
"bincode_derive",
"serde",
]
[[package]]
name = "bincode_derive"
version = "2.0.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c"
dependencies = [
"virtue",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.6.0" version = "2.6.0"
@ -608,6 +627,7 @@ dependencies = [
"actix-web", "actix-web",
"anyhow", "anyhow",
"asn1", "asn1",
"bincode",
"chrono", "chrono",
"clap", "clap",
"env_logger", "env_logger",
@ -2697,6 +2717,12 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "virtue"
version = "0.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.5.0" version = "2.5.0"

View File

@ -38,3 +38,4 @@ jsonwebtoken = { version = "9.3.0", features = ["use_pem"] }
prettytable-rs = "0.10.0" prettytable-rs = "0.10.0"
chrono = "0.4.38" chrono = "0.4.38"
serde_yml = "0.0.12" serde_yml = "0.0.12"
bincode = "=2.0.0-rc.3"

View File

@ -255,6 +255,18 @@ impl AppConfig {
pub fn relay_runtime_day_file_path(&self, relay_id: DeviceRelayID, day: u64) -> PathBuf { pub fn relay_runtime_day_file_path(&self, relay_id: DeviceRelayID, day: u64) -> PathBuf {
self.relay_runtime_stats_dir(relay_id).join(day.to_string()) self.relay_runtime_stats_dir(relay_id).join(day.to_string())
} }
/// Get energy consumption history path
pub fn energy_consumption_history(&self) -> PathBuf {
self.storage_path().join("consumption_history")
}
/// Get energy consumption history file path for a given day
pub fn energy_consumption_history_day(&self, number: u64) -> PathBuf {
self.storage_path()
.join("consumption_history")
.join(number.to_string())
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,6 +1,5 @@
use crate::constants; use crate::constants;
use crate::energy::consumption::EnergyConsumption; use crate::energy::consumption::EnergyConsumption;
use log::log;
pub struct ConsumptionCache { pub struct ConsumptionCache {
nb_vals: usize, nb_vals: usize,

View File

@ -0,0 +1,128 @@
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
);
}
}
}

View File

@ -7,6 +7,7 @@ use crate::devices::devices_list::DevicesList;
use crate::energy::consumption; use crate::energy::consumption;
use crate::energy::consumption::EnergyConsumption; use crate::energy::consumption::EnergyConsumption;
use crate::energy::consumption_cache::ConsumptionCache; use crate::energy::consumption_cache::ConsumptionCache;
use crate::energy::consumption_history_file::ConsumptionHistoryFile;
use crate::energy::engine::EnergyEngine; use crate::energy::engine::EnergyEngine;
use crate::utils::time_utils::time_secs; use crate::utils::time_utils::time_secs;
use actix::prelude::*; use actix::prelude::*;
@ -42,17 +43,19 @@ impl EnergyActor {
async fn refresh(&mut self) -> anyhow::Result<()> { async fn refresh(&mut self) -> anyhow::Result<()> {
// Refresh energy // Refresh energy
self.consumption_cache let latest_consumption = consumption::get_curr_consumption()
.add_value(
consumption::get_curr_consumption()
.await .await
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
log::error!( log::error!(
"Failed to fetch latest consumption value, will use fallback value! {e}" "Failed to fetch latest consumption value, will use fallback value! {e}"
); );
constants::FALLBACK_PRODUCTION_VALUE constants::FALLBACK_PRODUCTION_VALUE
}), });
); self.consumption_cache.add_value(latest_consumption);
let mut history = ConsumptionHistoryFile::open(time_secs())?;
history.set_consumption(time_secs(), latest_consumption)?;
history.save()?;
if self.last_engine_refresh + AppConfig::get().refresh_interval > time_secs() { if self.last_engine_refresh + AppConfig::get().refresh_interval > time_secs() {
return Ok(()); return Ok(());

View File

@ -1,5 +1,6 @@
pub mod consumption; pub mod consumption;
pub mod consumption_cache; pub mod consumption_cache;
pub mod consumption_history_file;
pub mod energy_actor; pub mod energy_actor;
pub mod engine; pub mod engine;
pub mod relay_state_history; pub mod relay_state_history;

View File

@ -47,7 +47,7 @@ impl RelayStateHistory {
Self { Self {
id, id,
day, day,
buff: vec![0; 3600 * 24 / TIME_INTERVAL], buff: vec![0; (3600 * 24 / (TIME_INTERVAL * 8)) + 1],
} }
} }

View File

@ -18,6 +18,7 @@ async fn main() -> std::io::Result<()> {
create_directory_if_missing(AppConfig::get().pki_path()).unwrap(); create_directory_if_missing(AppConfig::get().pki_path()).unwrap();
create_directory_if_missing(AppConfig::get().devices_config_path()).unwrap(); create_directory_if_missing(AppConfig::get().devices_config_path()).unwrap();
create_directory_if_missing(AppConfig::get().relays_runtime_stats_storage_path()).unwrap(); create_directory_if_missing(AppConfig::get().relays_runtime_stats_storage_path()).unwrap();
create_directory_if_missing(AppConfig::get().energy_consumption_history()).unwrap();
// Initialize PKI // Initialize PKI
pki::initialize_root_ca().expect("Failed to initialize Root CA!"); pki::initialize_root_ca().expect("Failed to initialize Root CA!");