From cbc99d715f092760cb9f82603c0f84f55870b306 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Mon, 17 Jul 2023 09:40:18 +0200 Subject: [PATCH] DPL: increase backoff while inverter is kept shut down (#310) if the new calculated power limit is below the minimum power limit setting, the inverter is shut down. the shutdown() function is called every time this condition is detected, which is also true if the inverter is kept shut down for longer. that happens while the battery is charging in particular (solar passthrough off). there are other cases. in such cases we still want to get into the DPL status "stable". to be able to determine this stable state, we must know if the call to shutdown did actually initiate a shutdown or if the inverter is already shut down. we then can forward this "changed" or "not changed" info up the call chain, where the loop() will know that the system is actually stable. --- include/PowerLimiter.h | 6 ++--- src/PowerLimiter.cpp | 59 ++++++++++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/include/PowerLimiter.h b/include/PowerLimiter.h index f448dc20d..f30dd6e86 100644 --- a/include/PowerLimiter.h +++ b/include/PowerLimiter.h @@ -44,7 +44,6 @@ class PowerLimiterClass { NoVeDirect, Settling, Stable, - LowerLimitUndercut }; void init(); @@ -57,7 +56,7 @@ class PowerLimiterClass { private: int32_t _lastRequestedPowerLimit = 0; - bool _shutdownInProgress; + uint32_t _shutdownTimeout = 0; Status _lastStatus = Status::Initializing; uint32_t _lastStatusPrinted = 0; uint32_t _lastCalculation = 0; @@ -72,7 +71,8 @@ class PowerLimiterClass { std::string const& getStatusText(Status status); void announceStatus(Status status); - void shutdown(Status status); + bool shutdown(Status status); + bool shutdown() { return shutdown(_lastStatus); } int32_t inverterPowerDcToAc(std::shared_ptr inverter, int32_t dcPower); void unconditionalSolarPassthrough(std::shared_ptr inverter); bool canUseDirectSolarPower(); diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index 7d5f0652d..61abfb2de 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -46,7 +46,6 @@ std::string const& PowerLimiterClass::getStatusText(PowerLimiterClass::Status st { Status::NoVeDirect, "VE.Direct disabled, connection broken, or data outdated" }, { Status::Settling, "waiting for the system to settle" }, { Status::Stable, "the system is stable, the last power limit is still valid" }, - { Status::LowerLimitUndercut, "calculated power limit undercuts configured lower limit" } }; auto iter = texts.find(status); @@ -73,26 +72,39 @@ void PowerLimiterClass::announceStatus(PowerLimiterClass::Status status) _lastStatusPrinted = millis(); } -void PowerLimiterClass::shutdown(PowerLimiterClass::Status status) +/** + * returns true if the inverter state was changed or is about to change, i.e., + * if it is actually in need of a shutdown. returns false otherwise, i.e., the + * inverter is already (assumed to be) shut down. + */ +bool PowerLimiterClass::shutdown(PowerLimiterClass::Status status) { announceStatus(status); - if (_inverter == nullptr || !_inverter->isProducing() || !_inverter->isReachable()) { + if (_inverter == nullptr || !_inverter->isProducing() || + (_shutdownTimeout > 0 && _shutdownTimeout < millis()) ) { + // we are actually (already) done with shutting down the inverter, + // or a shutdown attempt was initiated but it timed out. _inverter = nullptr; - _shutdownInProgress = false; - return; + _shutdownTimeout = 0; + return false; } - _shutdownInProgress = true; + if (!_inverter->isReachable()) { return true; } // retry later (until timeout) + + // retry shutdown for a maximum amount of time before giving up + if (_shutdownTimeout == 0) { _shutdownTimeout = millis() + 10 * 1000; } auto lastLimitCommandState = _inverter->SystemConfigPara()->getLastLimitCommandSuccess(); - if (CMD_PENDING == lastLimitCommandState) { return; } + if (CMD_PENDING == lastLimitCommandState) { return true; } auto lastPowerCommandState = _inverter->PowerCommand()->getLastPowerCommandSuccess(); - if (CMD_PENDING == lastPowerCommandState) { return; } + if (CMD_PENDING == lastPowerCommandState) { return true; } CONFIG_T& config = Configuration.get(); commitPowerLimit(_inverter, config.PowerLimiter_LowerPowerLimit, false); + + return true; } void PowerLimiterClass::loop() @@ -107,19 +119,22 @@ void PowerLimiterClass::loop() return announceStatus(Status::WaitingForValidTimestamp); } - if (_shutdownInProgress) { + if (_shutdownTimeout > 0) { // we transition from SHUTDOWN to OFF when we know the inverter was // shut down. until then, we retry shutting it down. in this case we // preserve the original status that lead to the decision to shut down. - return shutdown(_lastStatus); + shutdown(); + return; } if (!config.PowerLimiter_Enabled) { - return shutdown(Status::DisabledByConfig); + shutdown(Status::DisabledByConfig); + return; } if (PL_MODE_FULL_DISABLE == _mode) { - return shutdown(Status::DisabledByMqtt); + shutdown(Status::DisabledByMqtt); + return; } std::shared_ptr currentInverter = @@ -128,13 +143,15 @@ void PowerLimiterClass::loop() // in case of (newly) broken configuration, shut down // the last inverter we worked with (if any) if (currentInverter == nullptr) { - return shutdown(Status::InverterInvalid); + shutdown(Status::InverterInvalid); + return; } // if the DPL is supposed to manage another inverter now, we first // shut down the previous one, if any. then we pick up the new one. if (_inverter != nullptr && _inverter->serial() != currentInverter->serial()) { - return shutdown(Status::InverterChanged); + shutdown(Status::InverterChanged); + return; } // update our pointer as the configuration might have changed @@ -177,11 +194,13 @@ void PowerLimiterClass::loop() // the normal mode of operation requires a valid // power meter reading to calculate a power limit if (!config.PowerMeter_Enabled) { - return shutdown(Status::PowerMeterDisabled); + shutdown(Status::PowerMeterDisabled); + return; } if (millis() - PowerMeter.getLastPowerMeterUpdate() > (30 * 1000)) { - return shutdown(Status::PowerMeterTimeout); + shutdown(Status::PowerMeterTimeout); + return; } // concerns both power limits and start/stop/restart commands and is @@ -332,7 +351,8 @@ void PowerLimiterClass::unconditionalSolarPassthrough(std::shared_ptr inver // Stop the inverter if limit is below threshold. if (newPowerLimit < config.PowerLimiter_LowerPowerLimit) { - shutdown(Status::LowerLimitUndercut); - return true; + // the status must not change outside of loop(). this condition is + // communicated through log messages already. + return shutdown(); } // enforce configured upper power limit