From 24f063dd7b2fe34855b33bdbf1241be5a4cc3381 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Tue, 30 May 2023 22:10:27 +0200 Subject: [PATCH] Fix: Prevent partly calculated total data in web ui and display Leads to zeros where no zeros should be. --- include/Datastore.h | 49 ++++++--- src/Datastore.cpp | 187 +++++++++++++++++++++++++++----- src/Display_Graphic.cpp | 12 +- src/Led_Single.cpp | 4 +- src/MqttHandleInverterTotal.cpp | 14 +-- src/WebApi_ws_live.cpp | 6 +- 6 files changed, 211 insertions(+), 61 deletions(-) diff --git a/include/Datastore.h b/include/Datastore.h index b36312a..4f9545a 100644 --- a/include/Datastore.h +++ b/include/Datastore.h @@ -2,6 +2,8 @@ #pragma once #include +#include +#include class DatastoreClass { public: @@ -10,52 +12,69 @@ public: void loop(); // Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included - float totalAcYieldTotalEnabled = 0; + float getTotalAcYieldTotalEnabled(); // Sum of yield day of all enabled inverters, a inverter which is just disabled at night is also included - float totalAcYieldDayEnabled = 0; + float getTotalAcYieldDayEnabled(); // Sum of total AC power of all enabled inverters - float totalAcPowerEnabled = 0; + float getTotalAcPowerEnabled(); // Sum of total DC power of all enabled inverters - float totalDcPowerEnabled = 0; + float getTotalDcPowerEnabled(); // Sum of total DC power of all enabled inverters with maxStringPower set - float totalDcPowerIrradiation = 0; + float getTotalDcPowerIrradiation(); // Sum of total installed irradiation of all enabled inverters - float totalDcIrradiationInstalled = 0; + float getTotalDcIrradiationInstalled(); // Percentage (1-100) of total irradiation - float totalDcIrradiation = 0; + float getTotalDcIrradiation(); // Amount of relevant digits for yield total - unsigned int totalAcYieldTotalDigits = 0; + unsigned int getTotalAcYieldTotalDigits(); // Amount of relevant digits for yield total - unsigned int totalAcYieldDayDigits = 0; + unsigned int getTotalAcYieldDayDigits(); // Amount of relevant digits for AC power - unsigned int totalAcPowerDigits = 0; + unsigned int getTotalAcPowerDigits(); // Amount of relevant digits for DC power - unsigned int totalDcPowerDigits = 0; + unsigned int getTotalDcPowerDigits(); // True, if at least one inverter is reachable - bool isAtLeastOneReachable = false; + bool getIsAtLeastOneReachable(); // True if at least one inverter is producing - bool isAtLeastOneProducing = false; + bool getIsAtLeastOneProducing(); // True if all enabled inverters are producing - bool isAllEnabledProducing = false; + bool getIsAllEnabledProducing(); // True if all enabled inverters are reachable - bool isAllEnabledReachable = false; + bool getIsAllEnabledReachable(); private: TimeoutHelper _updateTimeout; + SemaphoreHandle_t _xSemaphore; + + float _totalAcYieldTotalEnabled = 0; + float _totalAcYieldDayEnabled = 0; + float _totalAcPowerEnabled = 0; + float _totalDcPowerEnabled = 0; + float _totalDcPowerIrradiation = 0; + float _totalDcIrradiationInstalled = 0; + float _totalDcIrradiation = 0; + unsigned int _totalAcYieldTotalDigits = 0; + unsigned int _totalAcYieldDayDigits = 0; + unsigned int _totalAcPowerDigits = 0; + unsigned int _totalDcPowerDigits = 0; + bool _isAtLeastOneReachable = false; + bool _isAtLeastOneProducing = false; + bool _isAllEnabledProducing = false; + bool _isAllEnabledReachable = false; }; extern DatastoreClass Datastore; \ No newline at end of file diff --git a/src/Datastore.cpp b/src/Datastore.cpp index fa5b8c6..880ff17 100644 --- a/src/Datastore.cpp +++ b/src/Datastore.cpp @@ -6,10 +6,17 @@ #include "Configuration.h" #include +#define DAT_SEMAPHORE_TAKE() \ + do { \ + } while (xSemaphoreTake(_xSemaphore, portMAX_DELAY) != pdPASS) +#define DAT_SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore) + DatastoreClass Datastore; DatastoreClass::DatastoreClass() { + _xSemaphore = xSemaphoreCreateMutex(); + DAT_SEMAPHORE_GIVE(); // release before first use } void DatastoreClass::init() @@ -24,23 +31,25 @@ void DatastoreClass::loop() uint8_t isProducing = 0; uint8_t isReachable = 0; - totalAcYieldTotalEnabled = 0; - totalAcYieldTotalDigits = 0; + DAT_SEMAPHORE_TAKE(); - totalAcYieldDayEnabled = 0; - totalAcYieldDayDigits = 0; + _totalAcYieldTotalEnabled = 0; + _totalAcYieldTotalDigits = 0; - totalAcPowerEnabled = 0; - totalAcPowerDigits = 0; + _totalAcYieldDayEnabled = 0; + _totalAcYieldDayDigits = 0; - totalDcPowerEnabled = 0; - totalDcPowerDigits = 0; + _totalAcPowerEnabled = 0; + _totalAcPowerDigits = 0; - totalDcPowerIrradiation = 0; - totalDcIrradiationInstalled = 0; + _totalDcPowerEnabled = 0; + _totalDcPowerDigits = 0; - isAllEnabledProducing = true; - isAllEnabledReachable = true; + _totalDcPowerIrradiation = 0; + _totalDcIrradiationInstalled = 0; + + _isAllEnabledProducing = true; + _isAllEnabledReachable = true; for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { auto inv = Hoymiles.getInverterByPos(i); @@ -57,7 +66,7 @@ void DatastoreClass::loop() isProducing++; } else { if (inv->getEnablePolling()) { - isAllEnabledProducing = false; + _isAllEnabledProducing = false; } } @@ -65,42 +74,164 @@ void DatastoreClass::loop() isReachable++; } else { if (inv->getEnablePolling()) { - isAllEnabledReachable = false; + _isAllEnabledReachable = false; } } for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) { if (cfg->Poll_Enable) { - totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT); - totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD); + _totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT); + _totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD); - totalAcYieldTotalDigits = max(totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT)); - totalAcYieldDayDigits = max(totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD)); + _totalAcYieldTotalDigits = max(_totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT)); + _totalAcYieldDayDigits = max(_totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD)); } if (inv->getEnablePolling()) { - totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC); - totalAcPowerDigits = max(totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC)); + _totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC); + _totalAcPowerDigits = max(_totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC)); } } for (auto& c : inv->Statistics()->getChannelsByType(TYPE_DC)) { if (inv->getEnablePolling()) { - totalDcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC); - totalDcPowerDigits = max(totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_DC, c, FLD_PDC)); + _totalDcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC); + _totalDcPowerDigits = max(_totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_DC, c, FLD_PDC)); if (inv->Statistics()->getStringMaxPower(c) > 0) { - totalDcPowerIrradiation += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC); - totalDcIrradiationInstalled += inv->Statistics()->getStringMaxPower(c); + _totalDcPowerIrradiation += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC); + _totalDcIrradiationInstalled += inv->Statistics()->getStringMaxPower(c); } } } } - isAtLeastOneProducing = isProducing > 0; - isAtLeastOneReachable = isReachable > 0; + _isAtLeastOneProducing = isProducing > 0; + _isAtLeastOneReachable = isReachable > 0; - totalDcIrradiation = totalDcIrradiationInstalled > 0 ? totalDcPowerIrradiation / totalDcIrradiationInstalled * 100.0f : 0; + _totalDcIrradiation = _totalDcIrradiationInstalled > 0 ? _totalDcPowerIrradiation / _totalDcIrradiationInstalled * 100.0f : 0; + + DAT_SEMAPHORE_GIVE(); _updateTimeout.reset(); } -} \ No newline at end of file +} + +float DatastoreClass::getTotalAcYieldTotalEnabled() +{ + DAT_SEMAPHORE_TAKE(); + float retval = _totalAcYieldTotalEnabled; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +float DatastoreClass::getTotalAcYieldDayEnabled() +{ + DAT_SEMAPHORE_TAKE(); + float retval = _totalAcYieldDayEnabled; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +float DatastoreClass::getTotalAcPowerEnabled() +{ + DAT_SEMAPHORE_TAKE(); + float retval = _totalAcPowerEnabled; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +float DatastoreClass::getTotalDcPowerEnabled() +{ + DAT_SEMAPHORE_TAKE(); + float retval = _totalDcPowerEnabled; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +float DatastoreClass::getTotalDcPowerIrradiation() +{ + DAT_SEMAPHORE_TAKE(); + float retval = _totalDcPowerIrradiation; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +float DatastoreClass::getTotalDcIrradiationInstalled() +{ + DAT_SEMAPHORE_TAKE(); + float retval = _totalDcIrradiationInstalled; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +float DatastoreClass::getTotalDcIrradiation() +{ + DAT_SEMAPHORE_TAKE(); + float retval = _totalDcIrradiation; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +unsigned int DatastoreClass::getTotalAcYieldTotalDigits() +{ + DAT_SEMAPHORE_TAKE(); + unsigned int retval = _totalAcYieldTotalDigits; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +unsigned int DatastoreClass::getTotalAcYieldDayDigits() +{ + DAT_SEMAPHORE_TAKE(); + unsigned int retval = _totalAcYieldDayDigits; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +unsigned int DatastoreClass::getTotalAcPowerDigits() +{ + DAT_SEMAPHORE_TAKE(); + unsigned int retval = _totalAcPowerDigits; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +unsigned int DatastoreClass::getTotalDcPowerDigits() +{ + DAT_SEMAPHORE_TAKE(); + unsigned int retval = _totalDcPowerDigits; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +bool DatastoreClass::getIsAtLeastOneReachable() +{ + DAT_SEMAPHORE_TAKE(); + bool retval = _isAtLeastOneReachable; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +bool DatastoreClass::getIsAtLeastOneProducing() +{ + DAT_SEMAPHORE_TAKE(); + bool retval = _isAtLeastOneProducing; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +bool DatastoreClass::getIsAllEnabledProducing() +{ + DAT_SEMAPHORE_TAKE(); + bool retval = _isAllEnabledProducing; + DAT_SEMAPHORE_GIVE(); + return retval; +} + +bool DatastoreClass::getIsAllEnabledReachable() +{ + DAT_SEMAPHORE_TAKE(); + bool retval = _isAllEnabledReachable; + DAT_SEMAPHORE_GIVE(); + return retval; +} diff --git a/src/Display_Graphic.cpp b/src/Display_Graphic.cpp index d098c50..972b3bc 100644 --- a/src/Display_Graphic.cpp +++ b/src/Display_Graphic.cpp @@ -141,12 +141,12 @@ void DisplayGraphicClass::loop() _display->clearBuffer(); //=====> Actual Production ========== - if (Datastore.isAtLeastOneReachable) { + if (Datastore.getIsAtLeastOneReachable()) { _display->setPowerSave(false); - if (Datastore.totalAcPowerEnabled > 999) { - snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], (Datastore.totalAcPowerEnabled / 1000)); + if (Datastore.getTotalAcPowerEnabled() > 999) { + snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], (Datastore.getTotalAcPowerEnabled() / 1000)); } else { - snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], Datastore.totalAcPowerEnabled); + snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], Datastore.getTotalAcPowerEnabled()); } printText(_fmtText, 0); _previousMillis = millis(); @@ -164,10 +164,10 @@ void DisplayGraphicClass::loop() //<======================= //=====> Today & Total Production ======= - snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.totalAcYieldDayEnabled); + snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.getTotalAcYieldDayEnabled()); printText(_fmtText, 1); - snprintf(_fmtText, sizeof(_fmtText), i18n_yield_total_kwh[_display_language], Datastore.totalAcYieldTotalEnabled); + snprintf(_fmtText, sizeof(_fmtText), i18n_yield_total_kwh[_display_language], Datastore.getTotalAcYieldTotalEnabled()); printText(_fmtText, 2); //<======================= diff --git a/src/Led_Single.cpp b/src/Led_Single.cpp index f4f1cf0..bd87ba1 100644 --- a/src/Led_Single.cpp +++ b/src/Led_Single.cpp @@ -59,10 +59,10 @@ void LedSingleClass::loop() _ledState[1] = LedState_t::Off; if (Hoymiles.getNumInverters()) { // set LED status - if (Datastore.isAllEnabledReachable && Datastore.isAllEnabledProducing) { + if (Datastore.getIsAllEnabledReachable() && Datastore.getIsAllEnabledProducing()) { _ledState[1] = LedState_t::On; } - if (Datastore.isAllEnabledReachable && !Datastore.isAllEnabledProducing) { + if (Datastore.getIsAllEnabledReachable() && !Datastore.getIsAllEnabledProducing()) { _ledState[1] = LedState_t::Blink; } } diff --git a/src/MqttHandleInverterTotal.cpp b/src/MqttHandleInverterTotal.cpp index f89436c..ac8e6a4 100644 --- a/src/MqttHandleInverterTotal.cpp +++ b/src/MqttHandleInverterTotal.cpp @@ -22,13 +22,13 @@ void MqttHandleInverterTotalClass::loop() } if (_lastPublish.occured()) { - MqttSettings.publish("ac/power", String(Datastore.totalAcPowerEnabled, Datastore.totalAcPowerDigits)); - MqttSettings.publish("ac/yieldtotal", String(Datastore.totalAcYieldTotalEnabled, Datastore.totalAcYieldTotalDigits)); - MqttSettings.publish("ac/yieldday", String(Datastore.totalAcYieldDayEnabled, Datastore.totalAcYieldDayDigits)); - MqttSettings.publish("ac/is_valid", String(Datastore.isAllEnabledReachable)); - MqttSettings.publish("dc/power", String(Datastore.totalDcPowerEnabled, Datastore.totalDcPowerDigits)); - MqttSettings.publish("dc/irradiation", String(Datastore.totalDcIrradiation, 3)); - MqttSettings.publish("dc/is_valid", String(Datastore.isAllEnabledReachable)); + MqttSettings.publish("ac/power", String(Datastore.getTotalAcPowerEnabled(), Datastore.getTotalAcPowerDigits())); + MqttSettings.publish("ac/yieldtotal", String(Datastore.getTotalAcYieldTotalEnabled(), Datastore.getTotalAcYieldTotalDigits())); + MqttSettings.publish("ac/yieldday", String(Datastore.getTotalAcYieldDayEnabled(), Datastore.getTotalAcYieldDayDigits())); + MqttSettings.publish("ac/is_valid", String(Datastore.getIsAllEnabledReachable())); + MqttSettings.publish("dc/power", String(Datastore.getTotalDcPowerEnabled(), Datastore.getTotalDcPowerDigits())); + MqttSettings.publish("dc/irradiation", String(Datastore.getTotalDcIrradiation(), 3)); + MqttSettings.publish("dc/is_valid", String(Datastore.getIsAllEnabledReachable())); _lastPublish.set(Configuration.get().Mqtt_PublishInterval * 1000); } diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index fb34763..74db2be 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -160,9 +160,9 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root) } JsonObject totalObj = root.createNestedObject("total"); - addTotalField(totalObj, "Power", Datastore.totalAcPowerEnabled, "W", Datastore.totalAcPowerDigits); - addTotalField(totalObj, "YieldDay", Datastore.totalAcYieldDayEnabled, "Wh", Datastore.totalAcYieldDayDigits); - addTotalField(totalObj, "YieldTotal", Datastore.totalAcYieldTotalEnabled, "kWh", Datastore.totalAcYieldTotalDigits); + addTotalField(totalObj, "Power", Datastore.getTotalAcPowerEnabled(), "W", Datastore.getTotalAcPowerDigits()); + addTotalField(totalObj, "YieldDay", Datastore.getTotalAcYieldDayEnabled(), "Wh", Datastore.getTotalAcYieldDayDigits()); + addTotalField(totalObj, "YieldTotal", Datastore.getTotalAcYieldTotalEnabled(), "kWh", Datastore.getTotalAcYieldTotalDigits()); JsonObject hintObj = root.createNestedObject("hints"); struct tm timeinfo;