refactor state machine
This commit is contained in:
parent
86ecc62b33
commit
a893260de0
@ -8,10 +8,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
STATE_DISCOVER = 0,
|
STATE_PL_SHUTDOWN = 0,
|
||||||
STATE_OFF,
|
STATE_ACTIVE
|
||||||
STATE_CONSUME_SOLAR_POWER_ONLY,
|
|
||||||
STATE_NORMAL_OPERATION
|
|
||||||
} plStates;
|
} plStates;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -32,7 +30,7 @@ private:
|
|||||||
uint32_t _lastLoop = 0;
|
uint32_t _lastLoop = 0;
|
||||||
int32_t _lastRequestedPowerLimit = 0;
|
int32_t _lastRequestedPowerLimit = 0;
|
||||||
uint32_t _lastLimitSetTime = 0;
|
uint32_t _lastLimitSetTime = 0;
|
||||||
plStates _plState = STATE_DISCOVER;
|
plStates _plState = STATE_ACTIVE;
|
||||||
|
|
||||||
float _powerMeter1Power;
|
float _powerMeter1Power;
|
||||||
float _powerMeter2Power;
|
float _powerMeter2Power;
|
||||||
|
|||||||
@ -23,23 +23,45 @@ void PowerLimiterClass::loop()
|
|||||||
{
|
{
|
||||||
CONFIG_T& config = Configuration.get();
|
CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
if (!config.PowerLimiter_Enabled
|
// Run inital checks to make sure we have met the basic conditions
|
||||||
|| !config.PowerMeter_Enabled
|
if ( !config.PowerMeter_Enabled
|
||||||
|| !Hoymiles.getRadio()->isIdle()
|
|| !Hoymiles.getRadio()->isIdle()
|
||||||
|| (millis() - _lastCommandSent) < (config.PowerLimiter_Interval * 1000)
|
|| (millis() - _lastCommandSent) < (config.PowerLimiter_Interval * 1000)
|
||||||
|| (millis() - _lastLoop) < (config.PowerLimiter_Interval * 1000)) {
|
|| (millis() - _lastLoop) < (config.PowerLimiter_Interval * 1000)) {
|
||||||
if (!config.PowerLimiter_Enabled)
|
|
||||||
_plState = STATE_DISCOVER; // ensure STATE_DISCOVER is set, if PowerLimiter will be enabled.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastLoop = millis();
|
_lastLoop = millis();
|
||||||
|
|
||||||
|
// Debug state transistions
|
||||||
|
|
||||||
|
|
||||||
|
MessageOutput.printf("****************** PL STATE: %i\r\n", _plState);
|
||||||
|
|
||||||
std::shared_ptr<InverterAbstract> inverter = Hoymiles.getInverterByPos(config.PowerLimiter_InverterId);
|
std::shared_ptr<InverterAbstract> inverter = Hoymiles.getInverterByPos(config.PowerLimiter_InverterId);
|
||||||
if (inverter == nullptr || !inverter->isReachable()) {
|
if (inverter == nullptr || !inverter->isReachable()) {
|
||||||
return;
|
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 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 acPower = inverter->Statistics()->getChannelFieldValue(TYPE_AC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_PAC);
|
||||||
float correctedDcVoltage = dcVoltage + (acPower * config.PowerLimiter_VoltageLoadCorrectionFactor);
|
float correctedDcVoltage = dcVoltage + (acPower * config.PowerLimiter_VoltageLoadCorrectionFactor);
|
||||||
@ -59,87 +81,26 @@ void PowerLimiterClass::loop()
|
|||||||
dcVoltage, config.PowerLimiter_VoltageStartThreshold, config.PowerLimiter_VoltageStopThreshold, inverter->isProducing());
|
dcVoltage, config.PowerLimiter_VoltageStartThreshold, config.PowerLimiter_VoltageStopThreshold, inverter->isProducing());
|
||||||
}
|
}
|
||||||
|
|
||||||
while(true) {
|
// Check if we need to move state away from STATE_PL_SHUTDOWN
|
||||||
switch(_plState) {
|
if (_plState == STATE_PL_SHUTDOWN) {
|
||||||
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
|
// Allow discharge when start threshold reached
|
||||||
if (isStopThresholdReached(inverter))
|
// This is also the trigger for drain strategy: EMPTY_WHEN_FULL
|
||||||
return;
|
|
||||||
// check for possible state changes
|
|
||||||
if (canUseDirectSolarPower()) {
|
|
||||||
_plState = STATE_CONSUME_SOLAR_POWER_ONLY;
|
|
||||||
}
|
|
||||||
if (isStartThresholdReached(inverter)) {
|
if (isStartThresholdReached(inverter)) {
|
||||||
_plState = STATE_NORMAL_OPERATION;
|
_plState = STATE_ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow discharge when drain strategy is EMPTY_AT_NIGHT
|
||||||
|
if (config.PowerLimiter_BatteryDrainStategy == EMPTY_AT_NIGHT) {
|
||||||
|
_plState = STATE_ACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t newPowerLimit = calcPowerLimit(inverter, canUseDirectSolarPower());
|
||||||
|
MessageOutput.printf("****************************** Powerlimit: %i\r\n", newPowerLimit);
|
||||||
setNewPowerLimit(inverter, newPowerLimit);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plStates PowerLimiterClass::getPowerLimiterState() {
|
plStates PowerLimiterClass::getPowerLimiterState() {
|
||||||
@ -154,7 +115,7 @@ bool PowerLimiterClass::canUseDirectSolarPower()
|
|||||||
{
|
{
|
||||||
CONFIG_T& config = Configuration.get();
|
CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
if (!config.PowerLimiter_SolarPassTroughEnabled
|
if (!config.PowerLimiter_SolarPassThroughEnabled
|
||||||
|| !config.Vedirect_Enabled) {
|
|| !config.Vedirect_Enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -167,6 +128,9 @@ bool PowerLimiterClass::canUseDirectSolarPower()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr<InverterAbstract> inverter, bool consumeSolarPowerOnly)
|
int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr<InverterAbstract> inverter, bool consumeSolarPowerOnly)
|
||||||
{
|
{
|
||||||
CONFIG_T& config = Configuration.get();
|
CONFIG_T& config = Configuration.get();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user