tesla-charge-controller/charge-controller-supervisor/src/main.rs

120 lines
3.1 KiB
Rust
Raw Normal View History

2024-12-28 18:54:21 +11:00
use clap::Parser;
use futures::StreamExt;
use std::path::PathBuf;
mod config;
#[derive(clap::Parser, Debug, Clone)]
#[clap(author, version, about, long_about = None)]
struct Args {
#[command(subcommand)]
command: Commands,
#[clap(long, default_value = "/etc/charge-controller-supervisor/config.json")]
config: PathBuf,
}
#[derive(clap::Subcommand, Debug, Clone)]
enum Commands {
/// Run charge controller server
Watch,
/// Print the default config file
GenerateConfig,
}
2024-12-28 20:39:07 +11:00
mod controller;
2024-12-28 18:54:21 +11:00
mod gauges;
mod pl;
mod tristar;
mod web;
pub const CHARGE_CONTROLLER_LABEL: &str = "charge_controller";
pub const PL_LABEL: &str = "pl_device";
pub const TRISTAR_LABEL: &str = "tristar_device";
#[tokio::main]
async fn main() {
env_logger::builder()
.format_module_path(false)
.format_timestamp(
if std::env::var("LOG_TIMESTAMP").is_ok_and(|v| v == "false") {
None
} else {
Some(env_logger::TimestampPrecision::Seconds)
},
)
.init();
if let Err(e) = run().await {
log::error!("{e:?}");
std::process::exit(1);
}
}
async fn run() -> eyre::Result<()> {
let args = Args::parse();
match args.command {
Commands::Watch => watch(args).await,
Commands::GenerateConfig => {
let config = config::Config::default();
let json = serde_json::to_string_pretty(&config)?;
println!("{json}");
Ok(())
}
}
}
async fn watch(args: Args) -> eyre::Result<()> {
let config: config::Config = serde_json::from_reader(std::fs::File::open(args.config)?)?;
let mut controllers = futures::stream::FuturesUnordered::new();
2024-12-28 20:39:07 +11:00
let mut map = std::collections::HashMap::new();
for config in config.charge_controllers {
let n = config.name.clone();
match controller::Controller::new(config) {
Ok(v) => {
map.insert(n, v.get_data_ptr());
controllers.push(run_loop(v));
}
Err(e) => log::error!("couldn't connect to {}: {e:?}", n),
2024-12-28 18:54:21 +11:00
}
}
let server = web::rocket(web::ServerState::new(config.primary_charge_controller, map));
2024-12-28 18:54:21 +11:00
let server_task = tokio::task::spawn(server.launch());
tokio::select! {
v = controllers.next() => {
match v {
Some(Err(e)) => {
log::error!("{e:?}");
}
_ => {
log::error!("no controller tasks left???");
}
}
}
v = server_task => {
2024-12-28 22:57:13 +11:00
if let Err(e)=v {
log::error!("server exited: {e:#?}");
} else {
std::process::exit(0);
}
2024-12-28 18:54:21 +11:00
}
}
std::process::exit(1)
}
2024-12-28 20:39:07 +11:00
async fn run_loop(mut controller: controller::Controller) -> eyre::Result<()> {
let mut timeout = tokio::time::interval(controller.timeout_interval());
2024-12-28 18:54:21 +11:00
loop {
timeout.tick().await;
if let Err(e) = controller.refresh().await {
log::warn!("error reading controller {}: {e:?}", controller.name());
}
}
}