Fix: Prevent partly calculated total data in web ui and display

Leads to zeros where no zeros should be.
This commit is contained in:
Thomas Basler 2023-05-30 22:10:27 +02:00
parent bd891f9a6d
commit 24f063dd7b
6 changed files with 211 additions and 61 deletions

View File

@ -2,6 +2,8 @@
#pragma once #pragma once
#include <TimeoutHelper.h> #include <TimeoutHelper.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
class DatastoreClass { class DatastoreClass {
public: public:
@ -10,52 +12,69 @@ public:
void loop(); void loop();
// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included // 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 // 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 // Sum of total AC power of all enabled inverters
float totalAcPowerEnabled = 0; float getTotalAcPowerEnabled();
// Sum of total DC power of all enabled inverters // 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 // 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 // Sum of total installed irradiation of all enabled inverters
float totalDcIrradiationInstalled = 0; float getTotalDcIrradiationInstalled();
// Percentage (1-100) of total irradiation // Percentage (1-100) of total irradiation
float totalDcIrradiation = 0; float getTotalDcIrradiation();
// Amount of relevant digits for yield total // Amount of relevant digits for yield total
unsigned int totalAcYieldTotalDigits = 0; unsigned int getTotalAcYieldTotalDigits();
// Amount of relevant digits for yield total // Amount of relevant digits for yield total
unsigned int totalAcYieldDayDigits = 0; unsigned int getTotalAcYieldDayDigits();
// Amount of relevant digits for AC power // Amount of relevant digits for AC power
unsigned int totalAcPowerDigits = 0; unsigned int getTotalAcPowerDigits();
// Amount of relevant digits for DC power // Amount of relevant digits for DC power
unsigned int totalDcPowerDigits = 0; unsigned int getTotalDcPowerDigits();
// True, if at least one inverter is reachable // True, if at least one inverter is reachable
bool isAtLeastOneReachable = false; bool getIsAtLeastOneReachable();
// True if at least one inverter is producing // True if at least one inverter is producing
bool isAtLeastOneProducing = false; bool getIsAtLeastOneProducing();
// True if all enabled inverters are producing // True if all enabled inverters are producing
bool isAllEnabledProducing = false; bool getIsAllEnabledProducing();
// True if all enabled inverters are reachable // True if all enabled inverters are reachable
bool isAllEnabledReachable = false; bool getIsAllEnabledReachable();
private: private:
TimeoutHelper _updateTimeout; 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; extern DatastoreClass Datastore;

View File

@ -6,10 +6,17 @@
#include "Configuration.h" #include "Configuration.h"
#include <Hoymiles.h> #include <Hoymiles.h>
#define DAT_SEMAPHORE_TAKE() \
do { \
} while (xSemaphoreTake(_xSemaphore, portMAX_DELAY) != pdPASS)
#define DAT_SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore)
DatastoreClass Datastore; DatastoreClass Datastore;
DatastoreClass::DatastoreClass() DatastoreClass::DatastoreClass()
{ {
_xSemaphore = xSemaphoreCreateMutex();
DAT_SEMAPHORE_GIVE(); // release before first use
} }
void DatastoreClass::init() void DatastoreClass::init()
@ -24,23 +31,25 @@ void DatastoreClass::loop()
uint8_t isProducing = 0; uint8_t isProducing = 0;
uint8_t isReachable = 0; uint8_t isReachable = 0;
totalAcYieldTotalEnabled = 0; DAT_SEMAPHORE_TAKE();
totalAcYieldTotalDigits = 0;
totalAcYieldDayEnabled = 0; _totalAcYieldTotalEnabled = 0;
totalAcYieldDayDigits = 0; _totalAcYieldTotalDigits = 0;
totalAcPowerEnabled = 0; _totalAcYieldDayEnabled = 0;
totalAcPowerDigits = 0; _totalAcYieldDayDigits = 0;
totalDcPowerEnabled = 0; _totalAcPowerEnabled = 0;
totalDcPowerDigits = 0; _totalAcPowerDigits = 0;
totalDcPowerIrradiation = 0; _totalDcPowerEnabled = 0;
totalDcIrradiationInstalled = 0; _totalDcPowerDigits = 0;
isAllEnabledProducing = true; _totalDcPowerIrradiation = 0;
isAllEnabledReachable = true; _totalDcIrradiationInstalled = 0;
_isAllEnabledProducing = true;
_isAllEnabledReachable = true;
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i); auto inv = Hoymiles.getInverterByPos(i);
@ -57,7 +66,7 @@ void DatastoreClass::loop()
isProducing++; isProducing++;
} else { } else {
if (inv->getEnablePolling()) { if (inv->getEnablePolling()) {
isAllEnabledProducing = false; _isAllEnabledProducing = false;
} }
} }
@ -65,42 +74,164 @@ void DatastoreClass::loop()
isReachable++; isReachable++;
} else { } else {
if (inv->getEnablePolling()) { if (inv->getEnablePolling()) {
isAllEnabledReachable = false; _isAllEnabledReachable = false;
} }
} }
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) { for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
if (cfg->Poll_Enable) { if (cfg->Poll_Enable) {
totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT); _totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT);
totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD); _totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD);
totalAcYieldTotalDigits = max<unsigned int>(totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT)); _totalAcYieldTotalDigits = max<unsigned int>(_totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT));
totalAcYieldDayDigits = max<unsigned int>(totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD)); _totalAcYieldDayDigits = max<unsigned int>(_totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD));
} }
if (inv->getEnablePolling()) { if (inv->getEnablePolling()) {
totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC); _totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
totalAcPowerDigits = max<unsigned int>(totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC)); _totalAcPowerDigits = max<unsigned int>(_totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC));
} }
} }
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_DC)) { for (auto& c : inv->Statistics()->getChannelsByType(TYPE_DC)) {
if (inv->getEnablePolling()) { if (inv->getEnablePolling()) {
totalDcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC); _totalDcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcPowerDigits = max<unsigned int>(totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_DC, c, FLD_PDC)); _totalDcPowerDigits = max<unsigned int>(_totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_DC, c, FLD_PDC));
if (inv->Statistics()->getStringMaxPower(c) > 0) { if (inv->Statistics()->getStringMaxPower(c) > 0) {
totalDcPowerIrradiation += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC); _totalDcPowerIrradiation += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcIrradiationInstalled += inv->Statistics()->getStringMaxPower(c); _totalDcIrradiationInstalled += inv->Statistics()->getStringMaxPower(c);
} }
} }
} }
} }
isAtLeastOneProducing = isProducing > 0; _isAtLeastOneProducing = isProducing > 0;
isAtLeastOneReachable = isReachable > 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(); _updateTimeout.reset();
} }
} }
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;
}

View File

@ -141,12 +141,12 @@ void DisplayGraphicClass::loop()
_display->clearBuffer(); _display->clearBuffer();
//=====> Actual Production ========== //=====> Actual Production ==========
if (Datastore.isAtLeastOneReachable) { if (Datastore.getIsAtLeastOneReachable()) {
_display->setPowerSave(false); _display->setPowerSave(false);
if (Datastore.totalAcPowerEnabled > 999) { if (Datastore.getTotalAcPowerEnabled() > 999) {
snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], (Datastore.totalAcPowerEnabled / 1000)); snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], (Datastore.getTotalAcPowerEnabled() / 1000));
} else { } 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); printText(_fmtText, 0);
_previousMillis = millis(); _previousMillis = millis();
@ -164,10 +164,10 @@ void DisplayGraphicClass::loop()
//<======================= //<=======================
//=====> Today & Total Production ======= //=====> 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); 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); printText(_fmtText, 2);
//<======================= //<=======================

View File

@ -59,10 +59,10 @@ void LedSingleClass::loop()
_ledState[1] = LedState_t::Off; _ledState[1] = LedState_t::Off;
if (Hoymiles.getNumInverters()) { if (Hoymiles.getNumInverters()) {
// set LED status // set LED status
if (Datastore.isAllEnabledReachable && Datastore.isAllEnabledProducing) { if (Datastore.getIsAllEnabledReachable() && Datastore.getIsAllEnabledProducing()) {
_ledState[1] = LedState_t::On; _ledState[1] = LedState_t::On;
} }
if (Datastore.isAllEnabledReachable && !Datastore.isAllEnabledProducing) { if (Datastore.getIsAllEnabledReachable() && !Datastore.getIsAllEnabledProducing()) {
_ledState[1] = LedState_t::Blink; _ledState[1] = LedState_t::Blink;
} }
} }

View File

@ -22,13 +22,13 @@ void MqttHandleInverterTotalClass::loop()
} }
if (_lastPublish.occured()) { if (_lastPublish.occured()) {
MqttSettings.publish("ac/power", String(Datastore.totalAcPowerEnabled, Datastore.totalAcPowerDigits)); MqttSettings.publish("ac/power", String(Datastore.getTotalAcPowerEnabled(), Datastore.getTotalAcPowerDigits()));
MqttSettings.publish("ac/yieldtotal", String(Datastore.totalAcYieldTotalEnabled, Datastore.totalAcYieldTotalDigits)); MqttSettings.publish("ac/yieldtotal", String(Datastore.getTotalAcYieldTotalEnabled(), Datastore.getTotalAcYieldTotalDigits()));
MqttSettings.publish("ac/yieldday", String(Datastore.totalAcYieldDayEnabled, Datastore.totalAcYieldDayDigits)); MqttSettings.publish("ac/yieldday", String(Datastore.getTotalAcYieldDayEnabled(), Datastore.getTotalAcYieldDayDigits()));
MqttSettings.publish("ac/is_valid", String(Datastore.isAllEnabledReachable)); MqttSettings.publish("ac/is_valid", String(Datastore.getIsAllEnabledReachable()));
MqttSettings.publish("dc/power", String(Datastore.totalDcPowerEnabled, Datastore.totalDcPowerDigits)); MqttSettings.publish("dc/power", String(Datastore.getTotalDcPowerEnabled(), Datastore.getTotalDcPowerDigits()));
MqttSettings.publish("dc/irradiation", String(Datastore.totalDcIrradiation, 3)); MqttSettings.publish("dc/irradiation", String(Datastore.getTotalDcIrradiation(), 3));
MqttSettings.publish("dc/is_valid", String(Datastore.isAllEnabledReachable)); MqttSettings.publish("dc/is_valid", String(Datastore.getIsAllEnabledReachable()));
_lastPublish.set(Configuration.get().Mqtt_PublishInterval * 1000); _lastPublish.set(Configuration.get().Mqtt_PublishInterval * 1000);
} }

View File

@ -160,9 +160,9 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
} }
JsonObject totalObj = root.createNestedObject("total"); JsonObject totalObj = root.createNestedObject("total");
addTotalField(totalObj, "Power", Datastore.totalAcPowerEnabled, "W", Datastore.totalAcPowerDigits); addTotalField(totalObj, "Power", Datastore.getTotalAcPowerEnabled(), "W", Datastore.getTotalAcPowerDigits());
addTotalField(totalObj, "YieldDay", Datastore.totalAcYieldDayEnabled, "Wh", Datastore.totalAcYieldDayDigits); addTotalField(totalObj, "YieldDay", Datastore.getTotalAcYieldDayEnabled(), "Wh", Datastore.getTotalAcYieldDayDigits());
addTotalField(totalObj, "YieldTotal", Datastore.totalAcYieldTotalEnabled, "kWh", Datastore.totalAcYieldTotalDigits); addTotalField(totalObj, "YieldTotal", Datastore.getTotalAcYieldTotalEnabled(), "kWh", Datastore.getTotalAcYieldTotalDigits());
JsonObject hintObj = root.createNestedObject("hints"); JsonObject hintObj = root.createNestedObject("hints");
struct tm timeinfo; struct tm timeinfo;