pub mod chrony; pub mod chrony_refclock; pub mod gpsd; pub mod hwmon; pub mod prs10; pub mod uccm; use async_trait::async_trait; use chrono::{DateTime, Utc}; use figment::{ Figment, providers::{Format, Serialized, Toml}, util::map, value::Map, }; use gethostname::gethostname; use influxdb2::models::DataPoint; use serde_derive::{Deserialize, Serialize}; use tokio::sync::broadcast::*; use std::{fmt::Debug, net::SocketAddr, path::Path, sync::Arc}; #[derive(Serialize, Deserialize, Clone)] pub struct InfluxConfig { pub enabled: bool, pub url: String, pub org: String, pub bucket: String, pub token: String, pub tags: Map, } impl Default for InfluxConfig { fn default() -> Self { let host = gethostname().into_string().unwrap(); InfluxConfig { enabled: false, url: "http://localhost:8086".into(), org: "default".into(), bucket: "default".into(), token: "".into(), tags: map! { "host".into() => host }, } } } #[derive(Serialize, Deserialize, Clone)] pub struct ChronyConfig { pub enabled: bool, pub timeout: u64, pub tracking_interval: u64, pub sources_interval: u64, pub measurement_prefix: String, pub tracking_measurement: String, pub sources_measurement: String, pub host: String, } impl Default for ChronyConfig { fn default() -> Self { ChronyConfig { enabled: false, timeout: 5, tracking_interval: 60, sources_interval: 300, measurement_prefix: "chrony.".into(), tracking_measurement: "tracking".into(), sources_measurement: "sources".into(), host: "127.0.0.1:323".into(), } } } #[derive(Serialize, Deserialize, Clone)] pub struct ChronySockConfig { pub enabled: bool, pub sock: String, } impl Default for ChronySockConfig { fn default() -> Self { ChronySockConfig { enabled: false, sock: "".into(), } } } #[derive(Serialize, Deserialize, Clone)] pub struct HwmonSensorConfig { pub name: String, pub sensor: String, } #[derive(Serialize, Deserialize, Clone)] pub struct HwmonConfig { pub enabled: bool, pub interval: u64, pub measurement: String, pub sensors: Map, } impl Default for HwmonConfig { fn default() -> Self { HwmonConfig { enabled: false, interval: 60, measurement: "hwmon".into(), sensors: map! {}, } } } #[derive(Serialize, Deserialize, Clone)] pub struct GpsdConfig { pub enabled: bool, pub interval: u64, pub host: String, } impl Default for GpsdConfig { fn default() -> Self { GpsdConfig { enabled: false, interval: 60, host: "localhost:2947".into(), } } } #[derive(Serialize, Deserialize, Clone)] pub struct Prs10Config { pub enabled: bool, pub port: String, pub baud: u32, pub timeout: std::time::Duration, pub status_interval: std::time::Duration, pub stats_interval: std::time::Duration, } impl Default for Prs10Config { fn default() -> Self { Prs10Config { enabled: false, port: "/dev/ttyS0".into(), baud: 9600, timeout: std::time::Duration::from_secs(1), status_interval: std::time::Duration::from_secs(10), stats_interval: std::time::Duration::from_secs(10), } } } #[derive(Clone, Debug)] pub struct TimeReport { pub system_time: DateTime, pub offset: chrono::Duration, pub leaps: isize, pub leap_flag: bool, pub valid: bool, } #[derive(Clone, Debug)] pub enum SourceStatus { Healthy, LossOfSignal(Option), LossOfSync(Option), Other(Option), Unknown, } #[derive(Clone, Debug)] pub enum MetricValue { Int(i64), Float(f64), Bool(bool), } #[derive(Clone, Debug)] pub struct SourceMetric { name: String, value: MetricValue, tags: Arc>, } impl SourceMetric { pub fn new_int(name: &str, value: i64, tags: Arc>) -> Self { Self { name: name.to_owned(), value: MetricValue::Int(value), tags, } } pub fn new_float(name: &str, value: f64, tags: Arc>) -> Self { Self { name: name.to_owned(), value: MetricValue::Float(value), tags, } } pub fn new_bool(name: &str, value: bool, tags: Arc>) -> Self { Self { name: name.to_owned(), value: MetricValue::Bool(value), tags, } } } pub trait SourceReportDetails: Debug + Send + Sync { fn to_metrics(&self) -> Vec; fn is_healthy(&self) -> bool; } #[derive(Clone, Debug)] pub struct SourceReport { pub name: String, pub status: SourceStatus, pub details: Arc, } #[derive(Serialize, Deserialize, Clone)] pub struct UCCMConfig { pub enabled: bool, pub port: String, pub baud: u32, pub status_interval: std::time::Duration, pub timeout: std::time::Duration, pub measurement: String, } impl Default for UCCMConfig { fn default() -> Self { UCCMConfig { enabled: false, port: "/dev/ttyS0".into(), baud: 57600, status_interval: std::time::Duration::from_secs(10), timeout: std::time::Duration::from_secs(1), measurement: "uccm_gpsdo".into(), } } } #[derive(Serialize, Deserialize, Clone, Default)] pub struct SourcesConfig { pub chrony: ChronyConfig, pub hwmon: HwmonConfig, pub uccm: UCCMConfig, pub gpsd: GpsdConfig, pub prs10: Prs10Config, } #[derive(Serialize, Deserialize, Clone, Default)] pub struct TargetsConfig { pub chrony: ChronySockConfig, } #[derive(Serialize, Deserialize, Clone, Default)] pub struct Config { pub influxdb: InfluxConfig, pub sources: SourcesConfig, pub targets: TargetsConfig, } pub fn load_config(filename: &Path) -> Figment { Figment::from(Serialized::defaults(Config::default())).merge(Toml::file(filename)) } #[derive(Debug, Clone)] pub enum ChimemonMessage { DataPoint(DataPoint), DataPoints(Vec), TimeReport(TimeReport), SourceReport(SourceReport), } impl From for ChimemonMessage { fn from(dp: DataPoint) -> Self { ChimemonMessage::DataPoint(dp) } } impl From> for ChimemonMessage { fn from(dps: Vec) -> Self { ChimemonMessage::DataPoints(dps) } } impl From for ChimemonMessage { fn from(tr: TimeReport) -> Self { ChimemonMessage::TimeReport(tr) } } impl From for ChimemonMessage { fn from(sr: SourceReport) -> Self { ChimemonMessage::SourceReport(sr) } } pub type ChimemonSourceChannel = Sender; pub type ChimemonTargetChannel = Receiver; #[async_trait] pub trait ChimemonSource { async fn run(self, chan: ChimemonSourceChannel); } #[async_trait] pub trait ChimemonTarget { async fn run(self, chan: ChimemonTargetChannel); }