use crate::card::model::RawCard; use crate::peripherals::Mutex; use crate::peripherals::storage::Nvs; use alloc::sync::Arc; use alloc::vec::Vec; use binary_serde::{BinarySerde, DeserializeError, Endianness}; use core::error::Error; use core::fmt::Display; use esp_nvs::Key; const NVS_NAMESPACE_CARDSTORE: Key = Key::from_str("CARDSTORE"); const NVS_NAMESPACE_CARDSTORE_METADATA: Key = Key::from_str("CSMETA"); const NVS_NAMESPACE_CARDSTORE_CARDS: Key = Key::from_str("CSCARDS"); const NVS_CARDSTORE_INITIALIZED_KEY: Key = Key::from_str("INITIALIZED"); const NVS_CARDSTORE_CARDCOUNT_KEY: Key = Key::from_str("CARDCOUNT"); const NVS_CARDSTORE_DUMMY_KEY: Key = Key::from_str("DUMMY"); #[derive(Debug)] pub enum CardStoreError { Store(esp_nvs::error::Error), CorruptedEntry(DeserializeError), NoEntry(u32), } impl Error for CardStoreError {} impl Display for CardStoreError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { CardStoreError::Store(e) => e.fmt(f), CardStoreError::CorruptedEntry(e) => e.fmt(f), CardStoreError::NoEntry(uuid) => write!(f, "No Card Entry\nuuid: 0x{uuid:x}"), } } } impl From for CardStoreError { fn from(error: esp_nvs::error::Error) -> CardStoreError { CardStoreError::Store(error) } } impl From for CardStoreError { fn from(error: DeserializeError) -> CardStoreError { CardStoreError::CorruptedEntry(error) } } #[derive(Debug, BinarySerde, PartialEq, Eq, Clone)] pub struct CardStoreMetaData { pub creation_date: u64, pub last_read_date: u64, // how often a card has been read by nfc pub read_count: u32, } impl CardStoreMetaData { fn new(creation_date: u64) -> Self { Self { creation_date: creation_date, last_read_date: creation_date, read_count: 0, } } } pub struct CardStore { nvs: Arc>, } impl CardStore { pub async fn new(nvs: Arc>) -> Result { if !Self::is_initialized(&nvs).await? { log::info!("CardStore has not been initialized"); Self::initialize(&nvs).await?; } log::info!("CardStore has been initialized"); let card_count = nvs .lock() .await .get::(&NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_CARDCOUNT_KEY)?; log::info!("Cards stored in flash: {card_count}"); Ok(Self { nvs }) } async fn is_initialized(nvs: &Mutex) -> Result { log::info!("check initialization"); let mut nvs_lock = nvs.lock().await; match nvs_lock.get::(&NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_INITIALIZED_KEY) { Ok(initialized) => { log::debug!("initialization_check: initialized {initialized:?}"); return Ok(true); } Err(esp_nvs::error::Error::NamespaceNotFound) => { log::debug!("initialization_check: error: namespace not found"); return Ok(false); } Err(esp_nvs::error::Error::KeyNotFound) => { log::debug!("initialization_check: error: key not found"); return Ok(false); } Err(error) => return Err(error.into()), }; } // initializing cardstore with 0 cards, so that all namespaces exist and there // are only keynotfound errors in the future async fn initialize(nvs: &Mutex) -> Result<(), CardStoreError> { log::info!("initializing card store"); Self::delete_all_cards(nvs).await?; let mut nvs_lock = nvs.lock().await; log::debug!("initialization: write card count"); // initialize card count nvs_lock.set::(&NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_CARDCOUNT_KEY, 0)?; // initialize card namespace let dummy_card = RawCard::default(); let mut carddata_serialized: [u8; RawCard::SERIALIZED_SIZE] = [0; RawCard::SERIALIZED_SIZE]; dummy_card.binary_serialize(&mut carddata_serialized, Endianness::Little); log::debug!("initialization: write dummy card"); nvs_lock.set::<&[u8]>( &NVS_NAMESPACE_CARDSTORE_CARDS, &NVS_CARDSTORE_DUMMY_KEY, &carddata_serialized, )?; // initialize metadata namespace log::debug!("initialization: write dummy metadata"); let dummy_metadata = CardStoreMetaData::new(0); let mut metadata_serialized: [u8; CardStoreMetaData::SERIALIZED_SIZE] = [0; CardStoreMetaData::SERIALIZED_SIZE]; dummy_metadata.binary_serialize(&mut metadata_serialized, Endianness::Little); nvs_lock.set::<&[u8]>( &NVS_NAMESPACE_CARDSTORE_METADATA, &NVS_CARDSTORE_DUMMY_KEY, &metadata_serialized, )?; // set initialized log::debug!("initialization: write initialized"); nvs_lock.set::( &NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_INITIALIZED_KEY, true, )?; Ok(()) } async fn delete_all_cards(nvs: &Mutex) -> Result<(), CardStoreError> { log::info!("delete all cards"); let mut nvs_lock = nvs.lock().await; let keys = nvs_lock.keys().collect::, _>>()?; for (namespace, key) in keys { if namespace == NVS_NAMESPACE_CARDSTORE || namespace == NVS_NAMESPACE_CARDSTORE_METADATA { nvs_lock.delete(&namespace, &key)?; } } Ok(()) } pub async fn load_card_count(&self) -> Result { log::debug!("load card count"); let mut nvs_lock = self.nvs.lock().await; let result = nvs_lock.get::(&NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_CARDCOUNT_KEY)?; log::debug!("card count: {result}"); Ok(result) } pub async fn load_raw_card(&self, uuid: u32) -> Result, CardStoreError> { log::debug!("load raw card\nuuid: 0x{uuid:x}"); let nvs_key = RawCard::nvs_key_from_uuid(uuid); let mut nvs_lock = self.nvs.lock().await; let carddata_binary: Vec = match nvs_lock.get(&NVS_NAMESPACE_CARDSTORE_CARDS, &nvs_key) { Ok(carddata) => carddata, Err(esp_nvs::error::Error::KeyNotFound) => return Ok(None), Err(error) => return Err(error.into()), }; let carddata = RawCard::binary_deserialize(carddata_binary.as_ref(), Endianness::Little)?; Ok(Some(carddata)) } async fn write_card(&self, nvs_key: Key, card: &RawCard) -> Result<(), CardStoreError> { log::debug!("writing card...\nkey: {nvs_key:?}"); let mut carddata_serialized: [u8; RawCard::SERIALIZED_SIZE] = [0; RawCard::SERIALIZED_SIZE]; card.binary_serialize(&mut carddata_serialized, Endianness::Little); self.nvs.lock().await.set::<&[u8]>( &NVS_NAMESPACE_CARDSTORE_CARDS, &nvs_key, &carddata_serialized, )?; Ok(()) } async fn update_card_metadata( &self, nvs_key: Key, timestamp: u64, ) -> Result { log::debug!("updating card metadata...\nkey: {nvs_key:?}\ntimestamp: {timestamp}"); let mut is_new = false; // update metadata let metadata: CardStoreMetaData = if let Some(mut metadata) = self.load_card_metadata(nvs_key).await? { log::debug!("found existing card"); metadata.last_read_date = timestamp; metadata.read_count += 1; metadata } else { log::debug!("new card"); is_new = true; CardStoreMetaData::new(timestamp) }; let mut metadata_serialized: [u8; CardStoreMetaData::SERIALIZED_SIZE] = [0; CardStoreMetaData::SERIALIZED_SIZE]; metadata.binary_serialize(&mut metadata_serialized, Endianness::Little); self.nvs.lock().await.set::<&[u8]>( &NVS_NAMESPACE_CARDSTORE_METADATA, &nvs_key, &metadata_serialized, )?; Ok(is_new) } async fn increment_card_count(&self) -> Result<(), CardStoreError> { log::debug!("incrementing card count"); let card_count: u32 = self.load_card_count().await?; log::debug!("previous card count: {card_count}"); self.nvs.lock().await.set::( &NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_CARDCOUNT_KEY, card_count + 1, )?; let card_count = self .nvs .lock() .await .get::(&NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_CARDCOUNT_KEY)?; log::info!("Cards stored in flash: {card_count}"); Ok(()) } pub async fn save_raw_card( &self, card: &RawCard, timestamp: u64, ) -> Result<(), CardStoreError> { log::debug!("saving raw card\ntimestamp: {timestamp}"); let nvs_key = card.get_nvs_key(); self.write_card(nvs_key, card).await?; let new_card = self.update_card_metadata(nvs_key, timestamp).await?; if new_card { self.increment_card_count().await?; } Ok(()) } pub async fn load_card_metadata( &self, nvs_key: Key, ) -> Result, CardStoreError> { log::debug!("load card meta data\nkey: {nvs_key:?}"); let mut nvs_lock = self.nvs.lock().await; let metadata_binary: Vec = match nvs_lock.get(&NVS_NAMESPACE_CARDSTORE_METADATA, &nvs_key) { Ok(metadata) => metadata, Err(esp_nvs::error::Error::KeyNotFound) => return Ok(None), Err(error) => return Err(error.into()), }; let metadata = CardStoreMetaData::binary_deserialize(metadata_binary.as_ref(), Endianness::Little)?; Ok(Some(metadata)) } }