file up & download and delete
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
use crate::config::PlantControllerConfig;
|
||||
use crate::{get_version, log::LogMessage, BOARD_ACCESS};
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::fmt::format;
|
||||
use alloc::format;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
@@ -21,6 +23,7 @@ use embassy_net::Stack;
|
||||
use embassy_time::Instant;
|
||||
use embedded_io_async::{Read, Write};
|
||||
use esp_println::println;
|
||||
use littlefs2_core::Path;
|
||||
use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -294,15 +297,6 @@ pub struct NightLampCommand {
|
||||
// anyhow::Ok(None)
|
||||
// }
|
||||
//
|
||||
// fn query_param(uri: &str, param_name: &str) -> Option<std::string::String> {
|
||||
// log::info!("{uri} get {param_name}");
|
||||
// let parsed = Url::parse(&format!("http://127.0.0.1/{uri}")).unwrap();
|
||||
// let value = parsed.query_pairs().find(|it| it.0 == param_name);
|
||||
// match value {
|
||||
// Some(found) => Some(found.1.into_owned()),
|
||||
// None => None,
|
||||
// }
|
||||
// }
|
||||
|
||||
struct HttpHandler {
|
||||
reboot_now: Arc<AtomicBool>,
|
||||
@@ -324,43 +318,147 @@ impl Handler for HttpHandler {
|
||||
let method = headers.method;
|
||||
let path = headers.path;
|
||||
|
||||
let status = match method {
|
||||
Method::Get => match path {
|
||||
"/favicon.ico" => {
|
||||
conn.initiate_response(200, Some("OK"), &[("Content-Type", "image/x-icon")])
|
||||
let prefix = "/file?filename=";
|
||||
let status = if path.starts_with(prefix) {
|
||||
let filename = &path[prefix.len()..];
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
info!("file request for {} with method {}", filename, method);
|
||||
match method {
|
||||
Method::Delete => {
|
||||
board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.delete_file(filename.to_owned())
|
||||
.await
|
||||
.unwrap();
|
||||
Some(200)
|
||||
}
|
||||
Method::Get => {
|
||||
let disp = format!("attachment; filename=\"{filename}\"");
|
||||
conn.initiate_response(
|
||||
200,
|
||||
Some("OK"),
|
||||
&[
|
||||
("Content-Type", "application/octet-stream"),
|
||||
("Content-Disposition", disp.as_str()),
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
let mut chunk = 0;
|
||||
loop {
|
||||
let read_chunk = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.get_file(filename.to_owned(), chunk)
|
||||
.await
|
||||
.unwrap();
|
||||
let length = read_chunk.1;
|
||||
info!("read {} bytes for file request for {}", length, filename);
|
||||
if length == 0 {
|
||||
info!("file request for {} finished", filename);
|
||||
break;
|
||||
}
|
||||
let data = &read_chunk.0[0..length];
|
||||
conn.write_all(data).await?;
|
||||
if length < 128 {
|
||||
info!("file request for {} finished", filename);
|
||||
break;
|
||||
}
|
||||
chunk = chunk + 1;
|
||||
}
|
||||
Some(200)
|
||||
}
|
||||
Method::Post => {
|
||||
//ensure file is deleted, otherwise we would need to truncate the file which will not work with streaming
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.delete_file(filename.to_owned())
|
||||
.await;
|
||||
let mut offset = 0_usize;
|
||||
loop {
|
||||
let mut buf = [0_u8; 1024];
|
||||
let to_write = conn.read(&mut buf).await?;
|
||||
if to_write == 0 {
|
||||
info!("file request for {} finished", filename);
|
||||
break;
|
||||
} else {
|
||||
info!(
|
||||
"writing {} bytes for file request for {}",
|
||||
to_write, filename
|
||||
);
|
||||
board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.write_file(filename.to_owned(), offset as u32, &buf[0..to_write])
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
offset = offset + to_write
|
||||
}
|
||||
|
||||
Some(200)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
match method {
|
||||
Method::Get => match path {
|
||||
"/favicon.ico" => {
|
||||
conn.initiate_response(
|
||||
200,
|
||||
Some("OK"),
|
||||
&[("Content-Type", "image/x-icon")],
|
||||
)
|
||||
.await?;
|
||||
conn.write_all(include_bytes!("favicon.ico")).await?;
|
||||
Some(200)
|
||||
}
|
||||
"/" => {
|
||||
conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/html")])
|
||||
conn.write_all(include_bytes!("favicon.ico")).await?;
|
||||
Some(200)
|
||||
}
|
||||
"/" => {
|
||||
conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/html")])
|
||||
.await?;
|
||||
conn.write_all(include_bytes!("index.html")).await?;
|
||||
Some(200)
|
||||
}
|
||||
"/bundle.js" => {
|
||||
conn.initiate_response(
|
||||
200,
|
||||
Some("OK"),
|
||||
&[("Content-Type", "text/javascript")],
|
||||
)
|
||||
.await?;
|
||||
conn.write_all(include_bytes!("index.html")).await?;
|
||||
Some(200)
|
||||
}
|
||||
"/bundle.js" => {
|
||||
conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/javascript")])
|
||||
.await?;
|
||||
conn.write_all(include_bytes!("bundle.js")).await?;
|
||||
Some(200)
|
||||
}
|
||||
"/reboot" => {
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
board.board_hal.get_esp().set_restart_to_conf(true);
|
||||
self.reboot_now.store(true, Ordering::Relaxed);
|
||||
Some(200)
|
||||
}
|
||||
&_ => {
|
||||
conn.write_all(include_bytes!("bundle.js")).await?;
|
||||
Some(200)
|
||||
}
|
||||
"/reboot" => {
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
board.board_hal.get_esp().set_restart_to_conf(true);
|
||||
self.reboot_now.store(true, Ordering::Relaxed);
|
||||
Some(200)
|
||||
}
|
||||
&_ => {
|
||||
let json = match path {
|
||||
"/version" => Some(get_version_web(conn).await),
|
||||
"/time" => Some(get_time(conn).await),
|
||||
"/battery" => Some(get_battery_state(conn).await),
|
||||
"/solar" => Some(get_solar_state(conn).await),
|
||||
"/get_config" => Some(get_config(conn).await),
|
||||
"/files" => Some(list_files(conn).await),
|
||||
"/log_localization" => Some(get_log_localization_config(conn).await),
|
||||
"/log" => Some(get_log(conn).await),
|
||||
"/wifiscan" => Some(wifi_scan(conn).await),
|
||||
_ => None,
|
||||
};
|
||||
match json {
|
||||
None => None,
|
||||
Some(json) => Some(handle_json(conn, json).await?),
|
||||
}
|
||||
}
|
||||
},
|
||||
Method::Post => {
|
||||
let json = match path {
|
||||
"/version" => Some(get_version_web(conn).await),
|
||||
"/time" => Some(get_time(conn).await),
|
||||
"/battery" => Some(get_battery_state(conn).await),
|
||||
"/solar" => Some(get_solar_state(conn).await),
|
||||
"/get_config" => Some(get_config(conn).await),
|
||||
"/files" => Some(list_files(conn).await),
|
||||
"/log_localization" => Some(get_log_localization_config(conn).await),
|
||||
"/log" => Some(get_log(conn).await),
|
||||
"/wifiscan" => Some(wifi_scan(conn).await),
|
||||
"/set_config" => Some(set_config(conn).await),
|
||||
_ => None,
|
||||
};
|
||||
match json {
|
||||
@@ -368,20 +466,9 @@ impl Handler for HttpHandler {
|
||||
Some(json) => Some(handle_json(conn, json).await?),
|
||||
}
|
||||
}
|
||||
},
|
||||
Method::Post => {
|
||||
let json = match path {
|
||||
"/wifiscan" => Some(wifi_scan(conn).await),
|
||||
"/set_config" => Some(set_config(conn).await),
|
||||
_ => None,
|
||||
};
|
||||
match json {
|
||||
None => None,
|
||||
Some(json) => Some(handle_json(conn, json).await?),
|
||||
}
|
||||
Method::Options | Method::Delete | Method::Head | Method::Put => None,
|
||||
_ => None,
|
||||
}
|
||||
Method::Options | Method::Delete | Method::Head | Method::Put => None,
|
||||
_ => None,
|
||||
};
|
||||
let code = match status {
|
||||
None => {
|
||||
@@ -390,6 +477,7 @@ impl Handler for HttpHandler {
|
||||
}
|
||||
Some(code) => code,
|
||||
};
|
||||
|
||||
conn.complete().await?;
|
||||
let response_time = Instant::now().duration_since(start).as_millis();
|
||||
|
||||
@@ -398,16 +486,87 @@ impl Handler for HttpHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// fn set_config(
|
||||
// request: &mut Request<&mut EspHttpConnection>,
|
||||
// ) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||
// let all = read_up_to_bytes_from_request(request, Some(4096))?;
|
||||
// let config: PlantControllerConfig = serde_json::from_slice(&all)?;
|
||||
// .fn_handler("/file", Method::Get, move |request| {
|
||||
// let filename = query_param(request.uri(), "filename").unwrap();
|
||||
// let file_handle = BOARD_ACCESS
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .board_hal
|
||||
// .get_esp()
|
||||
// .get_file_handle(&filename, false);
|
||||
// match file_handle {
|
||||
// Ok(mut file_handle) => {
|
||||
// let headers = [("Access-Control-Allow-Origin", "*")];
|
||||
// let mut response = request.into_response(200, None, &headers)?;
|
||||
// const BUFFER_SIZE: usize = 512;
|
||||
// let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||
// let mut total_read: usize = 0;
|
||||
// loop {
|
||||
// unsafe { vTaskDelay(1) };
|
||||
// let read = std::io::Read::read(&mut file_handle, &mut buffer)?;
|
||||
// total_read += read;
|
||||
// let to_write = &buffer[0..read];
|
||||
// response.write(to_write)?;
|
||||
// if read == 0 {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// log::info!("wrote {total_read} for file {filename}");
|
||||
// drop(file_handle);
|
||||
// response.flush()?;
|
||||
// }
|
||||
// Err(err) => {
|
||||
// //todo set headers here for filename to be error
|
||||
// let error_text = err.to_string();
|
||||
// log::info!("error handling get file {}", error_text);
|
||||
// cors_response(request, 500, &error_text)?;
|
||||
// }
|
||||
// }
|
||||
// anyhow::Ok(())
|
||||
// })
|
||||
// .unwrap();
|
||||
// server
|
||||
// .fn_handler("/file", Method::Post, move |mut request| {
|
||||
// let filename = query_param(request.uri(), "filename").unwrap();
|
||||
// let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
// let file_handle = board.board_hal.get_esp().get_file_handle(&filename, true);
|
||||
// match file_handle {
|
||||
// //TODO get free filesystem size, check against during write if not to large
|
||||
// Ok(mut file_handle) => {
|
||||
// const BUFFER_SIZE: usize = 512;
|
||||
// let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||
// let mut total_read: usize = 0;
|
||||
// let mut lastiter = 0;
|
||||
// loop {
|
||||
// let iter = (total_read / 1024) % 8;
|
||||
// if iter != lastiter {
|
||||
// for i in 0..PLANT_COUNT {
|
||||
// let _ = board.board_hal.fault(i, iter == i);
|
||||
// }
|
||||
// lastiter = iter;
|
||||
// }
|
||||
//
|
||||
// let mut board = BOARD_ACCESS.lock().expect("board access");
|
||||
// board.board_hal.set_config(config)?;
|
||||
// anyhow::Ok(Some("saved".to_owned()))
|
||||
// }
|
||||
// let read = request.read(&mut buffer)?;
|
||||
// total_read += read;
|
||||
// let to_write = &buffer[0..read];
|
||||
// std::io::Write::write(&mut file_handle, to_write)?;
|
||||
// if read == 0 {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// cors_response(request, 200, &format!("saved {total_read} bytes"))?;
|
||||
// }
|
||||
// Err(err) => {
|
||||
// //todo set headers here for filename to be error
|
||||
// let error_text = err.to_string();
|
||||
// log::info!("error handling get file {}", error_text);
|
||||
// cors_response(request, 500, &error_text)?;
|
||||
// }
|
||||
// }
|
||||
// drop(board);
|
||||
// anyhow::Ok(())
|
||||
// })
|
||||
// .unwrap();
|
||||
|
||||
async fn set_config<T, const N: usize>(
|
||||
request: &mut Connection<'_, T, N>,
|
||||
@@ -703,87 +862,7 @@ pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) {
|
||||
// })
|
||||
// .unwrap();
|
||||
// server
|
||||
// .fn_handler("/file", Method::Get, move |request| {
|
||||
// let filename = query_param(request.uri(), "filename").unwrap();
|
||||
// let file_handle = BOARD_ACCESS
|
||||
// .lock()
|
||||
// .unwrap()
|
||||
// .board_hal
|
||||
// .get_esp()
|
||||
// .get_file_handle(&filename, false);
|
||||
// match file_handle {
|
||||
// Ok(mut file_handle) => {
|
||||
// let headers = [("Access-Control-Allow-Origin", "*")];
|
||||
// let mut response = request.into_response(200, None, &headers)?;
|
||||
// const BUFFER_SIZE: usize = 512;
|
||||
// let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||
// let mut total_read: usize = 0;
|
||||
// loop {
|
||||
// unsafe { vTaskDelay(1) };
|
||||
// let read = std::io::Read::read(&mut file_handle, &mut buffer)?;
|
||||
// total_read += read;
|
||||
// let to_write = &buffer[0..read];
|
||||
// response.write(to_write)?;
|
||||
// if read == 0 {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// log::info!("wrote {total_read} for file {filename}");
|
||||
// drop(file_handle);
|
||||
// response.flush()?;
|
||||
// }
|
||||
// Err(err) => {
|
||||
// //todo set headers here for filename to be error
|
||||
// let error_text = err.to_string();
|
||||
// log::info!("error handling get file {}", error_text);
|
||||
// cors_response(request, 500, &error_text)?;
|
||||
// }
|
||||
// }
|
||||
// anyhow::Ok(())
|
||||
// })
|
||||
// .unwrap();
|
||||
// server
|
||||
// .fn_handler("/file", Method::Post, move |mut request| {
|
||||
// let filename = query_param(request.uri(), "filename").unwrap();
|
||||
// let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
// let file_handle = board.board_hal.get_esp().get_file_handle(&filename, true);
|
||||
// match file_handle {
|
||||
// //TODO get free filesystem size, check against during write if not to large
|
||||
// Ok(mut file_handle) => {
|
||||
// const BUFFER_SIZE: usize = 512;
|
||||
// let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||
// let mut total_read: usize = 0;
|
||||
// let mut lastiter = 0;
|
||||
// loop {
|
||||
// let iter = (total_read / 1024) % 8;
|
||||
// if iter != lastiter {
|
||||
// for i in 0..PLANT_COUNT {
|
||||
// let _ = board.board_hal.fault(i, iter == i);
|
||||
// }
|
||||
// lastiter = iter;
|
||||
// }
|
||||
//
|
||||
// let read = request.read(&mut buffer)?;
|
||||
// total_read += read;
|
||||
// let to_write = &buffer[0..read];
|
||||
// std::io::Write::write(&mut file_handle, to_write)?;
|
||||
// if read == 0 {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// cors_response(request, 200, &format!("saved {total_read} bytes"))?;
|
||||
// }
|
||||
// Err(err) => {
|
||||
// //todo set headers here for filename to be error
|
||||
// let error_text = err.to_string();
|
||||
// log::info!("error handling get file {}", error_text);
|
||||
// cors_response(request, 500, &error_text)?;
|
||||
// }
|
||||
// }
|
||||
// drop(board);
|
||||
// anyhow::Ok(())
|
||||
// })
|
||||
// .unwrap();
|
||||
|
||||
//
|
||||
// server
|
||||
// .fn_handler("/file", Method::Delete, move |request| {
|
||||
|
||||
Reference in New Issue
Block a user