use std::collections::HashMap;
use super::config::AlumetConfig;
use super::state::{AlumetState, ServiceEnergy};
use crate::score::energy_state::upsert_row;
use crate::score::prom_parser::{PromSample, sum_by_label};
#[must_use]
pub fn compute_energy_per_op_kwh(
joules_per_interval: f64,
energy_interval_secs: f64,
scrape_interval_secs: f64,
ops: u64,
) -> Option<f64> {
if ops == 0 || !joules_per_interval.is_finite() || joules_per_interval <= 0.0 {
return None;
}
if !energy_interval_secs.is_finite()
|| energy_interval_secs <= 0.0
|| !scrape_interval_secs.is_finite()
|| scrape_interval_secs <= 0.0
{
return None;
}
let watts = joules_per_interval / energy_interval_secs;
let window_joules = watts * scrape_interval_secs;
let kwh = window_joules / 3_600_000.0;
let per_op = kwh / ops as f64;
per_op.is_finite().then_some(per_op)
}
#[allow(clippy::implicit_hasher)]
pub fn apply_scrape(
state: &AlumetState,
samples: &[PromSample],
op_deltas: &HashMap<String, u64>,
cfg: &AlumetConfig,
now_ms: u64,
) -> usize {
let by_label = sum_by_label(samples);
let scrape_interval_secs = cfg.scrape_interval.as_secs_f64();
let mut next = state.current_owned();
let mut any_change = false;
let mut matched = 0usize;
for (service, label_value) in &cfg.service_mappings {
let Some(&joules) = by_label.get(label_value.as_str()) else {
continue;
};
matched += 1;
let Some(ops) = op_deltas.get(service).copied() else {
continue;
};
let Some(energy_per_op) =
compute_energy_per_op_kwh(joules, cfg.energy_interval_secs, scrape_interval_secs, ops)
else {
continue;
};
let row = ServiceEnergy {
energy_per_op_kwh: energy_per_op,
last_update_ms: now_ms,
};
upsert_row(&mut next, service, row);
any_change = true;
}
if any_change {
state.publish(next);
}
matched
}