From ccd46b657c0987338561c25b0896b75c969d8054 Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Tue, 13 Feb 2024 09:32:35 +1100 Subject: [PATCH] regulator state set button --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/charge_controllers/pl.rs | 17 ++++++++++++ src/errors.rs | 3 +++ src/server/mod.rs | 39 +++++++++++++++++++++++++--- webapp/index.html | 1 + webapp/regulator/index.html | 33 ++++++++++++++++++++++++ webapp/script.js | 50 ++++++++++++++++++++++++++++++++++++ 8 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 webapp/regulator/index.html diff --git a/Cargo.lock b/Cargo.lock index 4fd28e7..195f8d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2538,7 +2538,7 @@ dependencies = [ [[package]] name = "tesla-charge-controller" -version = "1.1.4" +version = "1.2.0" dependencies = [ "chrono", "clap 4.4.11", diff --git a/Cargo.toml b/Cargo.toml index bea0bdb..dcaff82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tesla-charge-controller" -version = "1.1.4" +version = "1.2.0" edition = "2021" license = "MITNFA" description = "Controls Tesla charge rate based on solar charge data" diff --git a/src/charge_controllers/pl.rs b/src/charge_controllers/pl.rs index 0a4f63b..75df3a4 100644 --- a/src/charge_controllers/pl.rs +++ b/src/charge_controllers/pl.rs @@ -50,6 +50,17 @@ impl From for RegulatorState { } } +impl From for u8 { + fn from(value: RegulatorState) -> Self { + match value { + RegulatorState::Boost => 0b00, + RegulatorState::Equalise => 0b01, + RegulatorState::Absorption => 0b10, + RegulatorState::Float => 0b11, + } + } +} + impl Default for RegulatorState { fn default() -> Self { Self::Absorption @@ -95,6 +106,7 @@ pub enum PliRequest { ReadRam(u8), ReadEeprom(u8), SyncTime, + SetRegulatorState(RegulatorState), } impl Pli { @@ -182,6 +194,11 @@ impl Pli { "Set time: {now} corresponds to {timestamp} + minutes {min} + seconds {sec}" ); } + PliRequest::SetRegulatorState(state) => { + log::warn!("Setting regulator state to {state:?}"); + self.write_ram(PlRamAddress::Rstate, state.into()) + .some_or_print_with("setting regulator state"); + } } } diff --git a/src/errors.rs b/src/errors.rs index 6a7c48f..084a008 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -34,6 +34,8 @@ pub enum ServerError { #[error("no data")] // 503 NoData, + #[error("invalid parameters")] + InvalidParameters, #[error("prometheus")] Prometheus(#[from] prometheus::Error), } @@ -49,6 +51,7 @@ impl<'a> Responder<'a, 'a> for ServerError { Err(match self { ServerError::Lock => rocket::http::Status::InternalServerError, ServerError::NoData => rocket::http::Status::ServiceUnavailable, + Self::InvalidParameters => rocket::http::Status::BadRequest, ServerError::Prometheus(_) => rocket::http::Status::InternalServerError, }) } diff --git a/src/server/mod.rs b/src/server/mod.rs index aec2850..dafb0aa 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -3,6 +3,7 @@ use std::sync::{Arc, RwLock}; use rocket::{ fairing::{Fairing, Info, Kind}, http::Header, + request::FromParam, serde::json::Json, Request, Response, State, }; @@ -11,9 +12,9 @@ use tokio::sync::mpsc::UnboundedSender; use crate::{ api_interface::InterfaceRequest, - charge_controllers::pl::{PlState, PliRequest}, + charge_controllers::pl::{PlState, PliRequest, RegulatorState}, config::{access_config, write_to_config}, - errors::ServerError, + errors::{PrintErrors, ServerError}, tesla_charge_rate::{TcrcRequest, TcrcState}, types::CarState, }; @@ -75,7 +76,8 @@ fn rocket(state: ServerState) -> rocket::Rocket { metrics, sync_time, read_ram, - read_eeprom + read_eeprom, + set_regulator_state ], ) } @@ -250,6 +252,37 @@ async fn regulator_state(state: &State) -> Result, Se .and_then(|v| Ok(Json(*(v.read()?)))) } +#[post("/set-regulator-state/")] +async fn set_regulator_state( + state: &State, + regulator_state: Option, +) -> Result<(), ServerError> { + if let Some(regulator_state) = regulator_state { + log::info!("Requesting regulator state set to {regulator_state:?}"); + state + .pli_requests + .send(PliRequest::SetRegulatorState(regulator_state)) + .some_or_print_with("requesting new pl regulator state"); + Ok(()) + } else { + Err(ServerError::InvalidParameters) + } +} + +impl FromParam<'_> for RegulatorState { + type Error = (); + + fn from_param(param: &'_ str) -> Result { + match param.to_lowercase().as_str() { + "boost" => Ok(Self::Boost), + "equalise" => Ok(Self::Equalise), + "absorption" => Ok(Self::Absorption), + "float" => Ok(Self::Float), + _ => Err(()), + } + } +} + pub struct Cors; #[rocket::async_trait] diff --git a/webapp/index.html b/webapp/index.html index 908abd5..e772999 100644 --- a/webapp/index.html +++ b/webapp/index.html @@ -33,6 +33,7 @@

Grafana→ Car state info→ + Regulator control→ PID control variables→ Shutoff voltage control→

diff --git a/webapp/regulator/index.html b/webapp/regulator/index.html new file mode 100644 index 0000000..d2eb43b --- /dev/null +++ b/webapp/regulator/index.html @@ -0,0 +1,33 @@ + + + + + + Tesla Charge Control + + + + + + +
+

+

Regulator state:

+
+
+ + +

+

+ ←Home +

+
+ + + \ No newline at end of file diff --git a/webapp/script.js b/webapp/script.js index 14da470..ab16059 100644 --- a/webapp/script.js +++ b/webapp/script.js @@ -63,6 +63,21 @@ function init_info() { }); } +function init_regulator() { + + refresh_interval = register(refresh_regulator_state); + + refresh_regulator_state(); + document.addEventListener("visibilitychange", () => { + if (document.hidden) { + clearInterval(refresh_interval); + } else { + refresh_regulator_state(); + refresh_interval = register(refresh_regulator_state); + } + }); +} + function register(func) { return setInterval(func, 5000); } @@ -175,6 +190,21 @@ function set_shutoff_time() { } } +function set_regulator_state() { + var set_button = document.getElementById("set-regulator-state"); + var state_input = document.getElementById("regstate"); + + set_button.disabled = true; + state_input.disabled = true; + + fetch(api_url + "/set-regulator-state/" + state_input.value, { method: "POST" }) + .then(async (response) => { + let delayres = await delay(300); + refresh_regulator_state(); + }); + +} + function set_load_divisor() { var set_button = document.getElementById("set-load-divisor"); var number_input = document.getElementById("load-divisor"); @@ -285,6 +315,18 @@ function refresh_shutoff() { .then((json) => update_shutoff(json)); } +function refresh_regulator_state() { + var set_button = document.getElementById("set-regulator-state"); + var state_input = document.getElementById("regstate"); + + set_button.disabled = false; + state_input.disabled = false; + + fetch(api_url + "/regulator-state") + .then((response) => { response.json() }) + .then((json) => update_regulator_state(json)); +} + function update_shutoff(data) { var voltage_set_button = document.getElementById("set-shutoff-voltage"); var voltage_number_input = document.getElementById("shutoff-voltage"); @@ -298,6 +340,14 @@ function update_shutoff(data) { time_number_input.value = data.time; } +function update_regulator_state(state) { + console.log(state); + if (state == null) { var cur = "Unknown" } else { var cur = state.regulator_state } + + var current_state = document.getElementById("current-state"); + current_state.textContent = "Current state: " + cur; +} + function refresh_buttons() { fetch(api_url + "/control-state") .then((response) => response.json())