2024-12-28 20:39:07 +11:00
|
|
|
pub struct Controller {
|
|
|
|
name: String,
|
|
|
|
interval: std::time::Duration,
|
|
|
|
inner: ControllerInner,
|
|
|
|
data: std::sync::Arc<tokio::sync::RwLock<CommonData>>,
|
2024-12-30 13:47:01 +11:00
|
|
|
voltage_rx: Option<tokio::sync::mpsc::UnboundedReceiver<VoltageCommand>>,
|
|
|
|
voltage_tx: Option<MultiTx>,
|
2024-12-28 20:39:07 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default, serde::Serialize, Clone)]
|
|
|
|
pub struct CommonData {
|
|
|
|
pub battery_voltage: f64,
|
|
|
|
pub target_voltage: f64,
|
|
|
|
pub battery_temp: f64,
|
|
|
|
}
|
|
|
|
|
2024-12-30 13:47:01 +11:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub enum VoltageCommand {
|
|
|
|
Set(f64),
|
|
|
|
}
|
|
|
|
|
2024-12-28 20:39:07 +11:00
|
|
|
impl Controller {
|
2024-12-30 13:47:01 +11:00
|
|
|
pub fn new(
|
|
|
|
config: crate::config::ChargeControllerConfig,
|
|
|
|
) -> eyre::Result<(
|
|
|
|
Self,
|
|
|
|
Option<tokio::sync::mpsc::UnboundedSender<VoltageCommand>>,
|
|
|
|
)> {
|
2024-12-28 20:39:07 +11:00
|
|
|
let inner = match config.variant {
|
|
|
|
crate::config::ChargeControllerVariant::Tristar => ControllerInner::Tristar(
|
2024-12-28 21:09:19 +11:00
|
|
|
crate::tristar::Tristar::new(&config.serial_port, &config.name, config.baud_rate)?,
|
2024-12-28 20:39:07 +11:00
|
|
|
),
|
|
|
|
crate::config::ChargeControllerVariant::Pl {
|
|
|
|
timeout_milliseconds,
|
|
|
|
} => ControllerInner::Pl(crate::pl::Pli::new(
|
2024-12-28 21:09:19 +11:00
|
|
|
&config.serial_port,
|
|
|
|
&config.name,
|
2024-12-28 20:39:07 +11:00
|
|
|
config.baud_rate,
|
|
|
|
timeout_milliseconds,
|
|
|
|
)?),
|
|
|
|
};
|
|
|
|
|
|
|
|
let data = CommonData::default();
|
|
|
|
|
|
|
|
let data = std::sync::Arc::new(tokio::sync::RwLock::new(data));
|
|
|
|
|
2024-12-30 13:47:01 +11:00
|
|
|
let (voltage_tx, voltage_rx) = if config.follow_primary {
|
|
|
|
let (a, b) = tokio::sync::mpsc::unbounded_channel();
|
|
|
|
(Some(a), Some(b))
|
|
|
|
} else {
|
|
|
|
(None, None)
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok((
|
|
|
|
Self {
|
|
|
|
name: config.name,
|
|
|
|
interval: std::time::Duration::from_secs(config.watch_interval_seconds),
|
|
|
|
inner,
|
|
|
|
data,
|
|
|
|
voltage_rx,
|
|
|
|
voltage_tx: None,
|
|
|
|
},
|
|
|
|
voltage_tx,
|
|
|
|
))
|
2024-12-28 20:39:07 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_data_ptr(&self) -> std::sync::Arc<tokio::sync::RwLock<CommonData>> {
|
|
|
|
self.data.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn refresh(&mut self) -> eyre::Result<()> {
|
|
|
|
let data = self.inner.refresh().await?;
|
2024-12-30 13:47:01 +11:00
|
|
|
|
|
|
|
if let Some(tx) = self.voltage_tx.as_mut() {
|
|
|
|
if *crate::IS_PRINTING_TARGETS {
|
|
|
|
log::error!(
|
|
|
|
"tristar {}: primary: sending target voltage {}",
|
|
|
|
self.name,
|
|
|
|
data.target_voltage
|
|
|
|
);
|
|
|
|
}
|
|
|
|
tx.send_to_all(VoltageCommand::Set(data.target_voltage));
|
|
|
|
}
|
|
|
|
|
2024-12-28 20:39:07 +11:00
|
|
|
*self.data.write().await = data;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn timeout_interval(&self) -> std::time::Duration {
|
|
|
|
self.interval
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn name(&self) -> &str {
|
|
|
|
&self.name
|
|
|
|
}
|
2024-12-30 13:47:01 +11:00
|
|
|
|
|
|
|
pub fn set_tx_to_secondary(
|
|
|
|
&mut self,
|
|
|
|
tx: Vec<tokio::sync::mpsc::UnboundedSender<VoltageCommand>>,
|
|
|
|
) {
|
|
|
|
if self.voltage_rx.is_some() {
|
|
|
|
panic!(
|
|
|
|
"trying to set {} as primary when it is also a secondary!",
|
|
|
|
self.name
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.voltage_tx = Some(MultiTx(tx))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_rx(&mut self) -> Option<&mut tokio::sync::mpsc::UnboundedReceiver<VoltageCommand>> {
|
|
|
|
self.voltage_rx.as_mut()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn process_command(&mut self, command: VoltageCommand) -> eyre::Result<()> {
|
|
|
|
match command {
|
|
|
|
VoltageCommand::Set(target_voltage) => {
|
|
|
|
self.inner.set_target_voltage(target_voltage).await
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MultiTx(Vec<tokio::sync::mpsc::UnboundedSender<VoltageCommand>>);
|
|
|
|
|
|
|
|
impl MultiTx {
|
|
|
|
fn send_to_all(&mut self, command: VoltageCommand) {
|
|
|
|
for sender in self.0.iter_mut() {
|
|
|
|
if let Err(e) = sender.send(command) {
|
|
|
|
log::error!("failed to send command {command:?}: {e:?}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-12-28 20:39:07 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum ControllerInner {
|
|
|
|
Pl(crate::pl::Pli),
|
|
|
|
Tristar(crate::tristar::Tristar),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ControllerInner {
|
|
|
|
pub async fn refresh(&mut self) -> eyre::Result<CommonData> {
|
|
|
|
match self {
|
|
|
|
ControllerInner::Pl(pli) => {
|
2024-12-28 22:39:15 +11:00
|
|
|
let pl_data = pli.refresh().await?;
|
2024-12-28 20:39:07 +11:00
|
|
|
Ok(pl_data)
|
|
|
|
}
|
|
|
|
ControllerInner::Tristar(tristar) => {
|
|
|
|
let tristar_data = tristar.refresh().await?;
|
|
|
|
Ok(tristar_data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-12-30 13:47:01 +11:00
|
|
|
|
|
|
|
pub async fn set_target_voltage(&mut self, target_voltage: f64) -> eyre::Result<()> {
|
|
|
|
match self {
|
|
|
|
ControllerInner::Pl(_) => todo!(),
|
|
|
|
ControllerInner::Tristar(tristar) => tristar.set_target_voltage(target_voltage).await,
|
|
|
|
}
|
|
|
|
}
|
2024-12-28 20:39:07 +11:00
|
|
|
}
|