From 0b2c98ca1232b21a3767eee362f98357ff9211ec Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 4 Dec 2023 15:32:28 +0100 Subject: [PATCH] Using sequential-storage to future proof settings structures --- Cargo.lock | 10 +++ Cargo.toml | 1 + src/hardware/flash.rs | 138 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 130 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c811f7c5a..1e17444b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -851,6 +851,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "sequential-storage" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12a9b1d6543a56b04200e1447774e6b9397b8a99e4f9a922bbc191d116c32817" +dependencies = [ + "embedded-storage", +] + [[package]] name = "serde" version = "1.0.193" @@ -1002,6 +1011,7 @@ dependencies = [ "rand_xorshift", "rtt-logger", "rtt-target", + "sequential-storage", "serde", "serde-json-core", "serial-settings", diff --git a/Cargo.toml b/Cargo.toml index 815483cee..679e75e92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ default-target = "thumbv7em-none-eabihf" members = ["ad9959", "serial-settings"] [dependencies] +sequential-storage = "0.6" embedded-io = "0.6" embedded-storage = "0.3" cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] } diff --git a/src/hardware/flash.rs b/src/hardware/flash.rs index 8ba2f7f9a..3fff12602 100644 --- a/src/hardware/flash.rs +++ b/src/hardware/flash.rs @@ -1,13 +1,13 @@ use crate::hardware::platform; use core::fmt::Write; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use miniconf::{TreeDeserialize, TreeKey, TreeSerialize}; +use postcard::ser_flavors::Flavor; use stm32h7xx_hal::flash::LockedFlashBank; -#[derive(Clone, serde::Serialize, serde::Deserialize, miniconf::Tree)] +#[derive(Clone, miniconf::Tree)] pub struct Settings { pub broker: heapless::String<255>, pub id: heapless::String<23>, - #[serde(skip)] #[tree(skip)] pub mac: smoltcp_nal::smoltcp::wire::EthernetAddress, } @@ -20,14 +20,32 @@ impl serial_settings::Settings for Settings { impl Settings { pub fn reload(&mut self, storage: &mut Flash) { + // Loop over flash and read settings let mut buffer = [0u8; 512]; - storage.read(0, &mut buffer).unwrap(); - let Ok(mut settings) = postcard::from_bytes::(&buffer) else { - return; - }; - - settings.mac = self.mac; - *self = settings; + for path in Settings::iter_paths::>("/") { + let path = path.unwrap(); + + // Try to fetch the setting from flash. + let Some(item) = + sequential_storage::map::fetch_item::( + storage, + storage.range(), + &mut buffer, + path.clone(), + ) + .unwrap() + else { + continue; + }; + + log::info!("Found `{path}` in flash settings"); + + let mut deserializer = postcard::Deserializer::from_flavor( + postcard::de_flavors::Slice::new(&item.data), + ); + self.deserialize_by_key(path.split('/').skip(1), &mut deserializer) + .unwrap(); + } } pub fn new(mac: smoltcp_nal::smoltcp::wire::EthernetAddress) -> Self { @@ -44,6 +62,12 @@ impl Settings { pub struct Flash(pub LockedFlashBank); +impl Flash { + fn range(&self) -> core::ops::Range { + 0..(self.0.len() as u32) + } +} + impl embedded_storage::nor_flash::ErrorType for Flash { type Error = ::Error; @@ -110,17 +134,69 @@ impl serial_settings::Platform for SerialSettingsPlatform { usbd_serial::SerialPort<'static, super::UsbBus>, >; type Settings = Settings; - type Error = - Error<::Error>; + type Error = Error< + ::Error, + >; fn save(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { - let serialized = postcard::to_slice(self.settings(), buf)?; - self.storage - .erase(0, serialized.len() as u32) - .map_err(Self::Error::Flash)?; - self.storage - .write(0, serialized) - .map_err(Self::Error::Flash)?; + for path in Settings::iter_paths::>("/") { + let path = path.unwrap(); + let range = self.storage.range(); + + let (buf1, buf2) = buf.split_at_mut(buf.len() / 2); + let mut serializer = postcard::Serializer { + output: postcard::ser_flavors::Slice::new(buf1), + }; + self.settings + .serialize_by_key(path.split('/').skip(1), &mut serializer) + .unwrap(); + + let serialized_setting = heapless::Vec::from_slice( + serializer.output.finalize().unwrap(), + ) + .unwrap(); + + // Check if the settings has changed from what's currently in flash (or if it doesn't + // yet exist). + let update = if let Some(item) = + sequential_storage::map::fetch_item::( + &mut self.storage, + range.clone(), + buf2, + path.clone(), + ) + .unwrap() + { + let changed = item.data != serialized_setting; + if changed { + log::info!( + "{path} yet exists in flash, but has changed. Updating" + ); + } + + changed + } else { + log::info!("{path} does not yet exist in flash. Setting it"); + true + }; + + // If the value needs to be rewritten to flash, update it now. + if update { + let item = SettingsItem { + data: serialized_setting, + path, + }; + + sequential_storage::map::store_item( + &mut self.storage, + range, + buf2, + item, + ) + .unwrap(); + } + } + Ok(()) } @@ -150,3 +226,27 @@ impl serial_settings::Platform for SerialSettingsPlatform { &mut self.interface } } + +#[derive(serde::Serialize, serde::Deserialize)] +struct SettingsItem { + // We only make these owned vec/string to get around lifetime limitations. + path: heapless::String<32>, + data: heapless::Vec, +} + +impl sequential_storage::map::StorageItem for SettingsItem { + type Key = heapless::String<32>; + type Error = postcard::Error; + + fn serialize_into(&self, buffer: &mut [u8]) -> Result { + Ok(postcard::to_slice(self, buffer)?.len()) + } + + fn deserialize_from(buffer: &[u8]) -> Result { + postcard::from_bytes(buffer) + } + + fn key(&self) -> Self::Key { + self.path.clone() + } +}