refactor, prs10 stats, etc

This commit is contained in:
2026-02-02 00:41:19 -08:00
parent 7f24bf5a91
commit 7717aa9177
12 changed files with 790 additions and 652 deletions

154
src/sources/hwmon.rs Normal file
View File

@@ -0,0 +1,154 @@
use crate::{
ChimemonSource, ChimemonSourceChannel, Config, SourceMetric, SourceReport, SourceReportDetails,
SourceStatus,
};
use async_trait::async_trait;
use std::{fs::File, io::Read, path::PathBuf, sync::Arc, time::Duration};
use tracing::{debug, error, info, warn};
pub struct HwmonSource {
config: Config,
sensors: Vec<Arc<HwmonSensor>>,
}
#[derive(Debug, Clone)]
struct HwmonSensor {
name: String,
value_path: PathBuf,
device: String,
sensor: String,
label: Option<String>,
tags: Arc<Vec<(String, String)>>,
}
impl HwmonSensor {
fn new(name: &str, device: &str, sensor: &str) -> Self {
let value_path = PathBuf::from(HWMON_ROOT)
.join(device)
.join(sensor.to_owned() + "_input");
let label_path_raw = PathBuf::from(HWMON_ROOT)
.join(device)
.join(sensor.to_owned() + "_label");
let label = if label_path_raw.is_file() {
let mut f =
File::open(&label_path_raw).expect(&format!("Unable to open `{label_path_raw:?}`"));
let mut label = String::new();
f.read_to_string(&mut label)
.expect(&format!("Unable to read from `{label_path_raw:?}"));
Some(label.trim().to_owned())
} else {
None
};
let mut tags_vec = vec![
("name".to_owned(), name.to_owned()),
("device".to_owned(), device.to_owned()),
("sensor".to_owned(), sensor.to_owned()),
];
if let Some(label) = &label {
tags_vec.push(("label".to_owned(), label.clone()))
}
Self {
value_path,
label,
device: device.to_owned(),
sensor: sensor.to_owned(),
name: name.to_owned(),
tags: Arc::new(tags_vec),
}
}
}
#[derive(Debug)]
struct HwmonReport {
values: Vec<(Arc<HwmonSensor>, f64)>,
}
impl SourceReportDetails for HwmonReport {
fn is_healthy(&self) -> bool {
//self.alarms.iter().any(|(_sensor, alarm)| *alarm)
true
}
fn to_metrics(&self) -> Vec<SourceMetric> {
let mut metrics = Vec::new();
for (sensor, value) in &self.values {
metrics.push(SourceMetric::new_float(
"hwmon_value",
*value,
sensor.tags.clone(),
))
}
// for (sensor, alarm) in &self.alarms {
// metrics.push(SourceMetric::new_bool(
// "hwmon_alarm",
// *alarm,
// sensor.tags.clone(),
// ))
// }
metrics
}
}
const HWMON_ROOT: &str = "/sys/class/hwmon";
impl HwmonSource {
pub fn new(config: Config) -> Self {
let sensors = config
.sources
.hwmon
.sensors
.iter()
.map(|(k, v)| Arc::new(HwmonSensor::new(k, &v.name, &v.sensor)))
.collect();
HwmonSource { config, sensors }
}
async fn get_raw_value(sensor: &HwmonSensor) -> Result<String, std::io::Error> {
tokio::fs::read_to_string(&sensor.value_path).await
}
}
#[async_trait]
impl ChimemonSource for HwmonSource {
async fn run(self, chan: ChimemonSourceChannel) {
info!("hwmon task started");
let mut interval =
tokio::time::interval(Duration::from_secs(self.config.sources.hwmon.interval));
loop {
interval.tick().await;
let mut values = Vec::new();
for s in &self.sensors {
if let Ok(sensor_val) = HwmonSource::get_raw_value(s).await {
debug!(
"hwmon {} raw value {}",
s.value_path.to_string_lossy(),
sensor_val
);
if let Ok(parsed) = sensor_val.trim().parse::<f64>() {
values.push((s.clone(), parsed));
} else {
error!(
"Unable to parse sensor value {sensor_val} at {}",
s.value_path.to_string_lossy()
);
}
} else {
error!("Unable to get hwmon sensor value");
}
}
let report = SourceReport {
name: "hwmon".to_owned(),
status: SourceStatus::Healthy,
details: Arc::new(HwmonReport { values }),
};
info!("Writing hwmon data");
match chan.send(report.into()) {
Ok(_) => {}
Err(e) => {
warn!("Unable to send to message channel ({e})")
}
}
}
}
}