PlantCtrl/rust/src/webserver/webserver.rs
Empire 7415e74643 fix upload
# Conflicts:
#	rust/src/webserver/webserver.rs
2024-08-12 21:17:16 +02:00

363 lines
14 KiB
Rust

//offer ota and config mode
use std::{
collections::VecDeque,
io::{BufRead, Read, Write},
str::from_utf8,
sync::{atomic::AtomicBool, Arc},
};
use crate::{espota::OtaUpdate, BOARD_ACCESS};
use core::result::Result::Ok;
use embedded_svc::http::{Method};
use esp_idf_hal::delay::Delay;
use esp_idf_svc::http::server::{Configuration, EspHttpServer};
use esp_idf_sys::vTaskDelay;
use heapless::String;
use serde::{Deserialize, Serialize};
use crate::{
config::{Config, WifiConfig},
plant_hal::PlantCtrlBoardInteraction,
};
#[derive(Serialize, Debug)]
struct SSIDList<'a> {
ssids: Vec<&'a String<32>>,
}
#[derive(Serialize, Debug)]
struct VersionInfo<'a> {
git_hash: &'a str,
build_time: &'a str,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct TestPump {
pump: usize,
}
pub fn httpd_initial(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
let mut server = shared();
server
.fn_handler("/", Method::Get, move |request| {
let mut response = request.into_ok_response()?;
response.write(include_bytes!("initial_config.html"))?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/wifiscan", Method::Post, move |request| {
let mut response = request.into_ok_response()?;
let mut board = BOARD_ACCESS.lock().unwrap();
match board.wifi_scan() {
Err(error) => {
response.write(format!("Error scanning wifi: {}", error).as_bytes())?;
}
Ok(scan_result) => {
let mut ssids: Vec<&String<32>> = Vec::new();
scan_result.iter().for_each(|s| ssids.push(&s.ssid));
let ssid_json = serde_json::to_string(&SSIDList { ssids })?;
println!("Sending ssid list {}", &ssid_json);
response.write(ssid_json.as_bytes())?;
}
}
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/wifisave", Method::Post, move |mut request| {
let mut buf = [0_u8; 2048];
let read = request.read(&mut buf);
if read.is_err() {
let error_text = read.unwrap_err().to_string();
println!("Could not parse wificonfig {}", error_text);
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return anyhow::Ok(());
}
let actual_data = &buf[0..read.unwrap()];
println!("raw {:?}", actual_data);
println!("Raw data {}", from_utf8(actual_data).unwrap());
let wifi_config: Result<WifiConfig, serde_json::Error> =
serde_json::from_slice(actual_data);
if wifi_config.is_err() {
let error_text = wifi_config.unwrap_err().to_string();
println!("Could not parse wificonfig {}", error_text);
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return anyhow::Ok(());
}
let mut board = BOARD_ACCESS.lock().unwrap();
board.set_wifi(&wifi_config.unwrap())?;
let mut response = request.into_status_response(202)?;
response.write("saved".as_bytes())?;
reboot_now.store(true, std::sync::atomic::Ordering::Relaxed);
anyhow::Ok(())
})
.unwrap();
server
}
pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
let mut server = shared();
server
.fn_handler("/", Method::Get, move |request| {
let mut response = request.into_ok_response()?;
response.write(include_bytes!("config.html"))?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/get_config", Method::Get, move |request| {
let mut response = request.into_ok_response()?;
let mut board = BOARD_ACCESS.lock().unwrap();
match board.get_config() {
Ok(config) => {
let config_json = serde_json::to_string(&config)?;
response.write(config_json.as_bytes())?;
}
Err(_) => {
let config_json = serde_json::to_string(&Config::default())?;
response.write(config_json.as_bytes())?;
}
}
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/set_config", Method::Post, move |mut request| {
let mut buf = [0_u8; 3072];
let read = request.read(&mut buf);
if read.is_err() {
let error_text = read.unwrap_err().to_string();
println!("Could not parse config {}", error_text);
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return anyhow::Ok(());
}
let actual_data = &buf[0..read.unwrap()];
println!("Raw data {}", from_utf8(actual_data).unwrap());
let config: Result<Config, serde_json::Error> = serde_json::from_slice(actual_data);
if config.is_err() {
let error_text = config.unwrap_err().to_string();
println!("Could not parse config {}", error_text);
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return Ok(());
}
let mut board = BOARD_ACCESS.lock().unwrap();
board.set_config(&config.unwrap())?;
let mut response = request.into_status_response(202)?;
response.write("saved".as_bytes())?;
reboot_now.store(true, std::sync::atomic::Ordering::Relaxed);
Ok(())
})
.unwrap();
server
}
pub fn shared() -> Box<EspHttpServer<'static>> {
let server_config = Configuration {
stack_size: 8192,
..Default::default()
};
let mut server: Box<EspHttpServer<'static>> =
Box::new(EspHttpServer::new(&server_config).unwrap());
server
.fn_handler("/version", Method::Get, |request| {
let mut response = request.into_ok_response()?;
let git_hash = env!("VERGEN_GIT_DESCRIBE");
let build_time = env!("VERGEN_BUILD_TIMESTAMP");
let version_info = VersionInfo {
git_hash,
build_time,
};
let version_info_json = serde_json::to_string(&version_info)?;
response.write(version_info_json.as_bytes())?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/bundle.js", Method::Get, |request| {
let mut response = request.into_ok_response()?;
response.write(include_bytes!("bundle.js"))?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/favicon.ico", Method::Get, |request| {
let mut response = request.into_ok_response()?;
response.write(include_bytes!("favicon.ico"))?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/ota", Method::Post, |mut request| {
let ota = OtaUpdate::begin();
if ota.is_err() {
let error_text = ota.unwrap_err().to_string();
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return anyhow::Ok(());
}
let mut ota = ota.unwrap();
println!("start ota");
//having a larger buffer is not really faster, requires more stack and prevents the progress bar from working ;)
const BUFFER_SIZE: usize = 512;
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
let mut total_read: usize = 0;
loop {
let read = request.read(&mut buffer).unwrap();
total_read += read;
println!("received {read} bytes ota {total_read}");
let to_write = &buffer[0..read];
let write_result = ota.write(to_write);
if write_result.is_err() {
let error_text = write_result.unwrap_err().to_string();
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return Ok(());
}
println!("wrote {read} bytes ota {total_read}");
if read == 0 {
break;
}
}
println!("finish ota");
let partition = ota.raw_partition();
println!("finalizing and changing boot partition to {partition:?}");
let finalizer = ota.finalize();
if finalizer.is_err() {
let error_text = finalizer.err().unwrap().to_string();
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return Ok(());
}
let mut finalizer = finalizer.unwrap();
println!("changing boot partition");
finalizer.set_as_boot_partition().unwrap();
finalizer.restart();
})
.unwrap();
server
.fn_handler("/boardtest", Method::Post, move |_| {
let mut board = BOARD_ACCESS.lock().unwrap();
board.test()?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/pumptest", Method::Post, |mut request| {
let mut buf = [0_u8; 3072];
let read = request.read(&mut buf);
if read.is_err() {
let error_text = read.unwrap_err().to_string();
println!("Could not parse testrequest {}", error_text);
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return anyhow::Ok(());
}
let actual_data = &buf[0..read.unwrap()];
println!("Raw data {}", from_utf8(actual_data).unwrap());
let pump_test: Result<TestPump, serde_json::Error> =
serde_json::from_slice(actual_data);
if pump_test.is_err() {
let error_text = pump_test.unwrap_err().to_string();
println!("Could not parse TestPump {}", error_text);
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return Ok(());
}
let mut board = BOARD_ACCESS.lock().unwrap();
board.test_pump(pump_test.unwrap().pump)?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/flashbattery", Method::Post, move |mut request| {
let mut board = BOARD_ACCESS.lock().unwrap();
let mut buffer: [u8; 128] = [0; 128];
let mut line_buffer: VecDeque<u8> = VecDeque::new();
let is_dry_run = !request.uri().ends_with("?flash=true");
let mut total_read: usize = 0;
let mut toggle = true;
let delay = Delay::new(1);
loop {
delay.delay_us(2);
let read = request.read(&mut buffer).unwrap();
total_read += read;
if read == 0 {
if line_buffer.len() > 0 {
println!("No further body but no endline");
let mut line = std::string::String::new();
line_buffer.read_to_string(&mut line).unwrap();
let msg = format!("Finished reading, but there is still some leftover in buffer and no full line {line}<br>");
println!("{}", msg);
let mut response = request.into_status_response(400_u16).unwrap();
response.write(msg.as_bytes()).unwrap();
response.flush().unwrap();
return anyhow::Ok(())
}
break;
}
let to_write = &buffer[0..read];
line_buffer.write_all(to_write).unwrap();
board.general_fault(toggle);
toggle = !toggle;
loop {
let has_line = line_buffer.contains(&b'\n');
if !has_line {
break;
}
let mut line = std::string::String::new();
line_buffer.read_line(&mut line)?;
let line2 = &line[0..line.len()-1];
println!("Processing dry:{} line {}", is_dry_run, line2);
let validate = board.flash_bq34_z100(&line2, is_dry_run);
delay.delay_us(2);
if validate.is_err() {
let mut response = request.into_status_response(400_u16).unwrap();
let err = validate.unwrap_err();
let err_str = err.to_string();
let err_msg = err_str.as_bytes();
println!("Error writing {}", err_str);
response
.write(err_msg)
.unwrap();
return anyhow::Ok(())
}
}
}
let mut response = request.into_status_response(200_u16).unwrap();
let msg = format!("Finished writing {total_read} bytes<br>");
response.write(msg.as_bytes()).unwrap();
board.general_fault(false);
anyhow::Ok(())
})
.unwrap();
server
}