checking persistence on desktop and esp32
Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
@@ -58,9 +58,10 @@ else ()
|
|||||||
WIN32 MACOSX_BUNDLE
|
WIN32 MACOSX_BUNDLE
|
||||||
${CMAKE_SOURCE_DIR}/main.cpp
|
${CMAKE_SOURCE_DIR}/main.cpp
|
||||||
${CMAKE_SOURCE_DIR}/Common.cpp
|
${CMAKE_SOURCE_DIR}/Common.cpp
|
||||||
${CMAKE_SOURCE_DIR}/ResourceManager.cpp
|
|
||||||
${CMAKE_SOURCE_DIR}/debug/DebugOverlay.cpp
|
${CMAKE_SOURCE_DIR}/debug/DebugOverlay.cpp
|
||||||
${CMAKE_SOURCE_DIR}/hal/u8x8_hal_sdl.cpp
|
${CMAKE_SOURCE_DIR}/hal/u8x8_hal_sdl.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/manager/PersistenceManager.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/manager/ResourceManager.cpp
|
||||||
${CMAKE_SOURCE_DIR}/model/AppContext.cpp
|
${CMAKE_SOURCE_DIR}/model/AppContext.cpp
|
||||||
${CMAKE_SOURCE_DIR}/model/Window.cpp
|
${CMAKE_SOURCE_DIR}/model/Window.cpp
|
||||||
${CMAKE_SOURCE_DIR}/ui/Device.cpp
|
${CMAKE_SOURCE_DIR}/ui/Device.cpp
|
||||||
|
@@ -16,7 +16,10 @@ set(SOURCE_FILES
|
|||||||
if (DEFINED ENV{IDF_PATH})
|
if (DEFINED ENV{IDF_PATH})
|
||||||
idf_component_register(SRCS
|
idf_component_register(SRCS
|
||||||
${SOURCE_FILES}
|
${SOURCE_FILES}
|
||||||
|
src/common/PersistenceManager.cpp
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
|
REQUIRES
|
||||||
|
nvs_flash
|
||||||
PRIV_REQUIRES
|
PRIV_REQUIRES
|
||||||
u8g2
|
u8g2
|
||||||
)
|
)
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
// Project-specific headers
|
// Project-specific headers
|
||||||
#include "common/Widget.h"
|
#include "common/Widget.h"
|
||||||
|
#include "common/IPersistenceManager.h"
|
||||||
#include "u8g2.h"
|
#include "u8g2.h"
|
||||||
|
|
||||||
class MenuItem;
|
class MenuItem;
|
||||||
@@ -25,102 +26,49 @@ class MenuItem;
|
|||||||
* @struct menu_options_t
|
* @struct menu_options_t
|
||||||
* @brief Configuration structure for menu widgets containing display context and callbacks
|
* @brief Configuration structure for menu widgets containing display context and callbacks
|
||||||
* @details This structure serves as a configuration container that provides menu widgets
|
* @details This structure serves as a configuration container that provides menu widgets
|
||||||
* with access to the display system, screen management functions, and input
|
* with access to the display system, screen management functions, input
|
||||||
* handling callbacks. It acts as the bridge between individual menu widgets
|
* handling callbacks, and persistent storage.
|
||||||
* and the broader application framework.
|
|
||||||
*
|
|
||||||
* The structure contains:
|
|
||||||
* - Display context for rendering operations
|
|
||||||
* - Screen management callbacks for navigation
|
|
||||||
* - Input handling callback for button events
|
|
||||||
*
|
|
||||||
* All callback functions use std::function for type safety and flexibility,
|
|
||||||
* allowing both function pointers and lambda expressions to be used.
|
|
||||||
*
|
|
||||||
* @note This structure should be initialized by the application framework
|
|
||||||
* and passed to menu widgets during construction.
|
|
||||||
*
|
*
|
||||||
* @see Widget
|
* @see Widget
|
||||||
* @see ButtonType
|
* @see ButtonType
|
||||||
|
* @see IPersistenceManager
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @brief Pointer to u8g2 display context for graphics output operations
|
* @brief Pointer to u8g2 display context for graphics output operations
|
||||||
* @details This pointer provides access to the u8g2 graphics library functions
|
|
||||||
* for rendering text, shapes, and other visual elements. It must be
|
|
||||||
* initialized and ready for drawing operations before being passed
|
|
||||||
* to menu widgets.
|
|
||||||
*
|
|
||||||
* @note The menu widgets do not take ownership of this pointer and assume
|
|
||||||
* it remains valid throughout their lifetime. Ensure the u8g2 context
|
|
||||||
* is properly managed by the application framework.
|
|
||||||
*
|
|
||||||
* @warning Must not be nullptr when passed to menu widgets
|
|
||||||
*/
|
*/
|
||||||
u8g2_t *u8g2;
|
u8g2_t *u8g2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback function to set the current active screen
|
* @brief Callback function to set the current active screen
|
||||||
* @param screen Smart pointer to the Widget that should become the active screen
|
|
||||||
*
|
|
||||||
* @details This callback replaces the currently active screen with the provided
|
|
||||||
* widget. It is typically used for direct screen transitions where the
|
|
||||||
* previous screen should be completely replaced rather than stacked.
|
|
||||||
*
|
|
||||||
* @note The callback takes ownership of the provided Widget through the shared_ptr.
|
|
||||||
* The previous screen will be destroyed unless other references exist.
|
|
||||||
*
|
|
||||||
* @see pushScreen for adding screens to a navigation stack
|
|
||||||
*/
|
*/
|
||||||
std::function<void(std::shared_ptr<Widget>)> setScreen;
|
std::function<void(std::shared_ptr<Widget>)> setScreen;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback function to add a new screen to the navigation stack
|
* @brief Callback function to add a new screen to the navigation stack
|
||||||
* @param screen Smart pointer to the Widget that should be pushed onto the screen stack
|
|
||||||
*
|
|
||||||
* @details This callback adds a new screen on top of the current screen stack,
|
|
||||||
* allowing for hierarchical navigation where users can return to
|
|
||||||
* previous screens. Commonly used for sub-menus, settings screens,
|
|
||||||
* or modal dialogs.
|
|
||||||
*
|
|
||||||
* @note The callback takes ownership of the provided Widget through the shared_ptr.
|
|
||||||
* The current screen remains in memory and can be returned to via popScreen().
|
|
||||||
*
|
|
||||||
* @see popScreen for removing screens from the navigation stack
|
|
||||||
* @see setScreen for direct screen replacement
|
|
||||||
*/
|
*/
|
||||||
std::function<void(std::shared_ptr<Widget>)> pushScreen;
|
std::function<void(std::shared_ptr<Widget>)> pushScreen;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback function to remove the top screen from the navigation stack
|
* @brief Callback function to remove the top screen from the navigation stack
|
||||||
* @details This callback removes the currently active screen and returns to the
|
|
||||||
* previous screen in the navigation stack. It is typically used for
|
|
||||||
* "back" or "cancel" operations in hierarchical menu systems.
|
|
||||||
*
|
|
||||||
* @note If the navigation stack is empty or contains only one screen, the
|
|
||||||
* behavior is implementation-dependent and should be handled gracefully
|
|
||||||
* by the application framework.
|
|
||||||
*
|
|
||||||
* @see pushScreen for adding screens to the navigation stack
|
|
||||||
*/
|
*/
|
||||||
std::function<void()> popScreen;
|
std::function<void()> popScreen;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback function to handle button press events
|
* @brief Callback function to handle button press events
|
||||||
* @param button The type of button that was pressed
|
|
||||||
*
|
|
||||||
* @details This callback is invoked when a button press event occurs that is
|
|
||||||
* not handled directly by the menu widget. It allows the application
|
|
||||||
* framework to implement global button handling logic, such as
|
|
||||||
* system-wide shortcuts or fallback behavior.
|
|
||||||
*
|
|
||||||
* @note This callback is typically used for buttons that have application-wide
|
|
||||||
* meaning (e.g., home button, menu button) rather than widget-specific
|
|
||||||
* navigation which is handled internally by the widgets.
|
|
||||||
*
|
|
||||||
* @see ButtonType for available button types
|
|
||||||
* @see Widget::onButtonClicked for widget-specific button handling
|
|
||||||
*/
|
*/
|
||||||
std::function<void(ButtonType button)> onButtonClicked;
|
std::function<void(ButtonType button)> onButtonClicked;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shared pointer to platform-independent persistence manager
|
||||||
|
* @details This provides access to persistent key-value storage across different
|
||||||
|
* platforms. The actual implementation (SDL3 or ESP32/NVS) is determined
|
||||||
|
* at compile time based on the target platform.
|
||||||
|
*
|
||||||
|
* @note The persistence manager is shared across all menu widgets and maintains
|
||||||
|
* its state throughout the application lifecycle.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<IPersistenceManager> persistenceManager;
|
||||||
|
|
||||||
} menu_options_t;
|
} menu_options_t;
|
136
components/insa/include/common/IPersistenceManager.h
Normal file
136
components/insa/include/common/IPersistenceManager.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @interface IPersistenceManager
|
||||||
|
* @brief Abstract interface for platform-independent persistence management
|
||||||
|
* @details This interface defines the contract for key-value storage and retrieval
|
||||||
|
* systems across different platforms (Desktop/SDL3 and ESP32).
|
||||||
|
*/
|
||||||
|
class IPersistenceManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IPersistenceManager() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Template methods for type-safe setting and retrieving of values
|
||||||
|
* @tparam T The type of value to set (must be one of: bool, int, float, double, std::string)
|
||||||
|
* @param key The key to associate with the value
|
||||||
|
* @param value The value to store
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
void SetValue(const std::string& key, const T& value) {
|
||||||
|
static_assert(std::is_same_v<T, bool> ||
|
||||||
|
std::is_same_v<T, int> ||
|
||||||
|
std::is_same_v<T, float> ||
|
||||||
|
std::is_same_v<T, double> ||
|
||||||
|
std::is_same_v<T, std::string>,
|
||||||
|
"Unsupported type for IPersistenceManager");
|
||||||
|
SetValueImpl(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Template method for type-safe retrieval of values
|
||||||
|
* @tparam T The type of value to retrieve
|
||||||
|
* @param key The key to look up
|
||||||
|
* @param defaultValue The default value to return if key is not found
|
||||||
|
* @return The stored value or default value if key doesn't exist
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
T GetValue(const std::string& key, const T& defaultValue = T{}) const {
|
||||||
|
return GetValueImpl<T>(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience methods for setting specific types
|
||||||
|
*/
|
||||||
|
void SetBool(const std::string& key, bool value) { SetValue(key, value); }
|
||||||
|
void SetInt(const std::string& key, int value) { SetValue(key, value); }
|
||||||
|
void SetFloat(const std::string& key, float value) { SetValue(key, value); }
|
||||||
|
void SetDouble(const std::string& key, double value) { SetValue(key, value); }
|
||||||
|
void SetString(const std::string& key, const std::string& value) { SetValue(key, value); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience methods for getting specific types with default values
|
||||||
|
*/
|
||||||
|
bool GetBool(const std::string& key, bool defaultValue = false) const {
|
||||||
|
return GetValue(key, defaultValue);
|
||||||
|
}
|
||||||
|
int GetInt(const std::string& key, int defaultValue = 0) const {
|
||||||
|
return GetValue(key, defaultValue);
|
||||||
|
}
|
||||||
|
float GetFloat(const std::string& key, float defaultValue = 0.0f) const {
|
||||||
|
return GetValue(key, defaultValue);
|
||||||
|
}
|
||||||
|
double GetDouble(const std::string& key, double defaultValue = 0.0) const {
|
||||||
|
return GetValue(key, defaultValue);
|
||||||
|
}
|
||||||
|
std::string GetString(const std::string& key, const std::string& defaultValue = "") const {
|
||||||
|
return GetValue(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Utility methods for key management
|
||||||
|
*/
|
||||||
|
virtual bool HasKey(const std::string& key) const = 0; ///< Check if a key exists
|
||||||
|
virtual void RemoveKey(const std::string& key) = 0; ///< Remove a key-value pair
|
||||||
|
virtual void Clear() = 0; ///< Clear all stored data
|
||||||
|
virtual size_t GetKeyCount() const = 0; ///< Get the number of stored keys
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Persistence operations
|
||||||
|
*/
|
||||||
|
virtual bool Save() = 0; ///< Save data to persistent storage
|
||||||
|
virtual bool Load() = 0; ///< Load data from persistent storage
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* @brief Template-specific implementations that must be overridden by derived classes
|
||||||
|
* @details These methods handle the actual storage and retrieval of different data types
|
||||||
|
*/
|
||||||
|
virtual void SetValueImpl(const std::string& key, bool value) = 0;
|
||||||
|
virtual void SetValueImpl(const std::string& key, int value) = 0;
|
||||||
|
virtual void SetValueImpl(const std::string& key, float value) = 0;
|
||||||
|
virtual void SetValueImpl(const std::string& key, double value) = 0;
|
||||||
|
virtual void SetValueImpl(const std::string& key, const std::string& value) = 0;
|
||||||
|
|
||||||
|
virtual bool GetValueImpl(const std::string& key, bool defaultValue) const = 0;
|
||||||
|
virtual int GetValueImpl(const std::string& key, int defaultValue) const = 0;
|
||||||
|
virtual float GetValueImpl(const std::string& key, float defaultValue) const = 0;
|
||||||
|
virtual double GetValueImpl(const std::string& key, double defaultValue) const = 0;
|
||||||
|
virtual std::string GetValueImpl(const std::string& key, const std::string& defaultValue) const = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Template dispatch methods for type-safe value retrieval
|
||||||
|
* @tparam T The type to retrieve
|
||||||
|
* @param key The key to look up
|
||||||
|
* @param defaultValue The default value to return
|
||||||
|
* @return The retrieved value or default if not found
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
T GetValueImpl(const std::string& key, const T& defaultValue) const
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<T, bool>) {
|
||||||
|
return GetValueImpl(key, defaultValue);
|
||||||
|
} else if constexpr (std::is_same_v<T, int>) {
|
||||||
|
return GetValueImpl(key, defaultValue);
|
||||||
|
} else if constexpr (std::is_same_v<T, float>) {
|
||||||
|
return GetValueImpl(key, defaultValue);
|
||||||
|
} else if constexpr (std::is_same_v<T, double>) {
|
||||||
|
return GetValueImpl(key, defaultValue);
|
||||||
|
} else if constexpr (std::is_same_v<T, std::string>) {
|
||||||
|
return GetValueImpl(key, defaultValue);
|
||||||
|
} else {
|
||||||
|
static_assert(std::is_same_v<T, bool> ||
|
||||||
|
std::is_same_v<T, int> ||
|
||||||
|
std::is_same_v<T, float> ||
|
||||||
|
std::is_same_v<T, double> ||
|
||||||
|
std::is_same_v<T, std::string>,
|
||||||
|
"Unsupported type for IPersistenceManager");
|
||||||
|
return defaultValue; // This line will never be reached, but satisfies compiler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
61
components/insa/include/common/PersistenceManager.h
Normal file
61
components/insa/include/common/PersistenceManager.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IPersistenceManager.h"
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <nvs.h>
|
||||||
|
#include <nvs_flash.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PersistenceManager
|
||||||
|
* @brief ESP32-specific implementation using NVS (Non-Volatile Storage)
|
||||||
|
* @details This implementation uses ESP32's NVS API for persistent storage
|
||||||
|
* in flash memory, providing a platform-optimized solution for
|
||||||
|
* embedded systems.
|
||||||
|
*/
|
||||||
|
class PersistenceManager : public IPersistenceManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
nvs_handle_t nvs_handle_;
|
||||||
|
std::string namespace_;
|
||||||
|
bool initialized_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PersistenceManager(const std::string &nvs_namespace = "config");
|
||||||
|
~PersistenceManager() override;
|
||||||
|
|
||||||
|
// IPersistenceManager implementation
|
||||||
|
bool HasKey(const std::string &key) const override;
|
||||||
|
void RemoveKey(const std::string &key) override;
|
||||||
|
void Clear() override;
|
||||||
|
size_t GetKeyCount() const override;
|
||||||
|
|
||||||
|
bool Save() override;
|
||||||
|
bool Load() override;
|
||||||
|
|
||||||
|
// ESP32-specific methods
|
||||||
|
bool Initialize();
|
||||||
|
void Deinitialize();
|
||||||
|
bool IsInitialized() const
|
||||||
|
{
|
||||||
|
return initialized_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Template-spezifische Implementierungen
|
||||||
|
void SetValueImpl(const std::string &key, bool value) override;
|
||||||
|
void SetValueImpl(const std::string &key, int value) override;
|
||||||
|
void SetValueImpl(const std::string &key, float value) override;
|
||||||
|
void SetValueImpl(const std::string &key, double value) override;
|
||||||
|
void SetValueImpl(const std::string &key, const std::string &value) override;
|
||||||
|
|
||||||
|
bool GetValueImpl(const std::string &key, bool defaultValue) const override;
|
||||||
|
int GetValueImpl(const std::string &key, int defaultValue) const override;
|
||||||
|
float GetValueImpl(const std::string &key, float defaultValue) const override;
|
||||||
|
double GetValueImpl(const std::string &key, double defaultValue) const override;
|
||||||
|
std::string GetValueImpl(const std::string &key, const std::string &defaultValue) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool EnsureInitialized() const;
|
||||||
|
};
|
@@ -29,5 +29,7 @@ private:
|
|||||||
*/
|
*/
|
||||||
void onButtonPressed(const MenuItem& menuItem, ButtonType button) override;
|
void onButtonPressed(const MenuItem& menuItem, ButtonType button) override;
|
||||||
|
|
||||||
|
static std::string CreateKey(int index);
|
||||||
|
|
||||||
menu_options_t *m_options; ///< Pointer to menu configuration options
|
menu_options_t *m_options; ///< Pointer to menu configuration options
|
||||||
};
|
};
|
@@ -58,7 +58,15 @@ void Menu::setItemSize(const size_t size)
|
|||||||
for (size_t i = m_items.size() - 1; i < size; i++)
|
for (size_t i = m_items.size() - 1; i < size; i++)
|
||||||
{
|
{
|
||||||
auto caption = std::string("Bereich ") + std::to_string(i + 1);
|
auto caption = std::string("Bereich ") + std::to_string(i + 1);
|
||||||
addSelection(i + 1, caption, m_items.at(0).getValues(), 0);
|
auto index = 0;
|
||||||
|
if (m_options && m_options->persistenceManager)
|
||||||
|
{
|
||||||
|
constexpr int key_length = 20;
|
||||||
|
char key[key_length] = "";
|
||||||
|
snprintf(key, key_length, "section_%zu", i + 1);
|
||||||
|
index = m_options->persistenceManager->GetValue(key, index);
|
||||||
|
}
|
||||||
|
addSelection(i + 1, caption, m_items.at(0).getValues(), index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
295
components/insa/src/common/PersistenceManager.cpp
Normal file
295
components/insa/src/common/PersistenceManager.cpp
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
#ifdef ESP_PLATFORM
|
||||||
|
#include "common/PersistenceManager.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
static const char *TAG = "PersistenceManager";
|
||||||
|
|
||||||
|
PersistenceManager::PersistenceManager(const std::string &nvs_namespace)
|
||||||
|
: namespace_(nvs_namespace), initialized_(false)
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistenceManager::~PersistenceManager()
|
||||||
|
{
|
||||||
|
Deinitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::Initialize()
|
||||||
|
{
|
||||||
|
if (initialized_)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize NVS
|
||||||
|
esp_err_t err = nvs_flash_init();
|
||||||
|
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
|
err = nvs_flash_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to initialize NVS flash: %s", esp_err_to_name(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open NVS handle
|
||||||
|
err = nvs_open(namespace_.c_str(), NVS_READWRITE, &nvs_handle_);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to open NVS handle: %s", esp_err_to_name(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized_ = true;
|
||||||
|
ESP_LOGI(TAG, "PersistenceManager initialized with namespace: %s", namespace_.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::Deinitialize()
|
||||||
|
{
|
||||||
|
if (initialized_)
|
||||||
|
{
|
||||||
|
nvs_close(nvs_handle_);
|
||||||
|
initialized_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::EnsureInitialized() const
|
||||||
|
{
|
||||||
|
if (!initialized_)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "PersistenceManager not initialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::HasKey(const std::string &key) const
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t required_size = 0;
|
||||||
|
esp_err_t err = nvs_get_blob(nvs_handle_, key.c_str(), nullptr, &required_size);
|
||||||
|
return err == ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::RemoveKey(const std::string &key)
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return;
|
||||||
|
|
||||||
|
esp_err_t err = nvs_erase_key(nvs_handle_, key.c_str());
|
||||||
|
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to remove key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::Clear()
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return;
|
||||||
|
|
||||||
|
esp_err_t err = nvs_erase_all(nvs_handle_);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to clear all keys: %s", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PersistenceManager::GetKeyCount() const
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nvs_iterator_t it = nullptr;
|
||||||
|
esp_err_t err = nvs_entry_find(NVS_DEFAULT_PART_NAME, namespace_.c_str(), NVS_TYPE_ANY, &it);
|
||||||
|
|
||||||
|
if (err != ESP_OK || it == nullptr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
while (it != nullptr)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
err = nvs_entry_next(&it);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nvs_release_iterator(it);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::Save()
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
esp_err_t err = nvs_commit(nvs_handle_);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to commit NVS: %s", esp_err_to_name(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::Load()
|
||||||
|
{
|
||||||
|
return EnsureInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::SetValueImpl(const std::string &key, bool value)
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t val = value ? 1 : 0;
|
||||||
|
esp_err_t err = nvs_set_u8(nvs_handle_, key.c_str(), val);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to set bool key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::SetValueImpl(const std::string &key, int value)
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return;
|
||||||
|
|
||||||
|
esp_err_t err = nvs_set_i32(nvs_handle_, key.c_str(), value);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to set int key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::SetValueImpl(const std::string &key, float value)
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return;
|
||||||
|
|
||||||
|
esp_err_t err = nvs_set_blob(nvs_handle_, key.c_str(), &value, sizeof(float));
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to set float key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::SetValueImpl(const std::string &key, double value)
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return;
|
||||||
|
|
||||||
|
esp_err_t err = nvs_set_blob(nvs_handle_, key.c_str(), &value, sizeof(double));
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to set double key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::SetValueImpl(const std::string &key, const std::string &value)
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return;
|
||||||
|
|
||||||
|
esp_err_t err = nvs_set_str(nvs_handle_, key.c_str(), value.c_str());
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to set string key '%s': %s", key.c_str(), esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::GetValueImpl(const std::string &key, bool defaultValue) const
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
uint8_t value;
|
||||||
|
esp_err_t err = nvs_get_u8(nvs_handle_, key.c_str(), &value);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return value != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PersistenceManager::GetValueImpl(const std::string &key, int defaultValue) const
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
int32_t value;
|
||||||
|
esp_err_t err = nvs_get_i32(nvs_handle_, key.c_str(), &value);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
float PersistenceManager::GetValueImpl(const std::string &key, float defaultValue) const
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
float value;
|
||||||
|
size_t required_size = sizeof(float);
|
||||||
|
esp_err_t err = nvs_get_blob(nvs_handle_, key.c_str(), &value, &required_size);
|
||||||
|
if (err != ESP_OK || required_size != sizeof(float))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
double PersistenceManager::GetValueImpl(const std::string &key, double defaultValue) const
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
double value;
|
||||||
|
size_t required_size = sizeof(double);
|
||||||
|
esp_err_t err = nvs_get_blob(nvs_handle_, key.c_str(), &value, &required_size);
|
||||||
|
if (err != ESP_OK || required_size != sizeof(double))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PersistenceManager::GetValueImpl(const std::string &key, const std::string &defaultValue) const
|
||||||
|
{
|
||||||
|
if (!EnsureInitialized())
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
size_t required_size = 0;
|
||||||
|
esp_err_t err = nvs_get_str(nvs_handle_, key.c_str(), nullptr, &required_size);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string value(required_size - 1, '\0'); // -1 for null terminator
|
||||||
|
err = nvs_get_str(nvs_handle_, key.c_str(), value.data(), &required_size);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ESP32
|
@@ -13,16 +13,32 @@ constexpr uint8_t MODE = 1; ///< ID for the light mode selection
|
|||||||
constexpr uint8_t LED_SETTINGS = 2; ///< ID for the LED settings menu item
|
constexpr uint8_t LED_SETTINGS = 2; ///< ID for the LED settings menu item
|
||||||
} // namespace LightMenuItem
|
} // namespace LightMenuItem
|
||||||
|
|
||||||
|
namespace LightMenuOptions
|
||||||
|
{
|
||||||
|
constexpr std::string LIGHT_ACTIVE = "light_active";
|
||||||
|
constexpr std::string LIGHT_MODE = "light_mode";
|
||||||
|
}
|
||||||
|
|
||||||
LightMenu::LightMenu(menu_options_t *options) : Menu(options), m_options(options)
|
LightMenu::LightMenu(menu_options_t *options) : Menu(options), m_options(options)
|
||||||
{
|
{
|
||||||
// Add toggle for enabling/disabling the light system
|
// Add toggle for enabling/disabling the light system
|
||||||
addToggle(LightMenuItem::ACTIVATE, "Einschalten", true);
|
bool active = false;
|
||||||
|
if (m_options && m_options->persistenceManager)
|
||||||
|
{
|
||||||
|
active = m_options->persistenceManager->GetValue(LightMenuOptions::LIGHT_ACTIVE, active);
|
||||||
|
}
|
||||||
|
addToggle(LightMenuItem::ACTIVATE, "Einschalten", active);
|
||||||
|
|
||||||
// Create mode selection options (Day/Night modes)
|
// Create mode selection options (Day/Night modes)
|
||||||
std::vector<std::string> values;
|
std::vector<std::string> values;
|
||||||
values.emplace_back("Tag"); // Day mode
|
values.emplace_back("Tag"); // Day mode
|
||||||
values.emplace_back("Nacht"); // Night mode
|
values.emplace_back("Nacht"); // Night mode
|
||||||
addSelection(LightMenuItem::MODE, "Modus", values, 0);
|
int mode_value = 0;
|
||||||
|
if (m_options && m_options->persistenceManager)
|
||||||
|
{
|
||||||
|
mode_value = m_options->persistenceManager->GetValue(LightMenuOptions::LIGHT_MODE, mode_value);
|
||||||
|
}
|
||||||
|
addSelection(LightMenuItem::MODE, "Modus", values, mode_value);
|
||||||
|
|
||||||
// Add menu item for accessing LED settings submenu
|
// Add menu item for accessing LED settings submenu
|
||||||
addText(LightMenuItem::LED_SETTINGS, "Einstellungen");
|
addText(LightMenuItem::LED_SETTINGS, "Einstellungen");
|
||||||
@@ -40,13 +56,28 @@ void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType butto
|
|||||||
if (button == ButtonType::SELECT)
|
if (button == ButtonType::SELECT)
|
||||||
{
|
{
|
||||||
toggle(menuItem);
|
toggle(menuItem);
|
||||||
|
if (m_options && m_options->persistenceManager)
|
||||||
|
{
|
||||||
|
const auto value = getItem(menuItem.getId()).getValue() == "1";
|
||||||
|
m_options->persistenceManager->SetValue(LightMenuOptions::LIGHT_ACTIVE, value);
|
||||||
|
m_options->persistenceManager->Save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LightMenuItem::MODE: {
|
case LightMenuItem::MODE: {
|
||||||
// Switch between day/night modes using left/right buttons
|
// Switch between day/night modes using left/right buttons
|
||||||
switchValue(menuItem, button);
|
const auto item = switchValue(menuItem, button);
|
||||||
|
if (button == ButtonType::LEFT || button == ButtonType::RIGHT)
|
||||||
|
{
|
||||||
|
if (m_options && m_options->persistenceManager)
|
||||||
|
{
|
||||||
|
const auto value = getItem(item.getId()).getIndex();
|
||||||
|
m_options->persistenceManager->SetValue(LightMenuOptions::LIGHT_MODE, value);
|
||||||
|
m_options->persistenceManager->Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,6 +9,14 @@ namespace LightSettingsMenuItem
|
|||||||
constexpr uint8_t SECTION_COUNTER = 0; ///< ID for the section counter menu item
|
constexpr uint8_t SECTION_COUNTER = 0; ///< ID for the section counter menu item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string LightSettingsMenu::CreateKey(const int index)
|
||||||
|
{
|
||||||
|
constexpr int key_length = 20;
|
||||||
|
char key[key_length] = "";
|
||||||
|
snprintf(key, key_length, "section_%d", index);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
LightSettingsMenu::LightSettingsMenu(menu_options_t *options) : Menu(options), m_options(options)
|
LightSettingsMenu::LightSettingsMenu(menu_options_t *options) : Menu(options), m_options(options)
|
||||||
{
|
{
|
||||||
// Create values vector for section counts (1-99)
|
// Create values vector for section counts (1-99)
|
||||||
@@ -19,7 +27,12 @@ LightSettingsMenu::LightSettingsMenu(menu_options_t *options) : Menu(options), m
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add section counter selection (allows choosing number of sections)
|
// Add section counter selection (allows choosing number of sections)
|
||||||
addSelection(LightSettingsMenuItem::SECTION_COUNTER, "Sektionen", values, 7);
|
auto value = 7;
|
||||||
|
if (m_options && m_options->persistenceManager)
|
||||||
|
{
|
||||||
|
value = m_options->persistenceManager->GetValue(CreateKey(0), value);
|
||||||
|
}
|
||||||
|
addSelection(LightSettingsMenuItem::SECTION_COUNTER, "Sektionen", values, value);
|
||||||
|
|
||||||
setItemSize(std::stoull(getItem(0).getValue()));
|
setItemSize(std::stoull(getItem(0).getValue()));
|
||||||
}
|
}
|
||||||
@@ -30,5 +43,15 @@ void LightSettingsMenu::onButtonPressed(const MenuItem &menuItem, const ButtonTy
|
|||||||
switchValue(menuItem, button);
|
switchValue(menuItem, button);
|
||||||
|
|
||||||
// Update the section list size based on the section counter value
|
// Update the section list size based on the section counter value
|
||||||
|
if (menuItem.getId() == 0)
|
||||||
|
{
|
||||||
setItemSize(std::stoull(getItem(0).getValue()));
|
setItemSize(std::stoull(getItem(0).getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Persist the changed section values if persistence is available
|
||||||
|
if (m_options && m_options->persistenceManager)
|
||||||
|
{
|
||||||
|
const auto value = getItem(menuItem.getId()).getIndex();
|
||||||
|
m_options->persistenceManager->SetValue(CreateKey(menuItem.getId()), value);
|
||||||
|
}
|
||||||
}
|
}
|
BIN
config.dat
Normal file
BIN
config.dat
Normal file
Binary file not shown.
@@ -3,11 +3,11 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_timer.h"
|
#include "esp_timer.h"
|
||||||
#include "hal/u8g2_esp32_hal.h"
|
#include "hal/u8g2_esp32_hal.h"
|
||||||
#include "string"
|
|
||||||
#include "u8g2.h"
|
#include "u8g2.h"
|
||||||
|
|
||||||
#include "button_handling.h"
|
#include "button_handling.h"
|
||||||
#include "common/InactivityTracker.h"
|
#include "common/InactivityTracker.h"
|
||||||
|
#include "common/PersistenceManager.h"
|
||||||
#include "ui/ScreenSaver.h"
|
#include "ui/ScreenSaver.h"
|
||||||
#include "ui/SplashScreen.h"
|
#include "ui/SplashScreen.h"
|
||||||
|
|
||||||
@@ -99,7 +99,9 @@ static void init_ui(void)
|
|||||||
.pushScreen = [](const std::shared_ptr<Widget> &screen) { pushScreen(screen); },
|
.pushScreen = [](const std::shared_ptr<Widget> &screen) { pushScreen(screen); },
|
||||||
.popScreen = []() { popScreen(); },
|
.popScreen = []() { popScreen(); },
|
||||||
.onButtonClicked = nullptr,
|
.onButtonClicked = nullptr,
|
||||||
|
.persistenceManager = std::make_shared<PersistenceManager>(),
|
||||||
};
|
};
|
||||||
|
options.persistenceManager->Load();
|
||||||
m_widget = std::make_shared<SplashScreen>(&options);
|
m_widget = std::make_shared<SplashScreen>(&options);
|
||||||
m_inactivityTracker = std::make_unique<InactivityTracker>(60000, []() {
|
m_inactivityTracker = std::make_unique<InactivityTracker>(60000, []() {
|
||||||
auto screensaver = std::make_shared<ScreenSaver>(&options);
|
auto screensaver = std::make_shared<ScreenSaver>(&options);
|
||||||
|
304
src/manager/PersistenceManager.cpp
Normal file
304
src/manager/PersistenceManager.cpp
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
#include "PersistenceManager.h"
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#ifndef ESP_PLATFORM
|
||||||
|
PersistenceManager::PersistenceManager(std::string filename)
|
||||||
|
: filename_(std::move(filename)) {
|
||||||
|
// Initialize SDL3 if not already done
|
||||||
|
if (!SDL_WasInit(SDL_INIT_EVENTS)) {
|
||||||
|
SDL_Init(SDL_INIT_EVENTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistenceManager::~PersistenceManager() {
|
||||||
|
Save(); // Automatically save on destruction
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::HasKey(const std::string& key) const {
|
||||||
|
return data_.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::RemoveKey(const std::string& key) {
|
||||||
|
data_.erase(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::Clear() {
|
||||||
|
data_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::Save() {
|
||||||
|
return SaveToFile(filename_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::Load() {
|
||||||
|
return LoadFromFile(filename_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::SaveToFile(const std::string& filename) {
|
||||||
|
SDL_IOStream* stream = SDL_IOFromFile(filename.c_str(), "wb");
|
||||||
|
if (!stream) {
|
||||||
|
SDL_Log("Error opening file for writing: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write number of entries
|
||||||
|
const size_t count = data_.size();
|
||||||
|
if (SDL_WriteIO(stream, &count, sizeof(count)) != sizeof(count)) {
|
||||||
|
SDL_Log("Error writing count: %s", SDL_GetError());
|
||||||
|
SDL_CloseIO(stream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write each entry
|
||||||
|
for (const auto& [key, value] : data_) {
|
||||||
|
// Write key length
|
||||||
|
size_t keyLength = key.length();
|
||||||
|
if (SDL_WriteIO(stream, &keyLength, sizeof(keyLength)) != sizeof(keyLength)) {
|
||||||
|
SDL_Log("Error writing key length: %s", SDL_GetError());
|
||||||
|
SDL_CloseIO(stream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write key
|
||||||
|
if (SDL_WriteIO(stream, key.c_str(), keyLength) != keyLength) {
|
||||||
|
SDL_Log("Error writing key: %s", SDL_GetError());
|
||||||
|
SDL_CloseIO(stream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write value
|
||||||
|
if (!WriteValueToStream(stream, value)) {
|
||||||
|
SDL_CloseIO(stream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_CloseIO(stream);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::LoadFromFile(const std::string& filename) {
|
||||||
|
SDL_IOStream* stream = SDL_IOFromFile(filename.c_str(), "rb");
|
||||||
|
if (!stream) {
|
||||||
|
SDL_Log("File not found or error opening: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_.clear();
|
||||||
|
|
||||||
|
// Read number of entries
|
||||||
|
size_t count;
|
||||||
|
if (SDL_ReadIO(stream, &count, sizeof(count)) != sizeof(count)) {
|
||||||
|
SDL_Log("Error reading count: %s", SDL_GetError());
|
||||||
|
SDL_CloseIO(stream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read each entry
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
// Read key length
|
||||||
|
size_t keyLength;
|
||||||
|
if (SDL_ReadIO(stream, &keyLength, sizeof(keyLength)) != sizeof(keyLength)) {
|
||||||
|
SDL_Log("Error reading key length: %s", SDL_GetError());
|
||||||
|
SDL_CloseIO(stream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read key
|
||||||
|
std::string key(keyLength, '\0');
|
||||||
|
if (SDL_ReadIO(stream, key.data(), keyLength) != keyLength) {
|
||||||
|
SDL_Log("Error reading key: %s", SDL_GetError());
|
||||||
|
SDL_CloseIO(stream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read value
|
||||||
|
ValueType value;
|
||||||
|
if (!ReadValueFromStream(stream, value)) {
|
||||||
|
SDL_CloseIO(stream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_CloseIO(stream);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Template-specific implementations
|
||||||
|
void PersistenceManager::SetValueImpl(const std::string& key, bool value) {
|
||||||
|
data_[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::SetValueImpl(const std::string& key, int value) {
|
||||||
|
data_[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::SetValueImpl(const std::string& key, float value) {
|
||||||
|
data_[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::SetValueImpl(const std::string& key, double value) {
|
||||||
|
data_[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PersistenceManager::SetValueImpl(const std::string& key, const std::string& value) {
|
||||||
|
data_[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::GetValueImpl(const std::string& key, bool defaultValue) const {
|
||||||
|
if (const auto it = data_.find(key); it != data_.end() && std::holds_alternative<bool>(it->second)) {
|
||||||
|
return std::get<bool>(it->second);
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PersistenceManager::GetValueImpl(const std::string& key, int defaultValue) const {
|
||||||
|
if (const auto it = data_.find(key); it != data_.end() && std::holds_alternative<int>(it->second)) {
|
||||||
|
return std::get<int>(it->second);
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float PersistenceManager::GetValueImpl(const std::string& key, float defaultValue) const {
|
||||||
|
if (const auto it = data_.find(key); it != data_.end() && std::holds_alternative<float>(it->second)) {
|
||||||
|
return std::get<float>(it->second);
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double PersistenceManager::GetValueImpl(const std::string& key, double defaultValue) const {
|
||||||
|
if (const auto it = data_.find(key); it != data_.end() && std::holds_alternative<double>(it->second)) {
|
||||||
|
return std::get<double>(it->second);
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PersistenceManager::GetValueImpl(const std::string& key, const std::string& defaultValue) const {
|
||||||
|
if (const auto it = data_.find(key); it != data_.end() && std::holds_alternative<std::string>(it->second)) {
|
||||||
|
return std::get<std::string>(it->second);
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private helper methods
|
||||||
|
bool PersistenceManager::WriteValueToStream(SDL_IOStream* stream, const ValueType& value) {
|
||||||
|
const TypeId typeId = GetTypeId(value);
|
||||||
|
|
||||||
|
// Write type ID
|
||||||
|
if (SDL_WriteIO(stream, &typeId, sizeof(typeId)) != sizeof(typeId)) {
|
||||||
|
SDL_Log("Error writing type ID: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write value based on type
|
||||||
|
switch (typeId) {
|
||||||
|
case TypeId::BOOL: {
|
||||||
|
const bool val = std::get<bool>(value);
|
||||||
|
return SDL_WriteIO(stream, &val, sizeof(val)) == sizeof(val);
|
||||||
|
}
|
||||||
|
case TypeId::INT: {
|
||||||
|
const int val = std::get<int>(value);
|
||||||
|
return SDL_WriteIO(stream, &val, sizeof(val)) == sizeof(val);
|
||||||
|
}
|
||||||
|
case TypeId::FLOAT: {
|
||||||
|
const float val = std::get<float>(value);
|
||||||
|
return SDL_WriteIO(stream, &val, sizeof(val)) == sizeof(val);
|
||||||
|
}
|
||||||
|
case TypeId::DOUBLE: {
|
||||||
|
const double val = std::get<double>(value);
|
||||||
|
return SDL_WriteIO(stream, &val, sizeof(val)) == sizeof(val);
|
||||||
|
}
|
||||||
|
case TypeId::STRING: {
|
||||||
|
const auto& str = std::get<std::string>(value);
|
||||||
|
const size_t length = str.length();
|
||||||
|
|
||||||
|
// Write string length
|
||||||
|
if (SDL_WriteIO(stream, &length, sizeof(length)) != sizeof(length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write string data
|
||||||
|
return SDL_WriteIO(stream, str.c_str(), length) == length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PersistenceManager::ReadValueFromStream(SDL_IOStream* stream, ValueType& value) {
|
||||||
|
TypeId typeId;
|
||||||
|
|
||||||
|
// Read type ID
|
||||||
|
if (SDL_ReadIO(stream, &typeId, sizeof(typeId)) != sizeof(typeId)) {
|
||||||
|
SDL_Log("Error reading type ID: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read value based on type
|
||||||
|
switch (typeId) {
|
||||||
|
case TypeId::BOOL: {
|
||||||
|
bool val;
|
||||||
|
if (SDL_ReadIO(stream, &val, sizeof(val)) == sizeof(val)) {
|
||||||
|
value = val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeId::INT: {
|
||||||
|
int val;
|
||||||
|
if (SDL_ReadIO(stream, &val, sizeof(val)) == sizeof(val)) {
|
||||||
|
value = val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeId::FLOAT: {
|
||||||
|
float val;
|
||||||
|
if (SDL_ReadIO(stream, &val, sizeof(val)) == sizeof(val)) {
|
||||||
|
value = val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeId::DOUBLE: {
|
||||||
|
double val;
|
||||||
|
if (SDL_ReadIO(stream, &val, sizeof(val)) == sizeof(val)) {
|
||||||
|
value = val;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeId::STRING: {
|
||||||
|
size_t length;
|
||||||
|
if (SDL_ReadIO(stream, &length, sizeof(length)) != sizeof(length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string str(length, '\0');
|
||||||
|
if (SDL_ReadIO(stream, str.data(), length) == length) {
|
||||||
|
value = str;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Log("Error reading value: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistenceManager::TypeId PersistenceManager::GetTypeId(const ValueType& value)
|
||||||
|
{
|
||||||
|
if (std::holds_alternative<bool>(value)) return TypeId::BOOL;
|
||||||
|
if (std::holds_alternative<int>(value)) return TypeId::INT;
|
||||||
|
if (std::holds_alternative<float>(value)) return TypeId::FLOAT;
|
||||||
|
if (std::holds_alternative<double>(value)) return TypeId::DOUBLE;
|
||||||
|
if (std::holds_alternative<std::string>(value)) return TypeId::STRING;
|
||||||
|
|
||||||
|
// Should never be reached
|
||||||
|
return TypeId::BOOL;
|
||||||
|
}
|
||||||
|
#endif // !ESP_PLATFORM
|
67
src/manager/PersistenceManager.h
Normal file
67
src/manager/PersistenceManager.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/IPersistenceManager.h"
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
class PersistenceManager final : public IPersistenceManager {
|
||||||
|
public:
|
||||||
|
using ValueType = std::variant<
|
||||||
|
bool,
|
||||||
|
int,
|
||||||
|
float,
|
||||||
|
double,
|
||||||
|
std::string
|
||||||
|
>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, ValueType> data_;
|
||||||
|
std::string filename_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PersistenceManager(std::string filename = "settings.dat");
|
||||||
|
~PersistenceManager() override;
|
||||||
|
|
||||||
|
// IPersistenceManager implementation
|
||||||
|
bool HasKey(const std::string& key) const override;
|
||||||
|
void RemoveKey(const std::string& key) override;
|
||||||
|
void Clear() override;
|
||||||
|
size_t GetKeyCount() const override { return data_.size(); }
|
||||||
|
|
||||||
|
bool Save() override;
|
||||||
|
bool Load() override;
|
||||||
|
|
||||||
|
// Erweiterte SDL3-spezifische Methoden
|
||||||
|
bool SaveToFile(const std::string& filename);
|
||||||
|
bool LoadFromFile(const std::string& filename);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Template-spezifische Implementierungen
|
||||||
|
void SetValueImpl(const std::string& key, bool value) override;
|
||||||
|
void SetValueImpl(const std::string& key, int value) override;
|
||||||
|
void SetValueImpl(const std::string& key, float value) override;
|
||||||
|
void SetValueImpl(const std::string& key, double value) override;
|
||||||
|
void SetValueImpl(const std::string& key, const std::string& value) override;
|
||||||
|
|
||||||
|
bool GetValueImpl(const std::string& key, bool defaultValue) const override;
|
||||||
|
int GetValueImpl(const std::string& key, int defaultValue) const override;
|
||||||
|
float GetValueImpl(const std::string& key, float defaultValue) const override;
|
||||||
|
double GetValueImpl(const std::string& key, double defaultValue) const override;
|
||||||
|
std::string GetValueImpl(const std::string& key, const std::string& defaultValue) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Interne Hilfsmethoden für Serialisierung
|
||||||
|
static bool WriteValueToStream(SDL_IOStream* stream, const ValueType& value) ;
|
||||||
|
static bool ReadValueFromStream(SDL_IOStream* stream, ValueType& value) ;
|
||||||
|
|
||||||
|
enum class TypeId : uint8_t {
|
||||||
|
BOOL = 0,
|
||||||
|
INT = 1,
|
||||||
|
FLOAT = 2,
|
||||||
|
DOUBLE = 3,
|
||||||
|
STRING = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
static TypeId GetTypeId(const ValueType& value);
|
||||||
|
};
|
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "MenuOptions.h"
|
#include "MenuOptions.h"
|
||||||
#include "common/InactivityTracker.h"
|
#include "common/InactivityTracker.h"
|
||||||
|
#include "manager/PersistenceManager.h"
|
||||||
#include "ui/ScreenSaver.h"
|
#include "ui/ScreenSaver.h"
|
||||||
#include "ui/SplashScreen.h"
|
#include "ui/SplashScreen.h"
|
||||||
#include "ui/widgets/Button.h"
|
#include "ui/widgets/Button.h"
|
||||||
@@ -82,7 +83,10 @@ Device::Device(void *appstate) : UIWidget(appstate)
|
|||||||
.popScreen = [this]() {
|
.popScreen = [this]() {
|
||||||
this->PopScreen();
|
this->PopScreen();
|
||||||
},
|
},
|
||||||
|
.persistenceManager = std::make_shared<PersistenceManager>(),
|
||||||
};
|
};
|
||||||
|
options.persistenceManager->Load();
|
||||||
|
|
||||||
m_widget = std::make_shared<SplashScreen>(&options);
|
m_widget = std::make_shared<SplashScreen>(&options);
|
||||||
m_inactivityTracker = std::make_unique<InactivityTracker>(60000, []() {
|
m_inactivityTracker = std::make_unique<InactivityTracker>(60000, []() {
|
||||||
const auto screensaver = std::make_shared<ScreenSaver>(&options);
|
const auto screensaver = std::make_shared<ScreenSaver>(&options);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#include "ui/widgets/Button.h"
|
#include "ui/widgets/Button.h"
|
||||||
|
|
||||||
#include "ResourceManager.h"
|
#include "../../manager/ResourceManager.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#include "D_Pad.h"
|
#include "D_Pad.h"
|
||||||
#include "ui/widgets/D_Pad.h"
|
#include "ui/widgets/D_Pad.h"
|
||||||
|
|
||||||
#include "ResourceManager.h"
|
#include "../../manager/ResourceManager.h"
|
||||||
|
|
||||||
D_Pad::D_Pad(void *appState, const float x, const float y, const float width, std::function<void(Direction)> callback)
|
D_Pad::D_Pad(void *appState, const float x, const float y, const float width, std::function<void(Direction)> callback)
|
||||||
: UIWidget(appState), m_x(x), m_y(y), m_width(width), m_callback(std::move(callback))
|
: UIWidget(appState), m_x(x), m_y(y), m_width(width), m_callback(std::move(callback))
|
||||||
|
Reference in New Issue
Block a user