use std::collections::HashMap;
use super::config::AlumetConfig;
use super::state::{AlumetState, DbEnergyState, 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 {
return None;
}
let kwh = compute_window_kwh(
joules_per_interval,
energy_interval_secs,
scrape_interval_secs,
)?;
let per_op = kwh / ops as f64;
per_op.is_finite().then_some(per_op)
}
#[must_use]
pub fn compute_window_kwh(
joules_per_interval: f64,
energy_interval_secs: f64,
scrape_interval_secs: f64,
) -> Option<f64> {
if !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 kwh = watts * scrape_interval_secs / 3_600_000.0;
kwh.is_finite().then_some(kwh)
}
#[allow(clippy::implicit_hasher)]
pub fn apply_scrape(
state: &AlumetState,
db_state: Option<&DbEnergyState>,
broker_state: Option<&DbEnergyState>,
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();
if let (Some(db_cfg), Some(db)) = (cfg.database.as_ref(), db_state)
&& let Some(&joules) = by_label.get(db_cfg.label_value.as_str())
&& let Some(kwh) =
compute_window_kwh(joules, cfg.energy_interval_secs, scrape_interval_secs)
{
db.add_window_kwh(kwh, now_ms);
}
if let (Some(broker_cfg), Some(broker)) = (cfg.broker.as_ref(), broker_state)
&& let Some(&joules) = by_label.get(broker_cfg.label_value.as_str())
&& let Some(kwh) =
compute_window_kwh(joules, cfg.energy_interval_secs, scrape_interval_secs)
{
broker.add_window_kwh(kwh, now_ms);
}
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
}