fix some baaad state management (LOL)

This commit is contained in:
Alex Janka 2024-01-17 09:11:18 +11:00
parent 42f249f0f4
commit 5cdda39451
4 changed files with 36 additions and 37 deletions

2
Cargo.lock generated
View file

@ -2256,7 +2256,7 @@ dependencies = [
[[package]] [[package]]
name = "tesla-charge-controller" name = "tesla-charge-controller"
version = "1.0.0" version = "1.0.1"
dependencies = [ dependencies = [
"async-channel", "async-channel",
"chrono", "chrono",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "tesla-charge-controller" name = "tesla-charge-controller"
version = "1.0.0" version = "1.0.1"
edition = "2021" edition = "2021"
license = "MITNFA" license = "MITNFA"
description = "Controls Tesla charge rate based on solar charge data" description = "Controls Tesla charge rate based on solar charge data"

View file

@ -10,6 +10,7 @@ use config::{access_config, CONFIG_PATH};
use errors::PrintErrors; use errors::PrintErrors;
use std::path::PathBuf; use std::path::PathBuf;
use tesla_charge_rate::TeslaChargeRateController; use tesla_charge_rate::TeslaChargeRateController;
use teslatte::vehicles::ChargingState;
use crate::config::Config; use crate::config::Config;
@ -134,6 +135,8 @@ async fn main() {
let mut charge_rate_update_interval = tokio::time::interval( let mut charge_rate_update_interval = tokio::time::interval(
std::time::Duration::from_secs(config.charge_rate_update_interval_seconds), std::time::Duration::from_secs(config.charge_rate_update_interval_seconds),
); );
let mut was_connected = false;
loop { loop {
// await either the next interval OR a message from the other thread // await either the next interval OR a message from the other thread
tokio::select! { tokio::select! {
@ -149,9 +152,22 @@ async fn main() {
}, },
_ = charge_rate_update_interval.tick() => { _ = charge_rate_update_interval.tick() => {
if interface.state.read().unwrap().is_charging_at_home() { if interface.state.read().unwrap().is_charging_at_home() {
was_connected = true;
if let Some(request) = tesla_charge_rate_controller.control_charge_rate() { if let Some(request) = tesla_charge_rate_controller.control_charge_rate() {
interface.process_request(request).await; interface.process_request(request).await;
} }
} else if was_connected
&& interface
.state
.read()
.unwrap()
.charge_state
.is_some_and(|v| v.charging_state == ChargingState::Disconnected)
{
was_connected = false;
tesla_charge_rate_controller.process_request(
tesla_charge_rate::TcrcRequest::EnableAutomaticControl,
);
} }
}, },
api_message = api_receiver.recv() => match api_message { api_message = api_receiver.recv() => match api_message {

View file

@ -1,11 +1,7 @@
use std::{ use std::sync::{Arc, RwLock};
sync::{Arc, RwLock},
time::{Duration, Instant},
};
use metrics::{describe_gauge, gauge, Gauge}; use metrics::{describe_gauge, gauge, Gauge};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use teslatte::vehicles::ChargingState;
use crate::{ use crate::{
api_interface::InterfaceRequest, api_interface::InterfaceRequest,
@ -18,7 +14,7 @@ pub struct TeslaChargeRateController {
pub car_state: Arc<RwLock<CarState>>, pub car_state: Arc<RwLock<CarState>>,
pub pl_state: Option<Arc<RwLock<PlState>>>, pub pl_state: Option<Arc<RwLock<PlState>>>,
pub tcrc_state: Arc<RwLock<TcrcState>>, pub tcrc_state: Arc<RwLock<TcrcState>>,
voltage_low: Option<Instant>, voltage_low: u64,
control_enable_gauge: Gauge, control_enable_gauge: Gauge,
} }
@ -49,7 +45,7 @@ impl TeslaChargeRateController {
car_state, car_state,
pl_state, pl_state,
tcrc_state: Default::default(), tcrc_state: Default::default(),
voltage_low: None, voltage_low: 0,
control_enable_gauge: gauge!("tcrc_control_enable"), control_enable_gauge: gauge!("tcrc_control_enable"),
} }
} }
@ -58,36 +54,23 @@ impl TeslaChargeRateController {
if let Some(pl_state) = self.pl_state.as_ref().and_then(|v| v.read().ok()) { if let Some(pl_state) = self.pl_state.as_ref().and_then(|v| v.read().ok()) {
if let Ok(car_state) = self.car_state.read() { if let Ok(car_state) = self.car_state.read() {
if let Some(charge_state) = car_state.charge_state { if let Some(charge_state) = car_state.charge_state {
// check if we're charging at home // we don't need to check if we're at home because we only call this when we are
if car_state.is_charging_at_home() { // TODO: enforce this through type system
// automatic control or not, check if we're below shutoff voltage // automatic control or not, check if we're below shutoff voltage
if pl_state.battery_voltage < access_config().shutoff_voltage { if pl_state.battery_voltage < access_config().shutoff_voltage {
if let Some(low_voltage_time) = self.voltage_low { self.voltage_low += 1;
// if we've been below shutoff for long enough, stop charging if (self.voltage_low * access_config().charge_rate_update_interval_seconds)
if Instant::now().duration_since(low_voltage_time) >= access_config().shutoff_voltage_time_seconds
>= Duration::from_secs(
access_config().shutoff_voltage_time_seconds,
)
{ {
return Some(InterfaceRequest::StopCharge); return Some(InterfaceRequest::StopCharge);
} }
} else { } else {
self.voltage_low = Some(Instant::now()); self.voltage_low = 0;
}
} else {
self.voltage_low = None;
if self.tcrc_state.read().is_ok_and(|v| v.control_enable) { if self.tcrc_state.read().is_ok_and(|v| v.control_enable) {
return get_control(&pl_state, &charge_state); return get_control(&pl_state, &charge_state);
} }
} }
} else if charge_state.charging_state == ChargingState::Disconnected {
// only disable automatic control until the next time we're unplugged
self.tcrc_state
.write()
.expect("failed to write to tcrc state")
.control_enable = true;
self.control_enable_gauge.set(1.);
}
} }
} }
} }