Max hour average from more than one hour. Plus a fix for real time cost calculation

This commit is contained in:
Gunnar Skjold
2022-07-05 20:34:48 +02:00
parent aaa318e511
commit 32ba6c31f0
8 changed files with 181 additions and 53 deletions

View File

@@ -657,6 +657,14 @@ bool AmsConfiguration::hasConfig() {
configVersion = 0;
return false;
}
case 94:
configVersion = -1; // Prevent loop
if(relocateConfig94()) {
configVersion = 95;
} else {
configVersion = 0;
return false;
}
case EEPROM_CHECK_SUM:
return true;
default:
@@ -822,6 +830,18 @@ bool AmsConfiguration::relocateConfig93() {
return ret;
}
bool AmsConfiguration::relocateConfig94() {
EnergyAccountingConfig eac;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_ENERGYACCOUNTING_START, eac);
eac.hours = 1;
EEPROM.put(CONFIG_ENERGYACCOUNTING_START, eac);
EEPROM.put(EEPROM_CONFIG_ADDRESS, 95);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::save() {
EEPROM.begin(EEPROM_SIZE);
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM);

View File

@@ -4,7 +4,7 @@
#include "Arduino.h"
#define EEPROM_SIZE 1024*3
#define EEPROM_CHECK_SUM 94 // Used to check if config is stored. Change if structure changes
#define EEPROM_CHECK_SUM 95 // Used to check if config is stored. Change if structure changes
#define EEPROM_CONFIG_ADDRESS 0
#define EEPROM_TEMP_CONFIG_ADDRESS 2048
@@ -161,7 +161,8 @@ struct EntsoeConfig {
struct EnergyAccountingConfig {
uint8_t thresholds[10];
}; // 10
uint8_t hours;
}; // 11
struct TempSensorConfig {
uint8_t address[8];
@@ -264,7 +265,8 @@ private:
bool relocateConfig90(); // 2.0.0
bool relocateConfig91(); // 2.0.2
bool relocateConfig92(); // 2.0.3
bool relocateConfig93(); // 2.1 beta
bool relocateConfig93(); // 2.1.0
bool relocateConfig94(); // 2.1.4
void saveToFs();
bool loadFromFs(uint8_t version);

View File

@@ -383,17 +383,17 @@ bool AmsDataStorage::isDayHappy() {
tmElements_t tm, last;
if(now < day.lastMeterReadTime) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(AmsDataStorage) Day %lld < %lld\n", (int64_t) now, (int64_t) day.lastMeterReadTime);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Day %lld < %lld\n", (int64_t) now, (int64_t) day.lastMeterReadTime);
return false;
}
if(now-day.lastMeterReadTime > 3600) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(AmsDataStorage) Day %lld - %lld > 3600\n", (int64_t) now, (int64_t) day.lastMeterReadTime);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Day %lld - %lld > 3600\n", (int64_t) now, (int64_t) day.lastMeterReadTime);
return false;
}
breakTime(tz->toLocal(now), tm);
breakTime(tz->toLocal(day.lastMeterReadTime), last);
if(tm.Hour > last.Hour) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(AmsDataStorage) Day %d > %d\n", tm.Hour, last.Hour);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Day %d > %d\n", tm.Hour, last.Hour);
return false;
}
@@ -411,17 +411,17 @@ bool AmsDataStorage::isMonthHappy() {
tmElements_t tm, last;
if(now < month.lastMeterReadTime) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(AmsDataStorage) Month %lld < %lld\n", (int64_t) now, (int64_t) month.lastMeterReadTime);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Month %lld < %lld\n", (int64_t) now, (int64_t) month.lastMeterReadTime);
return false;
}
if(now-month.lastMeterReadTime > 86400) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(AmsDataStorage) Month %lld - %lld > 3600\n", (int64_t) now, (int64_t) month.lastMeterReadTime);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Month %lld - %lld > 3600\n", (int64_t) now, (int64_t) month.lastMeterReadTime);
return false;
}
breakTime(tz->toLocal(now), tm);
breakTime(tz->toLocal(month.lastMeterReadTime), last);
if(tm.Day > last.Day) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(AmsDataStorage) Month %d > %d\n", tm.Day, last.Day);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Month %d > %d\n", tm.Day, last.Day);
return false;
}

View File

@@ -371,7 +371,8 @@ void setup() {
config.setEnergyAccountingConfig(*eac);
config.ackEnergyAccountingChange();
}
ea.setup(&ds, eapi, eac);
ea.setup(&ds, eac);
ea.setEapi(eapi);
ws.setup(&config, &gpioConfig, &meterConfig, &meterState, &ds, &ea);
#if defined(ESP32)
@@ -517,6 +518,7 @@ void loop() {
if(config.getEntsoeConfig(entsoe) && strlen(entsoe.token) > 0) {
if(eapi == NULL) {
eapi = new EntsoeApi(&Debug);
ea.setEapi(eapi);
ws.setEntsoeApi(eapi);
}
eapi->setup(entsoe);
@@ -561,7 +563,7 @@ void loop() {
if(config.isEnergyAccountingChanged()) {
EnergyAccountingConfig *eac = ea.getConfig();
config.getEnergyAccountingConfig(*eac);
ea.setup(&ds, eapi, eac);
ea.setup(&ds, eac);
config.ackEnergyAccountingChange();
}
@@ -1020,14 +1022,14 @@ bool readHanPort() {
bool saveData = false;
if(!ds.isHappy() && now > BUILD_EPOCH) {
debugD("Its time to update data storage");
debugV("Its time to update data storage");
tmElements_t tm;
breakTime(now, tm);
if(tm.Minute == 0) {
debugD(" using actual data");
debugV(" using actual data");
saveData = ds.update(&data);
} else if(meterState.getListType() >= 3) {
debugD(" using estimated data");
debugV(" using estimated data");
saveData = ds.update(&meterState);
}
if(saveData) {
@@ -1672,6 +1674,7 @@ void configFileParse() {
} else if(strncmp(buf, "energyaccounting ", 17) == 0) {
int i = 0;
EnergyAccountingData ead = { 1 };
uint16_t *maxHours = NULL;
char * pch = strtok (buf+17," ");
while (pch != NULL) {
if(i == 1) {
@@ -1679,7 +1682,7 @@ void configFileParse() {
ead.month = val;
} else if(i == 2) {
double val = String(pch).toDouble();
ead.maxHour = val * 100;
ead.unused = val * 100;
} else if(i == 3) {
double val = String(pch).toDouble();
ead.costYesterday = val * 100;
@@ -1689,11 +1692,22 @@ void configFileParse() {
} else if(i == 5) {
double val = String(pch).toDouble();
ead.costLastMonth = val * 100;
} else if(i == 6) {
int val = String(pch).toInt();
if(val > 0) {
maxHours = new uint16_t[val];
}
} else {
double val = String(pch).toDouble();
maxHours[i-7] = val * 100;
}
pch = strtok (NULL, " ");
i++;
}
ea.setData(ead);
if(maxHours != NULL) {
ea.setMaxHours(maxHours);
}
}
memset(buf, 0, 1024);
}

View File

@@ -8,11 +8,25 @@ EnergyAccounting::EnergyAccounting(RemoteDebug* debugger) {
this->debugger = debugger;
}
void EnergyAccounting::setup(AmsDataStorage *ds, EntsoeApi *eapi, EnergyAccountingConfig *config) {
void EnergyAccounting::setup(AmsDataStorage *ds, EnergyAccountingConfig *config) {
this->ds = ds;
this->eapi = eapi;
this->config = config;
this->currentThresholdIdx = 0;
uint16_t *maxHours = new uint16_t[config->hours];
for(uint8_t i = 0; i < config->hours; i++) {
maxHours[i] = 0;
}
if(this->maxHours != NULL) {
for(uint8_t i = 0; i < sizeof(this->maxHours)/2 && i < config->hours; i++) {
maxHours[i] = this->maxHours[i];
}
delete(this->maxHours);
}
this->maxHours = maxHours;
}
void EnergyAccounting::setEapi(EntsoeApi *eapi) {
this->eapi = eapi;
}
EnergyAccountingConfig* EnergyAccounting::getConfig() {
@@ -24,6 +38,7 @@ void EnergyAccounting::setTimezone(Timezone* tz) {
}
bool EnergyAccounting::update(AmsData* amsData) {
if(config == NULL) return false;
time_t now = time(nullptr);
if(now < BUILD_EPOCH) return false;
if(tz == NULL) {
@@ -38,24 +53,50 @@ bool EnergyAccounting::update(AmsData* amsData) {
if(!init) {
currentHour = local.Hour;
currentDay = local.Day;
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Initializing data at %lld\n", (int64_t) now);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Initializing data at %lld\n", (int64_t) now);
if(!load()) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Unable to load existing data");
data = { 1, local.Month, 0, 0, 0, 0 };
if(calcDayUse()) ret = true;
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Unable to load existing data\n");
data = { 2, local.Month, 0, 0, 0, 0 };
for(uint8_t i = 0; i < config->hours; i++) {
maxHours[i] = 0;
break;
}
} else if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(EnergyAccounting) Loaded max calculated from %d hours with highest consumption\n", config->hours);
for(uint8_t i = 0; i < config->hours; i++) {
debugger->printf("(EnergyAccounting) hour %d: %d\n", i+1, maxHours[i]*10);
}
debugger->printf("(EnergyAccounting) Loaded cost yesterday: %d, this month: %d, last month: %d\n", data.costYesterday, data.costThisMonth, data.costLastMonth);
}
init = true;
}
if(!initPrice && eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Initializing prices at %lld\n", (int64_t) now);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Initializing prices at %lld\n", (int64_t) now);
calcDayCost();
}
if(local.Hour != currentHour && (amsData->getListType() >= 3 || local.Minute == 1)) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) New local hour %d\n", local.Hour);
if(calcDayUse()) ret = true;
tmElements_t oneHrAgo;
breakTime(now-3600, oneHrAgo);
uint32_t val = ds->getHourImport(oneHrAgo.Hour) / 10;
for(uint8_t i = 0; i < config->hours; i++) {
if(val > maxHours[i]) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Adding new max (%d) which is larger than %d\n", val*10, maxHours[i]*10);
maxHours[i] = val;
ret = true;
break;
}
}
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(EnergyAccounting) Current max calculated from %d hours with highest consumption\n", config->hours);
for(uint8_t i = 0; i < config->hours; i++) {
debugger->printf("(EnergyAccounting) hour %d: %d\n", i+1, maxHours[i]*10);
}
}
if(local.Hour > 0) {
calcDayCost();
}
@@ -77,7 +118,9 @@ bool EnergyAccounting::update(AmsData* amsData) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) New month %d\n", local.Month);
data.costLastMonth = data.costThisMonth;
data.costThisMonth = 0;
data.maxHour = 0;
for(uint8_t i = 0; i < config->hours; i++) {
maxHours[i] = 0;
}
data.month = local.Month;
currentThresholdIdx = 0;
ret = true;
@@ -108,24 +151,6 @@ bool EnergyAccounting::update(AmsData* amsData) {
return ret;
}
bool EnergyAccounting::calcDayUse() {
time_t now = time(nullptr);
tmElements_t local, utc;
breakTime(tz->toLocal(now), local);
bool ret = false;
uint8_t lim = local.Day == 1 ? local.Hour : 24;
for(int i = 0; i < lim; i++) {
breakTime(now - ((lim - i) * 3600), utc);
int16_t val = ds->getHourImport(utc.Hour) / 10.0;
if(val > data.maxHour) {
data.maxHour = val;
ret = true;
}
}
return ret;
}
void EnergyAccounting::calcDayCost() {
time_t now = time(nullptr);
tmElements_t local, utc;
@@ -203,7 +228,15 @@ uint8_t EnergyAccounting::getCurrentThreshold() {
}
float EnergyAccounting::getMonthMax() {
return data.maxHour / 100.0;
uint8_t count = 0;
uint32_t maxHour = 0.0;
for(uint8_t i = 0; i < config->hours; i++) {
if(maxHours[i] > 0) {
maxHour += maxHours[i];
count++;
}
}
return maxHour > 0 ? maxHour / count / 100.0 : 0.0;
}
bool EnergyAccounting::load() {
@@ -220,14 +253,31 @@ bool EnergyAccounting::load() {
char buf[file.size()];
file.readBytes(buf, file.size());
EnergyAccountingData* data = (EnergyAccountingData*) buf;
file.close();
if(data->version == 1) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Data version %d\n", data->version);
if(data->version == 2) {
memcpy(&this->data, data, sizeof(this->data));
uint8_t b = 0;
for(uint8_t i = sizeof(this->data); i < file.size(); i+=2) {
memcpy(&this->maxHours[b++], buf+i, 2);
if(b > config->hours) break;
}
ret = true;
} else if(data->version == 1) {
memcpy(&this->data, data, sizeof(this->data));
for(uint8_t i = 0; i < config->hours; i++) {
maxHours[i] = data->unused;
}
data->version = 2;
ret = true;
} else {
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf("(EnergyAccounting) Unknown version\n");
ret = false;
}
file.close();
} else {
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf("(EnergyAccounting) File not found\n");
}
LittleFS.end();
@@ -244,9 +294,12 @@ bool EnergyAccounting::save() {
}
{
File file = LittleFS.open(FILE_ENERGYACCOUNTING, "w");
char buf[sizeof(data)];
char buf[sizeof(data)+sizeof(this->maxHours)];
memcpy(buf, &data, sizeof(data));
for(unsigned long i = 0; i < sizeof(data); i++) {
for(uint8_t i = 0; i < config->hours; i++) {
memcpy(buf+sizeof(data)+(i*2), &this->maxHours[i], 2);
}
for(uint8_t i = 0; i < sizeof(buf); i++) {
file.write(buf[i]);
}
file.close();
@@ -263,3 +316,13 @@ EnergyAccountingData EnergyAccounting::getData() {
void EnergyAccounting::setData(EnergyAccountingData& data) {
this->data = data;
}
uint16_t * EnergyAccounting::getMaxHours() {
return maxHours;
}
void EnergyAccounting::setMaxHours(uint16_t * maxHours) {
for(uint8_t i = 0; i < config->hours; i++) {
this->maxHours[i] = maxHours[i];
}
}

View File

@@ -9,7 +9,7 @@
struct EnergyAccountingData {
uint8_t version;
uint8_t month;
uint16_t maxHour;
uint16_t unused;
uint16_t costYesterday;
uint16_t costThisMonth;
uint16_t costLastMonth;
@@ -18,7 +18,8 @@ struct EnergyAccountingData {
class EnergyAccounting {
public:
EnergyAccounting(RemoteDebug*);
void setup(AmsDataStorage *ds, EntsoeApi *eapi, EnergyAccountingConfig *config);
void setup(AmsDataStorage *ds, EnergyAccountingConfig *config);
void setEapi(EntsoeApi *eapi);
void setTimezone(Timezone*);
EnergyAccountingConfig* getConfig();
bool update(AmsData* amsData);
@@ -38,6 +39,8 @@ public:
EnergyAccountingData getData();
void setData(EnergyAccountingData&);
uint16_t * getMaxHours();
void setMaxHours(uint16_t * maxHours);
private:
RemoteDebug* debugger = NULL;
@@ -50,9 +53,9 @@ private:
uint8_t currentHour = 0, currentDay = 0, currentThresholdIdx = 0;
double use, costHour, costDay;
EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 };
uint16_t *maxHours;
bool load();
bool calcDayUse();
void calcDayCost();
};

View File

@@ -606,6 +606,7 @@ void AmsWebServer::configThresholdsHtml() {
for(int i = 0; i < 9; i++) {
html.replace("{t" + String(i) + "}", String(config->thresholds[i]));
}
html.replace("{h}", String(config->hours));
server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN);
server.send_P(200, MIME_HTML, HEAD_HTML);
@@ -1353,6 +1354,7 @@ void AmsWebServer::handleSave() {
eac.thresholds[6] = server.arg("t6").toInt();
eac.thresholds[7] = server.arg("t7").toInt();
eac.thresholds[8] = server.arg("t8").toInt();
eac.hours = server.arg("h").toInt();
config->setEnergyAccountingConfig(eac);
}
@@ -2268,7 +2270,7 @@ void AmsWebServer::configFileDownload() {
EnergyAccountingConfig eac;
config->getEnergyAccountingConfig(eac);
if(eac.thresholds[9] > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, (char*) F("thresholds %d %d %d %d %d %d %d %d %d %d\n"),
if(eac.thresholds[9] > 0) server.sendContent(buf, snprintf_P(buf, BufferSize, (char*) F("thresholds %d %d %d %d %d %d %d %d %d %d %d\n"),
eac.thresholds[0],
eac.thresholds[1],
eac.thresholds[2],
@@ -2278,7 +2280,8 @@ void AmsWebServer::configFileDownload() {
eac.thresholds[6],
eac.thresholds[7],
eac.thresholds[8],
eac.thresholds[9]
eac.thresholds[9],
eac.hours
));
}
@@ -2424,15 +2427,25 @@ void AmsWebServer::configFileDownload() {
}
if(ea != NULL) {
EnergyAccountingConfig eac;
config->getEnergyAccountingConfig(eac);
EnergyAccountingData ead = ea->getData();
server.sendContent(buf, snprintf_P(buf, BufferSize, (char*) F("energyaccounting %d %d %.2f %.2f %.2f %.2f"),
ead.version,
ead.month,
ead.maxHour / 100.0,
0,
ead.costYesterday / 100.0,
ead.costThisMonth / 100.0,
ead.costLastMonth / 100.0
));
if(eac.hours > 0) {
uint16_t *maxHours = ea->getMaxHours();
server.sendContent(buf, snprintf(buf, BufferSize, " %d", eac.hours));
for(int i = 0; i < eac.hours; i++) {
server.sendContent(buf, snprintf(buf, BufferSize, " %d", maxHours[i]));
}
}
server.sendContent("\n");
}
}