tesla-charge-controller/src/tesla_charge_rate.rs

139 lines
4.1 KiB
Rust
Raw Normal View History

2024-01-17 09:11:18 +11:00
use std::sync::{Arc, RwLock};
2024-01-16 11:00:11 +11:00
2024-01-22 10:38:34 +11:00
use if_chain::if_chain;
2024-01-16 11:00:11 +11:00
use metrics::{describe_gauge, gauge, Gauge};
use serde::{Deserialize, Serialize};
use crate::{
api_interface::InterfaceRequest,
charge_controllers::pl::PlState,
config::access_config,
types::{CarState, ChargeState},
};
pub struct TeslaChargeRateController {
pub car_state: Arc<RwLock<CarState>>,
pub pl_state: Option<Arc<RwLock<PlState>>>,
pub tcrc_state: Arc<RwLock<TcrcState>>,
2024-01-22 12:40:29 +11:00
pid: PidLoop,
2024-01-17 09:11:18 +11:00
voltage_low: u64,
2024-01-16 11:00:11 +11:00
control_enable_gauge: Gauge,
}
pub enum TcrcRequest {
DisableAutomaticControl,
EnableAutomaticControl,
}
2024-01-16 11:10:20 +11:00
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
2024-01-16 11:00:11 +11:00
pub struct TcrcState {
pub control_enable: bool,
}
2024-01-16 11:10:20 +11:00
impl Default for TcrcState {
fn default() -> Self {
Self {
control_enable: true,
}
}
}
2024-01-16 11:00:11 +11:00
impl TeslaChargeRateController {
pub fn new(car_state: Arc<RwLock<CarState>>, pl_state: Option<Arc<RwLock<PlState>>>) -> Self {
describe_gauge!("tcrc_control_enable", "Enable Tesla charge rate control");
Self {
car_state,
pl_state,
tcrc_state: Default::default(),
2024-01-22 12:40:29 +11:00
pid: Default::default(),
2024-01-17 09:11:18 +11:00
voltage_low: 0,
2024-01-16 11:00:11 +11:00
control_enable_gauge: gauge!("tcrc_control_enable"),
}
}
pub fn control_charge_rate(&mut self) -> Option<InterfaceRequest> {
2024-01-22 12:40:29 +11:00
let delta_time = access_config().pid_controls.loop_time_seconds;
2024-01-22 10:38:34 +11:00
if_chain! {
if let Some(pl_state) = self.pl_state.as_ref().and_then(|v| v.read().ok());
if let Some(charge_state) = self.car_state.read().ok().and_then(|v| v.charge_state);
then {
if pl_state.battery_voltage < access_config().shutoff_voltage {
self.voltage_low += 1;
2024-01-22 12:40:29 +11:00
if (self.voltage_low * delta_time)
2024-01-22 10:38:34 +11:00
>= access_config().shutoff_voltage_time_seconds
{
return Some(InterfaceRequest::StopCharge);
}
} else {
self.voltage_low = 0;
if self.tcrc_state.read().is_ok_and(|v| v.control_enable) {
2024-01-22 12:40:29 +11:00
return self.pid.step(&pl_state, &charge_state, delta_time as f64).map(InterfaceRequest::SetChargeRate);
2024-01-16 11:00:11 +11:00
}
}
}
}
None
}
pub fn process_request(&mut self, message: TcrcRequest) {
match message {
TcrcRequest::DisableAutomaticControl => {
self.tcrc_state
.write()
.expect("failed to write to tcrc state")
.control_enable = false;
self.control_enable_gauge.set(0.);
}
TcrcRequest::EnableAutomaticControl => {
self.tcrc_state
.write()
.expect("failed to write to tcrc state")
.control_enable = true;
self.control_enable_gauge.set(1.);
}
}
}
}
2024-01-22 12:40:29 +11:00
#[derive(Default)]
struct PidLoop {
previous_error: f64,
}
2024-01-16 11:00:11 +11:00
2024-01-22 12:40:29 +11:00
impl PidLoop {
fn step(
&mut self,
pl_state: &PlState,
charge_state: &ChargeState,
delta_time: f64,
) -> Option<i64> {
let error = pl_state.target_voltage - pl_state.battery_voltage;
let derivative = (error - self.previous_error) / delta_time;
let config = access_config();
let extra_offsets = if pl_state.internal_load_current > 1. {
1.
} else {
0.
};
let new_target = ((config.pid_controls.proportional_gain * error)
+ (config.pid_controls.derivative_gain * derivative))
+ (charge_state.charge_amps as f64)
+ extra_offsets;
self.previous_error = error;
let new_target_int = new_target.round() as i64;
valid_rate(new_target_int, charge_state.charge_amps)
}
2024-01-16 11:00:11 +11:00
}
fn valid_rate(rate: i64, previous: i64) -> Option<i64> {
let config = access_config();
2024-01-17 08:35:00 +11:00
let new = rate.clamp(config.min_rate, config.max_rate);
if new == previous {
None
} else {
Some(new)
}
}