use crate::protocol::*;
use std::time::{Duration, Instant};
#[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("Serialport error: {0}")]
Serial(#[from] serialport::Error),
}
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct DalyBMS {
serial: Box<dyn serialport::SerialPort>,
last_execution: Instant,
delay: Duration,
status: Option<Status>, retries: u8,
}
impl DalyBMS {
pub fn new(port: &str) -> Result<Self> {
Ok(Self {
serial: serialport::new(port, 9600)
.data_bits(serialport::DataBits::Eight)
.parity(serialport::Parity::None)
.stop_bits(serialport::StopBits::One)
.flow_control(serialport::FlowControl::None)
.open()?,
last_execution: Instant::now(),
delay: MINIMUM_DELAY, status: None,
retries: 3,
})
}
pub fn set_retry(&mut self, n_retries: u8) {
self.retries = n_retries;
}
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) {
std::thread::sleep(time_until_delay_reached);
}
}
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 = self.serial.read(buf.as_mut_slice())?;
log::trace!("{received} pending bytes consumed");
} else {
break;
}
}
self.serial_await_delay();
log::trace!("write bytes: {tx_buffer:02X?}");
self.serial.write_all(tx_buffer)?;
if false {
log::trace!("flush connection");
self.serial.flush()?;
}
Ok(())
}
fn receive_bytes(&mut self, size: usize) -> Result<Vec<u8>> {
let mut rx_buffer = vec![0; size];
log::trace!("read {size} bytes");
self.serial.read_exact(&mut rx_buffer)?;
self.last_execution = Instant::now();
log::trace!("receive bytes: {rx_buffer:02X?}");
Ok(rx_buffer)
}
fn send_and_receive(&mut self, tx_buffer: &[u8], reply_size: usize) -> Result<Vec<u8>> {
self.send_bytes(tx_buffer)?;
self.receive_bytes(reply_size)
}
pub fn set_timeout(&mut self, timeout: Duration) -> Result<()> {
log::trace!("set timeout to {timeout:?}");
self.serial.set_timeout(timeout).map_err(Error::from)
}
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);
}
fn request_with_retry<F, T>(
&mut self,
tx_buffer: &[u8],
reply_size: usize,
request: F,
) -> Result<T>
where
F: Fn(&mut Self, &[u8], usize) -> Result<T>,
{
for t in 0..self.retries {
match request(self, tx_buffer, reply_size) {
Ok(result) => {
return Ok(result);
}
Err(err) => {
log::trace!(
"Failed try {} of {}, repeating ({err})",
t + 1,
self.retries
);
}
}
}
request(self, tx_buffer, reply_size)
}
pub fn get_soc(&mut self) -> Result<Soc> {
log::trace!("get SOC");
self.request_with_retry(
&Soc::request(Address::Host),
Soc::reply_size(),
|bms, tx_buffer, reply_size| {
Ok(Soc::decode(&bms.send_and_receive(tx_buffer, reply_size)?)?)
},
)
}
pub fn get_cell_voltage_range(&mut self) -> Result<CellVoltageRange> {
log::trace!("get cell voltage range");
self.request_with_retry(
&CellVoltageRange::request(Address::Host),
CellVoltageRange::reply_size(),
|bms, tx_buffer, reply_size| {
Ok(CellVoltageRange::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
)?)
},
)
}
pub fn get_temperature_range(&mut self) -> Result<TemperatureRange> {
log::trace!("get temperature range");
self.request_with_retry(
&TemperatureRange::request(Address::Host),
TemperatureRange::reply_size(),
|bms, tx_buffer, reply_size| {
Ok(TemperatureRange::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
)?)
},
)
}
pub fn get_mosfet_status(&mut self) -> Result<MosfetStatus> {
log::trace!("get mosfet status");
self.request_with_retry(
&MosfetStatus::request(Address::Host),
MosfetStatus::reply_size(),
|bms, tx_buffer, reply_size| {
Ok(MosfetStatus::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
)?)
},
)
}
pub fn get_status(&mut self) -> Result<Status> {
log::trace!("get status");
self.request_with_retry(
&Status::request(Address::Host),
Status::reply_size(),
|bms, tx_buffer, reply_size| {
let status = Status::decode(&bms.send_and_receive(tx_buffer, reply_size)?)?;
bms.status = Some(status.clone()); Ok(status)
},
)
}
pub 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);
};
self.request_with_retry(
&CellVoltages::request(Address::Host),
CellVoltages::reply_size(n_cells),
|bms, tx_buffer, reply_size| {
Ok(CellVoltages::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
n_cells,
)?)
},
)
}
pub 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);
};
self.request_with_retry(
&CellTemperatures::request(Address::Host),
CellTemperatures::reply_size(n_sensors),
|bms, tx_buffer, reply_size| {
Ok(CellTemperatures::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
n_sensors,
)?)
},
)
}
pub 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);
};
self.request_with_retry(
&CellBalanceState::request(Address::Host),
CellBalanceState::reply_size(),
|bms, tx_buffer, reply_size| {
Ok(CellBalanceState::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
n_cells,
)?)
},
)
}
pub fn get_errors(&mut self) -> Result<Vec<ErrorCode>> {
log::trace!("get errors");
self.request_with_retry(
&ErrorCode::request(Address::Host),
ErrorCode::reply_size(),
|bms, tx_buffer, reply_size| {
Ok(ErrorCode::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
)?)
},
)
}
pub fn set_discharge_mosfet(&mut self, enable: bool) -> Result<()> {
log::trace!("set discharge mosfet to {enable}");
self.request_with_retry(
&SetDischargeMosfet::request(Address::Host, enable),
SetDischargeMosfet::reply_size(),
|bms, tx_buffer, reply_size| {
Ok(SetDischargeMosfet::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
)?)
},
)
}
pub fn set_charge_mosfet(&mut self, enable: bool) -> Result<()> {
log::trace!("set charge mosfet to {enable}");
self.request_with_retry(
&SetChargeMosfet::request(Address::Host, enable),
SetChargeMosfet::reply_size(),
|bms, tx_buffer, reply_size| {
Ok(SetChargeMosfet::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
)?)
},
)
}
pub fn set_soc(&mut self, soc_percent: f32) -> Result<()> {
log::trace!("set SOC to {soc_percent}");
self.request_with_retry(
&SetSoc::request(Address::Host, soc_percent),
SetSoc::reply_size(),
|bms, tx_buffer, reply_size| {
Ok(SetSoc::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
)?)
},
)
}
pub fn reset(&mut self) -> Result<()> {
log::trace!("reset to factory default settings");
self.request_with_retry(
&BmsReset::request(Address::Host),
BmsReset::reply_size(),
|bms, tx_buffer, reply_size| {
Ok(BmsReset::decode(
&bms.send_and_receive(tx_buffer, reply_size)?,
)?)
},
)
}
}