rework card storage to use nvs
This commit was merged in pull request #60.
This commit is contained in:
@@ -42,12 +42,13 @@ pub async fn nfc_scanner(
|
|||||||
};
|
};
|
||||||
CARD_DATA.lock().await.replace(card);
|
CARD_DATA.lock().await.replace(card);
|
||||||
|
|
||||||
|
log::debug!("saving card to flash");
|
||||||
match peripherals
|
match peripherals
|
||||||
.store
|
.store
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.card_store
|
.card_store
|
||||||
.save_raw_card(split_data, peripherals.rtc.current_time_us())
|
.save_raw_card(&split_data, peripherals.rtc.current_time_us())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
@@ -75,6 +76,6 @@ pub async fn nfc_scanner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
embassy_time::Timer::after(embassy_time::Duration::from_millis(1000)).await;
|
embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-13
@@ -17,14 +17,13 @@ use creaturedex::drivers::spi_bus;
|
|||||||
use creaturedex::drivers::tertiary_lcd::{
|
use creaturedex::drivers::tertiary_lcd::{
|
||||||
BOX, EMPTY, EXAMPLE, HEART, TertiaryDisplay, TertiaryDisplayPinConfiguration,
|
BOX, EMPTY, EXAMPLE, HEART, TertiaryDisplay, TertiaryDisplayPinConfiguration,
|
||||||
};
|
};
|
||||||
use creaturedex::navigation::navigation::Mutex;
|
|
||||||
use creaturedex::navigation::inputs::Inputs;
|
use creaturedex::navigation::inputs::Inputs;
|
||||||
|
use creaturedex::navigation::navigation::Mutex;
|
||||||
use creaturedex::navigation::navigation::{self};
|
use creaturedex::navigation::navigation::{self};
|
||||||
use creaturedex::navigation::state::NavigationState;
|
|
||||||
use creaturedex::navigation::outputs::Outputs;
|
use creaturedex::navigation::outputs::Outputs;
|
||||||
|
use creaturedex::navigation::state::NavigationState;
|
||||||
use creaturedex::peripherals::Peripherals;
|
use creaturedex::peripherals::Peripherals;
|
||||||
use creaturedex::peripherals::storage::cardstore::CardStore;
|
use creaturedex::peripherals::storage::cardstore::CardStore;
|
||||||
use creaturedex::peripherals::storage::flash_store::FlashStore;
|
|
||||||
use creaturedex::peripherals::storage::store::Store;
|
use creaturedex::peripherals::storage::store::Store;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embedded_graphics::pixelcolor::BinaryColor;
|
use embedded_graphics::pixelcolor::BinaryColor;
|
||||||
@@ -51,6 +50,9 @@ extern crate alloc;
|
|||||||
|
|
||||||
const HEAP_SIZE: usize = 73744;
|
const HEAP_SIZE: usize = 73744;
|
||||||
|
|
||||||
|
const NVS_PARTITION_OFFSET: usize = 0x10000;
|
||||||
|
const NVS_PARTITION_SIZE: usize = 0xfa000;
|
||||||
|
|
||||||
// This creates a default app-descriptor required by the esp-idf bootloader.
|
// This creates a default app-descriptor required by the esp-idf bootloader.
|
||||||
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
|
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
|
||||||
esp_bootloader_esp_idf::esp_app_desc!();
|
esp_bootloader_esp_idf::esp_app_desc!();
|
||||||
@@ -122,18 +124,18 @@ async fn main(spawner: Spawner) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let flash_store = Box::leak(Box::new(
|
let storage = esp_storage::FlashStorage::new(esp_peripherals.FLASH);
|
||||||
FlashStore::new(esp_peripherals.FLASH).expect("Could not initialize Flash Store"),
|
|
||||||
));
|
|
||||||
|
|
||||||
let card_store = CardStore::new(flash_store)
|
let nvs = Mutex::new(
|
||||||
|
esp_nvs::Nvs::new(NVS_PARTITION_OFFSET, NVS_PARTITION_SIZE, storage)
|
||||||
|
.expect("failed to create nvs"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let card_store = CardStore::new(nvs)
|
||||||
.await
|
.await
|
||||||
.expect("Could not initialize Card Store");
|
.expect("Could not initialize Card Store");
|
||||||
|
|
||||||
let store = Store {
|
let store = Store { card_store };
|
||||||
flash_store,
|
|
||||||
card_store,
|
|
||||||
};
|
|
||||||
let rtc = Rtc::new(esp_peripherals.LPWR);
|
let rtc = Rtc::new(esp_peripherals.LPWR);
|
||||||
//setup_wifi(peripherals.WIFI, peripherals.FLASH, &spawner).await;
|
//setup_wifi(peripherals.WIFI, peripherals.FLASH, &spawner).await;
|
||||||
|
|
||||||
@@ -179,10 +181,14 @@ async fn main(spawner: Spawner) {
|
|||||||
let navigation_state = Box::leak(Box::new(Mutex::new(NavigationState::new())));
|
let navigation_state = Box::leak(Box::new(Mutex::new(NavigationState::new())));
|
||||||
|
|
||||||
log::info!("Setup complete, entering main loop");
|
log::info!("Setup complete, entering main loop");
|
||||||
spawner.spawn(navigation::run(inputs, outputs, peripherals, navigation_state).expect("run task failed"));
|
|
||||||
spawner.spawn(
|
spawner.spawn(
|
||||||
background_tasks::nfc_scanner(nfc_driver, outputs, peripherals, navigation_state).expect("nfc scanner task failed"),
|
navigation::run(inputs, outputs, peripherals, navigation_state).expect("run task failed"),
|
||||||
);
|
);
|
||||||
|
spawner.spawn(
|
||||||
|
background_tasks::nfc_scanner(nfc_driver, outputs, peripherals, navigation_state)
|
||||||
|
.expect("nfc scanner task failed"),
|
||||||
|
);
|
||||||
|
|
||||||
core::future::pending::<()>().await
|
core::future::pending::<()>().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
use base64::prelude::*;
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
|
use esp_nvs::Key;
|
||||||
|
|
||||||
use alloc::{string::String, vec::Vec};
|
use alloc::{string::String, vec::Vec};
|
||||||
use num_enum::FromPrimitive;
|
use num_enum::FromPrimitive;
|
||||||
@@ -22,6 +24,38 @@ pub struct RawCard {
|
|||||||
pub _padding: [u8; 2],
|
pub _padding: [u8; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for RawCard {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
event_encoding: Default::default(),
|
||||||
|
card_type: Default::default(),
|
||||||
|
card_uuid: Default::default(),
|
||||||
|
_reserved: Default::default(),
|
||||||
|
sprite: [0; 722],
|
||||||
|
packed_card_text: [0; 54],
|
||||||
|
secret: Default::default(),
|
||||||
|
_opaque_trailer: [0; 49],
|
||||||
|
_padding: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawCard {
|
||||||
|
pub fn get_uuid(&self) -> u32 {
|
||||||
|
((u16::from_ne_bytes(self.event_encoding) as u32) << 16)
|
||||||
|
| (u16::from_ne_bytes(self.card_uuid) as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_nvs_key(&self) -> Key {
|
||||||
|
let uuid = self.get_uuid();
|
||||||
|
Self::nvs_key_from_uuid(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nvs_key_from_uuid(uuid: u32) -> Key {
|
||||||
|
let base64 = BASE64_STANDARD.encode(uuid.to_ne_bytes());
|
||||||
|
Key::from_str(&base64)
|
||||||
|
}
|
||||||
|
}
|
||||||
static_assertions::const_assert!(RawCard::SERIALIZED_SIZE.is_multiple_of(4));
|
static_assertions::const_assert!(RawCard::SERIALIZED_SIZE.is_multiple_of(4));
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive)]
|
#[derive(Debug, Clone, Eq, PartialEq, FromPrimitive)]
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use esp_storage::FlashStorage;
|
||||||
|
|
||||||
pub struct MemoryRegion {
|
pub struct MemoryRegion {
|
||||||
pub offset: u32,
|
pub offset: u32,
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
@@ -26,7 +28,8 @@ pub const CARDSTORE_REGION: MemoryRegion = MemoryRegion::new(
|
|||||||
MAX_FLASH - SETTINGS_REGION.end(),
|
MAX_FLASH - SETTINGS_REGION.end(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
pub type Nvs = esp_nvs::Nvs<FlashStorage<'static>>;
|
||||||
|
|
||||||
pub mod cardstore;
|
pub mod cardstore;
|
||||||
pub mod flash_store;
|
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
|
|||||||
@@ -1,73 +1,24 @@
|
|||||||
use alloc::collections::BTreeMap;
|
use crate::card::model::RawCard;
|
||||||
|
use crate::peripherals::Mutex;
|
||||||
|
use crate::peripherals::storage::Nvs;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use binary_serde::{BinarySerde, DeserializeError, Endianness};
|
||||||
use core::error::Error;
|
use core::error::Error;
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
|
use esp_nvs::Key;
|
||||||
|
|
||||||
use binary_serde::{BinarySerde, DeserializeError, Endianness};
|
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");
|
||||||
|
|
||||||
//use crate::card::model::Nfcraw_card;
|
const NVS_CARDSTORE_INITIALIZED_KEY: Key = Key::from_str("INITIALIZED");
|
||||||
|
const NVS_CARDSTORE_CARDCOUNT_KEY: Key = Key::from_str("CARDCOUNT");
|
||||||
|
|
||||||
use crate::card::model::RawCard;
|
const NVS_CARDSTORE_DUMMY_KEY: Key = Key::from_str("DUMMY");
|
||||||
use crate::peripherals::storage::flash_store::{FlashStore, FlashStoreError};
|
|
||||||
|
|
||||||
use crate::peripherals::storage::{CARDSTORE_REGION, MemoryRegion};
|
|
||||||
|
|
||||||
const MAX_CARDS: usize = 20000;
|
|
||||||
|
|
||||||
pub const COUNT_REGION: MemoryRegion = MemoryRegion::new(CARDSTORE_REGION.offset, size_of::<u32>());
|
|
||||||
|
|
||||||
const ALLOCATION_TABLE_REGION: MemoryRegion = MemoryRegion::new(
|
|
||||||
COUNT_REGION.end() as u32,
|
|
||||||
MAX_CARDS * AllocationTableEntry::SERIALIZED_SIZE,
|
|
||||||
);
|
|
||||||
|
|
||||||
const CARDS_REGION: MemoryRegion = MemoryRegion::new(
|
|
||||||
ALLOCATION_TABLE_REGION.end() as u32,
|
|
||||||
CARDSTORE_REGION.size - ALLOCATION_TABLE_REGION.end(),
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Debug, BinarySerde, PartialEq, Eq, Clone)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct AllocationTableEntry {
|
|
||||||
uuid: u32,
|
|
||||||
// this is the numeric offset of the AllocationTableEntry
|
|
||||||
// as well as the card entry in flash memory
|
|
||||||
// size_of::<AllocationTableEntry>() * offset
|
|
||||||
// size_of::<RawCard>() * offset
|
|
||||||
offset: u32,
|
|
||||||
|
|
||||||
creation_date: u64,
|
|
||||||
last_read_date: u64,
|
|
||||||
|
|
||||||
// how often a card has been read by nfc
|
|
||||||
read_count: u32,
|
|
||||||
|
|
||||||
deleted: bool,
|
|
||||||
|
|
||||||
reserved: [u64; 4],
|
|
||||||
_padding: [u8; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assertions::const_assert!(AllocationTableEntry::SERIALIZED_SIZE.is_multiple_of(4));
|
|
||||||
|
|
||||||
impl AllocationTableEntry {
|
|
||||||
fn new(uuid: u32, offset: u32, timestamp: u64) -> Self {
|
|
||||||
Self {
|
|
||||||
uuid,
|
|
||||||
offset,
|
|
||||||
creation_date: timestamp,
|
|
||||||
last_read_date: timestamp,
|
|
||||||
read_count: 0,
|
|
||||||
deleted: true,
|
|
||||||
reserved: Default::default(),
|
|
||||||
_padding: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CardStoreError {
|
pub enum CardStoreError {
|
||||||
Store(FlashStoreError),
|
Store(esp_nvs::error::Error),
|
||||||
CorruptedEntry(DeserializeError),
|
CorruptedEntry(DeserializeError),
|
||||||
NoEntry(u32),
|
NoEntry(u32),
|
||||||
}
|
}
|
||||||
@@ -84,8 +35,8 @@ impl Display for CardStoreError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FlashStoreError> for CardStoreError {
|
impl From<esp_nvs::error::Error> for CardStoreError {
|
||||||
fn from(error: FlashStoreError) -> CardStoreError {
|
fn from(error: esp_nvs::error::Error) -> CardStoreError {
|
||||||
CardStoreError::Store(error)
|
CardStoreError::Store(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,140 +47,256 @@ impl From<DeserializeError> for CardStoreError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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 {
|
pub struct CardStore {
|
||||||
store: &'static FlashStore,
|
nvs: Mutex<Nvs>,
|
||||||
count: u32,
|
|
||||||
pub allocation_table: BTreeMap<u32, AllocationTableEntry>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CardStore {
|
impl CardStore {
|
||||||
pub async fn new(store: &'static FlashStore) -> Result<Self, CardStoreError> {
|
pub async fn new(nvs: Mutex<Nvs>) -> Result<Self, CardStoreError> {
|
||||||
let mut count_bytes: [u8; COUNT_REGION.size] = [0; COUNT_REGION.size];
|
if !Self::is_initialized(&nvs).await? {
|
||||||
|
log::info!("CardStore has not been initialized");
|
||||||
store.read(COUNT_REGION.offset, &mut count_bytes).await?;
|
Self::initialize(&nvs).await?;
|
||||||
|
|
||||||
let count: u32 = u32::from_ne_bytes(count_bytes);
|
|
||||||
|
|
||||||
log::info!("Found {count} cards in flash.");
|
|
||||||
|
|
||||||
let mut allocation_table_entries: Vec<u8> =
|
|
||||||
Vec::with_capacity(AllocationTableEntry::SERIALIZED_SIZE * count as usize);
|
|
||||||
|
|
||||||
store
|
|
||||||
.read(
|
|
||||||
ALLOCATION_TABLE_REGION.offset,
|
|
||||||
&mut allocation_table_entries,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let chunks =
|
|
||||||
allocation_table_entries.into_chunks::<{ AllocationTableEntry::SERIALIZED_SIZE }>();
|
|
||||||
|
|
||||||
let mut allocation_table = BTreeMap::<u32, AllocationTableEntry>::new();
|
|
||||||
for chunk in chunks {
|
|
||||||
let allocation_table_entry =
|
|
||||||
AllocationTableEntry::binary_deserialize(chunk.as_ref(), Endianness::Little)?;
|
|
||||||
allocation_table.insert(allocation_table_entry.uuid, allocation_table_entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
log::info!("CardStore has been initialized");
|
||||||
store,
|
let card_count = nvs
|
||||||
count,
|
.lock()
|
||||||
allocation_table,
|
.await
|
||||||
})
|
.get::<u32>(&NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_CARDCOUNT_KEY)?;
|
||||||
|
log::info!("Cards stored in flash: {card_count}");
|
||||||
|
|
||||||
|
Ok(Self { nvs })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_card_by_uuid(&self, uuid: u32) -> Result<RawCard, CardStoreError> {
|
async fn is_initialized(nvs: &Mutex<Nvs>) -> Result<bool, CardStoreError> {
|
||||||
let entry = self
|
log::info!("check initialization");
|
||||||
.allocation_table
|
let mut nvs_lock = nvs.lock().await;
|
||||||
.get(&uuid)
|
match nvs_lock.get::<bool>(&NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_INITIALIZED_KEY) {
|
||||||
.ok_or(CardStoreError::NoEntry(uuid))?;
|
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()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let mut raw_card: [u8; size_of::<RawCard>()] = [0; size_of::<RawCard>()];
|
// initializing cardstore with 0 cards, so that all namespaces exist and there
|
||||||
self.store.read(entry.offset, &mut raw_card).await?;
|
// are only keynotfound errors in the future
|
||||||
|
async fn initialize(nvs: &Mutex<Nvs>) -> Result<(), CardStoreError> {
|
||||||
|
log::info!("initializing card store");
|
||||||
|
|
||||||
Ok(RawCard::binary_deserialize(
|
Self::delete_all_cards(nvs).await?;
|
||||||
raw_card.as_ref(),
|
|
||||||
Endianness::Little,
|
let mut nvs_lock = nvs.lock().await;
|
||||||
)?)
|
|
||||||
|
log::debug!("initialization: write card count");
|
||||||
|
// initialize card count
|
||||||
|
nvs_lock.set::<u32>(&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::<bool>(
|
||||||
|
&NVS_NAMESPACE_CARDSTORE,
|
||||||
|
&NVS_CARDSTORE_INITIALIZED_KEY,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_all_cards(nvs: &Mutex<Nvs>) -> Result<(), CardStoreError> {
|
||||||
|
log::info!("delete all cards");
|
||||||
|
let mut nvs_lock = nvs.lock().await;
|
||||||
|
let keys = nvs_lock.keys().collect::<Result<Vec<_>, _>>()?;
|
||||||
|
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<u32, CardStoreError> {
|
||||||
|
log::debug!("load card count");
|
||||||
|
let mut nvs_lock = self.nvs.lock().await;
|
||||||
|
let result = nvs_lock.get::<u32>(&NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_CARDCOUNT_KEY)?;
|
||||||
|
log::debug!("card count: {result}");
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load_raw_card(&self, uuid: u32) -> Result<Option<RawCard>, 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<u8> = 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<bool, CardStoreError> {
|
||||||
|
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::<u32>(
|
||||||
|
&NVS_NAMESPACE_CARDSTORE,
|
||||||
|
&NVS_CARDSTORE_CARDCOUNT_KEY,
|
||||||
|
card_count + 1,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let card_count = self
|
||||||
|
.nvs
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.get::<u32>(&NVS_NAMESPACE_CARDSTORE, &NVS_CARDSTORE_CARDCOUNT_KEY)?;
|
||||||
|
log::info!("Cards stored in flash: {card_count}");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save_raw_card(
|
pub async fn save_raw_card(
|
||||||
&mut self,
|
&self,
|
||||||
raw_card: RawCard,
|
card: &RawCard,
|
||||||
timestamp: u64,
|
timestamp: u64,
|
||||||
) -> Result<(), CardStoreError> {
|
) -> Result<(), CardStoreError> {
|
||||||
let uuid: u32 = (u16::from_ne_bytes(raw_card.event_encoding) as u32) << 16
|
log::debug!("saving raw card\ntimestamp: {timestamp}");
|
||||||
| (u16::from_ne_bytes(raw_card.card_uuid) as u32);
|
let nvs_key = card.get_nvs_key();
|
||||||
log::info!("Saving card with uuid {:x}", uuid);
|
|
||||||
|
|
||||||
// check if we already got the card
|
self.write_card(nvs_key, card).await?;
|
||||||
// if not find the next deleted entry,
|
|
||||||
// if none create a new one at the end
|
|
||||||
let (entry, is_new) = match self.allocation_table.get_mut(&uuid) {
|
|
||||||
Some(entry) => {
|
|
||||||
log::debug!("old entry");
|
|
||||||
(entry, false)
|
|
||||||
}
|
|
||||||
None => match self
|
|
||||||
.allocation_table
|
|
||||||
.values_mut()
|
|
||||||
.find(|entry| entry.deleted)
|
|
||||||
{
|
|
||||||
Some(entry) => {
|
|
||||||
log::debug!("deleted entry");
|
|
||||||
(entry, true)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
log::debug!("new entry");
|
|
||||||
let entry = AllocationTableEntry::new(uuid, self.count + 1, timestamp);
|
|
||||||
self.allocation_table.insert(uuid, entry);
|
|
||||||
(
|
|
||||||
self.allocation_table
|
|
||||||
.get_mut(&uuid)
|
|
||||||
.expect("entry was just inserted"),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// only write card data if it is new. else it is already written in flash
|
let new_card = self.update_card_metadata(nvs_key, timestamp).await?;
|
||||||
if is_new {
|
|
||||||
log::debug!("writing RawCard to flash");
|
if new_card {
|
||||||
let mut serialized_card: [u8; RawCard::SERIALIZED_SIZE] = [0; RawCard::SERIALIZED_SIZE];
|
self.increment_card_count().await?;
|
||||||
raw_card.binary_serialize(&mut serialized_card, Endianness::Little);
|
|
||||||
self.store
|
|
||||||
.write_erase(
|
|
||||||
CARDS_REGION.offset + entry.offset * RawCard::SERIALIZED_SIZE as u32,
|
|
||||||
&mut serialized_card,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
entry.last_read_date = timestamp;
|
|
||||||
entry.read_count += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.deleted = false;
|
|
||||||
|
|
||||||
log::debug!("writing AllocationTableEntry to flash");
|
|
||||||
let mut serialized_entry: [u8; AllocationTableEntry::SERIALIZED_SIZE] =
|
|
||||||
[0; AllocationTableEntry::SERIALIZED_SIZE];
|
|
||||||
entry.binary_serialize(&mut serialized_entry, Endianness::Little);
|
|
||||||
self.store
|
|
||||||
.write_erase(
|
|
||||||
ALLOCATION_TABLE_REGION.offset
|
|
||||||
+ entry.offset * AllocationTableEntry::SERIALIZED_SIZE as u32,
|
|
||||||
&mut serialized_entry,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
self.count += 1;
|
|
||||||
log::debug!("writing card count to flash");
|
|
||||||
let mut serialized_count: [u8; 4] = self.count.to_le_bytes();
|
|
||||||
self.store
|
|
||||||
.write(COUNT_REGION.offset, serialized_count.as_mut_slice())
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn load_card_metadata(
|
||||||
|
&self,
|
||||||
|
nvs_key: Key,
|
||||||
|
) -> Result<Option<CardStoreMetaData>, CardStoreError> {
|
||||||
|
log::debug!("load card meta data\nkey: {nvs_key:?}");
|
||||||
|
let mut nvs_lock = self.nvs.lock().await;
|
||||||
|
let metadata_binary: Vec<u8> =
|
||||||
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ impl Display for FlashStoreError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FLASH_ADDR: u32 = 0x9000;
|
const FLASH_ADDR: u32 = 0x200000;
|
||||||
|
|
||||||
const INITIALIZATION_SIZE: usize = 0x1000;
|
const INITIALIZATION_SIZE: usize = 0x1000;
|
||||||
const FLASH_INITIALIZE_MAGIC: u32 = 0xde6de6de;
|
const FLASH_INITIALIZE_MAGIC: u32 = 0xde6de6de;
|
||||||
@@ -74,11 +74,13 @@ impl FlashStore {
|
|||||||
bytes.len()
|
bytes.len()
|
||||||
);
|
);
|
||||||
let mut lock = self.storage.lock().await;
|
let mut lock = self.storage.lock().await;
|
||||||
lock.erase(
|
let addr = FLASH_ADDR + offset;
|
||||||
FLASH_ADDR + offset,
|
let to = FLASH_ADDR + offset + bytes.len() as u32;
|
||||||
FLASH_ADDR + offset + bytes.len() as u32,
|
log::debug!("Erasing from 0x{addr:x} to 0x{to:x}");
|
||||||
)?;
|
//lock.erase(addr, to)?;
|
||||||
lock.write(FLASH_ADDR + offset, bytes)?;
|
|
||||||
|
log::debug!("Writing bytes...");
|
||||||
|
lock.write(addr, bytes)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
use crate::peripherals::storage::cardstore::CardStore;
|
use crate::peripherals::storage::cardstore::CardStore;
|
||||||
use crate::peripherals::storage::flash_store::FlashStore;
|
|
||||||
|
|
||||||
pub struct Store {
|
pub struct Store {
|
||||||
pub flash_store: &'static FlashStore,
|
|
||||||
pub card_store: CardStore,
|
pub card_store: CardStore,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use crate::navigation::navigation::NewState;
|
|||||||
use crate::navigation::outputs::Outputs;
|
use crate::navigation::outputs::Outputs;
|
||||||
use crate::peripherals::Peripherals;
|
use crate::peripherals::Peripherals;
|
||||||
use crate::peripherals::storage::MAGIC_REGION;
|
use crate::peripherals::storage::MAGIC_REGION;
|
||||||
use crate::peripherals::storage::cardstore::COUNT_REGION;
|
|
||||||
use crate::views::view::View;
|
use crate::views::view::View;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use core::error;
|
use core::error;
|
||||||
@@ -39,22 +38,6 @@ impl Navigable for FlashInfoView {
|
|||||||
.clear(Rgb565::BLACK)
|
.clear(Rgb565::BLACK)
|
||||||
.map_err(PrimaryDisplayError::from)?;
|
.map_err(PrimaryDisplayError::from)?;
|
||||||
let _style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
|
let _style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
|
||||||
let mut magic_bytes: [u8; MAGIC_REGION.size] = [0; MAGIC_REGION.size];
|
|
||||||
peripherals
|
|
||||||
.store
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.flash_store
|
|
||||||
.read(MAGIC_REGION.offset, &mut magic_bytes)
|
|
||||||
.await?;
|
|
||||||
let mut card_count_bytes: [u8; COUNT_REGION.size] = [0; COUNT_REGION.size];
|
|
||||||
peripherals
|
|
||||||
.store
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.flash_store
|
|
||||||
.read(COUNT_REGION.offset, &mut card_count_bytes)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
|
let style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
|
||||||
let textbox_style = TextBoxStyleBuilder::new()
|
let textbox_style = TextBoxStyleBuilder::new()
|
||||||
@@ -65,14 +48,18 @@ impl Navigable for FlashInfoView {
|
|||||||
let display_area = outputs.primary_display.bounding_box();
|
let display_area = outputs.primary_display.bounding_box();
|
||||||
let bounds = Rectangle::new(Point::new(0, 30), display_area.size);
|
let bounds = Rectangle::new(Point::new(0, 30), display_area.size);
|
||||||
|
|
||||||
|
log::debug!("locking store");
|
||||||
|
let store = peripherals.store.lock().await;
|
||||||
|
|
||||||
|
log::debug!("getting card count");
|
||||||
|
let card_count = store.card_store.load_card_count().await?;
|
||||||
|
|
||||||
TextBox::with_textbox_style(
|
TextBox::with_textbox_style(
|
||||||
&format!(
|
&format!(
|
||||||
"
|
"
|
||||||
Flash Init Magic: 0x{:x}\n
|
|
||||||
Card Count: {}\n
|
Card Count: {}\n
|
||||||
",
|
",
|
||||||
u32::from_le_bytes(magic_bytes),
|
card_count
|
||||||
u32::from_le_bytes(card_count_bytes)
|
|
||||||
),
|
),
|
||||||
bounds,
|
bounds,
|
||||||
style,
|
style,
|
||||||
|
|||||||
Reference in New Issue
Block a user