clean-up code

This commit is contained in:
proddy
2020-07-31 13:59:40 +02:00
parent 4fc6797f4e
commit 09895bb461
10 changed files with 200 additions and 313 deletions

View File

@@ -37,7 +37,6 @@ static bool __output_pins[256];
static int __output_level[256]; static int __output_level[256];
int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) { int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) {
memset(__output_pins, 0, sizeof(__output_pins)); memset(__output_pins, 0, sizeof(__output_pins));
memset(__output_level, 0, sizeof(__output_level)); memset(__output_level, 0, sizeof(__output_level));

View File

@@ -3,16 +3,11 @@
#define ASYNC_JSON_H_ #define ASYNC_JSON_H_
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
// #include <Print.h>
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024 #define DYNAMIC_JSON_DOCUMENT_SIZE 1024
constexpr const char * JSON_MIMETYPE = "application/json"; constexpr const char * JSON_MIMETYPE = "application/json";
/*
* Json Response
* */
class ChunkPrint : public Print { class ChunkPrint : public Print {
private: private:
uint8_t * _destination; uint8_t * _destination;
@@ -45,9 +40,8 @@ class ChunkPrint : public Print {
} }
}; };
class AsyncJsonResponse { class AsyncJsonResponse {
protected: protected:
DynamicJsonDocument _jsonBuffer; DynamicJsonDocument _jsonBuffer;
JsonVariant _root; JsonVariant _root;
@@ -71,7 +65,7 @@ class AsyncJsonResponse {
return _isValid; return _isValid;
} }
size_t setLength() { size_t setLength() {
return 0; return 0;
} }
size_t getSize() { size_t getSize() {
@@ -79,9 +73,6 @@ return 0;
} }
size_t _fillBuffer(uint8_t * data, size_t len) { size_t _fillBuffer(uint8_t * data, size_t len) {
// ChunkPrint dest(data, 0, len);
// serializeJson(_root, dest);
return len; return len;
} }
}; };

View File

@@ -24,7 +24,6 @@ struct AsyncMqttClientMessageProperties {
bool retain; bool retain;
}; };
namespace AsyncMqttClientInternals { namespace AsyncMqttClientInternals {
typedef std::function<void(bool sessionPresent)> OnConnectUserCallback; typedef std::function<void(bool sessionPresent)> OnConnectUserCallback;

View File

@@ -95,8 +95,6 @@ class EMSESPSettingsService {
void begin(); void begin();
private: private:
// HttpEndpoint<EMSESPSettings> _httpEndpoint;
// FSPersistence<EMSESPSettings> _fsPersistence;
}; };
#endif #endif

View File

@@ -68,8 +68,8 @@ typedef std::function<bool(AsyncWebServerRequest * request)> ArRequestFilterFunc
class AsyncWebHandler { class AsyncWebHandler {
protected: protected:
String _username; String _username;
String _password; String _password;
public: public:
AsyncWebHandler() AsyncWebHandler()
@@ -131,7 +131,6 @@ class AsyncWebServer {
} }
void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){}; void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){};
}; };

View File

@@ -24,46 +24,13 @@ class FSPersistence {
} }
void readFromFS() { void readFromFS() {
/*
File settingsFile = _fs->open(_filePath, "r");
if (settingsFile) {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
DeserializationError error = deserializeJson(jsonDocument, settingsFile);
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()) {
JsonObject jsonObject = jsonDocument.as<JsonObject>();
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
settingsFile.close();
return;
}
settingsFile.close();
}
*/
// If we reach here we have not been successful in loading the config,
// hard-coded emergency defaults are now applied.
applyDefaults(); applyDefaults();
} }
bool writeToFS() { bool writeToFS() {
// create and populate a new json object
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
JsonObject jsonObject = jsonDocument.to<JsonObject>(); JsonObject jsonObject = jsonDocument.to<JsonObject>();
_statefulService->read(jsonObject, _stateReader); _statefulService->read(jsonObject, _stateReader);
// serialize it to filesystem
/*
File settingsFile = _fs->open(_filePath, "w");
// failed to open file, return false
if (!settingsFile) {
return false;
}
// serialize the data to the file
serializeJson(jsonDocument, settingsFile);
settingsFile.close();
*/
return true; return true;
} }
@@ -90,8 +57,6 @@ class FSPersistence {
update_handler_id_t _updateHandlerId; update_handler_id_t _updateHandlerId;
protected: protected:
// We assume the updater supplies sensible defaults if an empty object
// is supplied, this virtual function allows that to be changed.
virtual void applyDefaults() { virtual void applyDefaults() {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize); DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
JsonObject jsonObject = jsonDocument.as<JsonObject>(); JsonObject jsonObject = jsonDocument.as<JsonObject>();

View File

@@ -22,12 +22,6 @@ class HttpGetEndpoint {
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN, AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) { _stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) {
/*
server->on(servicePath.c_str(),
HTTP_GET,
securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1),
authenticationPredicate));
*/
} }
HttpGetEndpoint(JsonStateReader<T> stateReader, HttpGetEndpoint(JsonStateReader<T> stateReader,
@@ -36,9 +30,6 @@ class HttpGetEndpoint {
const String& servicePath, const String& servicePath,
size_t bufferSize = DEFAULT_BUFFER_SIZE) : size_t bufferSize = DEFAULT_BUFFER_SIZE) :
_stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) { _stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) {
/*
server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1));
*/
} }
protected: protected:
@@ -47,12 +38,6 @@ class HttpGetEndpoint {
size_t _bufferSize; size_t _bufferSize;
void fetchSettings(AsyncWebServerRequest* request) { void fetchSettings(AsyncWebServerRequest* request) {
// AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize);
// JsonObject jsonObject = response->getRoot().to<JsonObject>();
// _statefulService->read(jsonObject, _stateReader);
// response->setLength();
// request->send(response);
} }
}; };
@@ -70,17 +55,7 @@ class HttpPostEndpoint {
_stateReader(stateReader), _stateReader(stateReader),
_stateUpdater(stateUpdater), _stateUpdater(stateUpdater),
_statefulService(statefulService), _statefulService(statefulService),
/*
_updateHandler(
servicePath,
securityManager->wrapCallback(
std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2),
authenticationPredicate),
bufferSize),
*/
_bufferSize(bufferSize) { _bufferSize(bufferSize) {
//_updateHandler.setMethod(HTTP_POST);
// server->addHandler(&_updateHandler);
} }
HttpPostEndpoint(JsonStateReader<T> stateReader, HttpPostEndpoint(JsonStateReader<T> stateReader,
@@ -92,42 +67,26 @@ class HttpPostEndpoint {
_stateReader(stateReader), _stateReader(stateReader),
_stateUpdater(stateUpdater), _stateUpdater(stateUpdater),
_statefulService(statefulService), _statefulService(statefulService),
/*
_updateHandler(servicePath,
std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2),
bufferSize),
*/
_bufferSize(bufferSize) { _bufferSize(bufferSize) {
// _updateHandler.setMethod(HTTP_POST);
// server->addHandler(&_updateHandler);
} }
protected: protected:
JsonStateReader<T> _stateReader; JsonStateReader<T> _stateReader;
JsonStateUpdater<T> _stateUpdater; JsonStateUpdater<T> _stateUpdater;
StatefulService<T>* _statefulService; StatefulService<T>* _statefulService;
//AsyncCallbackJsonWebHandler _updateHandler;
size_t _bufferSize; size_t _bufferSize;
void updateSettings(AsyncWebServerRequest* request, JsonVariant& json) { void updateSettings(AsyncWebServerRequest* request, JsonVariant& json) {
if (!json.is<JsonObject>()) { if (!json.is<JsonObject>()) {
// request->send(400);
return; return;
} }
JsonObject jsonObject = json.as<JsonObject>(); JsonObject jsonObject = json.as<JsonObject>();
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater); StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
if (outcome == StateUpdateResult::ERROR) { if (outcome == StateUpdateResult::ERROR) {
// request->send(400);
return; return;
} }
if (outcome == StateUpdateResult::CHANGED) { if (outcome == StateUpdateResult::CHANGED) {
// request->onDisconnect([this]() { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); });
} }
// AsyncJsonResponse* response = new AsyncJsonResponse(false, _bufferSize);
// jsonObject = response->getRoot().to<JsonObject>();
// _statefulService->read(jsonObject, _stateReader);
// response->setLength();
// request->send(response);
} }
}; };

View File

@@ -4,7 +4,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <Features.h> #include <Features.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
// #include <ESPUtils.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <list> #include <list>
@@ -21,82 +20,64 @@
#define MAX_JWT_SIZE 128 #define MAX_JWT_SIZE 128
class User { class User {
public: public:
String username; String username;
String password; String password;
bool admin; bool admin;
public: public:
User(String username, String password, bool admin) : username(username), password(password), admin(admin) { User(String username, String password, bool admin)
} : username(username)
, password(password)
, admin(admin) {
}
}; };
class Authentication { class Authentication {
public: public:
User* user; User * user;
boolean authenticated; boolean authenticated;
public: public:
Authentication(User& user) : user(new User(user)), authenticated(true) { Authentication(User & user)
} : user(new User(user))
Authentication() : user(nullptr), authenticated(false) { , authenticated(true) {
} }
~Authentication() { Authentication()
delete (user); : user(nullptr)
} , authenticated(false) {
}
~Authentication() {
delete (user);
}
}; };
typedef std::function<boolean(Authentication& authentication)> AuthenticationPredicate; typedef std::function<boolean(Authentication & authentication)> AuthenticationPredicate;
class AuthenticationPredicates { class AuthenticationPredicates {
public: public:
static bool NONE_REQUIRED(Authentication& authentication) { static bool NONE_REQUIRED(Authentication & authentication) {
return true; return true;
}; };
static bool IS_AUTHENTICATED(Authentication& authentication) { static bool IS_AUTHENTICATED(Authentication & authentication) {
return authentication.authenticated; return authentication.authenticated;
}; };
static bool IS_ADMIN(Authentication& authentication) { static bool IS_ADMIN(Authentication & authentication) {
return authentication.authenticated && authentication.user->admin; return authentication.authenticated && authentication.user->admin;
}; };
}; };
class SecurityManager { class SecurityManager {
public: public:
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
/* virtual Authentication authenticate(const String & username, const String & password) = 0;
* Authenticate, returning the user if found virtual String generateJWT(User * user) = 0;
*/
virtual Authentication authenticate(const String& username, const String& password) = 0;
/*
* Generate a JWT for the user provided
*/
virtual String generateJWT(User* user) = 0;
#endif #endif
/* virtual Authentication authenticateRequest(AsyncWebServerRequest * request) = 0;
* Check the request header for the Authorization token virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0;
*/ virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) = 0;
virtual Authentication authenticateRequest(AsyncWebServerRequest* request) = 0; virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) = 0;
/**
* Filter a request with the provided predicate, only returning true if the predicate matches.
*/
virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0;
/**
* Wrap the provided request to provide validation against an AuthenticationPredicate.
*/
virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest,
AuthenticationPredicate predicate) = 0;
/**
* Wrap the provided json request callback to provide validation against an AuthenticationPredicate.
*/
virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest,
AuthenticationPredicate predicate) = 0;
}; };
#endif // end SecurityManager_h #endif // end SecurityManager_h

View File

@@ -30,87 +30,79 @@
#if FT_ENABLED(FT_SECURITY) #if FT_ENABLED(FT_SECURITY)
class SecuritySettings { class SecuritySettings {
public: public:
String jwtSecret; String jwtSecret;
std::list<User> users; std::list<User> users;
static void read(SecuritySettings& settings, JsonObject& root) { static void read(SecuritySettings & settings, JsonObject & root) {
// secret // secret
root["jwt_secret"] = settings.jwtSecret; root["jwt_secret"] = settings.jwtSecret;
// users // users
JsonArray users = root.createNestedArray("users"); JsonArray users = root.createNestedArray("users");
for (User user : settings.users) { for (User user : settings.users) {
JsonObject userRoot = users.createNestedObject(); JsonObject userRoot = users.createNestedObject();
userRoot["username"] = user.username; userRoot["username"] = user.username;
userRoot["password"] = user.password; userRoot["password"] = user.password;
userRoot["admin"] = user.admin; userRoot["admin"] = user.admin;
}
} }
}
static StateUpdateResult update(JsonObject& root, SecuritySettings& settings) { static StateUpdateResult update(JsonObject & root, SecuritySettings & settings) {
// secret // secret
settings.jwtSecret = root["jwt_secret"] | FACTORY_JWT_SECRET; settings.jwtSecret = root["jwt_secret"] | FACTORY_JWT_SECRET;
// users // users
settings.users.clear(); settings.users.clear();
if (root["users"].is<JsonArray>()) { if (root["users"].is<JsonArray>()) {
for (JsonVariant user : root["users"].as<JsonArray>()) { for (JsonVariant user : root["users"].as<JsonArray>()) {
settings.users.push_back(User(user["username"], user["password"], user["admin"])); settings.users.push_back(User(user["username"], user["password"], user["admin"]));
} }
} else { } else {
settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true)); settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true));
settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false)); settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false));
}
return StateUpdateResult::CHANGED;
} }
return StateUpdateResult::CHANGED;
}
}; };
class SecuritySettingsService : public StatefulService<SecuritySettings>, public SecurityManager { class SecuritySettingsService : public StatefulService<SecuritySettings>, public SecurityManager {
public: public:
SecuritySettingsService(AsyncWebServer* server, FS* fs); SecuritySettingsService(AsyncWebServer * server, FS * fs);
void begin(); void begin();
// Functions to implement SecurityManager // Functions to implement SecurityManager
Authentication authenticate(const String& username, const String& password); Authentication authenticate(const String & username, const String & password);
Authentication authenticateRequest(AsyncWebServerRequest* request); Authentication authenticateRequest(AsyncWebServerRequest * request);
String generateJWT(User* user); String generateJWT(User * user);
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate); ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate);
private: private:
HttpEndpoint<SecuritySettings> _httpEndpoint; HttpEndpoint<SecuritySettings> _httpEndpoint;
FSPersistence<SecuritySettings> _fsPersistence; FSPersistence<SecuritySettings> _fsPersistence;
ArduinoJsonJWT _jwtHandler; ArduinoJsonJWT _jwtHandler;
void configureJWTHandler(); void configureJWTHandler();
Authentication authenticateJWT(String & jwt);
/* boolean validatePayload(JsonObject & parsedPayload, User * user);
* Lookup the user by JWT
*/
Authentication authenticateJWT(String& jwt);
/*
* Verify the payload is correct
*/
boolean validatePayload(JsonObject& parsedPayload, User* user);
}; };
#else #else
class SecuritySettingsService : public SecurityManager { class SecuritySettingsService : public SecurityManager {
public: public:
SecuritySettingsService(AsyncWebServer* server, FS* fs); SecuritySettingsService(AsyncWebServer * server, FS * fs);
~SecuritySettingsService(); ~SecuritySettingsService();
// minimal set of functions to support framework with security settings disabled // minimal set of functions to support framework with security settings disabled
Authentication authenticateRequest(AsyncWebServerRequest* request); Authentication authenticateRequest(AsyncWebServerRequest * request);
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate); ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
}; };
#endif // end FT_ENABLED(FT_SECURITY) #endif // end FT_ENABLED(FT_SECURITY)
#endif // end SecuritySettingsService_h #endif // end SecuritySettingsService_h

View File

@@ -12,133 +12,137 @@
#endif #endif
enum class StateUpdateResult { enum class StateUpdateResult {
CHANGED = 0, // The update changed the state and propagation should take place if required CHANGED = 0, // The update changed the state and propagation should take place if required
UNCHANGED, // The state was unchanged, propagation should not take place UNCHANGED, // The state was unchanged, propagation should not take place
ERROR // There was a problem updating the state, propagation should not take place ERROR // There was a problem updating the state, propagation should not take place
}; };
template <typename T> template <typename T>
using JsonStateUpdater = std::function<StateUpdateResult(JsonObject& root, T& settings)>; using JsonStateUpdater = std::function<StateUpdateResult(JsonObject & root, T & settings)>;
template <typename T> template <typename T>
using JsonStateReader = std::function<void(T& settings, JsonObject& root)>; using JsonStateReader = std::function<void(T & settings, JsonObject & root)>;
typedef size_t update_handler_id_t; typedef size_t update_handler_id_t;
typedef std::function<void(const String& originId)> StateUpdateCallback; typedef std::function<void(const String & originId)> StateUpdateCallback;
typedef struct StateUpdateHandlerInfo { typedef struct StateUpdateHandlerInfo {
static update_handler_id_t currentUpdatedHandlerId; static update_handler_id_t currentUpdatedHandlerId;
update_handler_id_t _id; update_handler_id_t _id;
StateUpdateCallback _cb; StateUpdateCallback _cb;
bool _allowRemove; bool _allowRemove;
StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove) : StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove)
_id(++currentUpdatedHandlerId), _cb(cb), _allowRemove(allowRemove){}; : _id(++currentUpdatedHandlerId)
, _cb(cb)
, _allowRemove(allowRemove){};
} StateUpdateHandlerInfo_t; } StateUpdateHandlerInfo_t;
template <class T> template <class T>
class StatefulService { class StatefulService {
public: public:
template <typename... Args> template <typename... Args>
#ifdef ESP32 #ifdef ESP32
StatefulService(Args&&... args) : StatefulService(Args &&... args)
_state(std::forward<Args>(args)...), _accessMutex(xSemaphoreCreateRecursiveMutex()) { : _state(std::forward<Args>(args)...)
} , _accessMutex(xSemaphoreCreateRecursiveMutex()) {
}
#else #else
StatefulService(Args&&... args) : _state(std::forward<Args>(args)...) { StatefulService(Args &&... args)
} : _state(std::forward<Args>(args)...) {
}
#endif #endif
update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) { update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) {
if (!cb) { if (!cb) {
return 0; return 0;
}
StateUpdateHandlerInfo_t updateHandler(cb, allowRemove);
_updateHandlers.push_back(updateHandler);
return updateHandler._id;
} }
StateUpdateHandlerInfo_t updateHandler(cb, allowRemove);
_updateHandlers.push_back(updateHandler);
return updateHandler._id;
}
void removeUpdateHandler(update_handler_id_t id) { void removeUpdateHandler(update_handler_id_t id) {
for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) { for (auto i = _updateHandlers.begin(); i != _updateHandlers.end();) {
if ((*i)._allowRemove && (*i)._id == id) { if ((*i)._allowRemove && (*i)._id == id) {
i = _updateHandlers.erase(i); i = _updateHandlers.erase(i);
} else { } else {
++i; ++i;
} }
}
} }
}
StateUpdateResult update(std::function<StateUpdateResult(T&)> stateUpdater, const String& originId) { StateUpdateResult update(std::function<StateUpdateResult(T &)> stateUpdater, const String & originId) {
beginTransaction(); beginTransaction();
StateUpdateResult result = stateUpdater(_state); StateUpdateResult result = stateUpdater(_state);
endTransaction(); endTransaction();
if (result == StateUpdateResult::CHANGED) { if (result == StateUpdateResult::CHANGED) {
callUpdateHandlers(originId); callUpdateHandlers(originId);
}
return result;
} }
return result;
}
StateUpdateResult updateWithoutPropagation(std::function<StateUpdateResult(T&)> stateUpdater) { StateUpdateResult updateWithoutPropagation(std::function<StateUpdateResult(T &)> stateUpdater) {
beginTransaction(); beginTransaction();
StateUpdateResult result = stateUpdater(_state); StateUpdateResult result = stateUpdater(_state);
endTransaction(); endTransaction();
return result; return result;
}
StateUpdateResult update(JsonObject& jsonObject, JsonStateUpdater<T> stateUpdater, const String& originId) {
beginTransaction();
StateUpdateResult result = stateUpdater(jsonObject, _state);
endTransaction();
if (result == StateUpdateResult::CHANGED) {
callUpdateHandlers(originId);
} }
return result;
}
StateUpdateResult updateWithoutPropagation(JsonObject& jsonObject, JsonStateUpdater<T> stateUpdater) { StateUpdateResult update(JsonObject & jsonObject, JsonStateUpdater<T> stateUpdater, const String & originId) {
beginTransaction(); beginTransaction();
StateUpdateResult result = stateUpdater(jsonObject, _state); StateUpdateResult result = stateUpdater(jsonObject, _state);
endTransaction(); endTransaction();
return result; if (result == StateUpdateResult::CHANGED) {
} callUpdateHandlers(originId);
}
void read(std::function<void(T&)> stateReader) { return result;
beginTransaction();
stateReader(_state);
endTransaction();
}
void read(JsonObject& jsonObject, JsonStateReader<T> stateReader) {
beginTransaction();
stateReader(_state, jsonObject);
endTransaction();
}
void callUpdateHandlers(const String& originId) {
for (const StateUpdateHandlerInfo_t& updateHandler : _updateHandlers) {
updateHandler._cb(originId);
} }
}
protected: StateUpdateResult updateWithoutPropagation(JsonObject & jsonObject, JsonStateUpdater<T> stateUpdater) {
T _state; beginTransaction();
StateUpdateResult result = stateUpdater(jsonObject, _state);
endTransaction();
return result;
}
inline void beginTransaction() { void read(std::function<void(T &)> stateReader) {
beginTransaction();
stateReader(_state);
endTransaction();
}
void read(JsonObject & jsonObject, JsonStateReader<T> stateReader) {
beginTransaction();
stateReader(_state, jsonObject);
endTransaction();
}
void callUpdateHandlers(const String & originId) {
for (const StateUpdateHandlerInfo_t & updateHandler : _updateHandlers) {
updateHandler._cb(originId);
}
}
protected:
T _state;
inline void beginTransaction() {
#ifdef ESP32 #ifdef ESP32
xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY); xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY);
#endif #endif
} }
inline void endTransaction() { inline void endTransaction() {
#ifdef ESP32 #ifdef ESP32
xSemaphoreGiveRecursive(_accessMutex); xSemaphoreGiveRecursive(_accessMutex);
#endif #endif
} }
private: private:
#ifdef ESP32 #ifdef ESP32
SemaphoreHandle_t _accessMutex; SemaphoreHandle_t _accessMutex;
#endif #endif
std::list<StateUpdateHandlerInfo_t> _updateHandlers; std::list<StateUpdateHandlerInfo_t> _updateHandlers;
}; };
#endif // end StatefulService_h #endif // end StatefulService_h