Improve graph calculation stability

This commit is contained in:
Gunnar Skjold
2022-02-12 11:12:48 +01:00
parent ef0041caa6
commit 0c1d666afd
5 changed files with 133 additions and 94 deletions

View File

@@ -2,6 +2,7 @@
#include <lwip/apps/sntp.h>
#include "LittleFS.h"
#include "AmsStorage.h"
#include "version.h"
AmsDataStorage::AmsDataStorage(RemoteDebug* debugger) {
day.version = 3;
@@ -14,37 +15,36 @@ void AmsDataStorage::setTimezone(Timezone* tz) {
}
bool AmsDataStorage::update(AmsData* data) {
if(isHappy()) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Data is up to date\n");
return false;
}
time_t now = time(nullptr);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Time is: %d\n", now);
if(tz == NULL) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Timezone is missing\n");
return false;
}
if(now < EPOCH_2021_01_01) {
if(data->getMeterTimestamp() > 0) {
if(now < BUILD_EPOCH) {
if(data->getMeterTimestamp() > BUILD_EPOCH) {
now = data->getMeterTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Using meter timestamp, which is: %d\n", now);
}
} else if(data->getPackageTimestamp() > 0) {
} else if(data->getPackageTimestamp() > BUILD_EPOCH) {
now = data->getPackageTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Using package timestamp, which is: %d\n", now);
}
}
}
if(now < EPOCH_2021_01_01) {
if(now < BUILD_EPOCH) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) Invalid time: %d\n", now);
}
return false;
}
if(now-day.lastMeterReadTime < 3500) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) It is only %d seconds since last update, ignoring\n", (now-day.lastMeterReadTime));
}
return false;
}
tmElements_t utc, ltz, utcYesterday, ltzYesterDay;
breakTime(now, utc);
@@ -52,7 +52,16 @@ bool AmsDataStorage::update(AmsData* data) {
breakTime(now-3600, utcYesterday);
breakTime(tz->toLocal(now-3600), ltzYesterDay);
if(day.lastMeterReadTime > EPOCH_2021_01_01) {
// Clear hours between last update and now
if(day.lastMeterReadTime > now) {
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Invalid future timestamp for day plot, resetting\n");
}
day.activeImport = data->getActiveImportCounter() * 1000;
day.activeExport = data->getActiveExportCounter() * 1000;
day.lastMeterReadTime = now;
return true;
} else {
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last day update: %d\n", day.lastMeterReadTime);
}
@@ -66,7 +75,15 @@ bool AmsDataStorage::update(AmsData* data) {
}
}
if(month.lastMeterReadTime > EPOCH_2021_01_01) {
// Clear days between last update and now
if(month.lastMeterReadTime > now) {
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Invalid future timestamp for month plot, resetting\n");
}
month.activeImport = data->getActiveImportCounter() * 1000;
month.activeExport = data->getActiveExportCounter() * 1000;
month.lastMeterReadTime = now;
} else {
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last month update: %d\n", month.lastMeterReadTime);
}
@@ -80,82 +97,69 @@ bool AmsDataStorage::update(AmsData* data) {
}
}
if(day.lastMeterReadTime > now) {
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Invalid future timestamp for day plot, resetting\n");
}
day.activeImport = data->getActiveImportCounter() * 1000;
day.activeExport = data->getActiveExportCounter() * 1000;
day.lastMeterReadTime = now;
return true;
}
if(data->getListType() < 3) return false;
else if(ltz.Minute > 1) return false;
bool ret = false;
// Update day plot
if(day.activeImport == 0 || now - day.lastMeterReadTime > 86400) {
day.activeImport = data->getActiveImportCounter() * 1000;
day.activeExport = data->getActiveExportCounter() * 1000;
day.lastMeterReadTime = now;
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Too long since last day update, clearing data\n");
}
for(int i = 0; i<24; i++) {
setHour(i, 0);
}
} else if(now - day.lastMeterReadTime < 4000) {
int16_t val = (((data->getActiveImportCounter() * 1000) - day.activeImport) - ((data->getActiveExportCounter() * 1000) - day.activeExport));
setHour(utcYesterday.Hour, val);
if(ltz.Minute > 0) {
if(day.activeImport == 0 || now - day.lastMeterReadTime > 86400) {
day.activeImport = data->getActiveImportCounter() * 1000;
day.activeExport = data->getActiveExportCounter() * 1000;
day.lastMeterReadTime = now;
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Too long since last day update, clearing data\n");
}
for(int i = 0; i<24; i++) {
setHour(i, 0);
}
} else if(now - day.lastMeterReadTime < 4000) {
int16_t val = (((data->getActiveImportCounter() * 1000) - day.activeImport) - ((data->getActiveExportCounter() * 1000) - day.activeExport));
setHour(utcYesterday.Hour, val);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(AmsDataStorage) Usage for hour %d: %d\n", ltzYesterDay.Hour, val);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(AmsDataStorage) Usage for hour %d: %d\n", ltzYesterDay.Hour, val);
day.activeImport = data->getActiveImportCounter() * 1000;
day.activeExport = data->getActiveExportCounter() * 1000;
day.lastMeterReadTime = now;
} else {
float mins = (now - day.lastMeterReadTime) / 60.0;
uint16_t im = ((data->getActiveImportCounter() * 1000) - day.activeImport);
uint16_t ex = ((data->getActiveExportCounter() * 1000) - day.activeExport);
float ipm = im / mins;
float epm = ex / mins;
day.activeImport = data->getActiveImportCounter() * 1000;
day.activeExport = data->getActiveExportCounter() * 1000;
day.lastMeterReadTime = now;
} else {
float mins = (now - day.lastMeterReadTime) / 60.0;
uint16_t im = ((data->getActiveImportCounter() * 1000) - day.activeImport);
uint16_t ex = ((data->getActiveExportCounter() * 1000) - day.activeExport);
float ipm = im / mins;
float epm = ex / mins;
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Since last day update, minutes: %.1f, import: %d (%.2f/min), export: %d (%.2f/min)\n", mins, im, ipm, ex, epm);
}
tmElements_t last;
breakTime(day.lastMeterReadTime, last);
day.lastMeterReadTime = day.lastMeterReadTime - (last.Minute * 60) - last.Second;
time_t stopAt = now - (utc.Minute * 60) - utc.Second;
while(day.lastMeterReadTime < stopAt) {
time_t cur = min(day.lastMeterReadTime + 3600, stopAt);
uint8_t minutes = round((cur - day.lastMeterReadTime) / 60.0);
if(minutes < 1) break;
breakTime(day.lastMeterReadTime, last);
float val = ((ipm * minutes) - (epm * minutes));
setHour(last.Hour, val);
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Estimated usage for hour %u: %.1f (%lu)\n", last.Hour, val, cur);
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Since last day update, minutes: %.1f, import: %d (%.2f/min), export: %d (%.2f/min)\n", mins, im, ipm, ex, epm);
}
day.activeImport += ipm * minutes;
day.activeExport += epm * minutes;
day.lastMeterReadTime = cur;
tmElements_t last;
breakTime(day.lastMeterReadTime, last);
day.lastMeterReadTime = day.lastMeterReadTime - (last.Minute * 60) - last.Second;
time_t stopAt = now - (utc.Minute * 60) - utc.Second;
while(day.lastMeterReadTime < stopAt) {
time_t cur = min(day.lastMeterReadTime + 3600, stopAt);
uint8_t minutes = round((cur - day.lastMeterReadTime) / 60.0);
if(minutes < 1) break;
breakTime(day.lastMeterReadTime, last);
float val = ((ipm * minutes) - (epm * minutes));
setHour(last.Hour, val);
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Estimated usage for hour %u: %.1f (%lu)\n", last.Hour, val, cur);
}
day.activeImport += ipm * minutes;
day.activeExport += epm * minutes;
day.lastMeterReadTime = cur;
}
}
ret = true;
}
// Update month plot
if(month.lastMeterReadTime > now) {
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Invalid future timestamp for month plot, resetting\n");
}
month.activeImport = data->getActiveImportCounter() * 1000;
month.activeExport = data->getActiveExportCounter() * 1000;
month.lastMeterReadTime = now;
} else if(ltz.Hour == 0) {
if(ltz.Hour == 0) {
if(month.activeImport == 0 || now - month.lastMeterReadTime > 2678400) {
month.activeImport = data->getActiveImportCounter() * 1000;
month.activeExport = data->getActiveExportCounter() * 1000;
@@ -217,8 +221,9 @@ bool AmsDataStorage::update(AmsData* data) {
month.lastMeterReadTime = cur;
}
}
ret = true;
}
return true;
return ret;
}
void AmsDataStorage::setHour(uint8_t hour, int32_t val) {
@@ -326,3 +331,23 @@ bool AmsDataStorage::setMonthData(MonthDataPoints& month) {
}
return false;
}
bool AmsDataStorage::isHappy() {
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return false;
tmElements_t tm, last;
if(now < day.lastMeterReadTime) return false;
if(now-day.lastMeterReadTime > 3600) return false;
breakTime(now, tm);
breakTime(day.lastMeterReadTime, last);
if(tm.Hour > last.Hour) return false;
if(now < month.lastMeterReadTime) return false;
if(now-month.lastMeterReadTime > 86400) return false;
breakTime(now, tm);
breakTime(month.lastMeterReadTime, last);
if(tm.Day > last.Day) return false;
return true;
}

View File

@@ -5,8 +5,6 @@
#include "RemoteDebug.h"
#include "Timezone.h"
#define EPOCH_2021_01_01 1609459200
struct DayDataPoints {
uint8_t version;
int16_t points[24];
@@ -38,6 +36,8 @@ public:
MonthDataPoints getMonthData();
bool setMonthData(MonthDataPoints&);
bool isHappy();
private:
Timezone* tz;
DayDataPoints day = {

View File

@@ -20,6 +20,8 @@ ADC_MODE(ADC_VCC);
#endif
#define WDT_TIMEOUT 60
#include "version.h"
#include "AmsToMqttBridge.h"
#include "AmsStorage.h"
#include "AmsDataStorage.h"
@@ -886,33 +888,44 @@ bool readHanPort() {
}
time_t now = time(nullptr);
if(now < EPOCH_2021_01_01 && data.getListType() >= 3) {
if(data.getMeterTimestamp() > EPOCH_2021_01_01) {
if(now < BUILD_EPOCH && data.getListType() >= 3) {
if(data.getMeterTimestamp() > BUILD_EPOCH) {
debugI("Using timestamp from meter");
now = data.getMeterTimestamp();
} else if(data.getPackageTimestamp() > EPOCH_2021_01_01) {
} else if(data.getPackageTimestamp() > BUILD_EPOCH) {
debugI("Using timestamp from meter (DLMS)");
now = data.getPackageTimestamp();
}
if(now > EPOCH_2021_01_01) {
if(now > BUILD_EPOCH) {
timeval tv { now, 0};
settimeofday(&tv, nullptr);
}
}
if(meterState.getListType() < 3 && now > EPOCH_2021_01_01) {
if(meterState.getListType() < 3 && now > BUILD_EPOCH) {
// TODO: Load an estimated value from dayplot
}
meterState.apply(data);
if(ds.update(&data)) {
debugI("Saving day plot");
ds.save();
}
if(ea.update(&data)) {
debugI("Saving energy accounting");
ea.save();
}
bool saveData = false;
if(!ds.isHappy() && now > BUILD_EPOCH) {
tmElements_t tm;
breakTime(now, tm);
if(tm.Minute == 0) {
saveData = ds.update(&data);
} else if(tm.Minute == 1) {
saveData = ds.update(&meterState);
}
if(saveData) {
debugI("Saving day plot");
ds.save();
}
}
}
delay(1);
return true;

View File

@@ -1,6 +1,7 @@
#include "EnergyAccounting.h"
#include "LittleFS.h"
#include "AmsStorage.h"
#include "version.h"
EnergyAccounting::EnergyAccounting(RemoteDebug* debugger) {
data.version = 1;
@@ -24,7 +25,7 @@ void EnergyAccounting::setTimezone(Timezone* tz) {
bool EnergyAccounting::update(AmsData* amsData) {
time_t now = time(nullptr);
if(now < EPOCH_2021_01_01) return false;
if(now < BUILD_EPOCH) return false;
if(tz == NULL) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(EnergyAccounting) Timezone is missing\n");
return false;
@@ -154,7 +155,7 @@ double EnergyAccounting::getCostThisHour() {
double EnergyAccounting::getUseToday() {
float ret = 0.0;
time_t now = time(nullptr);
if(now < EPOCH_2021_01_01) return 0;
if(now < BUILD_EPOCH) return 0;
tmElements_t local, utc;
breakTime(tz->toLocal(now), local);
for(int i = 0; i < local.Hour; i++) {
@@ -174,7 +175,7 @@ double EnergyAccounting::getCostYesterday() {
double EnergyAccounting::getUseThisMonth() {
time_t now = time(nullptr);
if(now < EPOCH_2021_01_01) return 0;
if(now < BUILD_EPOCH) return 0;
tmElements_t tm;
if(tz != NULL)
breakTime(tz->toLocal(now), tm);