file up & download and delete
This commit is contained in:
parent
cd63e76469
commit
584d6df2d0
@ -159,7 +159,7 @@ edge-nal-embassy = "0.6.0"
|
|||||||
static_cell = "2.1.1"
|
static_cell = "2.1.1"
|
||||||
cfg-if = "1.0.3"
|
cfg-if = "1.0.3"
|
||||||
edge-http = { version = "0.6.1", features = ["log"] }
|
edge-http = { version = "0.6.1", features = ["log"] }
|
||||||
littlefs2 = { version = "0.6.1", features = ["c-stubs", "alloc", "serde"] }
|
littlefs2 = { version = "0.6.1", features = ["c-stubs", "alloc"] }
|
||||||
littlefs2-core = "0.1.1"
|
littlefs2-core = "0.1.1"
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use anyhow::{anyhow, bail, Context};
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::hal::LittleFS2StorageAdapter::LittleFs2Filesystem;
|
use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use alloc::{format, string::String, vec::Vec};
|
use alloc::{format, string::String, vec::Vec};
|
||||||
@ -34,7 +34,7 @@ use esp_wifi::wifi::{
|
|||||||
ScanTypeConfig, WifiController, WifiDevice, WifiEvent, WifiState,
|
ScanTypeConfig, WifiController, WifiDevice, WifiEvent, WifiState,
|
||||||
};
|
};
|
||||||
use littlefs2::fs::Filesystem;
|
use littlefs2::fs::Filesystem;
|
||||||
use littlefs2_core::{FileType, PathBuf};
|
use littlefs2_core::{DynFile, FileType, OpenSeekFrom, Path, PathBuf, SeekFrom};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
|
||||||
#[link_section = ".rtc.data"]
|
#[link_section = ".rtc.data"]
|
||||||
@ -113,6 +113,79 @@ macro_rules! mk_static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Esp<'_> {
|
impl Esp<'_> {
|
||||||
|
pub(crate) async fn delete_file(&self, filename: String) -> anyhow::Result<()> {
|
||||||
|
let file = PathBuf::try_from(filename.as_str()).unwrap();
|
||||||
|
let access = self.fs.lock().await;
|
||||||
|
access
|
||||||
|
.remove(&*file)
|
||||||
|
.map_err(|err| anyhow!("Could not delete file: {:?}", err))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub(crate) async fn write_file(
|
||||||
|
&mut self,
|
||||||
|
filename: String,
|
||||||
|
offset: u32,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let file = PathBuf::try_from(filename.as_str()).unwrap();
|
||||||
|
let access = self.fs.lock().await;
|
||||||
|
info!("write file {} at offset {}", filename, offset);
|
||||||
|
match access.open_file_with_options_and_then(
|
||||||
|
|options| options.read(true).write(true).create(true),
|
||||||
|
&*file,
|
||||||
|
|file| {
|
||||||
|
file.seek(SeekFrom::Start(offset))?;
|
||||||
|
file.write(buf)?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(err) => {
|
||||||
|
bail!(format!("{err:?}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) async fn get_file(
|
||||||
|
&mut self,
|
||||||
|
filename: String,
|
||||||
|
chunk: u32,
|
||||||
|
) -> anyhow::Result<([u8; 128], usize)> {
|
||||||
|
use littlefs2::io::Error as lfs2Error;
|
||||||
|
|
||||||
|
let file = PathBuf::try_from(filename.as_str()).unwrap();
|
||||||
|
let access = self.fs.lock().await;
|
||||||
|
let mut buf = [0_u8; 128];
|
||||||
|
let mut read = 0;
|
||||||
|
let offset = chunk * 128;
|
||||||
|
info!("read file {} at offset {}", filename, offset);
|
||||||
|
match access.open_file_with_options_and_then(
|
||||||
|
|options| options.read(true),
|
||||||
|
&*file,
|
||||||
|
|file| {
|
||||||
|
let length = file.len()? as u32;
|
||||||
|
info!("file length {}", length);
|
||||||
|
if length == 0 {
|
||||||
|
Err(lfs2Error::IO)
|
||||||
|
} else if length > offset {
|
||||||
|
file.seek(SeekFrom::Start(offset))?;
|
||||||
|
info!("seek to {}", offset);
|
||||||
|
read = file.read(&mut buf)?;
|
||||||
|
info!("read {} bytes", read);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
//exactly at end, do nothing
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => {
|
||||||
|
bail!(format!("{err:?}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((buf, read))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_ota_slot(&mut self) -> String {
|
pub(crate) fn get_ota_slot(&mut self) -> String {
|
||||||
match self.ota.current_slot() {
|
match self.ota.current_slot() {
|
||||||
Ok(slot) => {
|
Ok(slot) => {
|
||||||
@ -435,11 +508,12 @@ impl Esp<'_> {
|
|||||||
match self.fs.lock().await.read_dir_and_then(&path, |dir| {
|
match self.fs.lock().await.read_dir_and_then(&path, |dir| {
|
||||||
for entry in dir {
|
for entry in dir {
|
||||||
let e = entry?;
|
let e = entry?;
|
||||||
|
if e.file_type() == FileType::File {
|
||||||
result.files.push(FileInfo {
|
result.files.push(FileInfo {
|
||||||
filename: e.path().to_string(),
|
filename: e.path().to_string(),
|
||||||
size: e.metadata().len(),
|
size: e.metadata().len(),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Result::Ok(())
|
Result::Ok(())
|
||||||
}) {
|
}) {
|
||||||
@ -450,16 +524,7 @@ impl Esp<'_> {
|
|||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
pub(crate) async fn delete_file(&self, _filename: &str) -> anyhow::Result<()> {
|
|
||||||
bail!("todo");
|
|
||||||
// let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename));
|
|
||||||
// match fs::remove_file(filepath) {
|
|
||||||
// OkStd(_) => anyhow::Ok(()),
|
|
||||||
// Err(err) => {
|
|
||||||
// bail!(format!("{err:?}"))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
// pub(crate) async fn get_file_handle(
|
// pub(crate) async fn get_file_handle(
|
||||||
// &self,
|
// &self,
|
||||||
// filename: &str,
|
// filename: &str,
|
||||||
@ -473,13 +538,17 @@ impl Esp<'_> {
|
|||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub(crate) fn init_rtc_deepsleep_memory(&self, init_rtc_store: bool, to_config_mode: bool) {
|
pub(crate) async fn init_rtc_deepsleep_memory(
|
||||||
|
&self,
|
||||||
|
init_rtc_store: bool,
|
||||||
|
to_config_mode: bool,
|
||||||
|
) {
|
||||||
if init_rtc_store {
|
if init_rtc_store {
|
||||||
unsafe {
|
unsafe {
|
||||||
LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT];
|
LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT];
|
||||||
CONSECUTIVE_WATERING_PLANT = [0; PLANT_COUNT];
|
CONSECUTIVE_WATERING_PLANT = [0; PLANT_COUNT];
|
||||||
LOW_VOLTAGE_DETECTED = false;
|
LOW_VOLTAGE_DETECTED = false;
|
||||||
crate::log::init();
|
crate::log::init().await;
|
||||||
RESTART_TO_CONF = to_config_mode;
|
RESTART_TO_CONF = to_config_mode;
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@ -493,14 +562,16 @@ impl Esp<'_> {
|
|||||||
0,
|
0,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
log(
|
log(
|
||||||
LogMessage::LowVoltage,
|
LogMessage::LowVoltage,
|
||||||
LOW_VOLTAGE_DETECTED as u32,
|
LOW_VOLTAGE_DETECTED as u32,
|
||||||
0,
|
0,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
for i in 0..PLANT_COUNT {
|
for i in 0..PLANT_COUNT {
|
||||||
log::info!(
|
log::info!(
|
||||||
"LAST_WATERING_TIMESTAMP[{}] = UTC {}",
|
"LAST_WATERING_TIMESTAMP[{}] = UTC {}",
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use embedded_storage::{ReadStorage, Storage};
|
use embedded_storage::{ReadStorage, Storage};
|
||||||
use esp_bootloader_esp_idf::partitions::FlashRegion;
|
use esp_bootloader_esp_idf::partitions::FlashRegion;
|
||||||
use esp_storage::FlashStorage;
|
use esp_storage::FlashStorage;
|
||||||
use littlefs2::consts::U1 as lfs2Array1;
|
use littlefs2::consts::U512 as lfsCache;
|
||||||
use littlefs2::consts::U512 as lfs2Array512;
|
use littlefs2::consts::U512 as lfsLookahead;
|
||||||
use littlefs2::driver::Storage as lfs2Storage;
|
use littlefs2::driver::Storage as lfs2Storage;
|
||||||
use littlefs2::fs::Filesystem as lfs2Filesystem;
|
use littlefs2::fs::Filesystem as lfs2Filesystem;
|
||||||
use littlefs2::io::Error as lfs2Error;
|
use littlefs2::io::Error as lfs2Error;
|
||||||
@ -14,20 +14,15 @@ pub struct LittleFs2Filesystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl lfs2Storage for LittleFs2Filesystem {
|
impl lfs2Storage for LittleFs2Filesystem {
|
||||||
const READ_SIZE: usize = 512;
|
const READ_SIZE: usize = 256;
|
||||||
const WRITE_SIZE: usize = 512;
|
const WRITE_SIZE: usize = 512;
|
||||||
const BLOCK_SIZE: usize = 1024; //usually optimal for flash access
|
const BLOCK_SIZE: usize = 512; //usually optimal for flash access
|
||||||
const BLOCK_COUNT: usize = 8 * 1024 * 1024 / 1024; //8mb in 32kb blocks
|
const BLOCK_COUNT: usize = 8 * 1024 * 1024 / 512; //8mb in 32kb blocks
|
||||||
const BLOCK_CYCLES: isize = 100;
|
const BLOCK_CYCLES: isize = 100;
|
||||||
type CACHE_SIZE = lfs2Array512;
|
type CACHE_SIZE = lfsCache;
|
||||||
type LOOKAHEAD_SIZE = lfs2Array1;
|
type LOOKAHEAD_SIZE = lfsLookahead;
|
||||||
|
|
||||||
fn read(&mut self, off: usize, buf: &mut [u8]) -> lfs2Result<usize> {
|
fn read(&mut self, off: usize, buf: &mut [u8]) -> lfs2Result<usize> {
|
||||||
info!(
|
|
||||||
"Littlefs2Filesystem read at offset {} with len {}",
|
|
||||||
off,
|
|
||||||
buf.len()
|
|
||||||
);
|
|
||||||
let read_size: usize = Self::READ_SIZE;
|
let read_size: usize = Self::READ_SIZE;
|
||||||
assert_eq!(off % read_size, 0);
|
assert_eq!(off % read_size, 0);
|
||||||
assert_eq!(buf.len() % read_size, 0);
|
assert_eq!(buf.len() % read_size, 0);
|
@ -1,7 +1,7 @@
|
|||||||
mod LittleFS2StorageAdapter;
|
|
||||||
pub(crate) mod battery;
|
pub(crate) mod battery;
|
||||||
pub mod esp;
|
pub mod esp;
|
||||||
mod initial_hal;
|
mod initial_hal;
|
||||||
|
mod little_fs2storage_adapter;
|
||||||
mod rtc;
|
mod rtc;
|
||||||
//mod water;
|
//mod water;
|
||||||
|
|
||||||
@ -34,12 +34,10 @@ use esp_bootloader_esp_idf::partitions::{
|
|||||||
};
|
};
|
||||||
use esp_hal::clock::CpuClock;
|
use esp_hal::clock::CpuClock;
|
||||||
use esp_hal::gpio::{Input, InputConfig, Pull};
|
use esp_hal::gpio::{Input, InputConfig, Pull};
|
||||||
use esp_println::println;
|
|
||||||
use measurements::{Current, Voltage};
|
use measurements::{Current, Voltage};
|
||||||
|
|
||||||
use crate::hal::LittleFS2StorageAdapter::LittleFs2Filesystem;
|
use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
|
||||||
use embassy_sync::mutex::Mutex;
|
use embassy_sync::mutex::Mutex;
|
||||||
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
|
|
||||||
use esp_alloc as _;
|
use esp_alloc as _;
|
||||||
use esp_backtrace as _;
|
use esp_backtrace as _;
|
||||||
use esp_bootloader_esp_idf::ota::Slot;
|
use esp_bootloader_esp_idf::ota::Slot;
|
||||||
@ -182,6 +180,7 @@ impl PlantHal {
|
|||||||
let peripherals: Peripherals = esp_hal::init(config);
|
let peripherals: Peripherals = esp_hal::init(config);
|
||||||
|
|
||||||
esp_alloc::heap_allocator!(size: 64 * 1024);
|
esp_alloc::heap_allocator!(size: 64 * 1024);
|
||||||
|
esp_alloc::heap_allocator!(#[link_section = ".dram2_uninit"] size: 64000);
|
||||||
let systimer = SystemTimer::new(peripherals.SYSTIMER);
|
let systimer = SystemTimer::new(peripherals.SYSTIMER);
|
||||||
|
|
||||||
let boot_button = Input::new(
|
let boot_button = Input::new(
|
||||||
@ -364,9 +363,11 @@ impl PlantHal {
|
|||||||
to_config_mode as u32,
|
to_config_mode as u32,
|
||||||
"",
|
"",
|
||||||
&format!("{reasons:?}"),
|
&format!("{reasons:?}"),
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
esp.init_rtc_deepsleep_memory(init_rtc_store, to_config_mode);
|
esp.init_rtc_deepsleep_memory(init_rtc_store, to_config_mode)
|
||||||
|
.await;
|
||||||
|
|
||||||
let config = esp.load_config().await;
|
let config = esp.load_config().await;
|
||||||
|
|
||||||
@ -453,7 +454,8 @@ impl PlantHal {
|
|||||||
0,
|
0,
|
||||||
"",
|
"",
|
||||||
&err.to_string(),
|
&err.to_string(),
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
HAL {
|
HAL {
|
||||||
board_hal: initial_hal::create_initial_board(
|
board_hal: initial_hal::create_initial_board(
|
||||||
free_pins,
|
free_pins,
|
||||||
|
@ -31,8 +31,6 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
|||||||
use embassy_sync::mutex::{Mutex, MutexGuard};
|
use embassy_sync::mutex::{Mutex, MutexGuard};
|
||||||
use embassy_sync::once_lock::OnceLock;
|
use embassy_sync::once_lock::OnceLock;
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use esp_alloc::heap_allocator;
|
|
||||||
use esp_bootloader_esp_idf::ota::OtaImageState;
|
|
||||||
use esp_hal::rom::ets_delay_us;
|
use esp_hal::rom::ets_delay_us;
|
||||||
use esp_hal::system::software_reset;
|
use esp_hal::system::software_reset;
|
||||||
use esp_println::{logger, println};
|
use esp_println::{logger, println};
|
||||||
@ -317,8 +315,7 @@ async fn safe_main(spawner: Spawner) -> anyhow::Result<()> {
|
|||||||
info!("executing config mode override");
|
info!("executing config mode override");
|
||||||
//config upload will trigger reboot!
|
//config upload will trigger reboot!
|
||||||
let reboot_now = Arc::new(AtomicBool::new(false));
|
let reboot_now = Arc::new(AtomicBool::new(false));
|
||||||
//TODO
|
//spawner.spawn(httpd(reboot_now.clone(), stack))?;
|
||||||
//let _webserver = httpd(reboot_now.clone());
|
|
||||||
let board = BOARD_ACCESS.get().await.lock().await;
|
let board = BOARD_ACCESS.get().await.lock().await;
|
||||||
wait_infinity(board, WaitType::ConfigButton, reboot_now.clone()).await;
|
wait_infinity(board, WaitType::ConfigButton, reboot_now.clone()).await;
|
||||||
} else {
|
} else {
|
||||||
@ -667,7 +664,8 @@ pub async fn do_secure_pump(
|
|||||||
current_ma as u32,
|
current_ma as u32,
|
||||||
plant_config.max_pump_current_ma.to_string().as_str(),
|
plant_config.max_pump_current_ma.to_string().as_str(),
|
||||||
step.to_string().as_str(),
|
step.to_string().as_str(),
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
board.board_hal.general_fault(true).await;
|
board.board_hal.general_fault(true).await;
|
||||||
board.board_hal.fault(plant_id, true).await?;
|
board.board_hal.fault(plant_id, true).await?;
|
||||||
if !plant_config.ignore_current_error {
|
if !plant_config.ignore_current_error {
|
||||||
@ -686,7 +684,8 @@ pub async fn do_secure_pump(
|
|||||||
current_ma as u32,
|
current_ma as u32,
|
||||||
plant_config.min_pump_current_ma.to_string().as_str(),
|
plant_config.min_pump_current_ma.to_string().as_str(),
|
||||||
step.to_string().as_str(),
|
step.to_string().as_str(),
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
board.board_hal.general_fault(true).await;
|
board.board_hal.general_fault(true).await;
|
||||||
board.board_hal.fault(plant_id, true).await?;
|
board.board_hal.fault(plant_id, true).await?;
|
||||||
if !plant_config.ignore_current_error {
|
if !plant_config.ignore_current_error {
|
||||||
@ -706,7 +705,8 @@ pub async fn do_secure_pump(
|
|||||||
0,
|
0,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@ -840,7 +840,7 @@ async fn update_charge_indicator(board: &mut MutexGuard<'_, CriticalSectionRawMu
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
async fn publish_firmware_info(
|
async fn publish_firmware_info(
|
||||||
mut board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
|
board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
|
||||||
version: VersionInfo,
|
version: VersionInfo,
|
||||||
ip_address: &String,
|
ip_address: &String,
|
||||||
timezone_time: &String,
|
timezone_time: &String,
|
||||||
@ -867,17 +867,6 @@ async fn publish_firmware_info(
|
|||||||
let _ = esp.mqtt_publish("/state", "online".as_bytes()).await;
|
let _ = esp.mqtt_publish("/state", "online".as_bytes()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_to_string(state: OtaImageState) -> &'static str {
|
|
||||||
match state {
|
|
||||||
OtaImageState::New => "New",
|
|
||||||
OtaImageState::PendingVerify => "PendingVerify",
|
|
||||||
OtaImageState::Valid => "Valid",
|
|
||||||
OtaImageState::Invalid => "Invalid",
|
|
||||||
OtaImageState::Aborted => "Aborted",
|
|
||||||
OtaImageState::Undefined => "Undefined",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn try_connect_wifi_sntp_mqtt() -> NetworkMode {
|
async fn try_connect_wifi_sntp_mqtt() -> NetworkMode {
|
||||||
let board = &mut BOARD_ACCESS.get().await.lock().await;
|
let board = &mut BOARD_ACCESS.get().await.lock().await;
|
||||||
let nw_conf = &board.board_hal.get_config().network.clone();
|
let nw_conf = &board.board_hal.get_config().network.clone();
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
use crate::config::PlantControllerConfig;
|
use crate::config::PlantControllerConfig;
|
||||||
use crate::{get_version, log::LogMessage, BOARD_ACCESS};
|
use crate::{get_version, log::LogMessage, BOARD_ACCESS};
|
||||||
use alloc::borrow::ToOwned;
|
use alloc::borrow::ToOwned;
|
||||||
|
use alloc::fmt::format;
|
||||||
|
use alloc::format;
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@ -21,6 +23,7 @@ use embassy_net::Stack;
|
|||||||
use embassy_time::Instant;
|
use embassy_time::Instant;
|
||||||
use embedded_io_async::{Read, Write};
|
use embedded_io_async::{Read, Write};
|
||||||
use esp_println::println;
|
use esp_println::println;
|
||||||
|
use littlefs2_core::Path;
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -294,15 +297,6 @@ pub struct NightLampCommand {
|
|||||||
// anyhow::Ok(None)
|
// 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 {
|
struct HttpHandler {
|
||||||
reboot_now: Arc<AtomicBool>,
|
reboot_now: Arc<AtomicBool>,
|
||||||
@ -324,43 +318,147 @@ impl Handler for HttpHandler {
|
|||||||
let method = headers.method;
|
let method = headers.method;
|
||||||
let path = headers.path;
|
let path = headers.path;
|
||||||
|
|
||||||
let status = match method {
|
let prefix = "/file?filename=";
|
||||||
Method::Get => match path {
|
let status = if path.starts_with(prefix) {
|
||||||
"/favicon.ico" => {
|
let filename = &path[prefix.len()..];
|
||||||
conn.initiate_response(200, Some("OK"), &[("Content-Type", "image/x-icon")])
|
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?;
|
.await?;
|
||||||
conn.write_all(include_bytes!("favicon.ico")).await?;
|
conn.write_all(include_bytes!("favicon.ico")).await?;
|
||||||
Some(200)
|
Some(200)
|
||||||
}
|
}
|
||||||
"/" => {
|
"/" => {
|
||||||
conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/html")])
|
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?;
|
.await?;
|
||||||
conn.write_all(include_bytes!("index.html")).await?;
|
conn.write_all(include_bytes!("bundle.js")).await?;
|
||||||
Some(200)
|
Some(200)
|
||||||
}
|
}
|
||||||
"/bundle.js" => {
|
"/reboot" => {
|
||||||
conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/javascript")])
|
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||||
.await?;
|
board.board_hal.get_esp().set_restart_to_conf(true);
|
||||||
conn.write_all(include_bytes!("bundle.js")).await?;
|
self.reboot_now.store(true, Ordering::Relaxed);
|
||||||
Some(200)
|
Some(200)
|
||||||
}
|
}
|
||||||
"/reboot" => {
|
&_ => {
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
let json = match path {
|
||||||
board.board_hal.get_esp().set_restart_to_conf(true);
|
"/version" => Some(get_version_web(conn).await),
|
||||||
self.reboot_now.store(true, Ordering::Relaxed);
|
"/time" => Some(get_time(conn).await),
|
||||||
Some(200)
|
"/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 {
|
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),
|
"/wifiscan" => Some(wifi_scan(conn).await),
|
||||||
|
"/set_config" => Some(set_config(conn).await),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
match json {
|
match json {
|
||||||
@ -368,20 +466,9 @@ impl Handler for HttpHandler {
|
|||||||
Some(json) => Some(handle_json(conn, json).await?),
|
Some(json) => Some(handle_json(conn, json).await?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
Method::Options | Method::Delete | Method::Head | Method::Put => None,
|
||||||
Method::Post => {
|
_ => None,
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
let code = match status {
|
let code = match status {
|
||||||
None => {
|
None => {
|
||||||
@ -390,6 +477,7 @@ impl Handler for HttpHandler {
|
|||||||
}
|
}
|
||||||
Some(code) => code,
|
Some(code) => code,
|
||||||
};
|
};
|
||||||
|
|
||||||
conn.complete().await?;
|
conn.complete().await?;
|
||||||
let response_time = Instant::now().duration_since(start).as_millis();
|
let response_time = Instant::now().duration_since(start).as_millis();
|
||||||
|
|
||||||
@ -398,16 +486,87 @@ impl Handler for HttpHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn set_config(
|
// .fn_handler("/file", Method::Get, move |request| {
|
||||||
// request: &mut Request<&mut EspHttpConnection>,
|
// let filename = query_param(request.uri(), "filename").unwrap();
|
||||||
// ) -> Result<Option<std::string::String>, anyhow::Error> {
|
// let file_handle = BOARD_ACCESS
|
||||||
// let all = read_up_to_bytes_from_request(request, Some(4096))?;
|
// .lock()
|
||||||
// let config: PlantControllerConfig = serde_json::from_slice(&all)?;
|
// .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");
|
// let read = request.read(&mut buffer)?;
|
||||||
// board.board_hal.set_config(config)?;
|
// total_read += read;
|
||||||
// anyhow::Ok(Some("saved".to_owned()))
|
// 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>(
|
async fn set_config<T, const N: usize>(
|
||||||
request: &mut Connection<'_, T, N>,
|
request: &mut Connection<'_, T, N>,
|
||||||
@ -703,87 +862,7 @@ pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) {
|
|||||||
// })
|
// })
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
// server
|
// 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
|
// server
|
||||||
// .fn_handler("/file", Method::Delete, move |request| {
|
// .fn_handler("/file", Method::Delete, move |request| {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user