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
#include <TimeoutHelper.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
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;

View File

@ -6,10 +6,17 @@
#include "Configuration.h"
#include <Hoymiles.h>
#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<unsigned int>(totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT));
totalAcYieldDayDigits = max<unsigned int>(totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD));
_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));
}
if (inv->getEnablePolling()) {
totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
totalAcPowerDigits = max<unsigned int>(totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(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));
}
}
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_DC)) {
if (inv->getEnablePolling()) {
totalDcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcPowerDigits = max<unsigned int>(totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(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));
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();
}
}
}
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();
//=====> 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);
//<=======================

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;