refactor metric tags & sources config
This commit is contained in:
59
src/lib.rs
59
src/lib.rs
@@ -4,7 +4,7 @@ pub mod targets;
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use figment::{
|
||||
Figment,
|
||||
Figment, Provider,
|
||||
providers::{Format, Serialized, Toml},
|
||||
util::map,
|
||||
value::Map,
|
||||
@@ -43,6 +43,7 @@ impl Default for InfluxConfig {
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(default)]
|
||||
pub struct ChronyConfig {
|
||||
pub enabled: bool,
|
||||
#[serde_as(as = "DurationSeconds<u64>")]
|
||||
@@ -73,6 +74,7 @@ impl Default for ChronyConfig {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(default)]
|
||||
pub struct ChronySockConfig {
|
||||
pub enabled: bool,
|
||||
pub sock: String,
|
||||
@@ -95,6 +97,7 @@ pub struct HwmonSensorConfig {
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(default)]
|
||||
pub struct HwmonConfig {
|
||||
pub enabled: bool,
|
||||
#[serde_as(as = "DurationSeconds<u64>")]
|
||||
@@ -116,6 +119,7 @@ impl Default for HwmonConfig {
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(default)]
|
||||
pub struct GpsdConfig {
|
||||
pub enabled: bool,
|
||||
#[serde_as(as = "DurationSeconds<u64>")]
|
||||
@@ -135,6 +139,7 @@ impl Default for GpsdConfig {
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(default)]
|
||||
pub struct Prs10Config {
|
||||
pub enabled: bool,
|
||||
pub port: String,
|
||||
@@ -178,40 +183,43 @@ pub enum SourceStatus {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum MetricValue {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
}
|
||||
|
||||
type MetricTag = (&'static str, String);
|
||||
type MetricTags = Vec<MetricTag>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SourceMetric {
|
||||
name: String,
|
||||
name: &'static str,
|
||||
value: MetricValue,
|
||||
tags: Arc<Vec<(String, String)>>,
|
||||
tags: Arc<MetricTags>,
|
||||
}
|
||||
|
||||
impl SourceMetric {
|
||||
pub fn new_int(name: &str, value: i64, tags: Arc<Vec<(String, String)>>) -> Self {
|
||||
pub fn new_int(name: &'static str, value: i64, tags: Arc<MetricTags>) -> Self {
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
name: name,
|
||||
value: MetricValue::Int(value),
|
||||
tags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_float(name: &str, value: f64, tags: Arc<Vec<(String, String)>>) -> Self {
|
||||
pub fn new_float(name: &'static str, value: f64, tags: Arc<MetricTags>) -> Self {
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
name: name,
|
||||
value: MetricValue::Float(value),
|
||||
tags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_bool(name: &str, value: bool, tags: Arc<Vec<(String, String)>>) -> Self {
|
||||
pub fn new_bool(name: &'static str, value: bool, tags: Arc<MetricTags>) -> Self {
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
name: name,
|
||||
value: MetricValue::Bool(value),
|
||||
tags,
|
||||
}
|
||||
@@ -232,6 +240,7 @@ pub struct SourceReport {
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(default)]
|
||||
pub struct UCCMConfig {
|
||||
pub enabled: bool,
|
||||
pub port: String,
|
||||
@@ -265,6 +274,23 @@ pub struct SourcesConfig {
|
||||
pub prs10: Prs10Config,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum SourceConfig {
|
||||
Chrony(ChronyConfig),
|
||||
Hwmon(HwmonConfig),
|
||||
Uccm(UCCMConfig),
|
||||
Gpsd(GpsdConfig),
|
||||
Prs10(Prs10Config),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct NamedSourceConfig {
|
||||
pub name: String,
|
||||
#[serde(flatten)]
|
||||
pub source: SourceConfig,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
pub struct TargetsConfig {
|
||||
pub chrony: ChronySockConfig,
|
||||
@@ -273,10 +299,19 @@ pub struct TargetsConfig {
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
pub struct Config {
|
||||
pub influxdb: InfluxConfig,
|
||||
pub sources: SourcesConfig,
|
||||
pub sources: Vec<NamedSourceConfig>,
|
||||
pub targets: TargetsConfig,
|
||||
}
|
||||
|
||||
impl Provider for Config {
|
||||
fn metadata(&self) -> figment::Metadata {
|
||||
figment::Metadata::named("Default config")
|
||||
}
|
||||
fn data(&self) -> Result<Map<figment::Profile, figment::value::Dict>, figment::Error> {
|
||||
Serialized::defaults(Config::default()).data()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_config(filename: &Path) -> Figment {
|
||||
Figment::from(Serialized::defaults(Config::default())).merge(Toml::file(filename))
|
||||
}
|
||||
@@ -317,6 +352,8 @@ pub type ChimemonTargetChannel = Receiver<ChimemonMessage>;
|
||||
|
||||
#[async_trait]
|
||||
pub trait ChimemonSource {
|
||||
type Config;
|
||||
fn new(name: &str, config: Self::Config) -> Self;
|
||||
async fn run(self, chan: ChimemonSourceChannel);
|
||||
}
|
||||
|
||||
|
||||
115
src/main.rs
115
src/main.rs
@@ -1,13 +1,13 @@
|
||||
use clap::{Parser, ValueEnum};
|
||||
use figment::{
|
||||
Figment,
|
||||
providers::{Format, Toml},
|
||||
};
|
||||
use futures::future::join_all;
|
||||
use std::path::Path;
|
||||
use tokio::sync::broadcast;
|
||||
use tokio::{sync::broadcast, task::JoinHandle};
|
||||
use tracing::{Instrument, debug, error, info, info_span, warn};
|
||||
use tracing_subscriber::{
|
||||
self, EnvFilter,
|
||||
fmt::format::{self, FmtSpan},
|
||||
prelude::*,
|
||||
};
|
||||
use tracing_subscriber::{self, EnvFilter, fmt::format::FmtSpan};
|
||||
|
||||
use chimemon::*;
|
||||
use sources::{
|
||||
@@ -36,6 +36,43 @@ struct Args {
|
||||
log_level: Level,
|
||||
}
|
||||
|
||||
fn run_source(config: NamedSourceConfig, chan: ChimemonSourceChannel) -> Option<JoinHandle<()>> {
|
||||
let NamedSourceConfig { name, source } = config;
|
||||
match source {
|
||||
SourceConfig::Chrony(source_config) if source_config.enabled => {
|
||||
let c = ChronyClient::new(&name, source_config);
|
||||
Some(tokio::spawn(
|
||||
c.run(chan).instrument(info_span!("chrony-task")),
|
||||
))
|
||||
}
|
||||
SourceConfig::Gpsd(source_config) if source_config.enabled => {
|
||||
let c = GpsdSource::new(&name, source_config);
|
||||
Some(tokio::spawn(
|
||||
c.run(chan).instrument(info_span!("gpsd-task")),
|
||||
))
|
||||
}
|
||||
SourceConfig::Hwmon(source_config) if source_config.enabled => {
|
||||
let c = HwmonSource::new(&name, source_config);
|
||||
Some(tokio::spawn(
|
||||
c.run(chan).instrument(info_span!("hwmon-task")),
|
||||
))
|
||||
}
|
||||
SourceConfig::Prs10(source_config) if source_config.enabled => {
|
||||
let c = Prs10Monitor::new(&name, source_config);
|
||||
Some(tokio::spawn(
|
||||
c.run(chan).instrument(info_span!("prs10-task")),
|
||||
))
|
||||
}
|
||||
SourceConfig::Uccm(source_config) if source_config.enabled => {
|
||||
let c = UCCMMonitor::new(&name, source_config);
|
||||
Some(tokio::spawn(
|
||||
c.run(chan.clone()).instrument(info_span!("uccm-task")),
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing_subscriber::fmt()
|
||||
@@ -47,7 +84,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args = Args::parse();
|
||||
|
||||
info!("{PROGRAM_NAME} v{VERSION} starting...");
|
||||
let fig = load_config(Path::new(&args.config_file));
|
||||
let fig = Figment::new()
|
||||
.merge(Config::default())
|
||||
.merge(Toml::file(&args.config_file));
|
||||
debug!("{fig:?}");
|
||||
let config: Config = fig.extract()?;
|
||||
|
||||
@@ -88,66 +127,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
));
|
||||
}
|
||||
|
||||
let chrony = if config.sources.chrony.enabled {
|
||||
Some(ChronyClient::new(config.to_owned()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(c) = chrony {
|
||||
tasks.push(tokio::spawn(
|
||||
c.run(sourcechan.clone())
|
||||
.instrument(info_span!("chrony-task")),
|
||||
));
|
||||
};
|
||||
|
||||
let hwmon = if config.sources.hwmon.enabled {
|
||||
Some(HwmonSource::new(config.to_owned()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(hwmon) = hwmon {
|
||||
tasks.push(tokio::spawn(
|
||||
hwmon
|
||||
.run(sourcechan.clone())
|
||||
.instrument(info_span!("hwmon-task")),
|
||||
));
|
||||
};
|
||||
|
||||
let uccm = if config.sources.uccm.enabled {
|
||||
Some(UCCMMonitor::new(config.to_owned()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(uccm) = uccm {
|
||||
tasks.push(tokio::spawn(
|
||||
uccm.run(sourcechan.clone())
|
||||
.instrument(info_span!("uccm-task")),
|
||||
));
|
||||
};
|
||||
|
||||
let gpsd = if config.sources.gpsd.enabled {
|
||||
Some(GpsdSource::new(config.to_owned()).await.unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(gpsd) = gpsd {
|
||||
tasks.push(tokio::spawn(
|
||||
gpsd.run(sourcechan.clone())
|
||||
.instrument(info_span!("gpsd-task")),
|
||||
))
|
||||
for source in config.sources {
|
||||
if let Some(task) = run_source(source, sourcechan.clone()) {
|
||||
tasks.push(task)
|
||||
}
|
||||
|
||||
let prs10 = if config.sources.prs10.enabled {
|
||||
Some(Prs10Monitor::new(config.sources.prs10))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(prs10) = prs10 {
|
||||
tasks.push(tokio::spawn(
|
||||
prs10
|
||||
.run(sourcechan.clone())
|
||||
.instrument(info_span!("prs10-task")),
|
||||
))
|
||||
}
|
||||
|
||||
let chrony_refclock = if config.targets.chrony.enabled {
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
use crate::{
|
||||
ChimemonSource, ChimemonSourceChannel, Config, SourceMetric, SourceReport, SourceReportDetails,
|
||||
SourceStatus,
|
||||
};
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use chrony_candm::reply::{self, ReplyBody, SourceMode};
|
||||
use chrony_candm::request::{self, RequestBody};
|
||||
use chrony_candm::{ClientOptions, blocking_query};
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::join;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::{
|
||||
ChimemonSource, ChimemonSourceChannel, ChronyConfig, MetricTags, SourceMetric, SourceReport,
|
||||
SourceReportDetails, SourceStatus,
|
||||
};
|
||||
|
||||
pub struct ChronyClient {
|
||||
pub server: SocketAddr,
|
||||
pub name: String,
|
||||
client_options: ClientOptions,
|
||||
config: Config,
|
||||
config: ChronyConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChronyTrackingReport {
|
||||
tags: Arc<Vec<(String, String)>>,
|
||||
tags: Arc<MetricTags>,
|
||||
pub ref_id: i64,
|
||||
pub ref_ip_addr: String,
|
||||
pub stratum: i64,
|
||||
@@ -78,9 +80,9 @@ impl SourceReportDetails for ChronySourcesReport {
|
||||
|
||||
for source in &self.sources {
|
||||
let tags = Arc::new(vec![
|
||||
("ref_id".to_owned(), source.ip_addr.to_string()),
|
||||
("ref_id", source.ip_addr.to_string()),
|
||||
(
|
||||
"mode".to_owned(),
|
||||
"mode",
|
||||
match source.mode {
|
||||
SourceMode::Client => String::from("server"),
|
||||
SourceMode::Peer => String::from("peer"),
|
||||
@@ -88,7 +90,7 @@ impl SourceReportDetails for ChronySourcesReport {
|
||||
},
|
||||
),
|
||||
(
|
||||
"state".to_owned(),
|
||||
"state",
|
||||
match source.state {
|
||||
reply::SourceState::Selected => String::from("best"),
|
||||
reply::SourceState::NonSelectable => String::from("unusable"),
|
||||
@@ -129,7 +131,7 @@ impl SourceReportDetails for ChronySourcesReport {
|
||||
|
||||
fn report_from_tracking(
|
||||
t: &reply::Tracking,
|
||||
config: &Config,
|
||||
config: &ChronyConfig,
|
||||
) -> Result<ChronyTrackingReport, Box<dyn std::error::Error>> {
|
||||
let report = ChronyTrackingReport {
|
||||
tags: Arc::new(vec![]), //TODO: allow configuring tags in the source
|
||||
@@ -151,25 +153,6 @@ fn report_from_tracking(
|
||||
}
|
||||
|
||||
impl ChronyClient {
|
||||
pub fn new(config: Config) -> Self {
|
||||
let server = config
|
||||
.sources
|
||||
.chrony
|
||||
.host
|
||||
.to_socket_addrs()
|
||||
.unwrap()
|
||||
.next()
|
||||
.expect("Unable to parse host:port:");
|
||||
let client_options = ClientOptions {
|
||||
n_tries: 3,
|
||||
timeout: config.sources.chrony.timeout,
|
||||
};
|
||||
ChronyClient {
|
||||
server,
|
||||
client_options,
|
||||
config,
|
||||
}
|
||||
}
|
||||
async fn query(&self, request: RequestBody) -> Result<reply::Reply, std::io::Error> {
|
||||
let server = self.server;
|
||||
let client_options = self.client_options;
|
||||
@@ -265,7 +248,7 @@ impl ChronyClient {
|
||||
|
||||
let tracking_data = report_from_tracking(&tracking, &self.config)?;
|
||||
let report = SourceReport {
|
||||
name: "chrony-tracking".to_owned(),
|
||||
name: self.name.clone(),
|
||||
status: SourceStatus::Unknown,
|
||||
details: Arc::new(tracking_data),
|
||||
};
|
||||
@@ -283,7 +266,7 @@ impl ChronyClient {
|
||||
let sources = self.get_sources().await?;
|
||||
let details = ChronySourcesReport { sources };
|
||||
let report = SourceReport {
|
||||
name: "chrony-sources".to_owned(),
|
||||
name: self.name.clone(),
|
||||
status: SourceStatus::Unknown,
|
||||
details: Arc::new(details),
|
||||
};
|
||||
@@ -295,11 +278,30 @@ impl ChronyClient {
|
||||
|
||||
#[async_trait]
|
||||
impl ChimemonSource for ChronyClient {
|
||||
type Config = ChronyConfig;
|
||||
fn new(name: &str, config: Self::Config) -> Self {
|
||||
let server = config
|
||||
.host
|
||||
.to_socket_addrs()
|
||||
.unwrap()
|
||||
.next()
|
||||
.expect("Unable to parse host:port:");
|
||||
let client_options = ClientOptions {
|
||||
n_tries: 3,
|
||||
timeout: config.timeout,
|
||||
};
|
||||
ChronyClient {
|
||||
name: name.to_owned(),
|
||||
server,
|
||||
client_options,
|
||||
config,
|
||||
}
|
||||
}
|
||||
async fn run(self, chan: ChimemonSourceChannel) {
|
||||
info!("Chrony task started");
|
||||
|
||||
let mut t_interval = tokio::time::interval(self.config.sources.chrony.tracking_interval);
|
||||
let mut s_interval = tokio::time::interval(self.config.sources.chrony.sources_interval);
|
||||
let mut t_interval = tokio::time::interval(self.config.tracking_interval);
|
||||
let mut s_interval = tokio::time::interval(self.config.sources_interval);
|
||||
|
||||
let t_future = async {
|
||||
let lchan = chan.clone();
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
use crate::{
|
||||
ChimemonMessage, ChimemonSource, ChimemonSourceChannel, Config, SourceMetric, SourceReport,
|
||||
SourceReportDetails, SourceStatus,
|
||||
};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::f64;
|
||||
use std::fmt::Debug;
|
||||
@@ -12,8 +7,7 @@ use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use backoff::ExponentialBackoff;
|
||||
use futures::StreamExt;
|
||||
use futures::{SinkExt, Stream};
|
||||
use futures::{SinkExt, Stream, StreamExt};
|
||||
use gpsd_proto::{Device, Gst, Mode, Pps, Sky, Tpv, UnifiedResponse, Version};
|
||||
use serde::Serialize;
|
||||
use serde_json;
|
||||
@@ -22,8 +16,14 @@ use tokio::time::{interval, timeout};
|
||||
use tokio_util::codec::{Framed, LinesCodec};
|
||||
use tracing::{debug, debug_span, info, instrument, warn};
|
||||
|
||||
use crate::{
|
||||
ChimemonMessage, ChimemonSource, ChimemonSourceChannel, GpsdConfig, SourceMetric, SourceReport,
|
||||
SourceReportDetails, SourceStatus,
|
||||
};
|
||||
|
||||
pub struct GpsdSource {
|
||||
pub config: Config,
|
||||
pub name: String,
|
||||
pub config: GpsdConfig,
|
||||
conn: GpsdTransport,
|
||||
devices: HashMap<String, Device>,
|
||||
last_gst: Option<Gst>,
|
||||
@@ -76,19 +76,20 @@ impl SourceReportDetails for GpsdSourceReport {
|
||||
self.fix_type != GpsdFixType::Unknown && self.fix_type != GpsdFixType::NoFix
|
||||
}
|
||||
fn to_metrics(&self) -> Vec<SourceMetric> {
|
||||
let no_tags = Arc::new(vec![]);
|
||||
let tags = Arc::new(vec![]);
|
||||
vec![
|
||||
SourceMetric::new_int("sats_visible", self.sats_visible as i64, no_tags.clone()),
|
||||
SourceMetric::new_int("sats_tracked", self.sats_tracked as i64, no_tags.clone()),
|
||||
SourceMetric::new_float("tdop", self.tdop, no_tags.clone()),
|
||||
SourceMetric::new_int("sats_visible", self.sats_visible as i64, tags.clone()),
|
||||
SourceMetric::new_int("sats_tracked", self.sats_tracked as i64, tags.clone()),
|
||||
SourceMetric::new_float("tdop", self.tdop, tags.clone()),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl GpsdSource {
|
||||
pub async fn new(config: Config) -> Result<Self, std::io::Error> {
|
||||
let conn = GpsdTransport::new(&config.sources.gpsd.host).await?;
|
||||
async fn inner_new(name: &str, config: GpsdConfig) -> Result<Self, std::io::Error> {
|
||||
let conn = GpsdTransport::new(&config.host).await?;
|
||||
Ok(Self {
|
||||
name: name.to_owned(),
|
||||
config,
|
||||
conn,
|
||||
devices: HashMap::new(),
|
||||
@@ -98,9 +99,6 @@ impl GpsdSource {
|
||||
last_sky: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GpsdSource {
|
||||
async fn send_status(&self, chan: &mut ChimemonSourceChannel) {
|
||||
let sky = self.last_sky.as_ref();
|
||||
let tpv = self.last_tpv.as_ref();
|
||||
@@ -118,7 +116,7 @@ impl GpsdSource {
|
||||
.map_or(f64::INFINITY, |tdop| tdop as f64);
|
||||
|
||||
chan.send(ChimemonMessage::SourceReport(SourceReport {
|
||||
name: "gpsd".into(),
|
||||
name: self.name.clone(),
|
||||
status: SourceStatus::Unknown,
|
||||
details: Arc::new(GpsdSourceReport {
|
||||
fix_type: tpv.map_or(GpsdFixType::Unknown, |tpv| tpv.mode.into()),
|
||||
@@ -163,10 +161,19 @@ impl GpsdSource {
|
||||
|
||||
#[async_trait]
|
||||
impl ChimemonSource for GpsdSource {
|
||||
type Config = GpsdConfig;
|
||||
fn new(name: &str, config: Self::Config) -> Self {
|
||||
// TODO: refactor so this mess isn't necessary
|
||||
// Should do async setup at the start of run(), not here
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
rt.block_on(Self::inner_new(name, config)).unwrap()
|
||||
}
|
||||
async fn run(mut self, mut chan: ChimemonSourceChannel) {
|
||||
info!("gpsd task started");
|
||||
self.conn.conn().await.unwrap();
|
||||
let mut ticker = interval(self.config.sources.gpsd.interval);
|
||||
let mut ticker = interval(self.config.interval);
|
||||
|
||||
let mut params = WatchParams::default();
|
||||
params.json = Some(true);
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use crate::{
|
||||
ChimemonSource, ChimemonSourceChannel, Config, SourceMetric, SourceReport, SourceReportDetails,
|
||||
SourceStatus,
|
||||
};
|
||||
use std::{fs::File, io::Read, path::PathBuf, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use std::{fs::File, io::Read, path::PathBuf, sync::Arc, time::Duration};
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
use crate::{
|
||||
ChimemonSource, ChimemonSourceChannel, HwmonConfig, MetricTags, SourceMetric, SourceReport,
|
||||
SourceReportDetails, SourceStatus,
|
||||
};
|
||||
pub struct HwmonSource {
|
||||
config: Config,
|
||||
name: String,
|
||||
config: HwmonConfig,
|
||||
sensors: Vec<Arc<HwmonSensor>>,
|
||||
}
|
||||
|
||||
@@ -18,7 +20,7 @@ struct HwmonSensor {
|
||||
device: String,
|
||||
sensor: String,
|
||||
label: Option<String>,
|
||||
tags: Arc<Vec<(String, String)>>,
|
||||
tags: Arc<MetricTags>,
|
||||
}
|
||||
|
||||
impl HwmonSensor {
|
||||
@@ -40,12 +42,12 @@ impl HwmonSensor {
|
||||
None
|
||||
};
|
||||
let mut tags_vec = vec![
|
||||
("name".to_owned(), name.to_owned()),
|
||||
("device".to_owned(), device.to_owned()),
|
||||
("sensor".to_owned(), sensor.to_owned()),
|
||||
("name", name.to_owned()),
|
||||
("device", device.to_owned()),
|
||||
("sensor", sensor.to_owned()),
|
||||
];
|
||||
if let Some(label) = &label {
|
||||
tags_vec.push(("label".to_owned(), label.clone()))
|
||||
tags_vec.push(("label", label.clone()))
|
||||
}
|
||||
Self {
|
||||
value_path,
|
||||
@@ -92,18 +94,6 @@ impl SourceReportDetails for HwmonReport {
|
||||
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
|
||||
}
|
||||
@@ -111,9 +101,23 @@ impl HwmonSource {
|
||||
|
||||
#[async_trait]
|
||||
impl ChimemonSource for HwmonSource {
|
||||
type Config = HwmonConfig;
|
||||
fn new(name: &str, config: Self::Config) -> Self {
|
||||
let sensors = config
|
||||
.sensors
|
||||
.iter()
|
||||
.map(|(k, v)| Arc::new(HwmonSensor::new(k, &v.name, &v.sensor)))
|
||||
.collect();
|
||||
|
||||
HwmonSource {
|
||||
name: name.to_owned(),
|
||||
config,
|
||||
sensors,
|
||||
}
|
||||
}
|
||||
async fn run(self, chan: ChimemonSourceChannel) {
|
||||
info!("hwmon task started");
|
||||
let mut interval = tokio::time::interval(self.config.sources.hwmon.interval);
|
||||
let mut interval = tokio::time::interval(self.config.interval);
|
||||
loop {
|
||||
interval.tick().await;
|
||||
let mut values = Vec::new();
|
||||
@@ -137,7 +141,7 @@ impl ChimemonSource for HwmonSource {
|
||||
}
|
||||
}
|
||||
let report = SourceReport {
|
||||
name: "hwmon".to_owned(),
|
||||
name: self.name.clone(),
|
||||
status: SourceStatus::Healthy,
|
||||
details: Arc::new(HwmonReport { values }),
|
||||
};
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
ChimemonMessage, ChimemonSource, ChimemonSourceChannel, Config, SourceMetric,
|
||||
SourceReportDetails, TimeReport,
|
||||
};
|
||||
use std::io::{BufRead, Cursor};
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bitflags::bitflags;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
@@ -11,9 +11,6 @@ use figment::value::Map;
|
||||
use influxdb2::models::DataPoint;
|
||||
use influxdb2::models::data_point::DataPointBuilder;
|
||||
use itertools::Itertools;
|
||||
use std::io::{BufRead, Cursor};
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, ReadHalf, WriteHalf};
|
||||
use tokio::join;
|
||||
use tokio::sync::Mutex;
|
||||
@@ -21,6 +18,11 @@ use tokio::time::sleep;
|
||||
use tokio_serial::{SerialPort, SerialStream};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
use crate::{
|
||||
ChimemonMessage, ChimemonSource, ChimemonSourceChannel, SourceMetric, SourceReportDetails,
|
||||
TimeReport, UCCMConfig,
|
||||
};
|
||||
|
||||
pub const GPS_EPOCH: i64 = 315964800; // Doesn't seem possible to have a const DateTime object
|
||||
pub type UccmEndian = BigEndian;
|
||||
|
||||
@@ -32,11 +34,11 @@ pub enum UCCMMonitorParseState {
|
||||
}
|
||||
|
||||
pub struct UCCMMonitor {
|
||||
// pub port: SerialStream,
|
||||
pub name: String,
|
||||
config: UCCMConfig,
|
||||
rx: ReadHalf<SerialStream>,
|
||||
tx: WriteHalf<SerialStream>,
|
||||
pub info: Option<UCCMInfo>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -399,30 +401,6 @@ impl TryFrom<&str> for UCCMGPSSatsReport {
|
||||
}
|
||||
|
||||
impl UCCMMonitor {
|
||||
pub fn new(config: Config) -> Self {
|
||||
let builder = tokio_serial::new(&config.sources.uccm.port, config.sources.uccm.baud)
|
||||
.timeout(config.sources.uccm.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);
|
||||
UCCMMonitor {
|
||||
// port,
|
||||
rx,
|
||||
tx,
|
||||
info: None,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_cmd(&mut self, cmd: &[u8]) -> Result<String, std::io::Error> {
|
||||
debug!("cmd: `{:?}`", String::from_utf8_lossy(cmd));
|
||||
self.tx.write_all(cmd).await.unwrap();
|
||||
@@ -481,7 +459,7 @@ impl UCCMMonitor {
|
||||
let mut last_loop_diag: Option<UCCMLoopDiagReport> = None;
|
||||
let mut last_gps_sats: Option<UCCMGPSSatsReport> = None;
|
||||
|
||||
let mut last_sent_report = Utc::now() - self.config.sources.uccm.status_interval;
|
||||
let mut last_sent_report = Utc::now() - self.config.status_interval;
|
||||
|
||||
loop {
|
||||
match tokio::io::AsyncReadExt::read_buf(&mut self.rx, &mut rdbuf).await {
|
||||
@@ -525,37 +503,33 @@ impl UCCMMonitor {
|
||||
}))
|
||||
.expect("Unable to send to channel");
|
||||
if sysnow - last_sent_report
|
||||
>= Duration::from_std(self.config.sources.uccm.status_interval)
|
||||
.unwrap()
|
||||
>= Duration::from_std(self.config.status_interval).unwrap()
|
||||
{
|
||||
let mut points = vec![
|
||||
tod.as_builder(
|
||||
&self.config.sources.uccm.measurement,
|
||||
&self.config.influxdb.tags,
|
||||
)
|
||||
.build()
|
||||
.unwrap(),
|
||||
];
|
||||
if let Some(loop_diag) = &last_loop_diag {
|
||||
points.push(
|
||||
loop_diag
|
||||
.as_builder(
|
||||
&self.config.sources.uccm.measurement,
|
||||
&self.config.influxdb.tags,
|
||||
)
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
if let Some(gps_sats) = &last_gps_sats {
|
||||
points.extend(gps_sats.build(
|
||||
&self.config.sources.uccm.measurement,
|
||||
&self.config.influxdb.tags,
|
||||
));
|
||||
}
|
||||
// let mut points = vec![
|
||||
// tod.as_builder(&self.config.measurement, &self.config.tags)
|
||||
// .build()
|
||||
// .unwrap(),
|
||||
// ];
|
||||
// if let Some(loop_diag) = &last_loop_diag {
|
||||
// points.push(
|
||||
// loop_diag
|
||||
// .as_builder(
|
||||
// &self.config.measurement,
|
||||
// &self.config.influxdb.tags,
|
||||
// )
|
||||
// .build()
|
||||
// .unwrap(),
|
||||
// )
|
||||
// }
|
||||
// if let Some(gps_sats) = &last_gps_sats {
|
||||
// points.extend(gps_sats.build(
|
||||
// &self.config.sources.uccm.measurement,
|
||||
// &self.config.influxdb.tags,
|
||||
// ));
|
||||
// }
|
||||
|
||||
chan.send(ChimemonMessage::DataPoints(points))
|
||||
.expect("Unable to send to channel");
|
||||
// chan.send(ChimemonMessage::DataPoints(points))
|
||||
// .expect("Unable to send to channel");
|
||||
last_sent_report = sysnow;
|
||||
}
|
||||
}
|
||||
@@ -597,6 +571,31 @@ impl UCCMMonitor {
|
||||
|
||||
#[async_trait]
|
||||
impl ChimemonSource for UCCMMonitor {
|
||||
type Config = UCCMConfig;
|
||||
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);
|
||||
UCCMMonitor {
|
||||
name: name.to_owned(),
|
||||
config,
|
||||
rx,
|
||||
tx,
|
||||
info: None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(mut self, chan: ChimemonSourceChannel) {
|
||||
info!("UCCM task starting");
|
||||
if self.get_info().await.is_err() {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use crate::{ChimemonMessage, ChimemonTarget, ChimemonTargetChannel, ChronySockConfig};
|
||||
use async_trait::async_trait;
|
||||
use libc::{c_double, c_int, timeval};
|
||||
use std::mem;
|
||||
use std::os::unix::net::UnixDatagram;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use libc::{c_double, c_int, timeval};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{ChimemonMessage, ChimemonTarget, ChimemonTargetChannel, ChronySockConfig};
|
||||
|
||||
const CHRONY_MAGIC: c_int = 0x534f434b;
|
||||
|
||||
pub struct ChronySockServer {
|
||||
|
||||
Reference in New Issue
Block a user