some cleanup

This commit is contained in:
Alex Janka 2024-01-22 10:38:34 +11:00
parent 7bbd0ed6f7
commit fc3f7103d1
7 changed files with 39 additions and 32 deletions

7
Cargo.lock generated
View file

@ -1072,6 +1072,12 @@ dependencies = [
"unicode-normalization", "unicode-normalization",
] ]
[[package]]
name = "if_chain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
[[package]] [[package]]
name = "include_dir" name = "include_dir"
version = "0.7.3" version = "0.7.3"
@ -2573,6 +2579,7 @@ dependencies = [
"chrono", "chrono",
"clap 4.4.11", "clap 4.4.11",
"env_logger 0.10.1", "env_logger 0.10.1",
"if_chain",
"include_dir", "include_dir",
"libmodbus-rs", "libmodbus-rs",
"log 0.4.20", "log 0.4.20",

View file

@ -31,3 +31,4 @@ env_logger = "0.10"
log = "0.4" log = "0.4"
serialport = "4.3" serialport = "4.3"
libmodbus-rs = "0.8.3" libmodbus-rs = "0.8.3"
if_chain = "1.0.2"

2
control-loop.txt Normal file
View file

@ -0,0 +1,2 @@
targeting pl's target voltage
if load current > 0 or duty cycle < ~0.8 then there is power to spare

View file

@ -39,7 +39,6 @@ enum Commands {
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let args = Args::parse();
env_logger::builder() env_logger::builder()
.format_module_path(false) .format_module_path(false)
.format_timestamp( .format_timestamp(
@ -51,10 +50,9 @@ async fn main() {
) )
.init(); .init();
let args = Args::parse();
let auth_path = args.config_dir.join("auth"); let auth_path = args.config_dir.join("auth");
let _ = CONFIG_PATH.set(args.config_dir.join("config.json")); let _ = CONFIG_PATH.set(args.config_dir.join("config.json"));
let _recorder = metrics_prometheus::install(); let _recorder = metrics_prometheus::install();
match args.command { match args.command {
@ -110,6 +108,8 @@ async fn main() {
} }
}; };
// spawn a loop for each additional charge controller to log
// failed connections will print an error but the program will continue
let _additional_controllers: Vec<_> = access_config() let _additional_controllers: Vec<_> = access_config()
.additional_charge_controllers .additional_charge_controllers
.clone() .clone()
@ -165,7 +165,7 @@ async fn main() {
tcrc_requests, tcrc_requests,
}); });
// spawn the api loop // spawn the api / charge rate control loop
tokio::task::spawn(async move { tokio::task::spawn(async move {
let mut normal_data_update_interval = let mut normal_data_update_interval =
tokio::time::interval(std::time::Duration::from_secs( tokio::time::interval(std::time::Duration::from_secs(
@ -208,6 +208,7 @@ async fn main() {
.charge_state .charge_state
.is_some_and(|v| v.charging_state == ChargingState::Disconnected) .is_some_and(|v| v.charging_state == ChargingState::Disconnected)
{ {
// reenable control when charger is disconnected
was_connected = false; was_connected = false;
tesla_charge_rate_controller.process_request( tesla_charge_rate_controller.process_request(
tesla_charge_rate::TcrcRequest::EnableAutomaticControl, tesla_charge_rate::TcrcRequest::EnableAutomaticControl,

View file

@ -1,3 +1,4 @@
use if_chain::if_chain;
use include_dir::{include_dir, Dir}; use include_dir::{include_dir, Dir};
use rocket::{ use rocket::{
http::{ContentType, Status}, http::{ContentType, Status},
@ -64,8 +65,10 @@ struct RawHtml {
impl<'r> Responder<'r, 'static> for RawHtml { impl<'r> Responder<'r, 'static> for RawHtml {
fn respond_to(self, request: &'r Request<'_>) -> rocket::response::Result<'static> { fn respond_to(self, request: &'r Request<'_>) -> rocket::response::Result<'static> {
let mut response = self.data.respond_to(request)?; let mut response = self.data.respond_to(request)?;
if let Some(ext) = self.name.extension() { if_chain! {
if let Some(ct) = ContentType::from_extension(&ext.to_string_lossy()) { if let Some(ext) = self.name.extension();
if let Some(ct) = ContentType::from_extension(&ext.to_string_lossy());
then {
response.set_header(ct); response.set_header(ct);
} }
} }

View file

@ -1,5 +1,6 @@
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use if_chain::if_chain;
use metrics::{describe_gauge, gauge, Gauge}; use metrics::{describe_gauge, gauge, Gauge};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -49,25 +50,21 @@ impl TeslaChargeRateController {
} }
pub fn control_charge_rate(&mut self) -> Option<InterfaceRequest> { pub fn control_charge_rate(&mut self) -> Option<InterfaceRequest> {
if let Some(pl_state) = self.pl_state.as_ref().and_then(|v| v.read().ok()) { if_chain! {
if let Ok(car_state) = self.car_state.read() { if let Some(pl_state) = self.pl_state.as_ref().and_then(|v| v.read().ok());
if let Some(charge_state) = car_state.charge_state { if let Some(charge_state) = self.car_state.read().ok().and_then(|v| v.charge_state);
// we don't need to check if we're at home because we only call this when we are then {
// TODO: enforce this through type system if pl_state.battery_voltage < access_config().shutoff_voltage {
self.voltage_low += 1;
// automatic control or not, check if we're below shutoff voltage if (self.voltage_low * access_config().charge_rate_update_interval_seconds)
if pl_state.battery_voltage < access_config().shutoff_voltage { >= access_config().shutoff_voltage_time_seconds
self.voltage_low += 1; {
if (self.voltage_low * access_config().charge_rate_update_interval_seconds) return Some(InterfaceRequest::StopCharge);
>= access_config().shutoff_voltage_time_seconds }
{ } else {
return Some(InterfaceRequest::StopCharge); self.voltage_low = 0;
} if self.tcrc_state.read().is_ok_and(|v| v.control_enable) {
} else { return get_control(&pl_state, &charge_state);
self.voltage_low = 0;
if self.tcrc_state.read().is_ok_and(|v| v.control_enable) {
return get_control(&pl_state, &charge_state);
}
} }
} }
} }
@ -105,6 +102,8 @@ fn get_control(pl_state: &PlState, charge_state: &ChargeState) -> Option<Interfa
return valid_rate(rate, charge_state.charge_amps).map(InterfaceRequest::SetChargeRate); return valid_rate(rate, charge_state.charge_amps).map(InterfaceRequest::SetChargeRate);
} }
// if max/min charge rate has changed, then the current charge rate might no longer be valid
// and we should move up/down to keep it in the new bounds
valid_rate(charge_state.charge_amps, charge_state.charge_amps) valid_rate(charge_state.charge_amps, charge_state.charge_amps)
.map(InterfaceRequest::SetChargeRate) .map(InterfaceRequest::SetChargeRate)
} }

View file

@ -84,13 +84,6 @@ pub struct ChargeState {
pub charge_limit_soc: i64, pub charge_limit_soc: i64,
} }
impl ChargeState {
#[allow(unused)]
pub fn range_km(&self) -> f64 {
self.battery_range * 1.60934
}
}
impl From<teslatte::vehicles::ChargeState> for ChargeState { impl From<teslatte::vehicles::ChargeState> for ChargeState {
fn from(value: teslatte::vehicles::ChargeState) -> Self { fn from(value: teslatte::vehicles::ChargeState) -> Self {
ChargeState { ChargeState {
@ -157,6 +150,7 @@ pub struct Coords {
pub longitude: f64, pub longitude: f64,
} }
// ~111 metres
const COORD_PRECISION: f64 = 0.001; const COORD_PRECISION: f64 = 0.001;
impl Coords { impl Coords {