Browse Source

0.5.102

Warning: old exports are not compatible any more!
further save settings improvements (only store inverters which are existing)
improved display of settings save return value
made save settings asynchronous (more heap memory is free)
pull/788/head
lumapu 2 years ago
parent
commit
57bda044e5
  1. 4
      src/CHANGES.md
  2. 2
      src/app.cpp
  3. 25
      src/app.h
  4. 2
      src/appInterface.h
  5. 47
      src/config/settings.h
  6. 17
      src/web/RestApi.h
  7. 4
      src/web/html/index.html
  8. 12
      src/web/html/setup.html
  9. 9
      src/web/web.h

4
src/CHANGES.md

@ -3,8 +3,12 @@
(starting from release version `0.5.66`)
## 0.5.102
* Warning: old exports are not compatible any more!
* fix JSON import #775
* fix save settings, at least already stored settings are not lost #771
* further save settings improvements (only store inverters which are existing)
* improved display of settings save return value
* made save settings asynchronous (more heap memory is free)
## 0.5.101
* fix SSD1306

2
src/app.cpp

@ -374,6 +374,8 @@ void app::resetSystem(void) {
mSendLastIvId = 0;
mShowRebootRequest = false;
mIVCommunicationOn = true;
mSavePending = false;
mSaveReboot = false;
memset(&mStat, 0, sizeof(statistics_t));
}

25
src/app.h

@ -68,9 +68,12 @@ class app : public IApp, public ah::Scheduler {
return Scheduler::getTimestamp();
}
bool saveSettings(bool stopFs = false) {
bool saveSettings(bool reboot) {
mShowRebootRequest = true; // only message on index, no reboot
return mSettings.saveSettings(stopFs);
mSavePending = true;
mSaveReboot = reboot;
once(std::bind(&app::tickSave, this), 2, "save");
return true;
}
bool readSettings(const char *path) {
@ -81,6 +84,14 @@ class app : public IApp, public ah::Scheduler {
return mSettings.eraseSettings(eraseWifi);
}
bool getSavePending() {
return mSavePending;
}
bool getLastSaveSucceed() {
return mSettings.getLastSaveSucceed();
}
statistics_t *getStatistics() {
return &mStat;
}
@ -214,6 +225,14 @@ class app : public IApp, public ah::Scheduler {
ESP.restart();
}
void tickSave(void) {
mSettings.saveSettings();
mSavePending = false;
if(mSaveReboot)
once(std::bind(&app::tickReboot, this), 2, "rboot");
}
void tickNtpUpdate(void);
void tickCalcSunrise(void);
void tickIVCommunication(void);
@ -253,6 +272,8 @@ class app : public IApp, public ah::Scheduler {
char mVersion[12];
settings mSettings;
settings_t *mConfig;
bool mSavePending;
bool mSaveReboot;
uint8_t mSendLastIvId;
bool mSendFirst;

2
src/appInterface.h

@ -17,6 +17,8 @@ class IApp {
virtual bool saveSettings(bool stopFs) = 0;
virtual bool readSettings(const char *path) = 0;
virtual bool eraseSettings(bool eraseWifi) = 0;
virtual bool getSavePending() = 0;
virtual bool getLastSaveSucceed() = 0;
virtual void setOnUpdate() = 0;
virtual void setRebootFlag() = 0;
virtual const char *getVersion() = 0;

47
src/config/settings.h

@ -15,9 +15,9 @@
#include "../utils/helper.h"
#if defined(ESP32)
#define MAX_ALLOWED_BUF_SIZE ESP.getMaxAllocHeap() - 2048
#define MAX_ALLOWED_BUF_SIZE ESP.getMaxAllocHeap() - 1024
#else
#define MAX_ALLOWED_BUF_SIZE ESP.getMaxFreeBlockSize() - 2048
#define MAX_ALLOWED_BUF_SIZE ESP.getMaxFreeBlockSize() - 1024
#endif
/**
@ -161,7 +161,9 @@ typedef struct {
class settings {
public:
settings() {}
settings() {
mLastSaveSucceed = false;
}
void setup() {
DPRINTLN(DBG_INFO, F("Initializing FS .."));
@ -208,6 +210,10 @@ class settings {
return mCfg.valid;
}
inline bool getLastSaveSucceed() {
return mLastSaveSucceed;
}
void getInfo(uint32_t *used, uint32_t *size) {
#if !defined(ESP32)
FSInfo info;
@ -254,7 +260,7 @@ class settings {
return mCfg.valid;
}
bool saveSettings(bool stopFs = false) {
bool saveSettings() {
DPRINTLN(DBG_DEBUG, F("save settings"));
DynamicJsonDocument json(MAX_ALLOWED_BUF_SIZE);
@ -269,31 +275,35 @@ class settings {
jsonPlugin(root.createNestedObject(F("plugin")), true);
jsonInst(root.createNestedObject(F("inst")), true);
DPRINT(DBG_INFO, "memory usage: ");
DPRINT(DBG_INFO, F("memory usage: "));
DBGPRINTLN(String(json.memoryUsage()));
DPRINT(DBG_INFO, F("capacity: "));
DBGPRINTLN(String(json.capacity()));
DPRINT(DBG_INFO, F("max alloc: "));
DBGPRINTLN(String(MAX_ALLOWED_BUF_SIZE));
if(json.overflowed()) {
DPRINTLN(DBG_ERROR, F("buffer too small!"));
mLastSaveSucceed = false;
return false;
}
File fp = LittleFS.open("/settings.json", "w");
if(!fp) {
DPRINTLN(DBG_ERROR, F("can't open settings file!"));
mLastSaveSucceed = false;
return false;
}
if(0 == serializeJson(root, fp)) {
DPRINTLN(DBG_ERROR, F("can't write settings file!"));
mLastSaveSucceed = false;
return false;
}
fp.close();
DPRINTLN(DBG_INFO, F("settings saved"));
if(stopFs)
stop();
mLastSaveSucceed = true;
return true;
}
@ -301,7 +311,7 @@ class settings {
if(true == eraseWifi)
return LittleFS.format();
loadDefaults(!eraseWifi);
return saveSettings(true);
return saveSettings();
}
private:
@ -549,12 +559,16 @@ class settings {
if(set)
ivArr = obj.createNestedArray(F("iv"));
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
if(set)
if(set) {
if(mCfg.inst.iv[i].serial.u64 != 0ULL)
jsonIv(ivArr.createNestedObject(), &mCfg.inst.iv[i], true);
else
}
else {
if(!obj[F("iv")][i].isNull())
jsonIv(obj[F("iv")][i], &mCfg.inst.iv[i]);
}
}
}
void jsonIv(JsonObject obj, cfgIv_t *cfg, bool set = false) {
if(set) {
@ -562,23 +576,24 @@ class settings {
obj[F("name")] = cfg->name;
obj[F("sn")] = cfg->serial.u64;
for(uint8_t i = 0; i < 4; i++) {
obj[F("yield")][i] = cfg->yieldCor[i];
obj[F("yc")][i] = cfg->yieldCor[i];
obj[F("pwr")][i] = cfg->chMaxPwr[i];
obj[F("chName")][i] = cfg->chName[i];
obj[F("chn")][i] = cfg->chName[i];
}
} else {
cfg->enabled = (bool)obj[F("en")];
snprintf(cfg->name, MAX_NAME_LENGTH, "%s", obj[F("name")].as<const char*>());
cfg->serial.u64 = obj[F("sn")];
for(uint8_t i = 0; i < 4; i++) {
cfg->yieldCor[i] = obj[F("yield")][i];
cfg->yieldCor[i] = obj[F("yc")][i];
cfg->chMaxPwr[i] = obj[F("pwr")][i];
snprintf(cfg->chName[i], MAX_NAME_LENGTH, "%s", obj[F("chName")][i].as<const char*>());
snprintf(cfg->chName[i], MAX_NAME_LENGTH, "%s", obj[F("chn")][i].as<const char*>());
}
}
}
settings_t mCfg;
bool mLastSaveSucceed;
};
#endif /*__SETTINGS_H__*/

17
src/web/RestApi.h

@ -80,6 +80,7 @@ class RestApi {
if(path == "html/system") getHtmlSystem(root);
else if(path == "html/logout") getHtmlLogout(root);
else if(path == "html/save") getHtmlSave(root);
else if(path == "html/chk_save") getHtmlChkSave(root);
else if(path == "system") getSysInfo(root);
else if(path == "generic") getGeneric(root);
else if(path == "reboot") getReboot(root);
@ -266,9 +267,19 @@ class RestApi {
void getHtmlSave(JsonObject obj) {
getGeneric(obj.createNestedObject(F("generic")));
obj[F("refresh")] = 2;
obj[F("refresh_url")] = "/setup";
obj[F("html")] = F("settings succesfully save");
obj[F("refresh")] = 1;
obj[F("refresh_url")] = F("/chk_save");
obj[F("html")] = F("saving settings ...");
}
void getHtmlChkSave(JsonObject obj) {
getGeneric(obj.createNestedObject(F("generic")));
obj[F("refresh")] = (mApp->getLastSaveSucceed()) ? 10 : 1;
obj[F("refresh_url")] = mApp->getSavePending() ? F("/chk_save") : F("/setup");
if(mApp->getSavePending())
obj[F("html")] = F("saving settings ...");
else
obj[F("html")] = mApp->getLastSaveSucceed() ? F("settings succesfully saved") : F("failed saving settings");
}
void getReboot(JsonObject obj) {

4
src/web/html/index.html

@ -102,12 +102,12 @@
if(obj["disNightComm"]) {
if(((obj["ts_sunrise"] - obj["ts_offset"]) < obj["ts_now"])
&& ((obj["ts_sunset"] + obj["ts_offset"]) > obj["ts_now"])) {
commInfo = "Polling inverter(s), will stop at sunset " + (new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE'));
commInfo = "Polling inverter(s), will pause at sunset " + (new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE'));
}
else {
commInfo = "Night time, inverter polling disabled, ";
if(obj["ts_now"] > (obj["ts_sunrise"] - obj["ts_offset"])) {
commInfo += "stopped at " + (new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE'));
commInfo += "paused at " + (new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE'));
}
else {
commInfo += "will start polling at " + (new Date((obj["ts_sunrise"] - obj["ts_offset"]) * 1000).toLocaleString('de-DE'));

12
src/web/html/setup.html

@ -157,7 +157,7 @@
<div class="col-4 col-sm-9"><input type="checkbox" name="invRstMid"/></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3 mb-2">Reset values when inverter polling stops at sunset</div>
<div class="col-8 col-sm-3 mb-2">Reset values when inverter polling pauses at sunset</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="invRstComStop"/></div>
</div>
<div class="row mb-3">
@ -209,7 +209,7 @@
<div class="col-12 col-sm-9"><select name="sunOffs"></select></div>
</div>
<div class="row mb-3">
<div class="col-8 col-sm-3">Stop polling inverters during night</div>
<div class="col-8 col-sm-3">Pause polling inverters during night</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="sunDisNightCom"/></div>
</div>
</fieldset>
@ -291,8 +291,8 @@
<fieldset class="mb-4">
<legend class="des">Import / Export JSON Settings</legend>
<div class="row mb-4 mt-4">
<div class="col-8 col-sm-3">Import</div>
<div class="col-4 col-sm-9">
<div class="col-12 col-sm-3 my-2">Import</div>
<div class="col-12 col-sm-9">
<form id="form" method="POST" action="/upload" enctype="multipart/form-data" accept-charset="utf-8">
<input type="file" name="upload">
<input type="button" class="btn" value="Import" onclick="hide()">
@ -300,8 +300,8 @@
</div>
</div>
<div class="row mb-4 mt-4">
<div class="col-8 col-sm-3">Export</div>
<div class="col-4 col-sm-9">
<div class="col-12 col-sm-3 my-2">Export</div>
<div class="col-12 col-sm-9">
<a class="btn" href="/get_setup" target="_blank">Export settings (JSON file)</a><span> (only values, passwords will be removed!)</span>
</div>
</div>

9
src/web/web.h

@ -68,6 +68,7 @@ class Web {
mWeb.on("/setup", HTTP_GET, std::bind(&Web::onSetup, this, std::placeholders::_1));
mWeb.on("/save", HTTP_ANY, std::bind(&Web::showSave, this, std::placeholders::_1));
mWeb.on("/chk_save", HTTP_ANY, std::bind(&Web::onCheckSave, this, std::placeholders::_1));
mWeb.on("/live", HTTP_ANY, std::bind(&Web::onLive, this, std::placeholders::_1));
//mWeb.on("/api1", HTTP_POST, std::bind(&Web::showWebApi, this, std::placeholders::_1));
@ -592,13 +593,15 @@ class Web {
mApp->saveSettings((request->arg("reboot") == "on"));
if (request->arg("reboot") == "on")
onReboot(request);
else {
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), system_html, system_html_len);
response->addHeader(F("Content-Encoding"), "gzip");
request->send(response);
}
void onCheckSave(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/html; charset=UTF-8"), system_html, system_html_len);
response->addHeader(F("Content-Encoding"), "gzip");
request->send(response);
}
void onLive(AsyncWebServerRequest *request) {

Loading…
Cancel
Save