save tests
This commit is contained in:
@@ -563,7 +563,7 @@ impl Esp<'_> {
|
|||||||
match self.savegame.load_slot(idx)? {
|
match self.savegame.load_slot(idx)? {
|
||||||
None => bail!("Slot {idx} is empty or invalid"),
|
None => bail!("Slot {idx} is empty or invalid"),
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
Ok(String::from_utf8_lossy(&*data).to_string())
|
Ok(String::from_utf8_lossy(&data).to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ use crate::hal::shared_flash::MutexFlashStorage;
|
|||||||
|
|
||||||
/// Size of each save slot in bytes (16 KB).
|
/// Size of each save slot in bytes (16 KB).
|
||||||
pub const SAVEGAME_SLOT_SIZE: usize = 16384;
|
pub const SAVEGAME_SLOT_SIZE: usize = 16384;
|
||||||
|
//keep a little of space at the end due to partition table offsets
|
||||||
|
const SAFETY: usize = 5;
|
||||||
/// Number of slots in the 8 MB storage partition.
|
/// Number of slots in the 8 MB storage partition.
|
||||||
pub const SAVEGAME_SLOT_COUNT: usize = 8 * 1024 * 1024 / SAVEGAME_SLOT_SIZE; // 512
|
pub const SAVEGAME_SLOT_COUNT: usize = (8 * 1024 * 1024) / SAVEGAME_SLOT_SIZE - SAFETY; // 507
|
||||||
|
|
||||||
/// Metadata about a single existing save slot, returned by [`SavegameManager::list_saves`].
|
/// Metadata about a single existing save slot, returned by [`SavegameManager::list_saves`].
|
||||||
#[derive(Serialize, Debug, Clone)]
|
#[derive(Serialize, Debug, Clone)]
|
||||||
@@ -51,9 +52,15 @@ impl Flash for SavegameFlashAdapter<'_> {
|
|||||||
/// embedded-savegame calls this before writing to a slot, so we erase
|
/// embedded-savegame calls this before writing to a slot, so we erase
|
||||||
/// the entire `SAVEGAME_SLOT_SIZE` bytes so subsequent writes land on
|
/// the entire `SAVEGAME_SLOT_SIZE` bytes so subsequent writes land on
|
||||||
/// pre-erased (0xFF) pages.
|
/// pre-erased (0xFF) pages.
|
||||||
|
/// Ensures addresses are aligned to ERASE_SIZE (4KB) boundaries.
|
||||||
fn erase(&mut self, addr: u32) -> Result<(), Self::Error> {
|
fn erase(&mut self, addr: u32) -> Result<(), Self::Error> {
|
||||||
|
const ERASE_SIZE: u32 = 4096;
|
||||||
|
// Align start address down to erase boundary
|
||||||
|
let aligned_start = (addr / ERASE_SIZE) * ERASE_SIZE;
|
||||||
|
// Align end address up to erase boundary
|
||||||
let end = addr + SAVEGAME_SLOT_SIZE as u32;
|
let end = addr + SAVEGAME_SLOT_SIZE as u32;
|
||||||
NorFlash::erase(self.region, addr, end).map_err(SavegameFlashError)
|
let aligned_end = ((end + ERASE_SIZE - 1) / ERASE_SIZE) * ERASE_SIZE;
|
||||||
|
NorFlash::erase(self.region, aligned_start, aligned_end).map_err(SavegameFlashError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,9 +98,10 @@ impl SavegameManager {
|
|||||||
///
|
///
|
||||||
/// `scan()` advances the internal wear-leveling pointer to the latest valid
|
/// `scan()` advances the internal wear-leveling pointer to the latest valid
|
||||||
/// slot before `append()` writes to the next free one.
|
/// slot before `append()` writes to the next free one.
|
||||||
|
/// Both operations are performed atomically on the same Storage instance.
|
||||||
pub fn save(&mut self, data: &mut [u8]) -> FatResult<()> {
|
pub fn save(&mut self, data: &mut [u8]) -> FatResult<()> {
|
||||||
let mut st = self.storage();
|
let mut st = self.storage();
|
||||||
st.scan()?;
|
let _slot = st.scan()?;
|
||||||
st.append(data)?;
|
st.append(data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,9 +577,9 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
start
|
start
|
||||||
);
|
);
|
||||||
to_write -= part.len();
|
to_write -= part.len();
|
||||||
chunk += 1;
|
|
||||||
self.get_rtc_module()
|
self.get_rtc_module()
|
||||||
.write((BACKUP_HEADER_MAX_SIZE + chunk * EEPROM_PAGE) as u32, part)?;
|
.write((BACKUP_HEADER_MAX_SIZE + chunk * EEPROM_PAGE) as u32, part)?;
|
||||||
|
chunk += 1;
|
||||||
}
|
}
|
||||||
info!("Backup complete");
|
info!("Backup complete");
|
||||||
self.clear_progress().await;
|
self.clear_progress().await;
|
||||||
@@ -591,13 +591,19 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
let mut store = alloc::vec![0_u8; info.size as usize];
|
let mut store = alloc::vec![0_u8; info.size as usize];
|
||||||
self.rtc_module
|
self.rtc_module
|
||||||
.read(BACKUP_HEADER_MAX_SIZE as u32, store.as_mut_slice())?;
|
.read(BACKUP_HEADER_MAX_SIZE as u32, store.as_mut_slice())?;
|
||||||
|
info!("Read backup data of size {}", store.len());
|
||||||
let mut checksum = X25.digest();
|
let mut checksum = X25.digest();
|
||||||
|
info!("Calculating CRC");
|
||||||
checksum.update(&store[..]);
|
checksum.update(&store[..]);
|
||||||
let crc = checksum.finalize();
|
let crc = checksum.finalize();
|
||||||
|
info!("CRC is {:04x}", crc);
|
||||||
if crc != info.crc16 {
|
if crc != info.crc16 {
|
||||||
|
warn!("CRC mismatch in backup data");
|
||||||
bail!("CRC mismatch in backup data")
|
bail!("CRC mismatch in backup data")
|
||||||
}
|
}
|
||||||
|
info!("CRC is correct");
|
||||||
let (decoded, _) = bincode::decode_from_slice(&store[..], CONFIG)?;
|
let (decoded, _) = bincode::decode_from_slice(&store[..], CONFIG)?;
|
||||||
|
info!("Backup data decoded");
|
||||||
Ok(decoded)
|
Ok(decoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -443,7 +443,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
|||||||
board.board_hal.get_esp().last_pump_time(plant_id);
|
board.board_hal.get_esp().last_pump_time(plant_id);
|
||||||
//state.active = true;
|
//state.active = true;
|
||||||
|
|
||||||
pump_info(&mut board, plant_id, true, pump_ineffective, 0, 0, 0, false).await;
|
pump_info(&mut board, plant_id, true, pump_ineffective, 0, 0, 0).await;
|
||||||
|
|
||||||
let result = do_secure_pump(&mut board, plant_id, plant_config, dry_run).await?;
|
let result = do_secure_pump(&mut board, plant_id, plant_config, dry_run).await?;
|
||||||
//stop pump regardless of prior result//todo refactor to inner?
|
//stop pump regardless of prior result//todo refactor to inner?
|
||||||
@@ -455,8 +455,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
|||||||
pump_ineffective,
|
pump_ineffective,
|
||||||
result.median_current_ma,
|
result.median_current_ma,
|
||||||
result.max_current_ma,
|
result.max_current_ma,
|
||||||
result.min_current_ma,
|
result.min_current_ma
|
||||||
result.error,
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
} else if !state.pump_in_timeout(plant_config, &timezone_time) {
|
} else if !state.pump_in_timeout(plant_config, &timezone_time) {
|
||||||
@@ -908,8 +907,7 @@ async fn pump_info(
|
|||||||
pump_ineffective: bool,
|
pump_ineffective: bool,
|
||||||
median_current_ma: u16,
|
median_current_ma: u16,
|
||||||
max_current_ma: u16,
|
max_current_ma: u16,
|
||||||
min_current_ma: u16,
|
min_current_ma: u16
|
||||||
_error: bool,
|
|
||||||
) {
|
) {
|
||||||
let pump_info = PumpInfo {
|
let pump_info = PumpInfo {
|
||||||
enabled: pump_active,
|
enabled: pump_active,
|
||||||
|
|||||||
Reference in New Issue
Block a user