From bda2d63e994ed815b7479ab827cbdb18d4850589 Mon Sep 17 00:00:00 2001
From: Alex Janka <alex@alexjanka.com>
Date: Sat, 28 Dec 2024 22:39:15 +1100
Subject: [PATCH] pl: async serial

---
 Cargo.lock                                    |  1 -
 charge-controller-supervisor/Cargo.toml       |  1 -
 .../src/controller.rs                         |  2 +-
 charge-controller-supervisor/src/pl.rs        | 87 +++++++++----------
 4 files changed, 42 insertions(+), 49 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index e889205..48b01bd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -252,7 +252,6 @@ dependencies = [
  "rocket",
  "serde",
  "serde_json",
- "serialport",
  "tokio",
  "tokio-modbus",
  "tokio-serial",
diff --git a/charge-controller-supervisor/Cargo.toml b/charge-controller-supervisor/Cargo.toml
index a5bebc2..f56b4a1 100644
--- a/charge-controller-supervisor/Cargo.toml
+++ b/charge-controller-supervisor/Cargo.toml
@@ -21,7 +21,6 @@ chrono = "0.4.39"
 prometheus = "0.13.4"
 env_logger = "0.11.6"
 log = "0.4.22"
-serialport = "4.6.1"
 futures = "0.3.31"
 tokio-modbus = "0.16.1"
 tokio-serial = "5.4.4"
diff --git a/charge-controller-supervisor/src/controller.rs b/charge-controller-supervisor/src/controller.rs
index 5df03a5..6932d71 100644
--- a/charge-controller-supervisor/src/controller.rs
+++ b/charge-controller-supervisor/src/controller.rs
@@ -69,7 +69,7 @@ impl ControllerInner {
     pub async fn refresh(&mut self) -> eyre::Result<CommonData> {
         match self {
             ControllerInner::Pl(pli) => {
-                let pl_data = pli.refresh()?;
+                let pl_data = pli.refresh().await?;
                 Ok(pl_data)
             }
             ControllerInner::Tristar(tristar) => {
diff --git a/charge-controller-supervisor/src/pl.rs b/charge-controller-supervisor/src/pl.rs
index f684d9b..e36689c 100644
--- a/charge-controller-supervisor/src/pl.rs
+++ b/charge-controller-supervisor/src/pl.rs
@@ -1,8 +1,7 @@
-use std::{io::Write, time::Duration};
-
 use chrono::Timelike;
 use serde::{Deserialize, Serialize};
-use serialport::SerialPort;
+use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use tokio_serial::{SerialPort, SerialPortBuilderExt};
 
 use crate::gauges::{
     BATTERY_TEMP, BATTERY_VOLTAGE, CHARGE_STATE, INPUT_CURRENT, PL_DUTY_CYCLE, PL_LOAD_CURRENT,
@@ -11,7 +10,7 @@ use crate::gauges::{
 
 pub struct Pli {
     friendly_name: String,
-    port: Box<dyn SerialPort>,
+    port: tokio_serial::SerialStream,
 }
 
 #[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
@@ -111,10 +110,10 @@ impl Pli {
         friendly_name: &str,
         baud_rate: u32,
         timeout: u64,
-    ) -> Result<Self, serialport::Error> {
-        let port = serialport::new(serial_port, baud_rate)
-            .timeout(Duration::from_millis(timeout))
-            .open()?;
+    ) -> Result<Self, tokio_serial::Error> {
+        let port = tokio_serial::new(serial_port, baud_rate)
+            .timeout(std::time::Duration::from_millis(timeout))
+            .open_native_async()?;
 
         Ok(Self {
             friendly_name: friendly_name.to_owned(),
@@ -122,8 +121,8 @@ impl Pli {
         })
     }
 
-    pub fn refresh(&mut self) -> eyre::Result<crate::controller::CommonData> {
-        let new_state = self.read_state()?;
+    pub async fn refresh(&mut self) -> eyre::Result<crate::controller::CommonData> {
+        let new_state = self.read_state().await?;
         BATTERY_VOLTAGE
             .with_label_values(&[&self.friendly_name])
             .set(new_state.battery_voltage);
@@ -153,14 +152,14 @@ impl Pli {
     }
 
     #[expect(dead_code, reason = "writing settings is not yet implemented")]
-    pub fn process_request(&mut self, message: PliRequest) -> eyre::Result<()> {
+    pub async fn process_request(&mut self, message: PliRequest) -> eyre::Result<()> {
         match message {
             PliRequest::ReadRam(address) => {
-                let data = self.read_ram(address)?;
+                let data = self.read_ram(address).await?;
                 log::warn!("Read RAM at {address}: data {data}");
             }
             PliRequest::ReadEeprom(address) => {
-                let data = self.read_eeprom(address)?;
+                let data = self.read_eeprom(address).await?;
                 log::warn!("Read EEPROM at {address}: data {data}");
             }
             PliRequest::SyncTime => {
@@ -168,85 +167,81 @@ impl Pli {
                 let timestamp = (((now.hour() * 10) + (now.minute() / 6)) & 0xFF) as u8;
                 let min = (now.minute() % 6) as u8;
                 let sec = (now.second() / 2).min(29) as u8;
-                self.write_ram(PlRamAddress::Hour, timestamp)?;
-                self.write_ram(PlRamAddress::Min, min)?;
-                self.write_ram(PlRamAddress::Sec, sec)?;
+                self.write_ram(PlRamAddress::Hour, timestamp).await?;
+                self.write_ram(PlRamAddress::Min, min).await?;
+                self.write_ram(PlRamAddress::Sec, sec).await?;
                 log::warn!(
                     "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())?;
+                self.write_ram(PlRamAddress::Rstate, state.into()).await?;
             }
         }
         Ok(())
     }
 
-    fn read_state(&mut self) -> eyre::Result<PlState> {
+    async fn read_state(&mut self) -> eyre::Result<PlState> {
         Ok(PlState {
-            battery_voltage: f64::from(self.read_ram(PlRamAddress::Batv)?) * (4. / 10.),
-            target_voltage: f64::from(self.read_ram(PlRamAddress::Vreg)?) * (4. / 10.),
-            duty_cycle: f64::from(self.read_ram(PlRamAddress::Dutycyc)?) / 255.,
-            internal_charge_current: f64::from(self.read_ram(PlRamAddress::Cint)?) * (4. / 10.),
-            internal_load_current: f64::from(self.read_ram(PlRamAddress::Lint)?) * (4. / 10.),
-            battery_temp: f64::from(self.read_ram(PlRamAddress::Battemp)?),
-            regulator_state: self.read_ram(PlRamAddress::Rstate)?.into(),
+            battery_voltage: f64::from(self.read_ram(PlRamAddress::Batv).await?) * (4. / 10.),
+            target_voltage: f64::from(self.read_ram(PlRamAddress::Vreg).await?) * (4. / 10.),
+            duty_cycle: f64::from(self.read_ram(PlRamAddress::Dutycyc).await?) / 255.,
+            internal_charge_current: f64::from(self.read_ram(PlRamAddress::Cint).await?)
+                * (4. / 10.),
+            internal_load_current: f64::from(self.read_ram(PlRamAddress::Lint).await?) * (4. / 10.),
+            battery_temp: f64::from(self.read_ram(PlRamAddress::Battemp).await?),
+            regulator_state: self.read_ram(PlRamAddress::Rstate).await?.into(),
         })
     }
 
-    fn send_command(&mut self, req: [u8; 4]) -> eyre::Result<()> {
+    async fn send_command(&mut self, req: [u8; 4]) -> eyre::Result<()> {
         self.flush()?;
-        self.port.write_all(&req)?;
+        self.port.write_all(&req).await?;
         Ok(())
     }
 
     fn flush(&mut self) -> eyre::Result<()> {
-        self.port.flush()?;
-        while let Ok(num) = self.port.bytes_to_read() {
-            if num == 0 {
-                return Ok(());
-            }
-            let _ = self.port.read(&mut [0; 8]);
-        }
+        self.port.clear(tokio_serial::ClearBuffer::All)?;
         Ok(())
     }
 
-    fn receive<const LENGTH: usize>(&mut self) -> eyre::Result<[u8; LENGTH]> {
+    async fn receive<const LENGTH: usize>(&mut self) -> eyre::Result<[u8; LENGTH]> {
         let mut buf = [0; LENGTH];
-        self.port.read_exact(&mut buf)?;
+        self.port.read_exact(&mut buf).await?;
         Ok(buf)
     }
 
-    fn read_ram<T>(&mut self, address: T) -> eyre::Result<u8>
+    async fn read_ram<T>(&mut self, address: T) -> eyre::Result<u8>
     where
         T: Into<u8>,
     {
-        self.send_command(command(20, address.into(), 0))?;
-        let buf = self.receive::<1>()?;
+        self.send_command(command(20, address.into(), 0)).await?;
+        let buf = self.receive::<1>().await?;
         if buf[0] == 200 {
-            Ok(self.receive::<1>()?[0])
+            Ok(self.receive::<1>().await?[0])
         } else {
             Err(eyre::eyre!("read error: result is {}", buf[0]))
         }
     }
 
-    fn write_ram<T>(&mut self, address: T, data: u8) -> eyre::Result<()>
+    async fn write_ram<T>(&mut self, address: T, data: u8) -> eyre::Result<()>
     where
         T: Into<u8>,
     {
-        self.send_command(command(152, address.into(), data))?;
+        self.send_command(command(152, address.into(), data))
+            .await?;
         Ok(())
     }
 
-    fn read_eeprom<T>(&mut self, address: T) -> eyre::Result<u8>
+    async fn read_eeprom<T>(&mut self, address: T) -> eyre::Result<u8>
     where
         T: Into<u8>,
     {
-        self.send_command(command(72, address.into(), 0))?;
-        let buf = self.receive::<1>()?;
+        self.send_command(command(72, address.into(), 0)).await?;
+        let buf = self.receive::<1>().await?;
         if buf[0] == 200 {
-            Ok(self.receive::<1>()?[0])
+            Ok(self.receive::<1>().await?[0])
         } else {
             Err(eyre::eyre!("read error: result is {}", buf[0]))
         }