use crate::fat_error::{FatError, FatResult}; use crate::hal::rtc::X25; use crate::BOARD_ACCESS; use alloc::borrow::ToOwned; use alloc::format; use alloc::string::{String, ToString}; use chrono::DateTime; use edge_http::io::server::Connection; use edge_nal::io::{Read, Write}; use log::info; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct WebBackupHeader { timestamp: String, size: u16, } pub(crate) async fn get_backup_config( conn: &mut Connection<'_, T, { N }>, ) -> FatResult> where T: Read + Write, { // First pass: verify checksum without sending data let mut checksum = X25.digest(); let mut chunk = 0_usize; loop { let mut board = BOARD_ACCESS.get().await.lock().await; board.board_hal.progress(chunk as u32).await; let (buf, len, expected_crc) = board .board_hal .get_rtc_module() .get_backup_config(chunk) .await?; // Update checksum with the actual data bytes of this chunk checksum.update(&buf[..len]); let is_last = len == 0 || len < buf.len(); if is_last { let actual_crc = checksum.finalize(); if actual_crc != expected_crc { BOARD_ACCESS .get() .await .lock() .await .board_hal .clear_progress() .await; conn.initiate_response( 409, Some( format!("Checksum mismatch expected {expected_crc} got {actual_crc}") .as_str(), ), &[], ) .await?; return Ok(Some(409)); } break; } chunk += 1; } // Second pass: stream data conn.initiate_response( 200, Some("OK"), &[ ("Access-Control-Allow-Origin", "*"), ("Access-Control-Allow-Headers", "*"), ("Access-Control-Allow-Methods", "*"), ], ) .await?; let mut chunk = 0_usize; loop { let mut board = BOARD_ACCESS.get().await.lock().await; board.board_hal.progress(chunk as u32).await; let (buf, len, _expected_crc) = board .board_hal .get_rtc_module() .get_backup_config(chunk) .await?; if len == 0 { break; } conn.write_all(&buf[..len]).await?; if len < buf.len() { break; } chunk += 1; } BOARD_ACCESS .get() .await .lock() .await .board_hal .clear_progress() .await; Ok(Some(200)) } pub(crate) async fn backup_config( conn: &mut Connection<'_, T, N>, ) -> FatResult> where T: Read + Write, { let mut offset = 0_usize; let mut buf = [0_u8; 32]; let mut checksum = X25.digest(); let mut counter = 0; loop { let to_write = conn.read(&mut buf).await?; if to_write == 0 { info!("backup finished"); break; } else { let mut board = BOARD_ACCESS.get().await.lock().await; board.board_hal.progress(counter).await; counter += 1; board .board_hal .get_rtc_module() .backup_config(offset, &buf[0..to_write]) .await?; checksum.update(&buf[0..to_write]); } offset += to_write; } let mut board = BOARD_ACCESS.get().await.lock().await; board .board_hal .get_rtc_module() .backup_config_finalize(checksum.finalize(), offset) .await?; board.board_hal.clear_progress().await; conn.initiate_response( 200, Some("OK"), &[ ("Access-Control-Allow-Origin", "*"), ("Access-Control-Allow-Headers", "*"), ("Access-Control-Allow-Methods", "*"), ], ) .await?; Ok(Some("saved".to_owned())) } pub(crate) async fn backup_info( _request: &mut Connection<'_, T, N>, ) -> Result, FatError> where T: Read + Write, { let mut board = BOARD_ACCESS.get().await.lock().await; let header = board.board_hal.get_rtc_module().get_backup_info().await; let json = match header { Ok(h) => { let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap(); let wbh = WebBackupHeader { timestamp: timestamp.to_rfc3339(), size: h.size, }; serde_json::to_string(&wbh)? } Err(err) => { let wbh = WebBackupHeader { timestamp: err.to_string(), size: 0, }; serde_json::to_string(&wbh)? } }; Ok(Some(json)) }