read-ram endpoint

This commit is contained in:
Alex Janka 2024-01-11 08:57:41 +11:00
parent c3469e5259
commit 162d32756c
5 changed files with 58 additions and 11 deletions

7
Cargo.lock generated
View file

@ -2243,9 +2243,9 @@ dependencies = [
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.4.0" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [ dependencies = [
"winapi-util", "winapi-util",
] ]
@ -2262,7 +2262,7 @@ dependencies = [
[[package]] [[package]]
name = "tesla-charge-controller" name = "tesla-charge-controller"
version = "0.1.13" version = "0.1.14"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-channel", "async-channel",
@ -2278,6 +2278,7 @@ dependencies = [
"ron", "ron",
"serde", "serde",
"serialport", "serialport",
"termcolor",
"teslatte", "teslatte",
"thiserror", "thiserror",
"tokio", "tokio",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "tesla-charge-controller" name = "tesla-charge-controller"
version = "0.1.13" version = "0.1.14"
edition = "2021" edition = "2021"
license = "MITNFA" license = "MITNFA"
description = "Controls Tesla charge rate based on solar charge data" description = "Controls Tesla charge rate based on solar charge data"
@ -30,3 +30,4 @@ prometheus = "0.13"
env_logger = "0.10" env_logger = "0.10"
log = "0.4" log = "0.4"
serialport = "4.3" serialport = "4.3"
termcolor = "1.4.1"

View file

@ -57,12 +57,15 @@ async fn main() {
ron::from_str(&std::fs::read_to_string(&config_path).unwrap()).unwrap(); ron::from_str(&std::fs::read_to_string(&config_path).unwrap()).unwrap();
// build the channel that takes messages from the webserver thread to the api thread // build the channel that takes messages from the webserver thread to the api thread
let (api_requests, receiver) = async_channel::unbounded(); let (api_requests, api_receiver) = async_channel::unbounded();
// and to the pli thread
let (pli_requests, pli_receiver) = async_channel::unbounded();
let server_handle = server::launch_server(server::ServerState { let server_handle = server::launch_server(server::ServerState {
config: config.clone(), config: config.clone(),
state: interface.state.clone(), state: interface.state.clone(),
api_requests, api_requests,
pli_requests,
}); });
// spawn the api loop // spawn the api loop
@ -72,7 +75,7 @@ async fn main() {
// await either the next interval OR a message from the other thread // await either the next interval OR a message from the other thread
tokio::select! { tokio::select! {
_ = interval.tick() => interface.refresh().await, _ = interval.tick() => interface.refresh().await,
message = receiver.recv() => match message { message = api_receiver.recv() => match message {
Ok(message) => interface.process_request(message).await, Ok(message) => interface.process_request(message).await,
Err(e) => error!("Error on receive channel: {e:#?}") Err(e) => error!("Error on receive channel: {e:#?}")
} }
@ -87,8 +90,13 @@ async fn main() {
let mut interval = let mut interval =
tokio::time::interval(std::time::Duration::from_secs(30)); tokio::time::interval(std::time::Duration::from_secs(30));
loop { loop {
interval.tick().await; tokio::select! {
pli.refresh(); _ = interval.tick() => pli.refresh(),
message = pli_receiver.recv() => match message {
Ok(message) => pli.process_request(message),
Err(e) => error!("Error on receive channel: {e:#?}")
}
}
} }
}); });
} }

View file

@ -1,8 +1,9 @@
use std::time::Duration; use std::{io::Write, time::Duration};
use anyhow::Context; use anyhow::Context;
use metrics::{describe_gauge, gauge, Gauge}; use metrics::{describe_gauge, gauge, Gauge};
use serialport::SerialPort; use serialport::SerialPort;
use termcolor::WriteColor;
pub struct Pli { pub struct Pli {
port: Box<dyn SerialPort>, port: Box<dyn SerialPort>,
@ -12,6 +13,11 @@ pub struct Pli {
internal_load_current: Gauge, internal_load_current: Gauge,
} }
#[derive(Debug, Clone, Copy)]
pub enum PliRequest {
ReadRam(u8),
}
impl Pli { impl Pli {
pub fn new(serial_port: String, baud_rate: u32) -> anyhow::Result<Self> { pub fn new(serial_port: String, baud_rate: u32) -> anyhow::Result<Self> {
let port = serialport::new(serial_port, baud_rate) let port = serialport::new(serial_port, baud_rate)
@ -53,6 +59,26 @@ impl Pli {
} }
} }
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:?}"),
},
}
}
fn send_command(&mut self, req: [u8; 4]) { fn send_command(&mut self, req: [u8; 4]) {
self.port self.port
.write_all(&req) .write_all(&req)
@ -68,7 +94,10 @@ impl Pli {
buf buf
} }
fn read_ram(&mut self, address: PlRamAddress) -> anyhow::Result<u8> { fn read_ram<T>(&mut self, address: T) -> anyhow::Result<u8>
where
T: Into<u8>,
{
self.send_command(command(20, address.into(), 0)); self.send_command(command(20, address.into(), 0));
let buf: [u8; 2] = self.receive(); let buf: [u8; 2] = self.receive();
if buf[0] == 200 { Some(buf[1]) } else { None } if buf[0] == 200 { Some(buf[1]) } else { None }

View file

@ -11,6 +11,7 @@ use rocket::{
use crate::{ use crate::{
api_interface::InterfaceRequest, api_interface::InterfaceRequest,
config::Config, config::Config,
pl_interface::PliRequest,
types::{CarState, ChargeState, ClimateState}, types::{CarState, ChargeState, ClimateState},
}; };
@ -22,6 +23,7 @@ pub struct ServerState {
pub config: Config, pub config: Config,
pub state: Arc<RwLock<CarState>>, pub state: Arc<RwLock<CarState>>,
pub api_requests: Sender<InterfaceRequest>, pub api_requests: Sender<InterfaceRequest>,
pub pli_requests: Sender<PliRequest>,
} }
pub async fn launch_server(state: ServerState) { pub async fn launch_server(state: ServerState) {
@ -47,7 +49,7 @@ fn rocket(state: ServerState) -> rocket::Rocket<rocket::Build> {
.mount("/", fileserver) .mount("/", fileserver)
.mount( .mount(
"/", "/",
routes![home, charge_state, flash, climate_state, metrics], routes![home, charge_state, flash, climate_state, metrics, read_ram],
) )
} }
@ -79,6 +81,12 @@ fn metrics() -> Option<String> {
.ok() .ok()
} }
#[get("/read-ram/<address>")]
async fn read_ram(address: u8, state: &State<ServerState>) -> String {
let _ = state.pli_requests.send(PliRequest::ReadRam(address)).await;
format!("reading at ram address {address}")
}
pub struct Cors; pub struct Cors;
#[rocket::async_trait] #[rocket::async_trait]