refactor metric tags & sources config

This commit is contained in:
2026-02-04 02:04:51 -08:00
parent d464cf8ee6
commit 156df9ae86
8 changed files with 326 additions and 302 deletions

View File

@@ -2,10 +2,6 @@ use std::any::type_name;
use std::str::FromStr;
use std::sync::Arc;
use crate::{
ChimemonMessage, ChimemonSource, ChimemonSourceChannel, Prs10Config, SourceMetric,
SourceReport, SourceReportDetails, SourceStatus,
};
use async_trait::async_trait;
use bitflags::bitflags;
use itertools::Itertools;
@@ -13,10 +9,14 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, ReadHalf, WriteHalf};
use tokio::select;
use tokio::sync::OnceCell;
use tokio::time::{interval, timeout};
use tokio_serial;
use tokio_serial::{SerialPort, SerialStream};
use tracing::{debug, debug_span, error, info, instrument, warn};
use crate::{
ChimemonMessage, ChimemonSource, ChimemonSourceChannel, MetricTags, Prs10Config, SourceMetric,
SourceReport, SourceReportDetails, SourceStatus,
};
#[derive(Debug)]
pub struct Prs10Info {
pub model: String,
@@ -54,7 +54,7 @@ bitflags! {
}
impl Prs10PowerLampFlags {
pub fn get_metrics(&self, no_tags: Arc<Vec<(String, String)>>) -> Vec<SourceMetric> {
pub fn get_metrics(&self, tags: Arc<MetricTags>) -> Vec<SourceMetric> {
// Define the mapping statically
const FLAG_LABELS: [(&Prs10PowerLampFlags, &str); 8] = [
(&Prs10PowerLampFlags::ELEC_VOLTAGE_LOW, "elec_voltage_low"),
@@ -72,7 +72,7 @@ impl Prs10PowerLampFlags {
.iter()
.map(|(flag, label)| {
// We track whether each flag is set (true) or not (false)
SourceMetric::new_bool(*label, self.contains(**flag), no_tags.clone())
SourceMetric::new_bool(*label, self.contains(**flag), tags.clone())
})
.collect()
}
@@ -180,17 +180,17 @@ impl SourceReportDetails for Prs10Status {
}
fn to_metrics(&self) -> Vec<SourceMetric> {
let no_tags = Arc::new(vec![]);
let tags = Arc::new(vec![]);
vec![
SourceMetric::new_int(
"volt_lamp_flags",
self.volt_lamp_flags.bits() as i64,
no_tags.clone(),
tags.clone(),
),
SourceMetric::new_int("rf_flags", self.rf_flags.bits() as i64, no_tags.clone()),
SourceMetric::new_int("temp_flags", self.temp_flags.bits() as i64, no_tags.clone()),
SourceMetric::new_int("fll_flags", self.fll_flags.bits() as i64, no_tags.clone()),
SourceMetric::new_int("pps_flags", self.pps_flags.bits() as i64, no_tags.clone()),
SourceMetric::new_int("rf_flags", self.rf_flags.bits() as i64, tags.clone()),
SourceMetric::new_int("temp_flags", self.temp_flags.bits() as i64, tags.clone()),
SourceMetric::new_int("fll_flags", self.fll_flags.bits() as i64, tags.clone()),
SourceMetric::new_int("pps_flags", self.pps_flags.bits() as i64, tags.clone()),
// system flags are kind of useless because we can't guarantee they get upstreamed and will only appear once since they are 'event flags'
]
}
@@ -254,104 +254,69 @@ impl SourceReportDetails for Prs10Stats {
true
}
fn to_metrics(&self) -> Vec<SourceMetric> {
let no_tags = Arc::new(vec![]);
let tags = Arc::new(vec![]);
vec![
// Integer Metrics
SourceMetric::new_int("ocxo_efc", self.ocxo_efc as i64, no_tags.clone()),
SourceMetric::new_int("ocxo_efc", self.ocxo_efc as i64, tags.clone()),
// Float Metrics
SourceMetric::new_float(
"error_signal_volts",
self.error_signal_volts,
no_tags.clone(),
),
SourceMetric::new_float("error_signal_volts", self.error_signal_volts, tags.clone()),
SourceMetric::new_float(
"detect_signal_volts",
self.detect_signal_volts,
no_tags.clone(),
tags.clone(),
),
SourceMetric::new_float("heat_volts", self.heat_volts, no_tags.clone()),
SourceMetric::new_float("elec_volts", self.elec_volts, no_tags.clone()),
SourceMetric::new_float("heat_volts", self.heat_volts, tags.clone()),
SourceMetric::new_float("elec_volts", self.elec_volts, tags.clone()),
SourceMetric::new_float(
"lamp_fet_drain_volts",
self.lamp_fet_drain_volts,
no_tags.clone(),
tags.clone(),
),
SourceMetric::new_float(
"lamp_fet_gate_volts",
self.lamp_fet_gate_volts,
no_tags.clone(),
tags.clone(),
),
SourceMetric::new_float("ocxo_heat_volts", self.ocxo_heat_volts, no_tags.clone()),
SourceMetric::new_float("cell_heat_volts", self.cell_heat_volts, no_tags.clone()),
SourceMetric::new_float("lamp_heat_volts", self.lamp_heat_volts, no_tags.clone()),
SourceMetric::new_float("rb_photo", self.rb_photo, no_tags.clone()),
SourceMetric::new_float("rb_photo_iv", self.rb_photo_iv, no_tags.clone()),
SourceMetric::new_float("case_temp", self.case_temp, no_tags.clone()),
SourceMetric::new_float("ocxo_therm", self.ocxo_therm, no_tags.clone()),
SourceMetric::new_float("cell_therm", self.cell_therm, no_tags.clone()),
SourceMetric::new_float("lamp_therm", self.lamp_therm, no_tags.clone()),
SourceMetric::new_float("ext_cal_volts", self.ext_cal_volts, no_tags.clone()),
SourceMetric::new_float("analog_gnd_volts", self.analog_gnd_volts, no_tags.clone()),
SourceMetric::new_float("ocxo_heat_volts", self.ocxo_heat_volts, tags.clone()),
SourceMetric::new_float("cell_heat_volts", self.cell_heat_volts, tags.clone()),
SourceMetric::new_float("lamp_heat_volts", self.lamp_heat_volts, tags.clone()),
SourceMetric::new_float("rb_photo", self.rb_photo, tags.clone()),
SourceMetric::new_float("rb_photo_iv", self.rb_photo_iv, tags.clone()),
SourceMetric::new_float("case_temp", self.case_temp, tags.clone()),
SourceMetric::new_float("ocxo_therm", self.ocxo_therm, tags.clone()),
SourceMetric::new_float("cell_therm", self.cell_therm, tags.clone()),
SourceMetric::new_float("lamp_therm", self.lamp_therm, tags.clone()),
SourceMetric::new_float("ext_cal_volts", self.ext_cal_volts, tags.clone()),
SourceMetric::new_float("analog_gnd_volts", self.analog_gnd_volts, tags.clone()),
SourceMetric::new_float(
"if_vco_varactor_volts",
self.if_vco_varactor_volts,
no_tags.clone(),
tags.clone(),
),
SourceMetric::new_float(
"op_vco_varactor_volts",
self.op_vco_varactor_volts,
no_tags.clone(),
tags.clone(),
),
SourceMetric::new_float(
"mul_amp_gain_volts",
self.mul_amp_gain_volts,
no_tags.clone(),
),
SourceMetric::new_float("rf_lock_volts", self.rf_lock_volts, no_tags.clone()),
SourceMetric::new_float("mul_amp_gain_volts", self.mul_amp_gain_volts, tags.clone()),
SourceMetric::new_float("rf_lock_volts", self.rf_lock_volts, tags.clone()),
// U16 Metrics (optional, but can be treated as integers)
SourceMetric::new_int(
"freq_offset_ppt",
self.freq_offset_ppt as i64,
no_tags.clone(),
),
SourceMetric::new_int("mag_efc", self.mag_efc as i64, no_tags.clone()),
SourceMetric::new_int("freq_offset_ppt", self.freq_offset_ppt as i64, tags.clone()),
SourceMetric::new_int("mag_efc", self.mag_efc as i64, tags.clone()),
]
}
}
#[derive(Debug)]
pub struct Prs10Monitor {
name: String,
config: Prs10Config,
rx: ReadHalf<SerialStream>,
tx: WriteHalf<SerialStream>,
info: OnceCell<Prs10Info>,
config: Prs10Config,
}
impl Prs10Monitor {
pub fn new(config: Prs10Config) -> Self {
let builder = tokio_serial::new(&config.port, config.baud)
.timeout(config.timeout)
.data_bits(tokio_serial::DataBits::Eight)
.parity(tokio_serial::Parity::None)
.stop_bits(tokio_serial::StopBits::One)
.flow_control(tokio_serial::FlowControl::None);
let mut port = SerialStream::open(&builder).expect("Must be able to open serial port");
port.set_exclusive(true).expect("Can't lock serial port");
info!(
"Opened serial port {}@{}",
port.name().unwrap(),
port.baud_rate().unwrap()
);
let (rx, tx) = tokio::io::split(port);
Self {
rx,
tx,
config,
info: OnceCell::new(),
}
}
pub fn info(&self) -> &Prs10Info {
self.info.get().expect("info() used before run()")
}
@@ -450,7 +415,7 @@ impl Prs10Monitor {
async fn status_poll(&mut self) -> Result<ChimemonMessage, Box<dyn std::error::Error>> {
let status = self.get_status().await?;
Ok(ChimemonMessage::SourceReport(SourceReport {
name: "prs10".into(),
name: self.name.clone(),
status: if status.is_healthy() {
SourceStatus::Healthy
} else {
@@ -480,7 +445,7 @@ impl Prs10Monitor {
drop(stats_guard);
Ok(ChimemonMessage::SourceReport(SourceReport {
name: "prs10".into(),
name: self.name.clone(),
status: SourceStatus::Unknown,
details: Arc::new(Prs10Stats {
ocxo_efc,
@@ -514,10 +479,35 @@ impl Prs10Monitor {
#[async_trait]
impl ChimemonSource for Prs10Monitor {
type Config = Prs10Config;
fn new(name: &str, config: Self::Config) -> Self {
let builder = tokio_serial::new(&config.port, config.baud)
.timeout(config.timeout)
.data_bits(tokio_serial::DataBits::Eight)
.parity(tokio_serial::Parity::None)
.stop_bits(tokio_serial::StopBits::One)
.flow_control(tokio_serial::FlowControl::None);
let mut port = SerialStream::open(&builder).expect("Must be able to open serial port");
port.set_exclusive(true).expect("Can't lock serial port");
info!(
"Opened serial port {}@{}",
port.name().unwrap(),
port.baud_rate().unwrap()
);
let (rx, tx) = tokio::io::split(port);
Self {
name: name.to_owned(),
config,
rx,
tx,
info: OnceCell::new(),
}
}
async fn run(mut self, chan: ChimemonSourceChannel) {
info!("PRS10 task starting");
if let Err(e) = self.set_info().await {
warn!("Error starting PRS10: {e:?}");
error!("Error starting PRS10: {e:?}");
return;
}
info!(