use std::{ops::Index, path::PathBuf, thread, time::Duration};
use file_backed_value::FileBackedValue;
use gethostname::gethostname;
use indexmap::IndexMap;
use crate::EnergyAccumulator;
pub struct IdlePower(IndexMap<String, f32>);
impl IdlePower {
pub fn init(path: Option<&PathBuf>, idle_seconds: usize, probes: &mut Box<dyn EnergyAccumulator>) -> Self {
let filename = gethostname()
.to_ascii_lowercase()
.to_str()
.map_or(format!("idle-{idle_seconds}s.json"), |user| {
format!("idle-{idle_seconds}s-{user}.json")
});
let mut file = if let Some(path) = path {
FileBackedValue::new_at(&filename, path)
} else {
FileBackedValue::new(&filename)
};
log::info!("Idle file (will be) stored at {:?}", file.path());
file.set_dirty_time(Duration::from_hours(24 * 6));
let idle = match file.get_or_insert_with(|| measure_idle(probes, idle_seconds)) {
Ok(idle) => idle.clone(),
Err(e) => {
log::error!("Error reading idle file: {:?}", e);
measure_idle(probes, idle_seconds)
},
};
Self(idle)
}
}
impl Index<&str> for IdlePower {
type Output = f32;
fn index(&self, key: &str) -> &Self::Output {
&self.0[key]
}
}
fn measure_idle(probes: &mut Box<dyn EnergyAccumulator>, seconds: usize) -> IndexMap<String, f32> {
if seconds < 10 {
log::warn!("Forcing mimimum idle time from {}s to 10s", seconds);
}
let seconds = seconds.max(10);
let before = seconds.min(30);
log::info!("Waiting for system to stabilise for {}s", before);
thread::sleep(Duration::from_secs(before as u64));
log::info!("Measuring idle for {}s", seconds);
let mut samples: IndexMap<String, Vec<f32>> = IndexMap::new();
const SECOND: Duration = Duration::from_secs(1);
for _ in 0..seconds {
probes.reset();
thread::sleep(SECOND);
for (k, v) in probes.elapsed() {
samples.entry(k).or_default().push(v);
}
}
let mut idle = IndexMap::new();
for (k, mut values) in samples {
values.sort_by(|a, b| a.total_cmp(b));
let mid = values.len() / 2;
let median = if values.len() % 2 == 0 {
(values[mid - 1] + values[mid]) / 2.0
} else {
values[mid]
};
idle.insert(k, median);
}
log::info!("Idle power draw: {:#?}", idle);
idle
}