From e0242b8f60ceda0db217c409bd42182cd44dc73e Mon Sep 17 00:00:00 2001 From: Leo Date: Sun, 29 Sep 2024 21:44:45 +0200 Subject: [PATCH] Update overscaling for better mixed setup management. --- include/Configuration.h | 1 + include/defaults.h | 1 + src/Configuration.cpp | 2 + src/PowerLimiter.cpp | 39 +-- src/WebApi_powerlimiter.cpp | 4 +- webapp/src/locales/de.json | 1 + webapp/src/locales/en.json | 1 + webapp/src/locales/fr.json | 1 + webapp/src/types/PowerLimiterConfig.ts | 1 + webapp/src/views/PowerLimiterAdminView.vue | 388 ++++++--------------- 10 files changed, 137 insertions(+), 302 deletions(-) diff --git a/include/Configuration.h b/include/Configuration.h index 3b99c38bb..f10f6c7c5 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -294,6 +294,7 @@ struct CONFIG_T { int32_t TargetPowerConsumptionHysteresis; int32_t LowerPowerLimit; int32_t BaseLoadLimit; + int32_t ShadedFactor; int32_t UpperPowerLimit; bool IgnoreSoc; uint32_t BatterySocStartThreshold; diff --git a/include/defaults.h b/include/defaults.h index 67b1d122e..a3ff261c6 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -139,6 +139,7 @@ #define POWERLIMITER_LOWER_POWER_LIMIT 10 #define POWERLIMITER_BASE_LOAD_LIMIT 100 #define POWERLIMITER_UPPER_POWER_LIMIT 800 +#define POWERLIMITER_SHADED_FACTOR 60 #define POWERLIMITER_IGNORE_SOC false #define POWERLIMITER_BATTERY_SOC_START_THRESHOLD 80 #define POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD 20 diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 61ee7758d..d8c68db65 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -264,6 +264,7 @@ bool ConfigurationClass::write() powerlimiter["lower_power_limit"] = config.PowerLimiter.LowerPowerLimit; powerlimiter["base_load_limit"] = config.PowerLimiter.BaseLoadLimit; powerlimiter["upper_power_limit"] = config.PowerLimiter.UpperPowerLimit; + powerlimiter["shaded_factor"] = config.PowerLimiter.ShadedFactor; powerlimiter["ignore_soc"] = config.PowerLimiter.IgnoreSoc; powerlimiter["battery_soc_start_threshold"] = config.PowerLimiter.BatterySocStartThreshold; powerlimiter["battery_soc_stop_threshold"] = config.PowerLimiter.BatterySocStopThreshold; @@ -630,6 +631,7 @@ bool ConfigurationClass::read() config.PowerLimiter.LowerPowerLimit = powerlimiter["lower_power_limit"] | POWERLIMITER_LOWER_POWER_LIMIT; config.PowerLimiter.BaseLoadLimit = powerlimiter["base_load_limit"] | POWERLIMITER_BASE_LOAD_LIMIT; config.PowerLimiter.UpperPowerLimit = powerlimiter["upper_power_limit"] | POWERLIMITER_UPPER_POWER_LIMIT; + config.PowerLimiter.ShadedFactor = powerlimiter["shaded_factor"] | POWERLIMITER_SHADED_FACTOR; config.PowerLimiter.IgnoreSoc = powerlimiter["ignore_soc"] | POWERLIMITER_IGNORE_SOC; config.PowerLimiter.BatterySocStartThreshold = powerlimiter["battery_soc_start_threshold"] | POWERLIMITER_BATTERY_SOC_START_THRESHOLD; config.PowerLimiter.BatterySocStopThreshold = powerlimiter["battery_soc_stop_threshold"] | POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD; diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index c96a5a26f..e3ba984b2 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -739,13 +739,14 @@ static int32_t scalePowerLimit(std::shared_ptr inverter, int32 // overscalling allows us to compensate for shaded panels by increasing the // total power limit, if the inverter is solar powered. if (allowOverscaling && isInverterSolarPowered) { - auto inverterOutputAC = inverter->Statistics()->getChannelFieldValue(TYPE_AC, CH0, FLD_PAC); float inverterEfficiencyFactor = getInverterEfficiency(inverter); - // 98% of the expected power is good enough - auto expectedAcPowerPerChannel = (currentLimitWatts / dcTotalChnls) * 0.98; + // defined % of the expected power is good enough + auto factor_shaded=(float)config.PowerLimiter.ShadedFactor/(float)100; + auto expectedAcPowerPerChannel = (currentLimitWatts / dcTotalChnls) * (float)factor_shaded; if (log) { + MessageOutput.printf("[DPL::scalePowerLimit] shaded factor raw %d, adapted %f \r\n",config.PowerLimiter.ShadedFactor,factor_shaded); MessageOutput.printf("[DPL::scalePowerLimit] expected AC power per channel %f W\r\n", expectedAcPowerPerChannel); } @@ -771,26 +772,24 @@ static int32_t scalePowerLimit(std::shared_ptr inverter, int32 // we currently need. if (dcShadedChnls == 0 || shadedChannelACPowerSum >= newLimit) { return newLimit; } - if (dcShadedChnls == dcTotalChnls) { - // keep the currentLimit when: - // - all channels are shaded - // - currentLimit >= newLimit - // - we get the expected AC power or less and - if (currentLimitWatts >= newLimit && inverterOutputAC <= newLimit) { - if (log) { - MessageOutput.printf("[DPL::scalePowerLimit] all channels are shaded, " - "keeping the current limit of %d W\r\n", currentLimitWatts); - } - - return currentLimitWatts; + size_t dcNonShadedChnls = dcTotalChnls - dcShadedChnls; - } else { - return newLimit; - } + if (log) { + MessageOutput.printf("[DPL::scalePowerLimit] newLimit %d W\r\n", newLimit); + MessageOutput.printf("[DPL::scalePowerLimit] shadedChannelACPowerSum %d W\r\n", (int)shadedChannelACPowerSum); + MessageOutput.printf("[DPL::scalePowerLimit] dcNonShadedChnls %d ch\r\n", dcNonShadedChnls); + MessageOutput.printf("[DPL::scalePowerLimit] dcTotalChnls %d ch\r\n", dcTotalChnls); } - size_t dcNonShadedChnls = dcTotalChnls - dcShadedChnls; - auto overScaledLimit = static_cast((newLimit - shadedChannelACPowerSum) / dcNonShadedChnls * dcTotalChnls); + // if all channels are shaded, hopefully 1 can patch-up + if (dcNonShadedChnls == 0) { + dcNonShadedChnls=1; + if (log) { + MessageOutput.printf("[DPL::scalePowerLimit] all channels are shaded, hopefully 1 can patch-up\r\n"); + } + } + + auto overScaledLimit = (float)(newLimit - (int)shadedChannelACPowerSum) / ((float)dcNonShadedChnls/(float)dcTotalChnls); if (overScaledLimit <= newLimit) { return newLimit; } diff --git a/src/WebApi_powerlimiter.cpp b/src/WebApi_powerlimiter.cpp index 89e671d8c..67aca3131 100644 --- a/src/WebApi_powerlimiter.cpp +++ b/src/WebApi_powerlimiter.cpp @@ -46,6 +46,7 @@ void WebApiPowerLimiterClass::onStatus(AsyncWebServerRequest* request) root["lower_power_limit"] = config.PowerLimiter.LowerPowerLimit; root["base_load_limit"] = config.PowerLimiter.BaseLoadLimit; root["upper_power_limit"] = config.PowerLimiter.UpperPowerLimit; + root["shaded_factor"] = config.PowerLimiter.ShadedFactor; root["ignore_soc"] = config.PowerLimiter.IgnoreSoc; root["battery_soc_start_threshold"] = config.PowerLimiter.BatterySocStartThreshold; root["battery_soc_stop_threshold"] = config.PowerLimiter.BatterySocStopThreshold; @@ -167,7 +168,8 @@ void WebApiPowerLimiterClass::onAdminPost(AsyncWebServerRequest* request) config.PowerLimiter.TargetPowerConsumptionHysteresis = root["target_power_consumption_hysteresis"].as(); config.PowerLimiter.LowerPowerLimit = root["lower_power_limit"].as(); config.PowerLimiter.BaseLoadLimit = root["base_load_limit"].as(); - config.PowerLimiter.UpperPowerLimit = root["upper_power_limit"].as(); + config.PowerLimiter.UpperPowerLimit = root["upper_power_limit"].as(); + config.PowerLimiter.ShadedFactor = root["shaded_factor"].as(); if (config.Battery.Enabled) { config.PowerLimiter.IgnoreSoc = root["ignore_soc"].as(); diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index d050860b8..d047723ed 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -664,6 +664,7 @@ "VoltageLoadCorrectionFactor": "Lastkorrekturfaktor", "BatterySocInfo": "Hinweis: Die Akku SoC (State of Charge) Werte werden nur benutzt, wenn die Batterie-Kommunikationsschnittstelle innerhalb der letzten Minute gültige Werte geschickt hat. Andernfalls werden als Fallback-Option die Spannungseinstellungen verwendet.", "InverterIsBehindPowerMeter": "Stromzählermessung beinhaltet Wechselrichter", + "ShadedFactor": "Factor to consider if panels are shaded 0-100, in % vs expected channel power", "InverterIsBehindPowerMeterHint": "Aktivieren falls sich der Stromzähler-Messwert um die Ausgangsleistung des Wechselrichters verringert, wenn dieser Strom produziert. Normalerweise ist das zutreffend.", "InverterIsSolarPowered": "Wechselrichter wird von Solarmodulen gespeist", "UseOverscalingToCompensateShading": "Verschattung durch Überskalierung ausgleichen", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index bb4d5f299..847d146a9 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -666,6 +666,7 @@ "VoltageLoadCorrectionFactor": "Load correction factor", "BatterySocInfo": "Hint: The battery SoC (State of Charge) values are only used if the battery communication interface reported SoC updates in the last minute. Otherwise the voltage thresholds will be used as fallback.", "InverterIsBehindPowerMeter": "PowerMeter reading includes inverter output", + "ShadedFactor": "Factor to consider if panels are shaded 0-100, in % vs expected channel power", "InverterIsBehindPowerMeterHint": "Enable this option if the power meter reading is reduced by the inverter's output when it produces power. This is typically true.", "InverterIsSolarPowered": "Inverter is powered by solar modules", "UseOverscalingToCompensateShading": "Compensate for shading", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index aed0a9b88..ca24a60d5 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -742,6 +742,7 @@ "VoltageLoadCorrectionFactor": "Load correction factor", "BatterySocInfo": "Hint: The battery SoC (State of Charge) values are only used if the battery communication interface reported SoC updates in the last minute. Otherwise the voltage thresholds will be used as fallback.", "InverterIsBehindPowerMeter": "PowerMeter reading includes inverter output", + "ShadedFactor": "Factor to consider if panels are shaded 0-100, in % vs expected channel power", "InverterIsBehindPowerMeterHint": "Enable this option if the power meter reading is reduced by the inverter's output when it produces power. This is typically true.", "InverterIsSolarPowered": "Inverter is powered by solar modules", "VoltageThresholds": "Battery Voltage Thresholds", diff --git a/webapp/src/types/PowerLimiterConfig.ts b/webapp/src/types/PowerLimiterConfig.ts index f8220a7d5..d3fd79376 100644 --- a/webapp/src/types/PowerLimiterConfig.ts +++ b/webapp/src/types/PowerLimiterConfig.ts @@ -34,6 +34,7 @@ export interface PowerLimiterConfig { lower_power_limit: number; base_load_limit: number; upper_power_limit: number; + shaded_factor: number; ignore_soc: boolean; battery_soc_start_threshold: number; battery_soc_stop_threshold: number; diff --git a/webapp/src/views/PowerLimiterAdminView.vue b/webapp/src/views/PowerLimiterAdminView.vue index 05b7bdd9c..9e4689547 100644 --- a/webapp/src/views/PowerLimiterAdminView.vue +++ b/webapp/src/views/PowerLimiterAdminView.vue @@ -8,19 +8,15 @@ {{ $t('powerlimiteradmin.ConfigAlertMessage') }} - +
{{ $t('powerlimiteradmin.ConfigHintsIntro') }}
  • - {{ $t('powerlimiteradmin.ConfigHintRequirement') }}: + {{ $t('powerlimiteradmin.ConfigHintRequirement') + }}: {{ $t('powerlimiteradmin.ConfigHintOptional') }}: {{ $t('powerlimiteradmin.ConfigHint' + hint.subject) }}
  • @@ -31,173 +27,102 @@
    - - - - - + + + + - - + + + v-model="powerLimiterConfigList.target_power_consumption_hysteresis" postfix="W" type="number" + wide /> - +
    - -
    - + - + v-model="powerLimiterConfigList.battery_always_use_at_night" type="checkbox" wide /> - + v-model="powerLimiterConfigList.use_overscaling_to_compensate_shading" type="checkbox" wide />
    - +
    - - - + + - - + + - - + + + :tooltip="$t('powerlimiteradmin.InverterIsBehindPowerMeterHint')" type="checkbox" wide /> -
    + + +
    - @@ -209,174 +134,75 @@
    - - - - + + + +
    - - - + + +
    - - + +
    - - - - - - - +
    + + + + + + + v-model="powerLimiterConfigList.full_solar_passthrough_soc" v-if="isSolarPassthroughEnabled()" + placeholder="80" min="0" max="100" postfix="%" type="number" wide />
    - - - - + + + +
    - - - + v-model="powerLimiterConfigList.full_solar_passthrough_start_voltage" placeholder="49" min="16" + max="66" postfix="V" type="number" step="0.01" wide /> + +
    - - - + + +