Record relays state
This commit is contained in:
parent
368eb13089
commit
565db05fb0
@ -1,4 +1,4 @@
|
||||
use crate::devices::device::DeviceId;
|
||||
use crate::devices::device::{DeviceId, DeviceRelayID};
|
||||
use clap::{Parser, Subcommand};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@ -81,7 +81,7 @@ pub struct AppConfig {
|
||||
pub production_margin: i32,
|
||||
|
||||
/// Energy refresh operations interval, in seconds
|
||||
#[arg(short('i'), long, env, default_value_t = 30)]
|
||||
#[arg(short('i'), long, env, default_value_t = 20)]
|
||||
pub refresh_interval: u64,
|
||||
|
||||
/// Consumption backend provider
|
||||
@ -235,6 +235,18 @@ impl AppConfig {
|
||||
pub fn device_csr_path(&self, id: &DeviceId) -> PathBuf {
|
||||
self.devices_config_path().join(format!("{}.csr", id.0))
|
||||
}
|
||||
|
||||
/// Get relays runtime storage path
|
||||
pub fn relays_runtime_stats_storage_path(&self) -> PathBuf {
|
||||
self.storage_path().join("relays_runtime")
|
||||
}
|
||||
|
||||
/// Get relay runtime stats path for a given day
|
||||
pub fn relay_runtime_file_path(&self, relay_id: DeviceRelayID, day: u64) -> PathBuf {
|
||||
self.relays_runtime_stats_storage_path()
|
||||
.join(relay_id.0.to_string())
|
||||
.join(day.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -77,7 +77,7 @@ pub struct DailyMinRuntime {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
|
||||
pub struct DeviceRelayID(uuid::Uuid);
|
||||
pub struct DeviceRelayID(pub uuid::Uuid);
|
||||
|
||||
impl Default for DeviceRelayID {
|
||||
fn default() -> Self {
|
||||
|
@ -38,8 +38,11 @@ impl EnergyActor {
|
||||
constants::FALLBACK_PRODUCTION_VALUE
|
||||
});
|
||||
|
||||
self.engine
|
||||
.refresh(self.curr_consumption, &self.devices.full_list());
|
||||
let devices_list = self.devices.full_list();
|
||||
|
||||
self.engine.refresh(self.curr_consumption, &devices_list);
|
||||
|
||||
self.engine.persist_relays_state(&devices_list)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use prettytable::{row, Table};
|
||||
use crate::constants;
|
||||
use crate::devices::device::{Device, DeviceId, DeviceRelay, DeviceRelayID};
|
||||
use crate::energy::consumption::EnergyConsumption;
|
||||
use crate::energy::relay_state_history::RelayStateHistory;
|
||||
use crate::utils::time_utils::time_secs;
|
||||
|
||||
#[derive(Default)]
|
||||
@ -320,4 +321,18 @@ impl EnergyEngine {
|
||||
|
||||
self.print_summary(curr_consumption, devices);
|
||||
}
|
||||
|
||||
/// Save relays state to disk
|
||||
pub fn persist_relays_state(&mut self, devices: &[Device]) -> anyhow::Result<()> {
|
||||
// Save all relays state
|
||||
for d in devices {
|
||||
for r in &d.relays {
|
||||
let mut file = RelayStateHistory::open(r.id, time_secs())?;
|
||||
file.set_state(time_secs(), self.relay_state(r.id).is_on())?;
|
||||
file.save()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,15 @@
|
||||
use crate::app_config::AppConfig;
|
||||
use crate::devices::device::DeviceRelayID;
|
||||
use crate::utils::files_utils;
|
||||
use crate::utils::time_utils::day_number;
|
||||
|
||||
const TIME_INTERVAL: usize = 30;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum RelayStateHistoryError {
|
||||
#[error("Given time is out of file bounds!")]
|
||||
TimeOutOfFileBound,
|
||||
}
|
||||
|
||||
/// # RelayStateHistory
|
||||
///
|
||||
@ -7,38 +18,124 @@ use crate::devices::device::DeviceRelayID;
|
||||
/// These file are binary file optimizing used space.
|
||||
pub struct RelayStateHistory {
|
||||
id: DeviceRelayID,
|
||||
day: usize,
|
||||
day: u64,
|
||||
buff: Vec<u8>,
|
||||
}
|
||||
|
||||
impl RelayStateHistory {
|
||||
/// Open relay state history file, if it exist, or create an empty one
|
||||
pub fn open(id: DeviceRelayID, day: usize) -> anyhow::Result<Self> {
|
||||
todo!()
|
||||
/// Open relay state history file, if it exists, or create an empty one
|
||||
pub fn open(id: DeviceRelayID, time: u64) -> anyhow::Result<Self> {
|
||||
let day = day_number(time);
|
||||
let path = AppConfig::get().relay_runtime_file_path(id, day);
|
||||
|
||||
if path.exists() {
|
||||
Ok(Self {
|
||||
id,
|
||||
day,
|
||||
buff: std::fs::read(path)?,
|
||||
})
|
||||
} else {
|
||||
log::debug!(
|
||||
"Stats for relay {id:?} for day {day} does not exists yet, creating memory buffer"
|
||||
);
|
||||
Ok(Self::new_memory(id, day))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new in memory dev relay state history
|
||||
fn new_memory(id: DeviceRelayID, day: usize) -> Self {
|
||||
todo!()
|
||||
fn new_memory(id: DeviceRelayID, day: u64) -> Self {
|
||||
Self {
|
||||
id,
|
||||
day,
|
||||
buff: vec![0; 3600 * 24 / TIME_INTERVAL],
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve time offset of a given time in buffer
|
||||
fn resolve_offset(&self, time: i64) -> anyhow::Result<(usize, u8)> {
|
||||
todo!()
|
||||
fn resolve_offset(&self, time: u64) -> anyhow::Result<(usize, u8)> {
|
||||
let start_of_day = self.day * 3600 * 24;
|
||||
|
||||
if time < start_of_day || time >= start_of_day + 3600 * 24 {
|
||||
return Err(RelayStateHistoryError::TimeOutOfFileBound.into());
|
||||
}
|
||||
|
||||
let relative_time = (time - start_of_day) / TIME_INTERVAL as u64;
|
||||
|
||||
Ok(((relative_time / 8) as usize, (relative_time % 8) as u8))
|
||||
}
|
||||
|
||||
/// Set new state of relay
|
||||
fn set_state(&mut self, time: i64, on: bool) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
pub fn set_state(&mut self, time: u64, on: bool) -> anyhow::Result<()> {
|
||||
let (idx, offset) = self.resolve_offset(time)?;
|
||||
|
||||
self.buff[idx] = if on {
|
||||
self.buff[idx] | (0x1 << offset)
|
||||
} else {
|
||||
self.buff[idx] & !(0x1 << offset)
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the state of relay at a given time
|
||||
fn get_state(&self, time: i64) -> anyhow::Result<bool> {
|
||||
todo!()
|
||||
pub fn get_state(&self, time: u64) -> anyhow::Result<bool> {
|
||||
let (idx, offset) = self.resolve_offset(time)?;
|
||||
|
||||
Ok(self.buff[idx] & (0x1 << offset) != 0)
|
||||
}
|
||||
|
||||
/// Persist device relay state history
|
||||
pub fn save(&self) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
let path = AppConfig::get().relay_runtime_file_path(self.id, self.day);
|
||||
files_utils::create_directory_if_missing(path.parent().unwrap())?;
|
||||
std::fs::write(path, &self.buff)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::devices::device::DeviceRelayID;
|
||||
use crate::energy::relay_state_history::RelayStateHistory;
|
||||
|
||||
#[test]
|
||||
fn test_relay_state_history() {
|
||||
let mut history = RelayStateHistory::new_memory(DeviceRelayID::default(), 0);
|
||||
|
||||
let val_1 = 5 * 30;
|
||||
let val_2 = 7 * 30;
|
||||
|
||||
for i in 0..500 {
|
||||
assert!(!history.get_state(i).unwrap())
|
||||
}
|
||||
|
||||
history.set_state(val_1, true).unwrap();
|
||||
|
||||
for i in 0..500 {
|
||||
assert_eq!(history.get_state(i).unwrap(), (i / 30) * 30 == val_1);
|
||||
}
|
||||
|
||||
history.set_state(val_2, true).unwrap();
|
||||
|
||||
for i in 0..500 {
|
||||
assert_eq!(
|
||||
history.get_state(i).unwrap(),
|
||||
(i / 30) * 30 == val_1 || (i / 30) * 30 == val_2
|
||||
);
|
||||
}
|
||||
|
||||
history.set_state(val_2, false).unwrap();
|
||||
|
||||
for i in 0..500 {
|
||||
assert_eq!(history.get_state(i).unwrap(), (i / 30) * 30 == val_1);
|
||||
}
|
||||
|
||||
history.set_state(val_1, false).unwrap();
|
||||
|
||||
for i in 0..500 {
|
||||
assert!(!history.get_state(i).unwrap())
|
||||
}
|
||||
|
||||
assert!(history.get_state(8989898).is_err());
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ async fn main() -> std::io::Result<()> {
|
||||
// Initialize storage
|
||||
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().relays_runtime_stats_storage_path()).unwrap();
|
||||
|
||||
// Initialize PKI
|
||||
pki::initialize_root_ca().expect("Failed to initialize Root CA!");
|
||||
|
@ -15,3 +15,21 @@ pub fn time_millis() -> u128 {
|
||||
.unwrap()
|
||||
.as_millis()
|
||||
}
|
||||
|
||||
/// Get the number of the day since 01-01-1970 of a given UNIX timestamp
|
||||
pub fn day_number(time: u64) -> u64 {
|
||||
time / (3600 * 24)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::utils::time_utils::day_number;
|
||||
|
||||
#[test]
|
||||
fn test_time_of_day() {
|
||||
assert_eq!(day_number(500), 0);
|
||||
assert_eq!(day_number(1726592301), 19983);
|
||||
assert_eq!(day_number(1726592401), 19983);
|
||||
assert_eq!(day_number(1726498701), 19982);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user