From 785bec0269507d816414b514f797029e1c7f6758 Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Sat, 12 Mar 2022 08:26:24 +0100 Subject: [PATCH] Added export to graph --- src/AmsDataStorage.cpp | 133 +++++++++------ src/AmsDataStorage.h | 32 ++-- src/EnergyAccounting.cpp | 8 +- src/web/AmsWebServer.cpp | 354 +++++++++++++++++++++++++-------------- web/application.js | 37 ++-- web/dayplot.json | 72 +++++--- web/monthplot.json | 93 ++++++---- 7 files changed, 477 insertions(+), 252 deletions(-) diff --git a/src/AmsDataStorage.cpp b/src/AmsDataStorage.cpp index ded49ef9..d303bb21 100644 --- a/src/AmsDataStorage.cpp +++ b/src/AmsDataStorage.cpp @@ -5,8 +5,8 @@ #include "version.h" AmsDataStorage::AmsDataStorage(RemoteDebug* debugger) { - day.version = 3; - month.version = 4; + day.version = 4; + month.version = 5; this->debugger = debugger; } @@ -71,7 +71,8 @@ bool AmsDataStorage::update(AmsData* data) { if(debugger->isActive(RemoteDebug::VERBOSE)) { debugger->printf("(AmsDataStorage) Clearing hour: %d\n", i); } - setHour(i, 0); + setHourImport(i, 0); + setHourExport(i, 0); } } @@ -93,7 +94,8 @@ bool AmsDataStorage::update(AmsData* data) { if(debugger->isActive(RemoteDebug::VERBOSE)) { debugger->printf("(AmsDataStorage) Clearing day: %d\n", i); } - setDay(i, 0); + setDayImport(i, 0); + setDayExport(i, 0); } } @@ -114,21 +116,24 @@ bool AmsDataStorage::update(AmsData* data) { debugger->printf("(AmsDataStorage) Too long since last day update, clearing data\n"); } for(int i = 0; i<24; i++) { - setHour(i, 0); + setHourImport(i, 0); + setHourExport(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); + uint32_t imp = (data->getActiveImportCounter() * 1000) - day.activeImport; + uint32_t exp = (data->getActiveExportCounter() * 1000) - day.activeExport; + setHourImport(utcYesterday.Hour, imp); + setHourExport(utcYesterday.Hour, exp); - 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 - %d\n", ltzYesterDay.Hour, imp, exp); 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); + uint32_t im = (data->getActiveImportCounter() * 1000) - day.activeImport; + uint32_t ex = (data->getActiveExportCounter() * 1000) - day.activeExport; float ipm = im / mins; float epm = ex / mins; @@ -146,15 +151,17 @@ bool AmsDataStorage::update(AmsData* data) { if(minutes < 1) break; breakTime(day.lastMeterReadTime, last); - float val = ((ipm * minutes) - (epm * minutes)); - setHour(last.Hour, val); + float imp = (ipm * minutes); + float exp = (epm * minutes); + setHourImport(last.Hour, imp); + setHourExport(last.Hour, exp); if(debugger->isActive(RemoteDebug::INFO)) { - debugger->printf("(AmsDataStorage) Estimated usage for hour %u: %.1f (%lld)\n", last.Hour, val, (int64_t) cur); + debugger->printf("(AmsDataStorage) Estimated usage for hour %u: %.1f - %.1f (%lld)\n", last.Hour, imp, exp, (int64_t) cur); } - day.activeImport += ipm * minutes; - day.activeExport += epm * minutes; + day.activeImport += imp; + day.activeExport += exp; day.lastMeterReadTime = cur; } } @@ -171,33 +178,23 @@ bool AmsDataStorage::update(AmsData* data) { debugger->printf("(AmsDataStorage) Too long since last month update, clearing data\n"); } for(int i = 1; i<=31; i++) { - setDay(i, 0); + setDayImport(i, 0); + setDayExport(i, 0); } } else if(now - month.lastMeterReadTime < 86500 && now - month.lastMeterReadTime > 86300) { - int32_t val = (month.activeImport == 0 ? 0 : ((data->getActiveImportCounter() * 1000) - month.activeImport) - ((data->getActiveExportCounter() * 1000) - month.activeExport)); + int32_t imp = (data->getActiveImportCounter() * 1000) - month.activeImport; + int32_t exp = (data->getActiveExportCounter() * 1000) - month.activeExport; if(debugger->isActive(RemoteDebug::INFO)) { - debugger->printf("(AmsDataStorage) Usage for day %d: %d\n", ltzYesterDay.Day, val); + debugger->printf("(AmsDataStorage) Usage for day %d: %d - %d\n", ltzYesterDay.Day, imp, exp); } - setDay(ltzYesterDay.Day, val); + setDayImport(ltzYesterDay.Day, imp); + setDayExport(ltzYesterDay.Day, exp); month.activeImport = data->getActiveImportCounter() * 1000; month.activeExport = data->getActiveExportCounter() * 1000; month.lastMeterReadTime = now; } else { - float hrs = (now - month.lastMeterReadTime) / 3600.0; - uint16_t im = ((data->getActiveImportCounter() * 1000) - month.activeImport); - uint16_t ex = ((data->getActiveExportCounter() * 1000) - month.activeExport); - float iph = im / hrs; - float eph = ex / hrs; - - // There is something wacky going on when it ends up here. The total value (im) is way way lower than it should be, which in - // turn causes low values for all estimates. And then when it returns to the normal case above, the value is waaay higher. - - if(debugger->isActive(RemoteDebug::DEBUG)) { - debugger->printf("(AmsDataStorage) Since last month update, hours: %.1f, import: %d (%.2f/hr), export: %d (%.2f/hr)\n", hrs, im, iph, ex, eph); - } - // Make sure last month read is at midnight tmElements_t last; breakTime(tz->toLocal(month.lastMeterReadTime), last); @@ -206,21 +203,33 @@ bool AmsDataStorage::update(AmsData* data) { debugger->printf("(AmsDataStorage) Last month read after resetting to midnight: %lld\n", (int64_t) month.lastMeterReadTime); } + float hrs = (now - month.lastMeterReadTime) / 3600.0; + uint32_t im = (data->getActiveImportCounter() * 1000) - month.activeImport; + uint32_t ex = (data->getActiveExportCounter() * 1000) - month.activeExport; + float iph = im / hrs; + float eph = ex / hrs; + + if(debugger->isActive(RemoteDebug::DEBUG)) { + debugger->printf("(AmsDataStorage) Since last month update, hours: %.1f, import: %d (%.2f/hr), export: %d (%.2f/hr)\n", hrs, im, iph, ex, eph); + } + time_t stopAt = now - (ltz.Hour * 3600) - (ltz.Minute * 60) - ltz.Second; while(month.lastMeterReadTime < stopAt) { time_t cur = min(month.lastMeterReadTime + 86400, stopAt); uint8_t hours = round((cur - month.lastMeterReadTime) / 3600.0); breakTime(tz->toLocal(month.lastMeterReadTime), last); - float val = ((iph * hours) - (eph * hours)); - setDay(last.Day, val); + float imp = (iph * hours); + float exp = (eph * hours); + setDayImport(last.Day, imp); + setDayExport(last.Day, exp); if(debugger->isActive(RemoteDebug::INFO)) { - debugger->printf("(AmsDataStorage) Estimated usage for day %u: %.1f (%lld)\n", last.Day, val, (int64_t) cur); + debugger->printf("(AmsDataStorage) Estimated usage for day %u: %.1f - %.1f (%lld)\n", last.Day, imp, exp, (int64_t) cur); } - month.activeImport += iph * hours; - month.activeExport += eph * hours; + month.activeImport += imp; + month.activeExport += exp; month.lastMeterReadTime = cur; } } @@ -229,24 +238,44 @@ bool AmsDataStorage::update(AmsData* data) { return ret; } -void AmsDataStorage::setHour(uint8_t hour, int32_t val) { +void AmsDataStorage::setHourImport(uint8_t hour, int32_t val) { if(hour < 0 || hour > 24) return; - day.points[hour] = val / 10; + day.hImport[hour] = val / 10; } -int32_t AmsDataStorage::getHour(uint8_t hour) { +int32_t AmsDataStorage::getHourImport(uint8_t hour) { if(hour < 0 || hour > 24) return 0; - return day.points[hour] * 10; + return day.hImport[hour] * 10; } -void AmsDataStorage::setDay(uint8_t day, int32_t val) { +void AmsDataStorage::setHourExport(uint8_t hour, int32_t val) { + if(hour < 0 || hour > 24) return; + day.hExport[hour] = val / 10; +} + +int32_t AmsDataStorage::getHourExport(uint8_t hour) { + if(hour < 0 || hour > 24) return 0; + return day.hExport[hour] * 10; +} + +void AmsDataStorage::setDayImport(uint8_t day, int32_t val) { if(day < 1 || day > 31) return; - month.points[day-1] = val / 10; + month.dImport[day-1] = val / 10; } -int32_t AmsDataStorage::getDay(uint8_t day) { +int32_t AmsDataStorage::getDayImport(uint8_t day) { if(day < 1 || day > 31) return 0; - return (month.points[day-1] * 10); + return (month.dImport[day-1] * 10); +} + +void AmsDataStorage::setDayExport(uint8_t day, int32_t val) { + if(day < 1 || day > 31) return; + month.dExport[day-1] = val / 10; +} + +int32_t AmsDataStorage::getDayExport(uint8_t day) { + if(day < 1 || day > 31) return 0; + return (month.dExport[day-1] * 10); } bool AmsDataStorage::load() { @@ -320,17 +349,27 @@ MonthDataPoints AmsDataStorage::getMonthData() { } bool AmsDataStorage::setDayData(DayDataPoints& day) { - if(day.version == 3) { + if(day.version == 4) { this->day = day; return true; + } else if(day.version == 3) { + this->day = day; + for(uint8_t i = 0; i < 24; i++) this->day.hExport[i] = 0; + this->day.version = 4; + return true; } return false; } bool AmsDataStorage::setMonthData(MonthDataPoints& month) { - if(month.version == 4) { + if(month.version == 5) { this->month = month; return true; + } else if(month.version == 4) { + this->month = month; + for(uint8_t i = 0; i < 31; i++) this->month.dExport[i] = 0; + this->month.version = 5; + return true; } return false; } diff --git a/src/AmsDataStorage.h b/src/AmsDataStorage.h index a566b97e..f113c6cc 100644 --- a/src/AmsDataStorage.h +++ b/src/AmsDataStorage.h @@ -7,27 +7,31 @@ struct DayDataPoints { uint8_t version; - int16_t points[24]; + int16_t hImport[24]; time_t lastMeterReadTime; uint32_t activeImport; uint32_t activeExport; -}; // 37 bytes + int16_t hExport[24]; +}; // 112 bytes struct MonthDataPoints { uint8_t version; - int16_t points[31]; + int16_t dImport[31]; time_t lastMeterReadTime; uint32_t activeImport; uint32_t activeExport; -}; // 75 bytes + int16_t dExport[31]; +}; // 141 bytes class AmsDataStorage { public: AmsDataStorage(RemoteDebug*); void setTimezone(Timezone*); bool update(AmsData*); - int32_t getHour(uint8_t); - int32_t getDay(uint8_t); + int32_t getHourImport(uint8_t); + int32_t getHourExport(uint8_t); + int32_t getDayImport(uint8_t); + int32_t getDayExport(uint8_t); bool load(); bool save(); @@ -43,14 +47,22 @@ public: private: Timezone* tz; DayDataPoints day = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; MonthDataPoints month = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; RemoteDebug* debugger; - void setHour(uint8_t, int32_t); - void setDay(uint8_t, int32_t); + void setHourImport(uint8_t, int32_t); + void setHourExport(uint8_t, int32_t); + void setDayImport(uint8_t, int32_t); + void setDayExport(uint8_t, int32_t); }; #endif diff --git a/src/EnergyAccounting.cpp b/src/EnergyAccounting.cpp index 12b757bf..621763ea 100644 --- a/src/EnergyAccounting.cpp +++ b/src/EnergyAccounting.cpp @@ -122,7 +122,7 @@ bool EnergyAccounting::calcDayUse() { 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->getHour(utc.Hour) / 10.0; + int16_t val = ds->getHourImport(utc.Hour) / 10.0; if(val > data.maxHour) { data.maxHour = val; ret = true; @@ -142,7 +142,7 @@ void EnergyAccounting::calcDayCost() { float price = eapi->getValueForHour(i - local.Hour); if(price == ENTSOE_NO_VALUE) break; breakTime(now - ((local.Hour - i) * 3600), utc); - int16_t wh = ds->getHour(utc.Hour); + int16_t wh = ds->getHourImport(utc.Hour); costDay += price * (wh / 1000.0); } initPrice = true; @@ -165,7 +165,7 @@ double EnergyAccounting::getUseToday() { breakTime(tz->toLocal(now), local); for(int i = 0; i < local.Hour; i++) { breakTime(now - ((local.Hour - i) * 3600), utc); - ret += ds->getHour(utc.Hour) / 1000.0; + ret += ds->getHourImport(utc.Hour) / 1000.0; } return ret + getUseThisHour(); } @@ -188,7 +188,7 @@ double EnergyAccounting::getUseThisMonth() { breakTime(now, tm); float ret = 0; for(int i = 0; i < tm.Day; i++) { - ret += ds->getDay(i) / 1000.0; + ret += ds->getDayImport(i) / 1000.0; } return ret + getUseToday(); } diff --git a/src/web/AmsWebServer.cpp b/src/web/AmsWebServer.cpp index fbf144fd..a1bafc29 100644 --- a/src/web/AmsWebServer.cpp +++ b/src/web/AmsWebServer.cpp @@ -755,30 +755,54 @@ void AmsWebServer::dayplotJson() { notFound(); } else { snprintf_P(buf, BufferSize, DAYPLOT_JSON, - ds->getHour(0) / 1000.0, - ds->getHour(1) / 1000.0, - ds->getHour(2) / 1000.0, - ds->getHour(3) / 1000.0, - ds->getHour(4) / 1000.0, - ds->getHour(5) / 1000.0, - ds->getHour(6) / 1000.0, - ds->getHour(7) / 1000.0, - ds->getHour(8) / 1000.0, - ds->getHour(9) / 1000.0, - ds->getHour(10) / 1000.0, - ds->getHour(11) / 1000.0, - ds->getHour(12) / 1000.0, - ds->getHour(13) / 1000.0, - ds->getHour(14) / 1000.0, - ds->getHour(15) / 1000.0, - ds->getHour(16) / 1000.0, - ds->getHour(17) / 1000.0, - ds->getHour(18) / 1000.0, - ds->getHour(19) / 1000.0, - ds->getHour(20) / 1000.0, - ds->getHour(21) / 1000.0, - ds->getHour(22) / 1000.0, - ds->getHour(23) / 1000.0 + ds->getHourImport(0) / 1000.0, + ds->getHourImport(1) / 1000.0, + ds->getHourImport(2) / 1000.0, + ds->getHourImport(3) / 1000.0, + ds->getHourImport(4) / 1000.0, + ds->getHourImport(5) / 1000.0, + ds->getHourImport(6) / 1000.0, + ds->getHourImport(7) / 1000.0, + ds->getHourImport(8) / 1000.0, + ds->getHourImport(9) / 1000.0, + ds->getHourImport(10) / 1000.0, + ds->getHourImport(11) / 1000.0, + ds->getHourImport(12) / 1000.0, + ds->getHourImport(13) / 1000.0, + ds->getHourImport(14) / 1000.0, + ds->getHourImport(15) / 1000.0, + ds->getHourImport(16) / 1000.0, + ds->getHourImport(17) / 1000.0, + ds->getHourImport(18) / 1000.0, + ds->getHourImport(19) / 1000.0, + ds->getHourImport(20) / 1000.0, + ds->getHourImport(21) / 1000.0, + ds->getHourImport(22) / 1000.0, + ds->getHourImport(23) / 1000.0, + ds->getHourExport(0) / 1000.0, + ds->getHourExport(1) / 1000.0, + ds->getHourExport(2) / 1000.0, + ds->getHourExport(3) / 1000.0, + ds->getHourExport(4) / 1000.0, + ds->getHourExport(5) / 1000.0, + ds->getHourExport(6) / 1000.0, + ds->getHourExport(7) / 1000.0, + ds->getHourExport(8) / 1000.0, + ds->getHourExport(9) / 1000.0, + ds->getHourExport(10) / 1000.0, + ds->getHourExport(11) / 1000.0, + ds->getHourExport(12) / 1000.0, + ds->getHourExport(13) / 1000.0, + ds->getHourExport(14) / 1000.0, + ds->getHourExport(15) / 1000.0, + ds->getHourExport(16) / 1000.0, + ds->getHourExport(17) / 1000.0, + ds->getHourExport(18) / 1000.0, + ds->getHourExport(19) / 1000.0, + ds->getHourExport(20) / 1000.0, + ds->getHourExport(21) / 1000.0, + ds->getHourExport(22) / 1000.0, + ds->getHourExport(23) / 1000.0 ); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); @@ -800,37 +824,68 @@ void AmsWebServer::monthplotJson() { notFound(); } else { snprintf_P(buf, BufferSize, MONTHPLOT_JSON, - ds->getDay(1) / 1000.0, - ds->getDay(2) / 1000.0, - ds->getDay(3) / 1000.0, - ds->getDay(4) / 1000.0, - ds->getDay(5) / 1000.0, - ds->getDay(6) / 1000.0, - ds->getDay(7) / 1000.0, - ds->getDay(8) / 1000.0, - ds->getDay(9) / 1000.0, - ds->getDay(10) / 1000.0, - ds->getDay(11) / 1000.0, - ds->getDay(12) / 1000.0, - ds->getDay(13) / 1000.0, - ds->getDay(14) / 1000.0, - ds->getDay(15) / 1000.0, - ds->getDay(16) / 1000.0, - ds->getDay(17) / 1000.0, - ds->getDay(18) / 1000.0, - ds->getDay(19) / 1000.0, - ds->getDay(20) / 1000.0, - ds->getDay(21) / 1000.0, - ds->getDay(22) / 1000.0, - ds->getDay(23) / 1000.0, - ds->getDay(24) / 1000.0, - ds->getDay(25) / 1000.0, - ds->getDay(26) / 1000.0, - ds->getDay(27) / 1000.0, - ds->getDay(28) / 1000.0, - ds->getDay(29) / 1000.0, - ds->getDay(30) / 1000.0, - ds->getDay(31) / 1000.0 + ds->getDayImport(1) / 1000.0, + ds->getDayImport(2) / 1000.0, + ds->getDayImport(3) / 1000.0, + ds->getDayImport(4) / 1000.0, + ds->getDayImport(5) / 1000.0, + ds->getDayImport(6) / 1000.0, + ds->getDayImport(7) / 1000.0, + ds->getDayImport(8) / 1000.0, + ds->getDayImport(9) / 1000.0, + ds->getDayImport(10) / 1000.0, + ds->getDayImport(11) / 1000.0, + ds->getDayImport(12) / 1000.0, + ds->getDayImport(13) / 1000.0, + ds->getDayImport(14) / 1000.0, + ds->getDayImport(15) / 1000.0, + ds->getDayImport(16) / 1000.0, + ds->getDayImport(17) / 1000.0, + ds->getDayImport(18) / 1000.0, + ds->getDayImport(19) / 1000.0, + ds->getDayImport(20) / 1000.0, + ds->getDayImport(21) / 1000.0, + ds->getDayImport(22) / 1000.0, + ds->getDayImport(23) / 1000.0, + ds->getDayImport(24) / 1000.0, + ds->getDayImport(25) / 1000.0, + ds->getDayImport(26) / 1000.0, + ds->getDayImport(27) / 1000.0, + ds->getDayImport(28) / 1000.0, + ds->getDayImport(29) / 1000.0, + ds->getDayImport(30) / 1000.0, + ds->getDayImport(31) / 1000.0, + ds->getDayExport(1) / 1000.0, + ds->getDayExport(2) / 1000.0, + ds->getDayExport(3) / 1000.0, + ds->getDayExport(4) / 1000.0, + ds->getDayExport(5) / 1000.0, + ds->getDayExport(6) / 1000.0, + ds->getDayExport(7) / 1000.0, + ds->getDayExport(8) / 1000.0, + ds->getDayExport(9) / 1000.0, + ds->getDayExport(10) / 1000.0, + ds->getDayExport(11) / 1000.0, + ds->getDayExport(12) / 1000.0, + ds->getDayExport(13) / 1000.0, + ds->getDayExport(14) / 1000.0, + ds->getDayExport(15) / 1000.0, + ds->getDayExport(16) / 1000.0, + ds->getDayExport(17) / 1000.0, + ds->getDayExport(18) / 1000.0, + ds->getDayExport(19) / 1000.0, + ds->getDayExport(20) / 1000.0, + ds->getDayExport(21) / 1000.0, + ds->getDayExport(22) / 1000.0, + ds->getDayExport(23) / 1000.0, + ds->getDayExport(24) / 1000.0, + ds->getDayExport(25) / 1000.0, + ds->getDayExport(26) / 1000.0, + ds->getDayExport(27) / 1000.0, + ds->getDayExport(28) / 1000.0, + ds->getDayExport(29) / 1000.0, + ds->getDayExport(30) / 1000.0, + ds->getDayExport(31) / 1000.0 ); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE); @@ -2155,83 +2210,138 @@ void AmsWebServer::configFileDownload() { if(ds != NULL) { DayDataPoints day = ds->getDayData(); - server.sendContent(buf, snprintf_P(buf, BufferSize, (char*) F("dayplot %d %lu %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"), + server.sendContent(buf, snprintf_P(buf, BufferSize, (char*) F("dayplot %d %lld %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"), day.version, day.lastMeterReadTime, day.activeImport, - ds->getHour(0), - ds->getHour(1), - ds->getHour(2), - ds->getHour(3), - ds->getHour(4), - ds->getHour(5), - ds->getHour(6), - ds->getHour(7), - ds->getHour(8), - ds->getHour(9), - ds->getHour(10), - ds->getHour(11), - ds->getHour(12), - ds->getHour(13), - ds->getHour(14), - ds->getHour(15), - ds->getHour(16), - ds->getHour(17), - ds->getHour(18), - ds->getHour(19), - ds->getHour(20), - ds->getHour(21), - ds->getHour(22), - ds->getHour(23) + ds->getHourImport(0), + ds->getHourImport(1), + ds->getHourImport(2), + ds->getHourImport(3), + ds->getHourImport(4), + ds->getHourImport(5), + ds->getHourImport(6), + ds->getHourImport(7), + ds->getHourImport(8), + ds->getHourImport(9), + ds->getHourImport(10), + ds->getHourImport(11), + ds->getHourImport(12), + ds->getHourImport(13), + ds->getHourImport(14), + ds->getHourImport(15), + ds->getHourImport(16), + ds->getHourImport(17), + ds->getHourImport(18), + ds->getHourImport(19), + ds->getHourImport(20), + ds->getHourImport(21), + ds->getHourImport(22), + ds->getHourImport(23) )); if(day.activeExport > 0) { - server.sendContent(buf, snprintf(buf, BufferSize, " %lu\n", - day.activeExport + server.sendContent(buf, snprintf(buf, BufferSize, " %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + day.activeExport, + ds->getHourExport(0), + ds->getHourExport(1), + ds->getHourExport(2), + ds->getHourExport(3), + ds->getHourExport(4), + ds->getHourExport(5), + ds->getHourExport(6), + ds->getHourExport(7), + ds->getHourExport(8), + ds->getHourExport(9), + ds->getHourExport(10), + ds->getHourExport(11), + ds->getHourExport(12), + ds->getHourExport(13), + ds->getHourExport(14), + ds->getHourExport(15), + ds->getHourExport(16), + ds->getHourExport(17), + ds->getHourExport(18), + ds->getHourExport(19), + ds->getHourExport(20), + ds->getHourExport(21), + ds->getHourExport(22), + ds->getHourExport(23) )); } else { server.sendContent("\n"); } MonthDataPoints month = ds->getMonthData(); - server.sendContent(buf, snprintf_P(buf, BufferSize, (char*) F("monthplot %d %lu %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"), + server.sendContent(buf, snprintf_P(buf, BufferSize, (char*) F("monthplot %d %lld %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"), month.version, month.lastMeterReadTime, month.activeImport, - ds->getDay(1), - ds->getDay(2), - ds->getDay(3), - ds->getDay(4), - ds->getDay(5), - ds->getDay(6), - ds->getDay(7), - ds->getDay(8), - ds->getDay(9), - ds->getDay(10), - ds->getDay(11), - ds->getDay(12), - ds->getDay(13), - ds->getDay(14), - ds->getDay(15), - ds->getDay(16), - ds->getDay(17), - ds->getDay(18), - ds->getDay(19), - ds->getDay(20), - ds->getDay(21), - ds->getDay(22), - ds->getDay(23), - ds->getDay(24), - ds->getDay(25), - ds->getDay(26), - ds->getDay(27), - ds->getDay(28), - ds->getDay(29), - ds->getDay(30), - ds->getDay(31) + ds->getDayImport(1), + ds->getDayImport(2), + ds->getDayImport(3), + ds->getDayImport(4), + ds->getDayImport(5), + ds->getDayImport(6), + ds->getDayImport(7), + ds->getDayImport(8), + ds->getDayImport(9), + ds->getDayImport(10), + ds->getDayImport(11), + ds->getDayImport(12), + ds->getDayImport(13), + ds->getDayImport(14), + ds->getDayImport(15), + ds->getDayImport(16), + ds->getDayImport(17), + ds->getDayImport(18), + ds->getDayImport(19), + ds->getDayImport(20), + ds->getDayImport(21), + ds->getDayImport(22), + ds->getDayImport(23), + ds->getDayImport(24), + ds->getDayImport(25), + ds->getDayImport(26), + ds->getDayImport(27), + ds->getDayImport(28), + ds->getDayImport(29), + ds->getDayImport(30), + ds->getDayImport(31) )); if(month.activeExport > 0) { - server.sendContent(buf, snprintf_P(buf, BufferSize, " %lu\n", - month.activeExport + server.sendContent(buf, snprintf_P(buf, BufferSize, " %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + month.activeExport, + ds->getDayExport(1), + ds->getDayExport(2), + ds->getDayExport(3), + ds->getDayExport(4), + ds->getDayExport(5), + ds->getDayExport(6), + ds->getDayExport(7), + ds->getDayExport(8), + ds->getDayExport(9), + ds->getDayExport(10), + ds->getDayExport(11), + ds->getDayExport(12), + ds->getDayExport(13), + ds->getDayExport(14), + ds->getDayExport(15), + ds->getDayExport(16), + ds->getDayExport(17), + ds->getDayExport(18), + ds->getDayExport(19), + ds->getDayExport(20), + ds->getDayExport(21), + ds->getDayExport(22), + ds->getDayExport(23), + ds->getDayExport(24), + ds->getDayExport(25), + ds->getDayExport(26), + ds->getDayExport(27), + ds->getDayExport(28), + ds->getDayExport(29), + ds->getDayExport(30), + ds->getDayExport(31) )); } else { server.sendContent("\n"); @@ -2436,7 +2546,7 @@ void AmsWebServer::configFileParse() { } } else if(strncmp(buf, "dayplot ", 8) == 0 && ds != NULL) { int i = 0; - DayDataPoints day = { 3 }; // Use a version we know the multiplier of the data points + DayDataPoints day = { 4 }; // Use a version we know the multiplier of the data points char * pch = strtok (buf+8," "); while (pch != NULL) { long val = String(pch).toInt(); @@ -2445,11 +2555,11 @@ void AmsWebServer::configFileParse() { } else if(i == 2) { day.activeImport = val; } else if(i > 2 && i < 27) { - day.points[i-3] = val / 10; + day.hImport[i-3] = val / 10; } else if(i == 27) { day.activeExport = val; } else if(i > 27 && i < 52) { - // TODO: Export points + day.hExport[i-28] = val / 10; } pch = strtok (NULL, " "); @@ -2458,7 +2568,7 @@ void AmsWebServer::configFileParse() { ds->setDayData(day); } else if(strncmp(buf, "monthplot ", 10) == 0 && ds != NULL) { int i = 0; - MonthDataPoints month = { 4 }; // Use a version we know the multiplier of the data points + MonthDataPoints month = { 5 }; // Use a version we know the multiplier of the data points char * pch = strtok (buf+10," "); while (pch != NULL) { long val = String(pch).toInt(); @@ -2467,11 +2577,11 @@ void AmsWebServer::configFileParse() { } else if(i == 2) { month.activeImport = val; } else if(i > 2 && i < 34) { - month.points[i-3] = val / 10; + month.dImport[i-3] = val / 10; } else if(i == 34) { month.activeExport = val; } else if(i > 34 && i < 66) { - // TODO: Export points + month.dExport[i-35] = val / 10; } pch = strtok (NULL, " "); diff --git a/web/application.js b/web/application.js index 1fdc878e..40fa0e84 100644 --- a/web/application.js +++ b/web/application.js @@ -31,6 +31,7 @@ var eo = { titleTextStyle: { fontSize: 14 }, + colors: ['#6f42c1', '#6f42c1'], backgroundColor: { fill:'transparent' }, bar: { groupWidth: '90%' }, legend: { position: 'none' }, @@ -40,6 +41,7 @@ var eo = { }, tooltip: { trigger: 'none'}, enableInteractivity: false, + isStacked: true }; // Month plot @@ -50,6 +52,7 @@ var mo = { titleTextStyle: { fontSize: 14 }, + colors: ['#6f42c1', '#6f42c1'], backgroundColor: { fill:'transparent' }, bar: { groupWidth: '90%' }, legend: { position: 'none' }, @@ -59,6 +62,7 @@ var mo = { }, tooltip: { trigger: 'none'}, enableInteractivity: false, + isStacked: true }; // Voltage plot @@ -468,24 +472,27 @@ var drawDay = function() { timeout: 30000, dataType: 'json', }).done(function(json) { - data = [['Hour','kWh', { role: 'style' }, { role: 'annotation' }]]; + data = [['Hour', 'Import', { role: 'style' }, { role: 'annotation' }, 'Export', { role: 'style' }]]; var r = 1; var hour = moment.utc().hours(); var offset = moment().utcOffset()/60; var min = 0; for(var i = hour; i<24; i++) { - var val = json["h"+zeropad(i)]; - data[r++] = [zeropad((i+offset)%24), val, "color: #6f42c1;opacity: 0.9;", val.toFixed(1)]; - Math.min(0, val); + var imp = json["i"+zeropad(i)]; + var exp = json["e"+zeropad(i)]; + data[r++] = [zeropad((i+offset)%24), imp, "opacity: 0.9;", exp == 0 ? imp.toFixed(1) : imp.toFixed(1) + '\n' + -exp.toFixed(1), exp == 0 ? 0 : -exp, "opacity: 0.9;"]; + min = Math.min(0, -exp); }; for(var i = 0; i < hour; i++) { - var val = json["h"+zeropad(i)]; - data[r++] = [zeropad((i+offset)%24), val, "color: #6f42c1;opacity: 0.9;", val.toFixed(1)]; - Math.min(0, val); + var imp = json["i"+zeropad(i)]; + var exp = json["e"+zeropad(i)]; + data[r++] = [zeropad((i+offset)%24), imp, "opacity: 0.9;", exp == 0 ? imp.toFixed(1) : imp.toFixed(1) + '\n' + -exp.toFixed(1), exp == 0 ? 0 : -exp, "opacity: 0.9;"]; + min = Math.min(0, -exp); }; ea = google.visualization.arrayToDataTable(data); if(min == 0) eo.vAxis.minValue = 0; + ep.draw(ea, eo); setTimeout(drawDay, (61-moment().minute())*60000); @@ -498,20 +505,22 @@ var drawMonth = function() { timeout: 30000, dataType: 'json', }).done(function(json) { - data = [['Day','kWh', { role: 'style' }, { role: 'annotation' }]]; + data = [['Hour', 'Import', { role: 'style' }, { role: 'annotation' }, 'Export', { role: 'style' }]]; var r = 1; var day = moment().date(); var eom = moment().subtract(1, 'months').endOf('month').date(); var min = 0; for(var i = day; i<=eom; i++) { - var val = json["d"+zeropad(i)]; - data[r++] = [zeropad((i)), val, "color: #6f42c1;opacity: 0.9;", val.toFixed(0)]; - Math.min(0, val); + var imp = json["i"+zeropad(i)]; + var exp = json["e"+zeropad(i)]; + data[r++] = [zeropad(i), imp, "opacity: 0.9;", exp == 0 ? imp.toFixed(0) : imp.toFixed(0) + '\n' + -exp.toFixed(0), exp == 0 ? 0 : -exp, "opacity: 0.9;"]; + min = Math.min(0, -exp); } for(var i = 1; i < day; i++) { - var val = json["d"+zeropad(i)]; - data[r++] = [zeropad((i)), val, "color: #6f42c1;opacity: 0.9;", val.toFixed(0)]; - Math.min(0, val); + var imp = json["i"+zeropad(i)]; + var exp = json["e"+zeropad(i)]; + data[r++] = [zeropad(i), imp, "opacity: 0.9;", exp == 0 ? imp.toFixed(0) : imp.toFixed(0) + '\n' + -exp.toFixed(0), exp == 0 ? 0 : -exp, "opacity: 0.9;"]; + min = Math.min(0, -exp); } ma = google.visualization.arrayToDataTable(data); if(min == 0) diff --git a/web/dayplot.json b/web/dayplot.json index 8cb01f9a..ed782610 100644 --- a/web/dayplot.json +++ b/web/dayplot.json @@ -1,26 +1,50 @@ { - "h00" : %.2f, - "h01" : %.2f, - "h02" : %.2f, - "h03" : %.2f, - "h04" : %.2f, - "h05" : %.2f, - "h06" : %.2f, - "h07" : %.2f, - "h08" : %.2f, - "h09" : %.2f, - "h10" : %.2f, - "h11" : %.2f, - "h12" : %.2f, - "h13" : %.2f, - "h14" : %.2f, - "h15" : %.2f, - "h16" : %.2f, - "h17" : %.2f, - "h18" : %.2f, - "h19" : %.2f, - "h20" : %.2f, - "h21" : %.2f, - "h22" : %.2f, - "h23" : %.2f + "i00" : %.2f, + "i01" : %.2f, + "i02" : %.2f, + "i03" : %.2f, + "i04" : %.2f, + "i05" : %.2f, + "i06" : %.2f, + "i07" : %.2f, + "i08" : %.2f, + "i09" : %.2f, + "i10" : %.2f, + "i11" : %.2f, + "i12" : %.2f, + "i13" : %.2f, + "i14" : %.2f, + "i15" : %.2f, + "i16" : %.2f, + "i17" : %.2f, + "i18" : %.2f, + "i19" : %.2f, + "i20" : %.2f, + "i21" : %.2f, + "i22" : %.2f, + "i23" : %.2f, + "e00" : %.2f, + "e01" : %.2f, + "e02" : %.2f, + "e03" : %.2f, + "e04" : %.2f, + "e05" : %.2f, + "e06" : %.2f, + "e07" : %.2f, + "e08" : %.2f, + "e09" : %.2f, + "e10" : %.2f, + "e11" : %.2f, + "e12" : %.2f, + "e13" : %.2f, + "e14" : %.2f, + "e15" : %.2f, + "e16" : %.2f, + "e17" : %.2f, + "e18" : %.2f, + "e19" : %.2f, + "e20" : %.2f, + "e21" : %.2f, + "e22" : %.2f, + "e23" : %.2f } diff --git a/web/monthplot.json b/web/monthplot.json index 3c2b2dfa..e4271129 100644 --- a/web/monthplot.json +++ b/web/monthplot.json @@ -1,33 +1,64 @@ { - "d01" : %.2f, - "d02" : %.2f, - "d03" : %.2f, - "d04" : %.2f, - "d05" : %.2f, - "d06" : %.2f, - "d07" : %.2f, - "d08" : %.2f, - "d09" : %.2f, - "d10" : %.2f, - "d11" : %.2f, - "d12" : %.2f, - "d13" : %.2f, - "d14" : %.2f, - "d15" : %.2f, - "d16" : %.2f, - "d17" : %.2f, - "d18" : %.2f, - "d19" : %.2f, - "d20" : %.2f, - "d21" : %.2f, - "d22" : %.2f, - "d23" : %.2f, - "d24" : %.2f, - "d25" : %.2f, - "d26" : %.2f, - "d27" : %.2f, - "d28" : %.2f, - "d29" : %.2f, - "d30" : %.2f, - "d31" : %.2f + "i01" : %.2f, + "i02" : %.2f, + "i03" : %.2f, + "i04" : %.2f, + "i05" : %.2f, + "i06" : %.2f, + "i07" : %.2f, + "i08" : %.2f, + "i09" : %.2f, + "i10" : %.2f, + "i11" : %.2f, + "i12" : %.2f, + "i13" : %.2f, + "i14" : %.2f, + "i15" : %.2f, + "i16" : %.2f, + "i17" : %.2f, + "i18" : %.2f, + "i19" : %.2f, + "i20" : %.2f, + "i21" : %.2f, + "i22" : %.2f, + "i23" : %.2f, + "i24" : %.2f, + "i25" : %.2f, + "i26" : %.2f, + "i27" : %.2f, + "i28" : %.2f, + "i29" : %.2f, + "i30" : %.2f, + "i31" : %.2f, + "e01" : %.2f, + "e02" : %.2f, + "e03" : %.2f, + "e04" : %.2f, + "e05" : %.2f, + "e06" : %.2f, + "e07" : %.2f, + "e08" : %.2f, + "e09" : %.2f, + "e10" : %.2f, + "e11" : %.2f, + "e12" : %.2f, + "e13" : %.2f, + "e14" : %.2f, + "e15" : %.2f, + "e16" : %.2f, + "e17" : %.2f, + "e18" : %.2f, + "e19" : %.2f, + "e20" : %.2f, + "e21" : %.2f, + "e22" : %.2f, + "e23" : %.2f, + "e24" : %.2f, + "e25" : %.2f, + "e26" : %.2f, + "e27" : %.2f, + "e28" : %.2f, + "e29" : %.2f, + "e30" : %.2f, + "e31" : %.2f }