use clap::Parser;
use prometheus_client::registry::Registry;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use std::{io, process};
use strudel::http::RequestContext;
use strudel::metrics::TemperatureMetrics;
use strudel::sensor::TemperatureReader;
use tokio::signal::unix::{self, SignalKind};
use tokio::task;
use tracing::Level;
const DEFAULT_REFRESH_SECS: u64 = 30;
const DEFAULT_LOG_LEVEL: Level = Level::INFO;
const DEFAULT_BIND_ADDR: ([u8; 4], u16) = ([0, 0, 0, 0], 9781);
#[derive(Debug, Parser)]
#[clap(name = "strudel", version = clap::crate_version ! ())]
struct StrudelApplication {
#[clap(long)]
bcm_pin: u8,
#[clap(long, default_value_t = DEFAULT_REFRESH_SECS)]
refresh_secs: u64,
#[clap(long, default_value_t = DEFAULT_LOG_LEVEL)]
log_level: Level,
#[clap(long, default_value_t = DEFAULT_BIND_ADDR.into())]
bind: SocketAddr,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let opts = StrudelApplication::parse();
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(opts.log_level)
.finish(),
)
.expect("failed to set tracing subscriber");
let reader = TemperatureReader::new(opts.bcm_pin).unwrap_or_else(|e| {
tracing::error!(message = "failed to initialize sensor reader", bcm_pin = opts.bcm_pin, error = %e);
process::exit(1)
});
let mut reg = <Registry>::default();
let metrics = TemperatureMetrics::new(&mut reg, reader);
task::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(opts.refresh_secs));
loop {
let _ = interval.tick().await;
metrics.collect().await;
}
});
let context = Arc::new(RequestContext::new(reg));
let handler = strudel::http::text_metrics(context);
let (sock, server) = warp::serve(handler)
.try_bind_with_graceful_shutdown(opts.bind, async {
tokio::select! {
_ = sigterm() => {}
_ = sigint() => {}
}
})
.unwrap_or_else(|e| {
tracing::error!(message = "error binding to address", address = %opts.bind, error = %e);
process::exit(1)
});
tracing::info!(message = "server started", address = %sock);
server.await;
tracing::info!("server shutdown");
Ok(())
}
async fn sigterm() -> io::Result<()> {
unix::signal(SignalKind::terminate())?.recv().await;
Ok(())
}
async fn sigint() -> io::Result<()> {
unix::signal(SignalKind::interrupt())?.recv().await;
Ok(())
}