Record received consumption from inverter
This commit is contained in:
parent
0e0da14fde
commit
228e1a7293
26
central_backend/Cargo.lock
generated
26
central_backend/Cargo.lock
generated
@ -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"
|
||||||
|
@ -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"
|
@ -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)]
|
||||||
|
@ -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,
|
||||||
|
128
central_backend/src/energy/consumption_history_file.rs
Normal file
128
central_backend/src/energy/consumption_history_file.rs
Normal 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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(
|
.await
|
||||||
consumption::get_curr_consumption()
|
.unwrap_or_else(|e| {
|
||||||
.await
|
log::error!(
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
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(());
|
||||||
|
@ -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;
|
||||||
|
@ -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],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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!");
|
||||||
|
Loading…
Reference in New Issue
Block a user