mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
clean-up code
This commit is contained in:
@@ -37,7 +37,6 @@ static bool __output_pins[256];
|
||||
static int __output_level[256];
|
||||
|
||||
int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) {
|
||||
|
||||
memset(__output_pins, 0, sizeof(__output_pins));
|
||||
memset(__output_level, 0, sizeof(__output_level));
|
||||
|
||||
|
||||
@@ -3,16 +3,11 @@
|
||||
#define ASYNC_JSON_H_
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
// #include <Print.h>
|
||||
|
||||
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
|
||||
|
||||
constexpr const char * JSON_MIMETYPE = "application/json";
|
||||
|
||||
/*
|
||||
* Json Response
|
||||
* */
|
||||
|
||||
class ChunkPrint : public Print {
|
||||
private:
|
||||
uint8_t * _destination;
|
||||
@@ -45,9 +40,8 @@ class ChunkPrint : public Print {
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncJsonResponse {
|
||||
class AsyncJsonResponse {
|
||||
protected:
|
||||
|
||||
DynamicJsonDocument _jsonBuffer;
|
||||
|
||||
JsonVariant _root;
|
||||
@@ -71,7 +65,7 @@ class AsyncJsonResponse {
|
||||
return _isValid;
|
||||
}
|
||||
size_t setLength() {
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t getSize() {
|
||||
@@ -79,9 +73,6 @@ return 0;
|
||||
}
|
||||
|
||||
size_t _fillBuffer(uint8_t * data, size_t len) {
|
||||
// ChunkPrint dest(data, 0, len);
|
||||
|
||||
// serializeJson(_root, dest);
|
||||
return len;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,7 +24,6 @@ struct AsyncMqttClientMessageProperties {
|
||||
bool retain;
|
||||
};
|
||||
|
||||
|
||||
namespace AsyncMqttClientInternals {
|
||||
|
||||
typedef std::function<void(bool sessionPresent)> OnConnectUserCallback;
|
||||
|
||||
@@ -95,8 +95,6 @@ class EMSESPSettingsService {
|
||||
void begin();
|
||||
|
||||
private:
|
||||
// HttpEndpoint<EMSESPSettings> _httpEndpoint;
|
||||
// FSPersistence<EMSESPSettings> _fsPersistence;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -68,8 +68,8 @@ typedef std::function<bool(AsyncWebServerRequest * request)> ArRequestFilterFunc
|
||||
|
||||
class AsyncWebHandler {
|
||||
protected:
|
||||
String _username;
|
||||
String _password;
|
||||
String _username;
|
||||
String _password;
|
||||
|
||||
public:
|
||||
AsyncWebHandler()
|
||||
@@ -131,7 +131,6 @@ class AsyncWebServer {
|
||||
}
|
||||
|
||||
void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -24,46 +24,13 @@ class FSPersistence {
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
bool writeToFS() {
|
||||
// create and populate a new json object
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
||||
JsonObject jsonObject = jsonDocument.to<JsonObject>();
|
||||
_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;
|
||||
}
|
||||
|
||||
@@ -90,8 +57,6 @@ class FSPersistence {
|
||||
update_handler_id_t _updateHandlerId;
|
||||
|
||||
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() {
|
||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(_bufferSize);
|
||||
JsonObject jsonObject = jsonDocument.as<JsonObject>();
|
||||
|
||||
@@ -22,12 +22,6 @@ class HttpGetEndpoint {
|
||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
|
||||
_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,
|
||||
@@ -36,9 +30,6 @@ class HttpGetEndpoint {
|
||||
const String& servicePath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE) :
|
||||
_stateReader(stateReader), _statefulService(statefulService), _bufferSize(bufferSize) {
|
||||
/*
|
||||
server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, std::placeholders::_1));
|
||||
*/
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -47,12 +38,6 @@ class HttpGetEndpoint {
|
||||
size_t _bufferSize;
|
||||
|
||||
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),
|
||||
_stateUpdater(stateUpdater),
|
||||
_statefulService(statefulService),
|
||||
/*
|
||||
_updateHandler(
|
||||
servicePath,
|
||||
securityManager->wrapCallback(
|
||||
std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2),
|
||||
authenticationPredicate),
|
||||
bufferSize),
|
||||
*/
|
||||
_bufferSize(bufferSize) {
|
||||
//_updateHandler.setMethod(HTTP_POST);
|
||||
// server->addHandler(&_updateHandler);
|
||||
}
|
||||
|
||||
HttpPostEndpoint(JsonStateReader<T> stateReader,
|
||||
@@ -92,42 +67,26 @@ class HttpPostEndpoint {
|
||||
_stateReader(stateReader),
|
||||
_stateUpdater(stateUpdater),
|
||||
_statefulService(statefulService),
|
||||
/*
|
||||
_updateHandler(servicePath,
|
||||
std::bind(&HttpPostEndpoint::updateSettings, this, std::placeholders::_1, std::placeholders::_2),
|
||||
bufferSize),
|
||||
*/
|
||||
_bufferSize(bufferSize) {
|
||||
// _updateHandler.setMethod(HTTP_POST);
|
||||
// server->addHandler(&_updateHandler);
|
||||
}
|
||||
|
||||
protected:
|
||||
JsonStateReader<T> _stateReader;
|
||||
JsonStateUpdater<T> _stateUpdater;
|
||||
StatefulService<T>* _statefulService;
|
||||
//AsyncCallbackJsonWebHandler _updateHandler;
|
||||
size_t _bufferSize;
|
||||
|
||||
void updateSettings(AsyncWebServerRequest* request, JsonVariant& json) {
|
||||
if (!json.is<JsonObject>()) {
|
||||
// request->send(400);
|
||||
return;
|
||||
}
|
||||
JsonObject jsonObject = json.as<JsonObject>();
|
||||
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
||||
if (outcome == StateUpdateResult::ERROR) {
|
||||
// request->send(400);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <Arduino.h>
|
||||
#include <Features.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
// #include <ESPUtils.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <list>
|
||||
|
||||
@@ -21,82 +20,64 @@
|
||||
#define MAX_JWT_SIZE 128
|
||||
|
||||
class User {
|
||||
public:
|
||||
String username;
|
||||
String password;
|
||||
bool admin;
|
||||
public:
|
||||
String username;
|
||||
String password;
|
||||
bool admin;
|
||||
|
||||
public:
|
||||
User(String username, String password, bool admin) : username(username), password(password), admin(admin) {
|
||||
}
|
||||
public:
|
||||
User(String username, String password, bool admin)
|
||||
: username(username)
|
||||
, password(password)
|
||||
, admin(admin) {
|
||||
}
|
||||
};
|
||||
|
||||
class Authentication {
|
||||
public:
|
||||
User* user;
|
||||
boolean authenticated;
|
||||
public:
|
||||
User * user;
|
||||
boolean authenticated;
|
||||
|
||||
public:
|
||||
Authentication(User& user) : user(new User(user)), authenticated(true) {
|
||||
}
|
||||
Authentication() : user(nullptr), authenticated(false) {
|
||||
}
|
||||
~Authentication() {
|
||||
delete (user);
|
||||
}
|
||||
public:
|
||||
Authentication(User & user)
|
||||
: user(new User(user))
|
||||
, authenticated(true) {
|
||||
}
|
||||
Authentication()
|
||||
: user(nullptr)
|
||||
, authenticated(false) {
|
||||
}
|
||||
~Authentication() {
|
||||
delete (user);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::function<boolean(Authentication& authentication)> AuthenticationPredicate;
|
||||
typedef std::function<boolean(Authentication & authentication)> AuthenticationPredicate;
|
||||
|
||||
class AuthenticationPredicates {
|
||||
public:
|
||||
static bool NONE_REQUIRED(Authentication& authentication) {
|
||||
return true;
|
||||
};
|
||||
static bool IS_AUTHENTICATED(Authentication& authentication) {
|
||||
return authentication.authenticated;
|
||||
};
|
||||
static bool IS_ADMIN(Authentication& authentication) {
|
||||
return authentication.authenticated && authentication.user->admin;
|
||||
};
|
||||
public:
|
||||
static bool NONE_REQUIRED(Authentication & authentication) {
|
||||
return true;
|
||||
};
|
||||
static bool IS_AUTHENTICATED(Authentication & authentication) {
|
||||
return authentication.authenticated;
|
||||
};
|
||||
static bool IS_ADMIN(Authentication & authentication) {
|
||||
return authentication.authenticated && authentication.user->admin;
|
||||
};
|
||||
};
|
||||
|
||||
class SecurityManager {
|
||||
public:
|
||||
public:
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
/*
|
||||
* Authenticate, returning the user if found
|
||||
*/
|
||||
virtual Authentication authenticate(const String& username, const String& password) = 0;
|
||||
|
||||
/*
|
||||
* Generate a JWT for the user provided
|
||||
*/
|
||||
virtual String generateJWT(User* user) = 0;
|
||||
|
||||
virtual Authentication authenticate(const String & username, const String & password) = 0;
|
||||
virtual String generateJWT(User * user) = 0;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check the request header for the Authorization token
|
||||
*/
|
||||
virtual Authentication authenticateRequest(AsyncWebServerRequest* request) = 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;
|
||||
virtual Authentication authenticateRequest(AsyncWebServerRequest * request) = 0;
|
||||
virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0;
|
||||
virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) = 0;
|
||||
virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) = 0;
|
||||
};
|
||||
|
||||
#endif // end SecurityManager_h
|
||||
#endif // end SecurityManager_h
|
||||
|
||||
@@ -30,87 +30,79 @@
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
class SecuritySettings {
|
||||
public:
|
||||
String jwtSecret;
|
||||
std::list<User> users;
|
||||
public:
|
||||
String jwtSecret;
|
||||
std::list<User> users;
|
||||
|
||||
static void read(SecuritySettings& settings, JsonObject& root) {
|
||||
// secret
|
||||
root["jwt_secret"] = settings.jwtSecret;
|
||||
static void read(SecuritySettings & settings, JsonObject & root) {
|
||||
// secret
|
||||
root["jwt_secret"] = settings.jwtSecret;
|
||||
|
||||
// users
|
||||
JsonArray users = root.createNestedArray("users");
|
||||
for (User user : settings.users) {
|
||||
JsonObject userRoot = users.createNestedObject();
|
||||
userRoot["username"] = user.username;
|
||||
userRoot["password"] = user.password;
|
||||
userRoot["admin"] = user.admin;
|
||||
// users
|
||||
JsonArray users = root.createNestedArray("users");
|
||||
for (User user : settings.users) {
|
||||
JsonObject userRoot = users.createNestedObject();
|
||||
userRoot["username"] = user.username;
|
||||
userRoot["password"] = user.password;
|
||||
userRoot["admin"] = user.admin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static StateUpdateResult update(JsonObject& root, SecuritySettings& settings) {
|
||||
// secret
|
||||
settings.jwtSecret = root["jwt_secret"] | FACTORY_JWT_SECRET;
|
||||
static StateUpdateResult update(JsonObject & root, SecuritySettings & settings) {
|
||||
// secret
|
||||
settings.jwtSecret = root["jwt_secret"] | FACTORY_JWT_SECRET;
|
||||
|
||||
// users
|
||||
settings.users.clear();
|
||||
if (root["users"].is<JsonArray>()) {
|
||||
for (JsonVariant user : root["users"].as<JsonArray>()) {
|
||||
settings.users.push_back(User(user["username"], user["password"], user["admin"]));
|
||||
}
|
||||
} else {
|
||||
settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true));
|
||||
settings.users.push_back(User(FACTORY_GUEST_USERNAME, FACTORY_GUEST_PASSWORD, false));
|
||||
// users
|
||||
settings.users.clear();
|
||||
if (root["users"].is<JsonArray>()) {
|
||||
for (JsonVariant user : root["users"].as<JsonArray>()) {
|
||||
settings.users.push_back(User(user["username"], user["password"], user["admin"]));
|
||||
}
|
||||
} else {
|
||||
settings.users.push_back(User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true));
|
||||
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 {
|
||||
public:
|
||||
SecuritySettingsService(AsyncWebServer* server, FS* fs);
|
||||
public:
|
||||
SecuritySettingsService(AsyncWebServer * server, FS * fs);
|
||||
|
||||
void begin();
|
||||
void begin();
|
||||
|
||||
// Functions to implement SecurityManager
|
||||
Authentication authenticate(const String& username, const String& password);
|
||||
Authentication authenticateRequest(AsyncWebServerRequest* request);
|
||||
String generateJWT(User* user);
|
||||
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate);
|
||||
// Functions to implement SecurityManager
|
||||
Authentication authenticate(const String & username, const String & password);
|
||||
Authentication authenticateRequest(AsyncWebServerRequest * request);
|
||||
String generateJWT(User * user);
|
||||
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate);
|
||||
|
||||
private:
|
||||
HttpEndpoint<SecuritySettings> _httpEndpoint;
|
||||
FSPersistence<SecuritySettings> _fsPersistence;
|
||||
ArduinoJsonJWT _jwtHandler;
|
||||
private:
|
||||
HttpEndpoint<SecuritySettings> _httpEndpoint;
|
||||
FSPersistence<SecuritySettings> _fsPersistence;
|
||||
ArduinoJsonJWT _jwtHandler;
|
||||
|
||||
void configureJWTHandler();
|
||||
|
||||
/*
|
||||
* Lookup the user by JWT
|
||||
*/
|
||||
Authentication authenticateJWT(String& jwt);
|
||||
|
||||
/*
|
||||
* Verify the payload is correct
|
||||
*/
|
||||
boolean validatePayload(JsonObject& parsedPayload, User* user);
|
||||
void configureJWTHandler();
|
||||
Authentication authenticateJWT(String & jwt);
|
||||
boolean validatePayload(JsonObject & parsedPayload, User * user);
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class SecuritySettingsService : public SecurityManager {
|
||||
public:
|
||||
SecuritySettingsService(AsyncWebServer* server, FS* fs);
|
||||
~SecuritySettingsService();
|
||||
public:
|
||||
SecuritySettingsService(AsyncWebServer * server, FS * fs);
|
||||
~SecuritySettingsService();
|
||||
|
||||
// minimal set of functions to support framework with security settings disabled
|
||||
Authentication authenticateRequest(AsyncWebServerRequest* request);
|
||||
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
// minimal set of functions to support framework with security settings disabled
|
||||
Authentication authenticateRequest(AsyncWebServerRequest * request);
|
||||
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
};
|
||||
|
||||
#endif // end FT_ENABLED(FT_SECURITY)
|
||||
#endif // end SecuritySettingsService_h
|
||||
#endif // end FT_ENABLED(FT_SECURITY)
|
||||
#endif // end SecuritySettingsService_h
|
||||
|
||||
@@ -12,133 +12,137 @@
|
||||
#endif
|
||||
|
||||
enum class StateUpdateResult {
|
||||
CHANGED = 0, // The update changed the state and propagation should take place if required
|
||||
UNCHANGED, // The state was unchanged, propagation should not take place
|
||||
ERROR // There was a problem updating the state, propagation should not take place
|
||||
CHANGED = 0, // The update changed the state and propagation should take place if required
|
||||
UNCHANGED, // The state was unchanged, propagation should not take place
|
||||
ERROR // There was a problem updating the state, propagation should not take place
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using JsonStateUpdater = std::function<StateUpdateResult(JsonObject& root, T& settings)>;
|
||||
using JsonStateUpdater = std::function<StateUpdateResult(JsonObject & root, T & settings)>;
|
||||
|
||||
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 std::function<void(const String& originId)> StateUpdateCallback;
|
||||
typedef size_t update_handler_id_t;
|
||||
typedef std::function<void(const String & originId)> StateUpdateCallback;
|
||||
|
||||
typedef struct StateUpdateHandlerInfo {
|
||||
static update_handler_id_t currentUpdatedHandlerId;
|
||||
update_handler_id_t _id;
|
||||
StateUpdateCallback _cb;
|
||||
bool _allowRemove;
|
||||
StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove) :
|
||||
_id(++currentUpdatedHandlerId), _cb(cb), _allowRemove(allowRemove){};
|
||||
static update_handler_id_t currentUpdatedHandlerId;
|
||||
update_handler_id_t _id;
|
||||
StateUpdateCallback _cb;
|
||||
bool _allowRemove;
|
||||
StateUpdateHandlerInfo(StateUpdateCallback cb, bool allowRemove)
|
||||
: _id(++currentUpdatedHandlerId)
|
||||
, _cb(cb)
|
||||
, _allowRemove(allowRemove){};
|
||||
} StateUpdateHandlerInfo_t;
|
||||
|
||||
template <class T>
|
||||
class StatefulService {
|
||||
public:
|
||||
template <typename... Args>
|
||||
public:
|
||||
template <typename... Args>
|
||||
#ifdef ESP32
|
||||
StatefulService(Args&&... args) :
|
||||
_state(std::forward<Args>(args)...), _accessMutex(xSemaphoreCreateRecursiveMutex()) {
|
||||
}
|
||||
StatefulService(Args &&... args)
|
||||
: _state(std::forward<Args>(args)...)
|
||||
, _accessMutex(xSemaphoreCreateRecursiveMutex()) {
|
||||
}
|
||||
#else
|
||||
StatefulService(Args&&... args) : _state(std::forward<Args>(args)...) {
|
||||
}
|
||||
StatefulService(Args &&... args)
|
||||
: _state(std::forward<Args>(args)...) {
|
||||
}
|
||||
#endif
|
||||
|
||||
update_handler_id_t addUpdateHandler(StateUpdateCallback cb, bool allowRemove = true) {
|
||||
if (!cb) {
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StateUpdateResult update(std::function<StateUpdateResult(T&)> stateUpdater, const String& originId) {
|
||||
beginTransaction();
|
||||
StateUpdateResult result = stateUpdater(_state);
|
||||
endTransaction();
|
||||
if (result == StateUpdateResult::CHANGED) {
|
||||
callUpdateHandlers(originId);
|
||||
StateUpdateResult update(std::function<StateUpdateResult(T &)> stateUpdater, const String & originId) {
|
||||
beginTransaction();
|
||||
StateUpdateResult result = stateUpdater(_state);
|
||||
endTransaction();
|
||||
if (result == StateUpdateResult::CHANGED) {
|
||||
callUpdateHandlers(originId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
StateUpdateResult updateWithoutPropagation(std::function<StateUpdateResult(T&)> stateUpdater) {
|
||||
beginTransaction();
|
||||
StateUpdateResult result = stateUpdater(_state);
|
||||
endTransaction();
|
||||
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);
|
||||
StateUpdateResult updateWithoutPropagation(std::function<StateUpdateResult(T &)> stateUpdater) {
|
||||
beginTransaction();
|
||||
StateUpdateResult result = stateUpdater(_state);
|
||||
endTransaction();
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
StateUpdateResult updateWithoutPropagation(JsonObject& jsonObject, JsonStateUpdater<T> stateUpdater) {
|
||||
beginTransaction();
|
||||
StateUpdateResult result = stateUpdater(jsonObject, _state);
|
||||
endTransaction();
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
T _state;
|
||||
StateUpdateResult updateWithoutPropagation(JsonObject & jsonObject, JsonStateUpdater<T> stateUpdater) {
|
||||
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
|
||||
xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY);
|
||||
xSemaphoreTakeRecursive(_accessMutex, portMAX_DELAY);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
inline void endTransaction() {
|
||||
inline void endTransaction() {
|
||||
#ifdef ESP32
|
||||
xSemaphoreGiveRecursive(_accessMutex);
|
||||
xSemaphoreGiveRecursive(_accessMutex);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
#ifdef ESP32
|
||||
SemaphoreHandle_t _accessMutex;
|
||||
SemaphoreHandle_t _accessMutex;
|
||||
#endif
|
||||
std::list<StateUpdateHandlerInfo_t> _updateHandlers;
|
||||
std::list<StateUpdateHandlerInfo_t> _updateHandlers;
|
||||
};
|
||||
|
||||
#endif // end StatefulService_h
|
||||
#endif // end StatefulService_h
|
||||
|
||||
Reference in New Issue
Block a user