From a0fe4ba5381412706eb297e87840141d0583517b Mon Sep 17 00:00:00 2001 From: Peter Siegmund Date: Fri, 20 Jun 2025 20:53:13 +0200 Subject: [PATCH] code optimization - lifecycle functions for widgets - persistence functions Signed-off-by: Peter Siegmund --- components/insa/include/common/Widget.h | 35 ++--- components/insa/src/common/Widget.cpp | 16 +++ components/insa/src/ui/LightMenu.cpp | 6 +- components/insa/src/ui/LightSettingsMenu.cpp | 2 +- components/ruth/CMakeLists.txt | 2 +- components/ruth/espressif/persistence.c | 116 ++++++++++++++++ components/ruth/include/persistence.h | 9 +- components/ruth/persistence.c | 133 ++----------------- main/app_task.cpp | 11 ++ src/ui/Device.cpp | 39 ++++-- src/ui/Device.h | 2 - 11 files changed, 218 insertions(+), 153 deletions(-) create mode 100644 components/ruth/espressif/persistence.c diff --git a/components/insa/include/common/Widget.h b/components/insa/include/common/Widget.h index 05a457b..d101faa 100644 --- a/components/insa/include/common/Widget.h +++ b/components/insa/include/common/Widget.h @@ -22,23 +22,23 @@ * must implement or can override. The class follows the template method pattern, * allowing derived classes to customize behavior while maintaining a consistent * interface for the UI system. - * + * * @note All widgets should inherit from this class to ensure compatibility with * the UI management system. - * + * * @see u8g2_t * @see ButtonType */ class Widget { -public: + public: /** * @brief Constructs a widget with the given u8g2 display context * @param u8g2 Pointer to the u8g2 display context used for rendering operations - * + * * @pre u8g2 must not be nullptr * @post Widget is initialized with the provided display context - * + * * @note The widget does not take ownership of the u8g2 context and assumes * it remains valid for the lifetime of the widget. */ @@ -51,15 +51,20 @@ public: */ virtual ~Widget() = default; + virtual void enter(); + virtual void pause(); + virtual void resume(); + virtual void exit(); + /** * @brief Updates the widget's internal state based on elapsed time * @param dt Delta time in milliseconds since the last update call - * + * * @details This method is called once per frame by the UI system to handle * animations, timers, state transitions, or other time-dependent behavior. * The base implementation is empty, allowing derived classes to override * only if time-based updates are needed. - * + * * @note Override this method in derived classes to implement time-based behavior * such as animations, blinking effects, or timeout handling. */ @@ -71,10 +76,10 @@ public: * their specific rendering logic using the u8g2 display context. * The base implementation is empty, requiring derived classes to * provide their own rendering code. - * + * * @pre u8g2 context must be initialized and ready for drawing operations * @post Widget's visual representation is drawn to the display buffer - * + * * @note This method is called during the rendering phase of each frame. * Derived classes should use the u8g2 member variable to perform * drawing operations. @@ -84,29 +89,29 @@ public: /** * @brief Handles button press events * @param button The type of button that was pressed - * + * * @details This method is called when a button press event occurs and allows * the widget to respond to user input. The base implementation is empty, * allowing derived classes to override only if input handling is needed. - * + * * @note Override this method in derived classes to implement button-specific * behavior such as navigation, selection, or state changes. - * + * * @see ButtonType for available button types */ virtual void onButtonClicked(ButtonType button); -protected: + protected: /** * @brief Pointer to the u8g2 display context used for rendering operations * @details This member provides access to the u8g2 graphics library functions * for drawing text, shapes, bitmaps, and other UI elements. It is * initialized during construction and remains valid for the widget's lifetime. - * + * * @note This member is protected to allow derived classes direct access while * preventing external modification. Derived classes should use this * context for all rendering operations. - * + * * @warning Do not modify or delete this pointer. The widget does not own * the u8g2 context and assumes it is managed externally. */ diff --git a/components/insa/src/common/Widget.cpp b/components/insa/src/common/Widget.cpp index 3a85b84..124c1a6 100644 --- a/components/insa/src/common/Widget.cpp +++ b/components/insa/src/common/Widget.cpp @@ -4,6 +4,22 @@ Widget::Widget(u8g2_t *u8g2) : u8g2(u8g2) { } +void Widget::enter() +{ +} + +void Widget::pause() +{ +} + +void Widget::resume() +{ +} + +void Widget::exit() +{ +} + void Widget::update(uint64_t dt) { } diff --git a/components/insa/src/ui/LightMenu.cpp b/components/insa/src/ui/LightMenu.cpp index 9135cd6..30a45e2 100644 --- a/components/insa/src/ui/LightMenu.cpp +++ b/components/insa/src/ui/LightMenu.cpp @@ -45,7 +45,7 @@ void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType butto if (m_options && m_options->persistence && m_options->persistence->save) { const auto value = getItem(item.getId()).getValue(); - m_options->persistence->save("light_activated", value.c_str()); + m_options->persistence->save(VALUE_TYPE_STRING, "light_activated", value.c_str()); } break; } @@ -57,8 +57,8 @@ void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType butto { if (m_options && m_options->persistence && m_options->persistence->save) { - const auto value = std::to_string(getItem(item.getId()).getIndex()); - m_options->persistence->save("light_mode", value.c_str()); + const auto value = getItem(item.getId()).getIndex(); + m_options->persistence->save(VALUE_TYPE_INT32, "light_mode", &value); } } break; diff --git a/components/insa/src/ui/LightSettingsMenu.cpp b/components/insa/src/ui/LightSettingsMenu.cpp index 2065d9a..be9e8d6 100644 --- a/components/insa/src/ui/LightSettingsMenu.cpp +++ b/components/insa/src/ui/LightSettingsMenu.cpp @@ -37,6 +37,6 @@ void LightSettingsMenu::onButtonPressed(const MenuItem &menuItem, const ButtonTy { const auto key = "section_" + std::to_string(menuItem.getId()); const auto value = getItem(menuItem.getId()).getValue(); - m_options->persistence->save(key.c_str(), value.c_str()); + m_options->persistence->save(VALUE_TYPE_STRING, key.c_str(), value.c_str()); } } \ No newline at end of file diff --git a/components/ruth/CMakeLists.txt b/components/ruth/CMakeLists.txt index 9211134..ce88710 100644 --- a/components/ruth/CMakeLists.txt +++ b/components/ruth/CMakeLists.txt @@ -1,6 +1,6 @@ if (DEFINED ENV{IDF_PATH}) idf_component_register(SRCS - persistence.c + espressif/persistence.c INCLUDE_DIRS "include" PRIV_REQUIRES nvs_flash diff --git a/components/ruth/espressif/persistence.c b/components/ruth/espressif/persistence.c new file mode 100644 index 0000000..47e11f0 --- /dev/null +++ b/components/ruth/espressif/persistence.c @@ -0,0 +1,116 @@ +#include "persistence.h" + +#include "esp_err.h" +#include "esp_log.h" +#include "esp_mac.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "nvs_flash.h" + +static const char *TAG = "persistence"; + +static nvs_handle_t persistence_handle; +static SemaphoreHandle_t persistence_mutex; + +void *persistence_init(const char *namespace_name) +{ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(nvs_open(namespace_name, NVS_READWRITE, &persistence_handle)); + + persistence_mutex = xSemaphoreCreateMutex(); + if (persistence_mutex == NULL) + { + ESP_LOGE(TAG, "Failed to create mutex"); + } + return &persistence_handle; +} + +void persistence_save(persistence_value_t value_type, const char *key, const void *value) +{ + if (persistence_mutex != NULL) + { + if (xSemaphoreTake(persistence_mutex, portMAX_DELAY) == pdTRUE) + { + esp_err_t err = ESP_ERR_INVALID_ARG; + + switch (value_type) + { + case VALUE_TYPE_STRING: + err = nvs_set_str(persistence_handle, key, (char *)value); + break; + + case VALUE_TYPE_INT32: + err = nvs_set_i32(persistence_handle, key, *(int32_t *)value); + break; + + default: + ESP_LOGE(TAG, "Unsupported value type"); + break; + } + + if (err == ESP_OK) + { + ESP_ERROR_CHECK(nvs_commit(persistence_handle)); + } + else + { + ESP_LOGE(TAG, "Error saving key %s: %s", key, esp_err_to_name(err)); + } + + xSemaphoreGive(persistence_mutex); + } + } +} + +void *persistence_load(persistence_value_t value_type, const char *key, void *out) +{ + if (persistence_mutex != NULL) + { + if (xSemaphoreTake(persistence_mutex, portMAX_DELAY) == pdTRUE) + { + esp_err_t err = ESP_ERR_INVALID_ARG; + + switch (value_type) + { + case VALUE_TYPE_STRING: + err = nvs_get_str(persistence_handle, key, (char *)out, NULL); + break; + + case VALUE_TYPE_INT32: + err = nvs_get_i32(persistence_handle, key, (int32_t *)out); + break; + + default: + ESP_LOGE(TAG, "Unsupported value type"); + break; + } + + if (err != ESP_OK) + { + ESP_LOGE(TAG, "Error loading key %s: %s", key, esp_err_to_name(err)); + } + + xSemaphoreGive(persistence_mutex); + } + } + + return out; +} + +void persistence_deinit() +{ + if (persistence_mutex != NULL) + { + vSemaphoreDelete(persistence_mutex); + persistence_mutex = NULL; + } + + nvs_close(persistence_handle); +} diff --git a/components/ruth/include/persistence.h b/components/ruth/include/persistence.h index 92836ff..ed5b714 100644 --- a/components/ruth/include/persistence.h +++ b/components/ruth/include/persistence.h @@ -9,10 +9,17 @@ typedef enum typedef struct { void *handle; - void (*save)(const char *key, const char *value); + void (*save)(persistence_value_t value_type, const char *key, const void *value); } persistence_t; +#ifdef __cplusplus +extern "C" +{ +#endif void *persistence_init(const char *namespace_name); void persistence_save(persistence_value_t value_type, const char *key, const void *value); void *persistence_load(persistence_value_t value_type, const char *key, void *out); void persistence_deinit(); +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/ruth/persistence.c b/components/ruth/persistence.c index 7ee6a4b..9c6f2ab 100644 --- a/components/ruth/persistence.c +++ b/components/ruth/persistence.c @@ -1,13 +1,25 @@ #include "persistence.h" #include "stddef.h" +#include void *persistence_init(const char *namespace_name) { return NULL; } -void persistence_save(persistence_value_t value_type, const char *key, const void *value) +void persistence_save(const persistence_value_t value_type, const char *key, const void *value) { + printf("Key: %s - ", key); + switch (value_type) + { + case VALUE_TYPE_STRING: + printf("Value (s): %s\n", (char *)value); + break; + + case VALUE_TYPE_INT32: + printf("Value (i): %d\n", *(int32_t *)value); + break; + } } void *persistence_load(persistence_value_t value_type, const char *key, void *out) @@ -17,121 +29,4 @@ void *persistence_load(persistence_value_t value_type, const char *key, void *ou void persistence_deinit() { -} - -/* -#include "esp_err.h" -#include "esp_log.h" -#include "esp_mac.h" -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "nvs_flash.h" - -static const char *TAG = "persistence"; - -static nvs_handle_t persistence_handle; -static SemaphoreHandle_t persistence_mutex; - -void *persistence_init(const char *namespace_name) -{ - esp_err_t ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) - { - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); - } - ESP_ERROR_CHECK(ret); - - ESP_ERROR_CHECK(nvs_open(namespace_name, NVS_READWRITE, &persistence_handle)); - - persistence_mutex = xSemaphoreCreateMutex(); - if (persistence_mutex == NULL) - { - ESP_LOGE(TAG, "Failed to create mutex"); - } - return &persistence_handle; -} - -void persistence_save(persistence_value_t value_type, const char *key, const void *value) -{ - if (persistence_mutex != NULL) - { - if (xSemaphoreTake(persistence_mutex, portMAX_DELAY) == pdTRUE) - { - esp_err_t err = ESP_ERR_INVALID_ARG; - - switch (value_type) - { - case VALUE_TYPE_STRING: - err = nvs_set_str(persistence_handle, key, (char *)value); - break; - - case VALUE_TYPE_INT32: - err = nvs_set_i32(persistence_handle, key, *(int32_t *)value); - break; - - default: - ESP_LOGE(TAG, "Unsupported value type"); - break; - } - - if (err == ESP_OK) - { - ESP_ERROR_CHECK(nvs_commit(persistence_handle)); - } - else - { - ESP_LOGE(TAG, "Error saving key %s: %s", key, esp_err_to_name(err)); - } - - xSemaphoreGive(persistence_mutex); - } - } -} - -void *persistence_load(persistence_value_t value_type, const char *key, void *out) -{ - if (persistence_mutex != NULL) - { - if (xSemaphoreTake(persistence_mutex, portMAX_DELAY) == pdTRUE) - { - esp_err_t err = ESP_ERR_INVALID_ARG; - - switch (value_type) - { - case VALUE_TYPE_STRING: - err = nvs_get_str(persistence_handle, key, (char *)out, NULL); - break; - - case VALUE_TYPE_INT32: - err = nvs_get_i32(persistence_handle, key, (int32_t *)out); - break; - - default: - ESP_LOGE(TAG, "Unsupported value type"); - break; - } - - if (err != ESP_OK) - { - ESP_LOGE(TAG, "Error loading key %s: %s", key, esp_err_to_name(err)); - } - - xSemaphoreGive(persistence_mutex); - } - } - - return out; -} - -void persistence_deinit() -{ - if (persistence_mutex != NULL) - { - vSemaphoreDelete(persistence_mutex); - persistence_mutex = NULL; - } - - nvs_close(persistence_handle); -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/main/app_task.cpp b/main/app_task.cpp index 08d2f9e..fca2b10 100644 --- a/main/app_task.cpp +++ b/main/app_task.cpp @@ -59,6 +59,7 @@ void setScreen(const std::shared_ptr &screen) m_widget = screen; m_history.clear(); m_history.emplace_back(m_widget); + m_widget->enter(); } } @@ -66,7 +67,12 @@ void pushScreen(const std::shared_ptr &screen) { if (screen != nullptr) { + if (m_widget) + { + m_widget->pause(); + } m_widget = screen; + m_widget->enter(); m_history.emplace_back(m_widget); } } @@ -76,7 +82,12 @@ void popScreen() if (m_history.size() >= 2) { m_history.pop_back(); + if (m_widget) + { + m_widget->exit(); + } m_widget = m_history.back(); + m_widget->resume(); } } diff --git a/src/ui/Device.cpp b/src/ui/Device.cpp index 5480484..8a11f07 100644 --- a/src/ui/Device.cpp +++ b/src/ui/Device.cpp @@ -3,6 +3,7 @@ #include #include +#include "persistence.h" #include "MenuOptions.h" #include "common/InactivityTracker.h" #include "ui/ScreenSaver.h" @@ -55,11 +56,15 @@ Device::Device(void *appstate) : UIWidget(appstate) m_children.push_back(std::make_shared