diff --git a/Cargo.lock b/Cargo.lock index 19daa58..0735435 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2243,9 +2243,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -2262,7 +2262,7 @@ dependencies = [ [[package]] name = "tesla-charge-controller" -version = "0.1.13" +version = "0.1.14" dependencies = [ "anyhow", "async-channel", @@ -2278,6 +2278,7 @@ dependencies = [ "ron", "serde", "serialport", + "termcolor", "teslatte", "thiserror", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 9214bbf..eb5ebc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tesla-charge-controller" -version = "0.1.13" +version = "0.1.14" edition = "2021" license = "MITNFA" description = "Controls Tesla charge rate based on solar charge data" @@ -30,3 +30,4 @@ prometheus = "0.13" env_logger = "0.10" log = "0.4" serialport = "4.3" +termcolor = "1.4.1" diff --git a/src/main.rs b/src/main.rs index 7918285..b5e51ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,12 +57,15 @@ async fn main() { ron::from_str(&std::fs::read_to_string(&config_path).unwrap()).unwrap(); // build the channel that takes messages from the webserver thread to the api thread - let (api_requests, receiver) = async_channel::unbounded(); + let (api_requests, api_receiver) = async_channel::unbounded(); + // and to the pli thread + let (pli_requests, pli_receiver) = async_channel::unbounded(); let server_handle = server::launch_server(server::ServerState { config: config.clone(), state: interface.state.clone(), api_requests, + pli_requests, }); // spawn the api loop @@ -72,7 +75,7 @@ async fn main() { // await either the next interval OR a message from the other thread tokio::select! { _ = interval.tick() => interface.refresh().await, - message = receiver.recv() => match message { + message = api_receiver.recv() => match message { Ok(message) => interface.process_request(message).await, Err(e) => error!("Error on receive channel: {e:#?}") } @@ -87,8 +90,13 @@ async fn main() { let mut interval = tokio::time::interval(std::time::Duration::from_secs(30)); loop { - interval.tick().await; - pli.refresh(); + tokio::select! { + _ = interval.tick() => pli.refresh(), + message = pli_receiver.recv() => match message { + Ok(message) => pli.process_request(message), + Err(e) => error!("Error on receive channel: {e:#?}") + } + } } }); } diff --git a/src/pl_interface.rs b/src/pl_interface.rs index b49dda7..c58fc4e 100644 --- a/src/pl_interface.rs +++ b/src/pl_interface.rs @@ -1,8 +1,9 @@ -use std::time::Duration; +use std::{io::Write, time::Duration}; use anyhow::Context; use metrics::{describe_gauge, gauge, Gauge}; use serialport::SerialPort; +use termcolor::WriteColor; pub struct Pli { port: Box, @@ -12,6 +13,11 @@ pub struct Pli { internal_load_current: Gauge, } +#[derive(Debug, Clone, Copy)] +pub enum PliRequest { + ReadRam(u8), +} + impl Pli { pub fn new(serial_port: String, baud_rate: u32) -> anyhow::Result { let port = serialport::new(serial_port, baud_rate) @@ -53,6 +59,26 @@ impl Pli { } } + pub fn process_request(&mut self, message: PliRequest) { + match message { + PliRequest::ReadRam(address) => match self.read_ram(address) { + Ok(data) => { + let mut stdout = + termcolor::StandardStream::stdout(termcolor::ColorChoice::Always); + let _ = stdout.set_color( + termcolor::ColorSpec::new().set_fg(Some(termcolor::Color::Green)), + ); + if writeln!(&mut stdout, "Read RAM at {address}: data {data}").is_err() { + log::warn!( + "Failed to set stdout colour\nRead RAM at {address}: data {data}" + ); + }; + } + Err(e) => log::error!("RAM read error: {e:?}"), + }, + } + } + fn send_command(&mut self, req: [u8; 4]) { self.port .write_all(&req) @@ -68,7 +94,10 @@ impl Pli { buf } - fn read_ram(&mut self, address: PlRamAddress) -> anyhow::Result { + fn read_ram(&mut self, address: T) -> anyhow::Result + where + T: Into, + { self.send_command(command(20, address.into(), 0)); let buf: [u8; 2] = self.receive(); if buf[0] == 200 { Some(buf[1]) } else { None } diff --git a/src/server/mod.rs b/src/server/mod.rs index fd644d5..8499854 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -11,6 +11,7 @@ use rocket::{ use crate::{ api_interface::InterfaceRequest, config::Config, + pl_interface::PliRequest, types::{CarState, ChargeState, ClimateState}, }; @@ -22,6 +23,7 @@ pub struct ServerState { pub config: Config, pub state: Arc>, pub api_requests: Sender, + pub pli_requests: Sender, } pub async fn launch_server(state: ServerState) { @@ -47,7 +49,7 @@ fn rocket(state: ServerState) -> rocket::Rocket { .mount("/", fileserver) .mount( "/", - routes![home, charge_state, flash, climate_state, metrics], + routes![home, charge_state, flash, climate_state, metrics, read_ram], ) } @@ -79,6 +81,12 @@ fn metrics() -> Option { .ok() } +#[get("/read-ram/
")] +async fn read_ram(address: u8, state: &State) -> String { + let _ = state.pli_requests.send(PliRequest::ReadRam(address)).await; + format!("reading at ram address {address}") +} + pub struct Cors; #[rocket::async_trait]