From 9999fa28e8c15cd6f4ebd40143908ff20c80b090 Mon Sep 17 00:00:00 2001 From: MalteSchm Date: Wed, 5 Apr 2023 20:40:07 +0200 Subject: [PATCH] refactor state machine merging --- include/PowerLimiter.h | 8 +-- src/PowerLimiter.cpp | 136 +++++++++++++++-------------------------- 2 files changed, 52 insertions(+), 92 deletions(-) diff --git a/include/PowerLimiter.h b/include/PowerLimiter.h index 6e9357a1f..7ef471781 100644 --- a/include/PowerLimiter.h +++ b/include/PowerLimiter.h @@ -8,10 +8,8 @@ #include typedef enum { - STATE_DISCOVER = 0, - STATE_OFF, - STATE_CONSUME_SOLAR_POWER_ONLY, - STATE_NORMAL_OPERATION + STATE_PL_SHUTDOWN = 0, + STATE_ACTIVE } plStates; typedef enum { @@ -34,7 +32,7 @@ class PowerLimiterClass { uint32_t _lastLoop = 0; int32_t _lastRequestedPowerLimit = 0; uint32_t _lastLimitSetTime = 0; - plStates _plState = STATE_DISCOVER; + plStates _plState = STATE_ACTIVE; bool _disabled = false; float _powerMeter1Power; diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index 9bd91c4d4..9240ff539 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -22,26 +22,46 @@ void PowerLimiterClass::init() void PowerLimiterClass::loop() { CONFIG_T& config = Configuration.get(); - - if (!config.PowerLimiter_Enabled - || _disabled - || !config.PowerMeter_Enabled + + // Run inital checks to make sure we have met the basic conditions + if ( !config.PowerMeter_Enabled || !Hoymiles.getRadio()->isIdle() || (millis() - _lastCommandSent) < (config.PowerLimiter_Interval * 1000) || (millis() - _lastLoop) < (config.PowerLimiter_Interval * 1000)) { - if (!config.PowerLimiter_Enabled - || _disabled) - _plState = STATE_DISCOVER; // ensure STATE_DISCOVER is set, if PowerLimiter will be enabled. return; } _lastLoop = millis(); + // Debug state transistions + + + MessageOutput.printf("****************** PL STATE: %i\r\n", _plState); + std::shared_ptr inverter = Hoymiles.getInverterByPos(config.PowerLimiter_InverterId); if (inverter == nullptr || !inverter->isReachable()) { return; } + // Make sure inverter is turned off if PL is disabled by user + // Make sure inverter is turned off when lower battery threshold is reached + // In this case we willbe in some state and want to reach STATE_PL_SHUTDOWN + if ((!config.PowerLimiter_Enabled && _plState != STATE_PL_SHUTDOWN) + || isStopThresholdReached(inverter)) { + if (inverter->isProducing()) { + MessageOutput.printf("PL initiated inverter shutdown.\r\n"); + inverter->sendPowerControlRequest(Hoymiles.getRadio(), false); + } else { + _plState = STATE_PL_SHUTDOWN; + } + return; + } + + // PL is disabled + if (!config.PowerLimiter_Enabled) { + return; + } + float dcVoltage = inverter->Statistics()->getChannelFieldValue(TYPE_DC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_UDC); float acPower = inverter->Statistics()->getChannelFieldValue(TYPE_AC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_PAC); float correctedDcVoltage = dcVoltage + (acPower * config.PowerLimiter_VoltageLoadCorrectionFactor); @@ -61,87 +81,26 @@ void PowerLimiterClass::loop() dcVoltage, config.PowerLimiter_VoltageStartThreshold, config.PowerLimiter_VoltageStopThreshold, inverter->isProducing()); } - while(true) { - switch(_plState) { - case STATE_DISCOVER: - if (!inverter->isProducing() || isStopThresholdReached(inverter)) { - _plState = STATE_OFF; - } - else if (canUseDirectSolarPower()) { - _plState = STATE_CONSUME_SOLAR_POWER_ONLY; - } - else { - _plState = STATE_NORMAL_OPERATION; - } - break; - case STATE_OFF: - // if on turn off - if (inverter->isProducing()) { - MessageOutput.printf("[PowerLimiterClass::loop] DC voltage: %.2f Corrected DC voltage: %.2f...\r\n", - dcVoltage, correctedDcVoltage); - setNewPowerLimit(inverter, -1); - return; - } - - // do nothing if battery is empty - if (isStopThresholdReached(inverter)) - return; - // check for possible state changes - if (canUseDirectSolarPower()) { - _plState = STATE_CONSUME_SOLAR_POWER_ONLY; - } - if (isStartThresholdReached(inverter)) { - _plState = STATE_NORMAL_OPERATION; - } - return; - break; - case STATE_CONSUME_SOLAR_POWER_ONLY: { - int32_t newPowerLimit = calcPowerLimit(inverter, true); - if (isStopThresholdReached(inverter)) { - _plState = STATE_OFF; - break; - } - if (isStartThresholdReached(inverter)) { - _plState = STATE_NORMAL_OPERATION; - break; - } - - if (!canUseDirectSolarPower()) { - if (config.PowerLimiter_BatteryDrainStategy == EMPTY_AT_NIGHT) - _plState = STATE_NORMAL_OPERATION; - else - _plState = STATE_OFF; - break; - } - - setNewPowerLimit(inverter, newPowerLimit); - return; - break; - } - case STATE_NORMAL_OPERATION: { - int32_t newPowerLimit = calcPowerLimit(inverter, false); - if (isStopThresholdReached(inverter)) { - _plState = STATE_OFF; - break; - } - if (!isStartThresholdReached(inverter) && canUseDirectSolarPower() && (config.PowerLimiter_BatteryDrainStategy == EMPTY_AT_NIGHT)) { - _plState = STATE_CONSUME_SOLAR_POWER_ONLY; - break; - } - - // check if grid power consumption is not within the upper and lower threshold of the target consumption - if (newPowerLimit >= (config.PowerLimiter_TargetPowerConsumption - config.PowerLimiter_TargetPowerConsumptionHysteresis) && - newPowerLimit <= (config.PowerLimiter_TargetPowerConsumption + config.PowerLimiter_TargetPowerConsumptionHysteresis) && - _lastRequestedPowerLimit >= (config.PowerLimiter_TargetPowerConsumption - config.PowerLimiter_TargetPowerConsumptionHysteresis) && - _lastRequestedPowerLimit <= (config.PowerLimiter_TargetPowerConsumption + config.PowerLimiter_TargetPowerConsumptionHysteresis) ) { - return; - } - setNewPowerLimit(inverter, newPowerLimit);; - return; - break; - } - } + // Check if we need to move state away from STATE_PL_SHUTDOWN + if (_plState == STATE_PL_SHUTDOWN) { + + // Allow discharge when start threshold reached + // This is also the trigger for drain strategy: EMPTY_WHEN_FULL + if (isStartThresholdReached(inverter)) { + _plState = STATE_ACTIVE; + } + + // Allow discharge when drain strategy is EMPTY_AT_NIGHT + if (config.PowerLimiter_BatteryDrainStategy == EMPTY_AT_NIGHT) { + _plState = STATE_ACTIVE; + } + + return; } + + int32_t newPowerLimit = calcPowerLimit(inverter, canUseDirectSolarPower()); + MessageOutput.printf("****************************** Powerlimit: %i\r\n", newPowerLimit); + setNewPowerLimit(inverter, newPowerLimit); } plStates PowerLimiterClass::getPowerLimiterState() { @@ -177,6 +136,9 @@ bool PowerLimiterClass::canUseDirectSolarPower() return true; } + + + int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr inverter, bool consumeSolarPowerOnly) { CONFIG_T& config = Configuration.get();