Remove refrence to SPIFFS global from persistence code, factor out persistence code so it can be re-used.

This commit is contained in:
Rick Watson 2018-12-10 22:35:12 +00:00
parent 4e200f3d08
commit d03c03fd9d
2 changed files with 104 additions and 76 deletions

98
src/SettingsPersistence.h Normal file
View File

@ -0,0 +1,98 @@
#ifndef SettingsPersistence_h
#define SettingsPersistence_h
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <AsyncJson.h>
#include <ArduinoJson.h>
#include <AsyncJsonRequestWebHandler.h>
#include <AsyncJsonCallbackResponse.h>
/**
* At the moment, not expecting settings service to have to deal with large JSON
* files this could be made configurable fairly simply, it's exposed on
* AsyncJsonRequestWebHandler with a setter.
*/
#define MAX_SETTINGS_SIZE 1024
/*
* Mixin for classes which need to save settings to/from a file on the the file system as JSON.
*/
class SettingsPersistence {
protected:
// will store and retrieve config from the file system
FS* _fs;
// the file path our settings will be saved to
char const* _filePath;
bool writeToFS() {
// create and populate a new json object
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
writeToJsonObject(root);
// serialize it to filesystem
File configFile = _fs->open(_filePath, "w");
// failed to open file, return false
if (!configFile) {
return false;
}
root.printTo(configFile);
configFile.close();
return true;
}
void readFromFS(){
File configFile = _fs->open(_filePath, "r");
// use defaults if no config found
if (configFile) {
// Protect against bad data uploaded to file system
// We never expect the config file to get very large, so cap it.
size_t size = configFile.size();
if (size <= MAX_SETTINGS_SIZE) {
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(configFile);
if (root.success()) {
readFromJsonObject(root);
configFile.close();
return;
}
}
configFile.close();
}
// If we reach here we have not been successful in loading the config,
// hard-coded emergency defaults are now applied.
applyDefaultConfig();
}
// serialization routene, from local config to JsonObject
virtual void readFromJsonObject(JsonObject& root){}
virtual void writeToJsonObject(JsonObject& root){}
// We assume the readFromJsonObject supplies sensible defaults if an empty object
// is supplied, this virtual function allows that to be changed.
virtual void applyDefaultConfig(){
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
readFromJsonObject(root);
}
public:
SettingsPersistence(FS* fs, char const* servicePath, char const* filePath):
_fs(fs), _filePath(filePath) {}
virtual ~SettingsPersistence() {}
};
#endif // end SettingsPersistence

View File

@ -7,79 +7,24 @@
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <WiFi.h> #include <WiFi.h>
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <SPIFFS.h>
#endif #endif
#include <SettingsPersistence.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <FS.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <AsyncJsonRequestWebHandler.h> #include <AsyncJsonRequestWebHandler.h>
#include <AsyncJsonCallbackResponse.h> #include <AsyncJsonCallbackResponse.h>
/**
* At the moment, not expecting settings service to have to deal with large JSON
* files this could be made configurable fairly simply, it's exposed on
* AsyncJsonRequestWebHandler with a setter.
*/
#define MAX_SETTINGS_SIZE 1024
/* /*
* Abstraction of a service which stores it's settings as JSON in SPIFFS. * Abstraction of a service which stores it's settings as JSON in a file system.
*/ */
class SettingsService { class SettingsService : public SettingsPersistence {
private: private:
char const* _filePath;
AsyncJsonRequestWebHandler _updateHandler; AsyncJsonRequestWebHandler _updateHandler;
bool writeToSPIFFS() {
// create and populate a new json object
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
writeToJsonObject(root);
// serialize it to SPIFFS
File configFile = SPIFFS.open(_filePath, "w");
// failed to open file, return false
if (!configFile) {
return false;
}
root.printTo(configFile);
configFile.close();
return true;
}
void readFromSPIFFS(){
File configFile = SPIFFS.open(_filePath, "r");
// use defaults if no config found
if (configFile) {
// Protect against bad data uploaded to SPIFFS
// We never expect the config file to get very large, so cap it.
size_t size = configFile.size();
if (size <= MAX_SETTINGS_SIZE) {
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(configFile);
if (root.success()) {
readFromJsonObject(root);
configFile.close();
return;
}
}
configFile.close();
}
// If we reach here we have not been successful in loading the config,
// hard-coded emergency defaults are now applied.
applyDefaultConfig();
}
void fetchConfig(AsyncWebServerRequest *request){ void fetchConfig(AsyncWebServerRequest *request){
AsyncJsonResponse * response = new AsyncJsonResponse(); AsyncJsonResponse * response = new AsyncJsonResponse();
writeToJsonObject(response->getRoot()); writeToJsonObject(response->getRoot());
@ -91,7 +36,7 @@ private:
if (json.is<JsonObject>()){ if (json.is<JsonObject>()){
JsonObject& newConfig = json.as<JsonObject>(); JsonObject& newConfig = json.as<JsonObject>();
readFromJsonObject(newConfig); readFromJsonObject(newConfig);
writeToSPIFFS(); writeToFS();
// write settings back with a callback to reconfigure the wifi // write settings back with a callback to reconfigure the wifi
AsyncJsonCallbackResponse * response = new AsyncJsonCallbackResponse([this] () {onConfigUpdated();}); AsyncJsonCallbackResponse * response = new AsyncJsonCallbackResponse([this] () {onConfigUpdated();});
@ -108,28 +53,13 @@ private:
// will serve setting endpoints from here // will serve setting endpoints from here
AsyncWebServer* _server; AsyncWebServer* _server;
// will store and retrieve config from the file system
FS* _fs;
// reads the local config from the
virtual void readFromJsonObject(JsonObject& root){}
virtual void writeToJsonObject(JsonObject& root){}
// implement to perform action when config has been updated // implement to perform action when config has been updated
virtual void onConfigUpdated(){} virtual void onConfigUpdated(){}
// We assume the readFromJsonObject supplies sensible defaults if an empty object
// is supplied, this virtual function allows that to be changed.
virtual void applyDefaultConfig(){
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
readFromJsonObject(root);
}
public: public:
SettingsService(AsyncWebServer* server, FS* fs, char const* servicePath, char const* filePath): SettingsService(AsyncWebServer* server, FS* fs, char const* servicePath, char const* filePath):
_filePath(filePath), _server(server), _fs(fs){ SettingsPersistence(fs, servicePath, filePath), _server(server) {
// configure fetch config handler // configure fetch config handler
_server->on(servicePath, HTTP_GET, std::bind(&SettingsService::fetchConfig, this, std::placeholders::_1)); _server->on(servicePath, HTTP_GET, std::bind(&SettingsService::fetchConfig, this, std::placeholders::_1));
@ -145,7 +75,7 @@ private:
virtual ~SettingsService() {} virtual ~SettingsService() {}
virtual void begin() { virtual void begin() {
readFromSPIFFS(); readFromFS();
} }
}; };