#ifndef StatefulService_h #define StatefulService_h /** * ESP32 SvelteKit * * A simple, secure and extensible framework for IoT projects for ESP32 platforms * with responsive Sveltekit front-end built with TailwindCSS and DaisyUI. * https://github.com/theelims/ESP32-sveltekit * * Copyright (C) 2018 - 2023 rjwats * Copyright (C) 2023 theelims * Copyright (C) 2024 runeharlyk * * All Rights Reserved. This software may be modified and distributed under * the terms of the LGPL v3 license. See the LICENSE file for details. **/ #include #include #include #include #include #include #include template using JsonStateUpdater = std::function; template using JsonStateReader = std::function; typedef size_t update_handler_id_t; typedef size_t hook_handler_id_t; typedef std::function StateUpdateCallback; typedef std::function StateHookCallback; typedef struct StateUpdateHandlerInfo { static inline update_handler_id_t currentUpdatedHandlerId = 0; update_handler_id_t _id; StateUpdateCallback _cb; bool _allowRemove; StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove) : _id(++currentUpdatedHandlerId), _cb(cb), _allowRemove(allowRemove) {}; } StateUpdateHandlerInfo_t; typedef struct StateHookHandlerInfo { static inline hook_handler_id_t currentHookHandlerId = 0; hook_handler_id_t _id; StateHookCallback _cb; bool _allowRemove; StateHookHandlerInfo(StateHookCallback cb, bool allowRemove) : _id(++currentHookHandlerId), _cb(cb), _allowRemove(allowRemove) {}; } StateHookHandlerInfo_t; template class StatefulService { public: template StatefulService(Args &&...args) : _state(std::forward(args)...), _accessMutex(xSemaphoreCreateRecursiveMutex()) {} update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) { if (!cb) { return 0; } StateUpdateHandlerInfo_t updateHandler(cb, allowRemove); _updateHandlers.push_back(updateHandler); return updateHandler._id; } void removeUpdateHandler(update_handler_id_t id) { for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) { if ((*i)._allowRemove && (*i)._id == id) { i = _updateHandlers.erase(i); } else { ++i; } } } hook_handler_id_t addHookHandler(StateHookCallback cb, bool allowRemove = true) { if (!cb) { return 0; } StateHookHandlerInfo_t hookHandler(cb, allowRemove); _hookHandlers.push_back(hookHandler); return hookHandler._id; } void removeHookHandler(hook_handler_id_t id) { for (auto i = _hookHandlers.begin(); i != _hookHandlers.end();) { if ((*i)._allowRemove && (*i)._id == id) { i = _hookHandlers.erase(i); } else { ++i; } } } StateUpdateResult update(std::function stateUpdater, const String &originId) { beginTransaction(); StateUpdateResult result = stateUpdater(_state); endTransaction(); callHookHandlers(originId, result); if (result == StateUpdateResult::CHANGED) { callUpdateHandlers(originId); } return result; } StateUpdateResult updateWithoutPropagation(std::function stateUpdater) { beginTransaction(); StateUpdateResult result = stateUpdater(_state); endTransaction(); return result; } StateUpdateResult update(JsonObject &jsonObject, JsonStateUpdater stateUpdater, const String &originId) { beginTransaction(); StateUpdateResult result = stateUpdater(jsonObject, _state); endTransaction(); callHookHandlers(originId, result); if (result == StateUpdateResult::CHANGED) { callUpdateHandlers(originId); } return result; } StateUpdateResult updateWithoutPropagation(JsonObject &jsonObject, JsonStateUpdater stateUpdater) { beginTransaction(); StateUpdateResult result = stateUpdater(jsonObject, _state); endTransaction(); return result; } void read(std::function stateReader) { beginTransaction(); stateReader(_state); endTransaction(); } void read(JsonObject &jsonObject, JsonStateReader stateReader) { beginTransaction(); stateReader(_state, jsonObject); endTransaction(); } void callUpdateHandlers(const String &originId) { for (const StateUpdateHandlerInfo_t &updateHandler : _updateHandlers) { updateHandler._cb(originId); } } void callHookHandlers(const String &originId, StateUpdateResult &result) { for (const StateHookHandlerInfo_t &hookHandler : _hookHandlers) { hookHandler._cb(originId, result); } } protected: T _state; inline void beginTransaction() { xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY); } inline void endTransaction() { xSemaphoreGiveRecursive(_accessMutex); } private: SemaphoreHandle_t _accessMutex; std::list _updateHandlers; std::list _hookHandlers; }; #endif // end StatefulService_h