use crate::protocol::*;
use std::time::{Duration, Instant};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio_serial::{SerialPort, SerialPortBuilderExt};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("get_status() has to be called at least once before")]
StatusError,
#[error("Daly error: {0}")]
DalyError(#[from] crate::Error),
#[error("IO error: {0}")]
IOError(#[from] std::io::Error),
#[error("Tokio serial error: {0}")]
TokioSerial(#[from] tokio_serial::Error),
#[error("Tokio timeout elapsed: {0}")]
TokioElapsed(#[from] tokio::time::error::Elapsed),
}
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct DalyBMS {
serial: tokio_serial::SerialStream,
last_execution: Instant,
io_timeout: Duration, delay: Duration, status: Option<Status>, retries: u8,
}
macro_rules! request_with_retry {
($self:ident, $X:ident, $request_bytes:expr, $reply_size:expr) => {{
'retry_block: {
for t in 0..$self.retries {
match $self.send_and_receive($request_bytes, $reply_size).await {
Ok(reply_bytes) => match $X::decode(&reply_bytes) {
Ok(result) => break 'retry_block Ok(result),
Err(err) => {
log::trace!(
"Failed try {} of {}, repeating ({err})",
t + 1,
$self.retries
);
}
},
Err(err) => {
log::trace!(
"Failed try {} of {}, repeating ({err})",
t + 1,
$self.retries
);
}
}
}
Ok($X::decode(
&$self.send_and_receive($request_bytes, $reply_size).await?,
)?)
}
}};
($self:ident, $X:ident, $request_bytes:expr, $reply_size:expr, $decode_arg:expr) => {{
'retry_block: {
for t in 0..$self.retries {
match $self.send_and_receive($request_bytes, $reply_size).await {
Ok(reply_bytes) => match $X::decode(&reply_bytes, $decode_arg) {
Ok(result) => break 'retry_block Ok(result),
Err(err) => {
log::trace!(
"Failed try {} of {}, repeating ({err})",
t + 1,
$self.retries
);
}
},
Err(err) => {
log::trace!(
"Failed try {} of {}, repeating ({err})",
t + 1,
$self.retries
);
}
}
}
Ok($X::decode(
&$self.send_and_receive($request_bytes, $reply_size).await?,
$decode_arg,
)?)
}
}};
}
impl DalyBMS {
pub fn new(port: &str) -> Result<Self> {
Ok(Self {
serial: tokio_serial::new(port, 9600)
.data_bits(tokio_serial::DataBits::Eight)
.parity(tokio_serial::Parity::None)
.stop_bits(tokio_serial::StopBits::One)
.flow_control(tokio_serial::FlowControl::None)
.open_native_async()?,
last_execution: Instant::now(),
delay: MINIMUM_DELAY, io_timeout: Duration::from_secs(5), status: None,
retries: 3,
})
}
pub fn set_retry(&mut self, n_retries: u8) {
self.retries = n_retries;
}
async fn serial_await_delay(&self) {
let last_exec_diff = Instant::now().duration_since(self.last_execution);
if let Some(time_until_delay_reached) = self.delay.checked_sub(last_exec_diff) {
tokio::time::sleep(time_until_delay_reached).await;
}
}
async fn send_bytes(&mut self, tx_buffer: &[u8]) -> Result<()> {
loop {
log::trace!("read to see if there is any pending data");
let pending = self.serial.bytes_to_read()?;
log::trace!("got {pending} pending bytes");
if pending > 0 {
let mut buf: Vec<u8> = vec![0; 64]; let received =
tokio::time::timeout(self.io_timeout, self.serial.read(buf.as_mut_slice()))
.await??;
log::trace!("{received} pending bytes consumed");
} else {
break;
}
}
self.serial_await_delay().await;
log::trace!("write bytes: {tx_buffer:02X?}");
tokio::time::timeout(self.io_timeout, self.serial.write_all(tx_buffer)).await??;
if false {
log::trace!("flush connection");
tokio::time::timeout(self.io_timeout, self.serial.flush()).await??;
}
Ok(())
}
async fn receive_bytes(&mut self, size: usize) -> Result<Vec<u8>> {
let mut rx_buffer = vec![0; size];
log::trace!("read {size} bytes");
tokio::time::timeout(self.io_timeout, self.serial.read_exact(&mut rx_buffer)).await??;
self.last_execution = Instant::now();
log::trace!("receive_bytes: {rx_buffer:02X?}");
Ok(rx_buffer)
}
async fn send_and_receive(&mut self, tx_buffer: &[u8], reply_size: usize) -> Result<Vec<u8>> {
self.send_bytes(tx_buffer).await?;
self.receive_bytes(reply_size).await
}
pub fn set_timeout(&mut self, timeout: Duration) -> Result<()> {
log::trace!("set timeout to {timeout:?}");
self.io_timeout = timeout;
Ok(())
}
pub fn set_delay(&mut self, delay: Duration) {
if delay < MINIMUM_DELAY {
log::warn!("delay {delay:?} lower minimum {MINIMUM_DELAY:?}, use minimum");
self.delay = MINIMUM_DELAY;
} else {
self.delay = delay;
}
log::trace!("set delay to {:?}", self.delay);
}
pub async fn get_soc(&mut self) -> Result<Soc> {
log::trace!("get SOC");
request_with_retry!(self, Soc, &Soc::request(Address::Host), Soc::reply_size())
}
pub async fn get_cell_voltage_range(&mut self) -> Result<CellVoltageRange> {
log::trace!("get cell voltage range");
request_with_retry!(
self,
CellVoltageRange,
&CellVoltageRange::request(Address::Host),
CellVoltageRange::reply_size()
)
}
pub async fn get_temperature_range(&mut self) -> Result<TemperatureRange> {
log::trace!("get temperature range");
request_with_retry!(
self,
TemperatureRange,
&TemperatureRange::request(Address::Host),
TemperatureRange::reply_size()
)
}
pub async fn get_mosfet_status(&mut self) -> Result<MosfetStatus> {
log::trace!("get mosfet status");
request_with_retry!(
self,
MosfetStatus,
&MosfetStatus::request(Address::Host),
MosfetStatus::reply_size()
)
}
pub async fn get_status(&mut self) -> Result<Status> {
log::trace!("get status");
match request_with_retry!(
self,
Status,
&Status::request(Address::Host),
Status::reply_size()
) {
Ok(status) => {
self.status = Some(status.clone()); Ok(status)
}
Err(err) => Err(err),
}
}
pub async fn get_cell_voltages(&mut self) -> Result<CellVoltages> {
log::trace!("get cell voltages");
let n_cells = if let Some(status) = &self.status {
status.cells
} else {
return Err(Error::StatusError);
};
request_with_retry!(
self,
CellVoltages,
&CellVoltages::request(Address::Host),
CellVoltages::reply_size(n_cells),
n_cells
)
}
pub async fn get_cell_temperatures(&mut self) -> Result<Vec<i32>> {
log::trace!("get cell temperatures");
let n_sensors = if let Some(status) = &self.status {
status.temperature_sensors
} else {
return Err(Error::StatusError);
};
request_with_retry!(
self,
CellTemperatures,
&CellTemperatures::request(Address::Host),
CellTemperatures::reply_size(n_sensors),
n_sensors
)
}
pub async fn get_balancing_status(&mut self) -> Result<Vec<bool>> {
log::trace!("get balancing status");
let n_cells = if let Some(status) = &self.status {
status.cells
} else {
return Err(Error::StatusError);
};
request_with_retry!(
self,
CellBalanceState,
&CellBalanceState::request(Address::Host),
CellBalanceState::reply_size(),
n_cells
)
}
pub async fn get_errors(&mut self) -> Result<Vec<ErrorCode>> {
log::trace!("get errors");
request_with_retry!(
self,
ErrorCode,
&ErrorCode::request(Address::Host),
ErrorCode::reply_size()
)
}
pub async fn set_discharge_mosfet(&mut self, enable: bool) -> Result<()> {
log::trace!("set discharge mosfet to {enable}");
request_with_retry!(
self,
SetDischargeMosfet,
&SetDischargeMosfet::request(Address::Host, enable),
SetDischargeMosfet::reply_size()
)
}
pub async fn set_charge_mosfet(&mut self, enable: bool) -> Result<()> {
log::trace!("set charge mosfet to {enable}");
request_with_retry!(
self,
SetChargeMosfet,
&SetChargeMosfet::request(Address::Host, enable),
SetChargeMosfet::reply_size()
)
}
pub async fn set_soc(&mut self, soc_percent: f32) -> Result<()> {
log::trace!("set SOC to {soc_percent}");
request_with_retry!(
self,
SetSoc,
&SetSoc::request(Address::Host, soc_percent),
SetSoc::reply_size()
)
}
pub async fn reset(&mut self) -> Result<()> {
log::trace!("reset to factory default settings");
request_with_retry!(
self,
BmsReset,
&BmsReset::request(Address::Host),
BmsReset::reply_size()
)
}
}