From b7747f2b53a89e20419253e786512e62f910589b Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 29 Aug 2024 12:31:45 +0200 Subject: [PATCH] Updating sequential-storage implementation --- Cargo.lock | 9 +- Cargo.toml | 2 +- src/hardware/mod.rs | 6 +- src/hardware/setup.rs | 3 +- src/main.rs | 6 +- src/net/mqtt_control.rs | 8 +- src/settings/flash.rs | 286 +++++++++++++--------------------------- 7 files changed, 116 insertions(+), 204 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df3af69..adba409 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1381,12 +1381,15 @@ dependencies = [ [[package]] name = "serial-settings" version = "0.1.0" -source = "git+https://github.com/quartiq/stabilizer?rev=5452272931e1ad70547b578052ffbb186fb72514#5452272931e1ad70547b578052ffbb186fb72514" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e625da19d0598abfecd5f8ead895ec4542c79a3139234cc7fd9cb3fe3ba0ff" dependencies = [ "embedded-io", "heapless 0.8.0", + "log", "menu", "miniconf", + "postcard", "yafnv", ] @@ -1751,9 +1754,9 @@ dependencies = [ [[package]] name = "yafnv" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66aad9090f952997c6570d94039d80b5b5e26d182c5f39ef7b05d64535fb5f88" +checksum = "a98cd19b5e3fbcda406d58efc9f5cb2d6e82e381708d52f357b3b3cfb19559b1" dependencies = [ "num-traits", ] diff --git a/Cargo.toml b/Cargo.toml index ef59d91..04daaf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ enc424j600 = "0.4" embedded-hal = "1" smoltcp-nal = { version = "0.5", features=["shared-stack"] } -serial-settings = {git = "https://github.com/quartiq/stabilizer", rev = "5452272931e1ad70547b578052ffbb186fb72514"} +serial-settings = "0.1" stm32f4xx-hal = {version = "0.21.0", features = ["stm32f407", "usb_fs"] } postcard = "1" diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index 3b319b4..cf6015b 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -45,8 +45,10 @@ pub enum Mac { Enc424j600(enc424j600::Enc424j600), } -pub type SerialTerminal = - serial_settings::Runner<'static, crate::settings::flash::SerialSettingsPlatform, 5>; +pub type SerialSettingsPlatform = + crate::settings::flash::SerialSettingsPlatform; + +pub type SerialTerminal = serial_settings::Runner<'static, SerialSettingsPlatform, 5>; pub type NetworkStack = smoltcp_nal::NetworkStack<'static, Mac, SystemTimer>; diff --git a/src/hardware/setup.rs b/src/hardware/setup.rs index 240b1db..ac0513c 100644 --- a/src/hardware/setup.rs +++ b/src/hardware/setup.rs @@ -330,7 +330,7 @@ pub fn setup( // values stored in flash. This helps preserve backwards compatibility with older Booster // firmware versions that didn't store settings in flash. We no longer persist settings to // EEPROM, so flash will have the latest and greatest settings data. - crate::settings::flash::load_from_flash(&mut settings, &mut flash); + crate::settings::flash::SerialSettingsPlatform::load(&mut settings, &mut flash); let mut mac = { let mut spi = { @@ -548,6 +548,7 @@ pub fn setup( serial_settings::Runner::new( crate::settings::flash::SerialSettingsPlatform { metadata, + _settings_marker: core::marker::PhantomData, interface: serial_settings::BestEffortInterface::new(usb_serial), storage: flash, }, diff --git a/src/main.rs b/src/main.rs index cfbc4b1..e8696c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -250,10 +250,12 @@ mod app { .lock(|watchdog| watchdog.check_in(WatchdogClient::Usb)); (&mut c.shared.usb_terminal, &mut c.shared.settings).lock(|terminal, settings| { + // Handle the USB serial terminal. c.local.usb.process(terminal); - if terminal.process(settings).unwrap() { - update_settings::spawn().unwrap(); + if terminal.poll(settings).unwrap() { + update_settings::spawn().unwrap() } + // Process any log output. LOGGER.process(terminal); }); diff --git a/src/net/mqtt_control.rs b/src/net/mqtt_control.rs index 5323d15..27655c0 100644 --- a/src/net/mqtt_control.rs +++ b/src/net/mqtt_control.rs @@ -13,8 +13,10 @@ use core::fmt::Write; use heapless::{String, Vec}; use serde::Serialize; -use crate::settings::{flash::SerialSettingsPlatform, Settings}; +use crate::hardware::SerialSettingsPlatform; +use crate::settings::Settings; use miniconf::{IntoKeys, Keys, Path, Postcard, TreeKey}; +use serial_settings::Platform; /// Default metadata message if formatting errors occur. const DEFAULT_METADATA: &str = "{\"message\":\"Truncated: See USB terminal\"}"; @@ -266,7 +268,9 @@ pub fn save_settings_to_flash( .len(); data.truncate(len); - settings_platform.save_item(&mut buf[..], channel_path.0, data); + settings_platform + .store(&mut buf[..], channel_path.0.as_bytes(), &data) + .map_err(|_| "Failed to save to flash")?; } Ok(0) diff --git a/src/settings/flash.rs b/src/settings/flash.rs index c35ebbd..33be111 100644 --- a/src/settings/flash.rs +++ b/src/settings/flash.rs @@ -1,184 +1,132 @@ //! Booster NGFW Application use heapless::{String, Vec}; -use miniconf::{JsonCoreSlash, Path, Postcard, TreeKey}; -use sequential_storage::map; +use miniconf::{JsonCoreSlash, Path, Postcard}; use crate::hardware::{flash::Flash, platform}; +use embassy_futures::block_on; use embedded_io::Write; - -pub fn load_from_flash JsonCoreSlash<'d, Y>, const Y: usize>( - structure: &mut T, - storage: &mut Flash, -) { - // Loop over flash and read settings - let mut buffer = [0u8; 512]; - for path in T::nodes::, '/'>>() { - let (path, _) = path.unwrap(); - let path = path.0; - - // Try to fetch the setting from flash. - let item = match embassy_futures::block_on(map::fetch_item::( - storage, - storage.range(), - &mut sequential_storage::cache::NoCache::new(), - &mut buffer, - &SettingsKey(path.clone()), - )) { - Err(e) => { - log::warn!("Failed to fetch `{path}` from flash: {e:?}"); - continue; - } - Ok(Some(item)) => item, - _ => continue, - }; - - // An empty vector may be saved to flash to "erase" a setting, since the H7 doesn't support - // multi-write NOR flash. If we see an empty vector, ignore this entry. - if item.0.is_empty() { - continue; - } - - log::info!("Loading initial `{path}` from flash"); - - let flavor = postcard::de_flavors::Slice::new(&item.0); - if let Err(e) = structure.set_postcard_by_key(path.split('/').skip(1), flavor) { - log::warn!("Failed to deserialize `{path}` from flash: {e:?}"); - } - } - log::info!("Loaded settings from Flash"); -} +use sequential_storage::{ + cache::NoCache, + map::{fetch_item, store_item, SerializationError}, +}; +use serial_settings::{BestEffortInterface, Platform, Settings}; #[derive(Default, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct SettingsKey(String<64>); +pub struct SettingsKey(Vec); -impl map::Key for SettingsKey { - fn serialize_into(&self, buffer: &mut [u8]) -> Result { +impl sequential_storage::map::Key for SettingsKey { + fn serialize_into(&self, buffer: &mut [u8]) -> Result { Ok(postcard::to_slice(self, buffer) - .map_err(|_| map::SerializationError::BufferTooSmall)? + .map_err(|_| SerializationError::BufferTooSmall)? .len()) } - fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), map::SerializationError> { + fn deserialize_from(buffer: &[u8]) -> Result<(Self, usize), SerializationError> { let original_length = buffer.len(); - let (result, remainder) = postcard::take_from_bytes(buffer) - .map_err(|_| map::SerializationError::BufferTooSmall)?; + let (result, remainder) = + postcard::take_from_bytes(buffer).map_err(|_| SerializationError::BufferTooSmall)?; Ok((result, original_length - remainder.len())) } } -#[derive(Default, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct SettingsItem(Vec); - -impl<'a> map::Value<'a> for SettingsItem { - fn serialize_into(&self, buffer: &mut [u8]) -> Result { - if buffer.len() < self.0.len() { - return Err(map::SerializationError::BufferTooSmall); - } - - buffer[..self.0.len()].copy_from_slice(&self.0); - Ok(self.0.len()) - } +pub struct SerialSettingsPlatform { + /// The interface to read/write data to/from serially (via text) to the user. + pub interface: BestEffortInterface, - fn deserialize_from(buffer: &'a [u8]) -> Result { - let vec = Vec::from_slice(buffer).map_err(|_| map::SerializationError::BufferTooSmall)?; - Ok(Self(vec)) - } -} + pub _settings_marker: core::marker::PhantomData, -pub struct SerialSettingsPlatform { - pub metadata: &'static crate::hardware::metadata::ApplicationMetadata, pub storage: Flash, - /// The interface to read/write data to/from serially (via text) to the user. - pub interface: serial_settings::BestEffortInterface, + pub metadata: &'static crate::hardware::metadata::ApplicationMetadata, } -impl SerialSettingsPlatform { - pub fn save_item(&mut self, buffer: &mut [u8], key: String<64>, value: Vec) { - let path = SettingsKey(key); - let range = self.storage.range(); +impl SerialSettingsPlatform +where + C: for<'d> JsonCoreSlash<'d, Y>, +{ + pub fn load(structure: &mut C, storage: &mut Flash) { + // Loop over flash and read settings + let mut buffer = [0u8; 512]; + for path in C::nodes::, '/'>>() { + let (path, _node) = path.unwrap(); + + // Try to fetch the setting from flash. + let value: &[u8] = match block_on(fetch_item( + storage, + storage.range(), + &mut NoCache::new(), + &mut buffer, + &SettingsKey(path.clone().into_inner().into_bytes()), + )) { + Err(e) => { + log::warn!("Failed to fetch `{}` from flash: {e:?}", path.as_str()); + continue; + } + Ok(Some(value)) => value, + Ok(None) => continue, + }; - // Check if the settings has changed from what's currently in flash (or if it doesn't - // yet exist). - if embassy_futures::block_on(map::fetch_item::( - &mut self.storage, - range.clone(), - &mut sequential_storage::cache::NoCache::new(), - buffer, - &path, - )) - .unwrap() - .map(|old| old.0 != value) - .unwrap_or(true) - { - log::info!("Storing `{}` to flash", path.0); - embassy_futures::block_on(map::store_item( - &mut self.storage, - range, - &mut sequential_storage::cache::NoCache::new(), - buffer, - &path, - &SettingsItem(value), - )) - .unwrap(); - } - } -} + // An empty vector may be saved to flash to "erase" a setting, since the H7 doesn't support + // multi-write NOR flash. If we see an empty vector, ignore this entry. + if value.is_empty() { + continue; + } -#[derive(Debug)] -pub enum Error { - Postcard(postcard::Error), - Flash(F), -} + log::info!("Loading initial `{}` from flash", path.as_str()); -impl From for Error { - fn from(e: postcard::Error) -> Self { - Self::Postcard(e) + let flavor = postcard::de_flavors::Slice::new(value); + if let Err(e) = structure.set_postcard_by_key(&path, flavor) { + log::warn!( + "Failed to deserialize `{}` from flash: {e:?}", + path.as_str() + ); + } + } } } -impl serial_settings::Platform<5> for SerialSettingsPlatform { - type Interface = serial_settings::BestEffortInterface; +impl Platform for SerialSettingsPlatform +where + C: Settings, +{ + type Interface = BestEffortInterface; - type Settings = crate::settings::Settings; + type Error = + sequential_storage::Error<::Error>; - type Error = Error<::Error>; + type Settings = C; - fn save( + fn fetch<'a>( &mut self, - buffer: &mut [u8], - key: Option<&str>, - settings: &Self::Settings, - ) -> Result<(), Self::Error> { - let mut save_setting = |path: String<64>| { - let path = SettingsKey(path); - - let mut data = Vec::new(); - data.resize(data.capacity(), 0).unwrap(); - let flavor = postcard::ser_flavors::Slice::new(&mut data); - let len = match settings.get_postcard_by_key(path.0.split('/').skip(1), flavor) { - Err(e) => { - log::warn!("Failed to save `{}` to flash: {e:?}", path.0); - return; - } - Ok(slice) => slice.len(), - }; - data.truncate(len); - - self.save_item(buffer, path.0, data) - }; + buf: &'a mut [u8], + key: &[u8], + ) -> Result, Self::Error> { + let range = self.storage.range(); + block_on(fetch_item( + &mut self.storage, + range, + &mut NoCache::new(), + buf, + &SettingsKey(Vec::try_from(key).unwrap()), + )) + .map(|v| v.filter(|v: &&[u8]| !v.is_empty())) + } - if let Some(path) = key { - save_setting(path.parse().unwrap()); - } else { - for path in Self::Settings::nodes::, '/'>>() { - let (path, _) = path.unwrap(); - save_setting(path.0); - } - } + fn store(&mut self, buf: &mut [u8], key: &[u8], value: &[u8]) -> Result<(), Self::Error> { + let range = self.storage.range(); + block_on(store_item( + &mut self.storage, + range, + &mut NoCache::new(), + buf, + &SettingsKey(Vec::try_from(key).unwrap()), + &value, + )) + } - Ok(()) + fn clear(&mut self, buf: &mut [u8], key: &[u8]) -> Result<(), Self::Error> { + self.store(buf, key, b"") } /// Execute a platform specific command. @@ -255,54 +203,6 @@ impl serial_settings::Platform<5> for SerialSettingsPlatform { } } - fn clear(&mut self, buffer: &mut [u8], key: Option<&str>) { - let mut erase_setting = |path| -> Result<(), Self::Error> { - let path = SettingsKey(path); - let range = self.storage.range(); - - // Check if there's an entry for this item in our flash map. The item might be a - // sentinel value indicating "erased". Because we can't write flash memory twice, we - // instead append a sentry "erased" value to the map where the serialized value is - // empty. - let maybe_item = - embassy_futures::block_on(map::fetch_item::( - &mut self.storage, - range.clone(), - &mut sequential_storage::cache::NoCache::new(), - buffer, - &path, - )) - .unwrap(); - - // An entry may exist in the map with no data as a sentinel that this path was - // previously erased. If we find this, there's no need to store a duplicate "item is - // erased" sentinel in flash. We only need to logically erase the path from the map if - // it existed there in the first place. - if matches!(maybe_item, Some(item) if !item.0.is_empty()) { - embassy_futures::block_on(map::store_item( - &mut self.storage, - range, - &mut sequential_storage::cache::NoCache::new(), - buffer, - &path, - &SettingsItem(Vec::new()), - )) - .unwrap(); - } - - Ok(()) - }; - - if let Some(key) = key { - erase_setting(key.parse().unwrap()).unwrap(); - } else { - for path in Self::Settings::nodes::, '/'>>() { - let (path, _) = path.unwrap(); - erase_setting(path.0).unwrap(); - } - } - } - /// Return a mutable reference to the `Interface`. fn interface_mut(&mut self) -> &mut Self::Interface { &mut self.interface