Record received consumption from inverter
This commit is contained in:
		
							
								
								
									
										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(
 | 
					 | 
				
			||||||
                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(());
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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!");
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user