diff --git a/include/PowerLimiterInverter.h b/include/PowerLimiterInverter.h index ccd1db127..97030a995 100644 --- a/include/PowerLimiterInverter.h +++ b/include/PowerLimiterInverter.h @@ -75,9 +75,17 @@ class PowerLimiterInverter { protected: PowerLimiterInverter(bool verboseLogging, PowerLimiterInverterConfig const& config); + enum class Eligibility : unsigned { + Unreachable, + SendingCommandsDisabled, + MaxOutputUnknown, + CurrentLimitUnknown, + Eligible + }; + // returns false if the inverter cannot participate // in achieving the requested change in power output - bool isEligible() const; + Eligibility isEligible() const; uint16_t getCurrentLimitWatts() const; diff --git a/src/PowerLimiterBatteryInverter.cpp b/src/PowerLimiterBatteryInverter.cpp index f4eaf1a6b..f4250f4e3 100644 --- a/src/PowerLimiterBatteryInverter.cpp +++ b/src/PowerLimiterBatteryInverter.cpp @@ -5,7 +5,7 @@ PowerLimiterBatteryInverter::PowerLimiterBatteryInverter(bool verboseLogging, Po uint16_t PowerLimiterBatteryInverter::getMaxReductionWatts(bool allowStandby) const { - if (!isEligible()) { return 0; } + if (isEligible() != Eligibility::Eligible) { return 0; } if (!isProducing()) { return 0; } @@ -18,7 +18,7 @@ uint16_t PowerLimiterBatteryInverter::getMaxReductionWatts(bool allowStandby) co uint16_t PowerLimiterBatteryInverter::getMaxIncreaseWatts() const { - if (!isEligible()) { return 0; } + if (isEligible() != Eligibility::Eligible) { return 0; } if (!isProducing()) { return getConfiguredMaxPowerWatts(); @@ -38,7 +38,7 @@ uint16_t PowerLimiterBatteryInverter::getMaxIncreaseWatts() const uint16_t PowerLimiterBatteryInverter::applyReduction(uint16_t reduction, bool allowStandby) { - if (!isEligible()) { return 0; } + if (isEligible() != Eligibility::Eligible) { return 0; } if (reduction == 0) { return 0; } @@ -67,7 +67,7 @@ uint16_t PowerLimiterBatteryInverter::applyReduction(uint16_t reduction, bool al uint16_t PowerLimiterBatteryInverter::applyIncrease(uint16_t increase) { - if (!isEligible()) { return 0; } + if (isEligible() != Eligibility::Eligible) { return 0; } if (increase == 0) { return 0; } diff --git a/src/PowerLimiterInverter.cpp b/src/PowerLimiterInverter.cpp index c4fc0e9f8..09c23b545 100644 --- a/src/PowerLimiterInverter.cpp +++ b/src/PowerLimiterInverter.cpp @@ -35,22 +35,24 @@ PowerLimiterInverter::PowerLimiterInverter(bool verboseLogging, PowerLimiterInve snprintf(_logPrefix, sizeof(_logPrefix), "[DPL inverter %s]:", _serialStr); } -bool PowerLimiterInverter::isEligible() const +PowerLimiterInverter::Eligibility PowerLimiterInverter::isEligible() const { - if (!isReachable() || !isSendingCommandsEnabled()) { return false; } + if (!isReachable()) { return Eligibility::Unreachable; } - // after startup, the limit effective at the inverter is not known. the - // respective message to request this info is only sent after a significant - // backoff (4 minutes). this is to avoid error messages to appear in the - // inverter's event log. we will wait until the current limit is known. - if (getCurrentLimitWatts() == 0) { return false; } + if (!isSendingCommandsEnabled()) { return Eligibility::SendingCommandsDisabled; } // the model-dependent maximum AC power output is only known after the // first DevInfoSimpleCommand succeeded. we desperately need this info, so // the inverter is not eligible until this value is known. - if (getInverterMaxPowerWatts() == 0) { return false; } + if (getInverterMaxPowerWatts() == 0) { return Eligibility::MaxOutputUnknown; } + + // after startup, the limit effective at the inverter is not known. the + // respective message to request this info is only sent after a significant + // backoff (~5 minutes, see upstream FAQ). this is to avoid error messages + // to appear in the inverter's event log. + if (getCurrentLimitWatts() == 0) { return Eligibility::CurrentLimitUnknown; } - return true; + return Eligibility::Eligible; } bool PowerLimiterInverter::update() @@ -62,6 +64,27 @@ bool PowerLimiterInverter::update() return false; }; + switch (isEligible()) { + case Eligibility::Eligible: + break; + + case Eligibility::CurrentLimitUnknown: + // we actually can and must do something about this: set the configured + // lower power limit. the inverter becomes eligible shortly and + // inverters whose current limit is not fetched for some reason (see + // #1427) are "woken up". + if (!_oTargetPowerLimitWatts.has_value()) { + MessageOutput.printf("%s bootstrapping by setting " + "lower power limit\r\n", _logPrefix); + _oTargetPowerLimitWatts = _config.LowerPowerLimit; + } + break; + + default: + return reset(); + break; + } + // do not reset _updateTimeouts below if no state change requested if (!_oTargetPowerState.has_value() && !_oTargetPowerLimitWatts.has_value()) { return reset(); @@ -286,6 +309,25 @@ void PowerLimiterInverter::debug() const { if (!_verboseLogging) { return; } + String eligibility("disqualified"); + switch (isEligible()) { + case Eligibility::Unreachable: + eligibility += " (unreachable)"; + break; + case Eligibility::SendingCommandsDisabled: + eligibility += " (sending commands disabled)"; + break; + case Eligibility::MaxOutputUnknown: + eligibility += " (max output unknown)"; + break; + case Eligibility::CurrentLimitUnknown: + eligibility += " (current limit unknown)"; + break; + case Eligibility::Eligible: + eligibility = "eligible"; + break; + } + MessageOutput.printf( "%s\r\n" " %s-powered, %s %d W\r\n" @@ -299,8 +341,7 @@ void PowerLimiterInverter::debug() const _config.LowerPowerLimit, getCurrentLimitWatts(), _config.UpperPowerLimit, getInverterMaxPowerWatts(), (isSendingCommandsEnabled()?"enabled":"disabled"), - (isReachable()?"reachable":"offline"), - (isEligible()?"eligible":"disqualified"), + (isReachable()?"reachable":"offline"), eligibility.c_str(), getMaxReductionWatts(false), getMaxReductionWatts(true), getMaxIncreaseWatts(), (_oTargetPowerLimitWatts.has_value()?*_oTargetPowerLimitWatts:-1), (_oTargetPowerLimitWatts.has_value()?"update":"unchanged"), diff --git a/src/PowerLimiterSolarInverter.cpp b/src/PowerLimiterSolarInverter.cpp index 548e7c047..1acaa62e7 100644 --- a/src/PowerLimiterSolarInverter.cpp +++ b/src/PowerLimiterSolarInverter.cpp @@ -6,7 +6,7 @@ PowerLimiterSolarInverter::PowerLimiterSolarInverter(bool verboseLogging, PowerL uint16_t PowerLimiterSolarInverter::getMaxReductionWatts(bool) const { - if (!isEligible()) { return 0; } + if (isEligible() != Eligibility::Eligible) { return 0; } auto low = std::min(getCurrentLimitWatts(), getCurrentOutputAcWatts()); if (low <= _config.LowerPowerLimit) { return 0; } @@ -16,7 +16,7 @@ uint16_t PowerLimiterSolarInverter::getMaxReductionWatts(bool) const uint16_t PowerLimiterSolarInverter::getMaxIncreaseWatts() const { - if (!isEligible()) { return 0; } + if (isEligible() != Eligibility::Eligible) { return 0; } // the maximum increase possible for this inverter int16_t maxTotalIncrease = getConfiguredMaxPowerWatts() - getCurrentOutputAcWatts(); @@ -75,7 +75,7 @@ uint16_t PowerLimiterSolarInverter::getMaxIncreaseWatts() const uint16_t PowerLimiterSolarInverter::applyReduction(uint16_t reduction, bool) { - if (!isEligible()) { return 0; } + if (isEligible() != Eligibility::Eligible) { return 0; } if (reduction == 0) { return 0; } @@ -90,7 +90,7 @@ uint16_t PowerLimiterSolarInverter::applyReduction(uint16_t reduction, bool) uint16_t PowerLimiterSolarInverter::applyIncrease(uint16_t increase) { - if (!isEligible()) { return 0; } + if (isEligible() != Eligibility::Eligible) { return 0; } if (increase == 0) { return 0; }