2024-01-11 10:28:01 +11:00
|
|
|
use std::{
|
|
|
|
io::Write,
|
|
|
|
sync::{Arc, RwLock},
|
|
|
|
time::Duration,
|
|
|
|
};
|
2024-01-10 15:22:28 +11:00
|
|
|
|
2024-01-10 15:45:54 +11:00
|
|
|
use anyhow::Context;
|
2024-01-10 15:22:28 +11:00
|
|
|
use metrics::{describe_gauge, gauge, Gauge};
|
2024-01-11 10:28:01 +11:00
|
|
|
use serde::{Deserialize, Serialize};
|
2024-01-10 15:22:28 +11:00
|
|
|
use serialport::SerialPort;
|
2024-01-11 08:57:41 +11:00
|
|
|
use termcolor::WriteColor;
|
2024-01-10 15:22:28 +11:00
|
|
|
|
|
|
|
pub struct Pli {
|
2024-01-11 10:28:01 +11:00
|
|
|
pub state: Arc<RwLock<PlState>>,
|
2024-01-10 15:22:28 +11:00
|
|
|
port: Box<dyn SerialPort>,
|
|
|
|
voltage_gauge: Gauge,
|
2024-01-11 13:13:30 +11:00
|
|
|
target_voltage_gauge: Gauge,
|
2024-01-10 15:45:54 +11:00
|
|
|
duty_cycle: Gauge,
|
2024-01-10 16:27:02 +11:00
|
|
|
internal_charge_current: Gauge,
|
|
|
|
internal_load_current: Gauge,
|
2024-01-11 10:28:01 +11:00
|
|
|
regulator_gauges: RegulatorGauges,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
|
|
|
pub struct PlState {
|
|
|
|
pub battery_voltage: f64,
|
2024-01-11 13:13:30 +11:00
|
|
|
pub target_voltage: f64,
|
2024-01-11 10:28:01 +11:00
|
|
|
pub duty_cycle: f64,
|
|
|
|
pub internal_charge_current: f64,
|
|
|
|
pub internal_load_current: f64,
|
|
|
|
pub regulator_state: RegulatorState,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for PlState {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
battery_voltage: Default::default(),
|
2024-01-11 13:13:30 +11:00
|
|
|
target_voltage: Default::default(),
|
2024-01-11 10:28:01 +11:00
|
|
|
duty_cycle: Default::default(),
|
|
|
|
internal_charge_current: Default::default(),
|
|
|
|
internal_load_current: Default::default(),
|
|
|
|
regulator_state: RegulatorState::Absorption,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
|
|
|
pub enum RegulatorState {
|
|
|
|
Boost,
|
|
|
|
Equalise,
|
|
|
|
Absorption,
|
|
|
|
Float,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u8> for RegulatorState {
|
|
|
|
fn from(value: u8) -> Self {
|
|
|
|
match value & 0b11 {
|
|
|
|
0b00 => Self::Boost,
|
|
|
|
0b01 => Self::Equalise,
|
|
|
|
0b10 => Self::Absorption,
|
|
|
|
0b11 => Self::Float,
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct RegulatorGauges {
|
|
|
|
boost: Gauge,
|
|
|
|
equalise: Gauge,
|
|
|
|
absorption: Gauge,
|
|
|
|
float: Gauge,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RegulatorGauges {
|
|
|
|
fn new() -> Self {
|
|
|
|
describe_gauge!("pl_regulator", "Regulator state");
|
|
|
|
let boost = gauge!("pl_regulator", "state" => "boost");
|
|
|
|
let equalise = gauge!("pl_regulator", "state" => "equalise");
|
|
|
|
let absorption = gauge!("pl_regulator", "state" => "absorption");
|
|
|
|
let float = gauge!("pl_regulator", "state" => "float");
|
|
|
|
Self {
|
|
|
|
boost,
|
|
|
|
equalise,
|
|
|
|
absorption,
|
|
|
|
float,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set(&mut self, state: &RegulatorState) {
|
|
|
|
match state {
|
|
|
|
RegulatorState::Boost => {
|
|
|
|
self.boost.set(1.);
|
|
|
|
self.equalise.set(0.);
|
|
|
|
self.absorption.set(0.);
|
|
|
|
self.float.set(0.);
|
|
|
|
}
|
|
|
|
RegulatorState::Equalise => {
|
|
|
|
self.boost.set(0.);
|
|
|
|
self.equalise.set(1.);
|
|
|
|
self.absorption.set(0.);
|
|
|
|
self.float.set(0.);
|
|
|
|
}
|
|
|
|
RegulatorState::Absorption => {
|
|
|
|
self.boost.set(0.);
|
|
|
|
self.equalise.set(0.);
|
|
|
|
self.absorption.set(1.);
|
|
|
|
self.float.set(0.);
|
|
|
|
}
|
|
|
|
RegulatorState::Float => {
|
|
|
|
self.boost.set(0.);
|
|
|
|
self.equalise.set(0.);
|
|
|
|
self.absorption.set(0.);
|
|
|
|
self.float.set(1.);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-01-10 15:22:28 +11:00
|
|
|
}
|
|
|
|
|
2024-01-11 08:57:41 +11:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub enum PliRequest {
|
|
|
|
ReadRam(u8),
|
|
|
|
}
|
|
|
|
|
2024-01-10 15:22:28 +11:00
|
|
|
impl Pli {
|
|
|
|
pub fn new(serial_port: String, baud_rate: u32) -> anyhow::Result<Self> {
|
|
|
|
let port = serialport::new(serial_port, baud_rate)
|
|
|
|
.timeout(Duration::from_millis(250))
|
|
|
|
.open()?;
|
|
|
|
|
|
|
|
describe_gauge!("pl_battery_voltage", "Battery voltage");
|
|
|
|
let voltage_gauge = gauge!("pl_battery_voltage");
|
2024-01-11 13:13:30 +11:00
|
|
|
describe_gauge!("pl_target_voltage", "Target voltage");
|
|
|
|
let target_voltage_gauge = gauge!("pl_target_voltage");
|
2024-01-10 15:45:54 +11:00
|
|
|
describe_gauge!("pl_duty_cycle", "Duty cycle");
|
|
|
|
let duty_cycle = gauge!("pl_duty_cycle");
|
2024-01-10 16:27:02 +11:00
|
|
|
describe_gauge!("pl_internal_charge_current", "Internal charge current");
|
|
|
|
let internal_charge_current = gauge!("pl_internal_charge_current");
|
|
|
|
describe_gauge!("pl_internal_load_current", "Internal load current");
|
|
|
|
let internal_load_current = gauge!("pl_internal_load_current");
|
2024-01-10 15:45:54 +11:00
|
|
|
|
2024-01-10 15:22:28 +11:00
|
|
|
Ok(Self {
|
2024-01-11 10:28:01 +11:00
|
|
|
state: Arc::new(RwLock::new(Default::default())),
|
2024-01-10 15:22:28 +11:00
|
|
|
port,
|
|
|
|
voltage_gauge,
|
2024-01-11 13:13:30 +11:00
|
|
|
target_voltage_gauge,
|
2024-01-10 15:45:54 +11:00
|
|
|
duty_cycle,
|
2024-01-10 16:27:02 +11:00
|
|
|
internal_charge_current,
|
|
|
|
internal_load_current,
|
2024-01-11 10:28:01 +11:00
|
|
|
regulator_gauges: RegulatorGauges::new(),
|
2024-01-10 15:22:28 +11:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn refresh(&mut self) {
|
2024-01-11 10:28:01 +11:00
|
|
|
if let Ok(new_state) = self.read_state() {
|
|
|
|
self.voltage_gauge.set(new_state.battery_voltage);
|
2024-01-11 13:13:30 +11:00
|
|
|
self.target_voltage_gauge.set(new_state.target_voltage);
|
2024-01-11 10:28:01 +11:00
|
|
|
self.duty_cycle.set(new_state.duty_cycle);
|
2024-01-10 16:27:02 +11:00
|
|
|
self.internal_charge_current
|
2024-01-11 10:28:01 +11:00
|
|
|
.set(new_state.internal_charge_current);
|
2024-01-10 16:27:02 +11:00
|
|
|
self.internal_load_current
|
2024-01-11 10:28:01 +11:00
|
|
|
.set(new_state.internal_load_current);
|
|
|
|
self.regulator_gauges.set(&new_state.regulator_state);
|
|
|
|
|
|
|
|
*self.state.write().expect("PLI state handler panicked!!") = new_state;
|
2024-01-10 16:27:02 +11:00
|
|
|
}
|
2024-01-10 15:22:28 +11:00
|
|
|
}
|
|
|
|
|
2024-01-11 08:57:41 +11:00
|
|
|
pub fn process_request(&mut self, message: PliRequest) {
|
|
|
|
match message {
|
|
|
|
PliRequest::ReadRam(address) => match self.read_ram(address) {
|
|
|
|
Ok(data) => {
|
|
|
|
let mut stdout =
|
|
|
|
termcolor::StandardStream::stdout(termcolor::ColorChoice::Always);
|
|
|
|
let _ = stdout.set_color(
|
|
|
|
termcolor::ColorSpec::new().set_fg(Some(termcolor::Color::Green)),
|
|
|
|
);
|
|
|
|
if writeln!(&mut stdout, "Read RAM at {address}: data {data}").is_err() {
|
|
|
|
log::warn!(
|
|
|
|
"Failed to set stdout colour\nRead RAM at {address}: data {data}"
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
Err(e) => log::error!("RAM read error: {e:?}"),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-11 10:28:01 +11:00
|
|
|
fn read_state(&mut self) -> anyhow::Result<PlState> {
|
|
|
|
Ok(PlState {
|
|
|
|
battery_voltage: (self.read_ram(PlRamAddress::Batv)? as f64) * (4. / 10.),
|
2024-01-11 13:13:30 +11:00
|
|
|
target_voltage: (self.read_ram(PlRamAddress::Vreg)? as f64) * (4. / 10.),
|
2024-01-11 10:28:01 +11:00
|
|
|
duty_cycle: (self.read_ram(PlRamAddress::Dutycyc)? as f64) / 255.,
|
|
|
|
internal_charge_current: (self.read_ram(PlRamAddress::Cint)? as f64) * (4. / 10.),
|
|
|
|
internal_load_current: (self.read_ram(PlRamAddress::Lint)? as f64) * (4. / 10.),
|
|
|
|
regulator_state: self.read_ram(PlRamAddress::Rstate)?.into(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-01-10 15:22:28 +11:00
|
|
|
fn send_command(&mut self, req: [u8; 4]) {
|
|
|
|
self.port
|
|
|
|
.write_all(&req)
|
|
|
|
.expect("failed to write to serial port");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn receive<const LENGTH: usize>(&mut self) -> [u8; LENGTH] {
|
|
|
|
let mut buf = [0; LENGTH];
|
|
|
|
match self.port.read_exact(&mut buf) {
|
2024-01-10 15:45:54 +11:00
|
|
|
Ok(_) => {}
|
|
|
|
Err(e) => log::error!("PLI serial read error: {e:#?}"),
|
2024-01-10 15:22:28 +11:00
|
|
|
}
|
|
|
|
buf
|
|
|
|
}
|
|
|
|
|
2024-01-11 08:57:41 +11:00
|
|
|
fn read_ram<T>(&mut self, address: T) -> anyhow::Result<u8>
|
|
|
|
where
|
|
|
|
T: Into<u8>,
|
|
|
|
{
|
2024-01-10 16:27:02 +11:00
|
|
|
self.send_command(command(20, address.into(), 0));
|
2024-01-10 15:45:54 +11:00
|
|
|
let buf: [u8; 2] = self.receive();
|
|
|
|
if buf[0] == 200 { Some(buf[1]) } else { None }
|
|
|
|
.context(format!("Error from PLI: {}", buf[0]))
|
2024-01-10 15:22:28 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-10 16:27:02 +11:00
|
|
|
enum PlRamAddress {
|
|
|
|
Dutycyc,
|
|
|
|
Batv,
|
2024-01-11 10:28:01 +11:00
|
|
|
Rstate,
|
2024-01-11 13:13:30 +11:00
|
|
|
Vreg,
|
2024-01-10 16:27:02 +11:00
|
|
|
Cint,
|
|
|
|
Lint,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<PlRamAddress> for u8 {
|
|
|
|
fn from(value: PlRamAddress) -> Self {
|
|
|
|
match value {
|
|
|
|
PlRamAddress::Dutycyc => 39,
|
|
|
|
PlRamAddress::Batv => 50,
|
2024-01-11 10:28:01 +11:00
|
|
|
PlRamAddress::Rstate => 101,
|
2024-01-11 13:13:30 +11:00
|
|
|
PlRamAddress::Vreg => 105,
|
2024-01-10 16:27:02 +11:00
|
|
|
PlRamAddress::Cint => 213,
|
|
|
|
PlRamAddress::Lint => 217,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-10 15:22:28 +11:00
|
|
|
fn command(operation: u8, address: u8, data: u8) -> [u8; 4] {
|
|
|
|
[operation, address, data, !operation]
|
|
|
|
}
|