@@ -46,7 +46,6 @@ std::string const& PowerLimiterClass::getStatusText(PowerLimiterClass::Status st
46
46
{ Status::NoVeDirect, " VE.Direct disabled, connection broken, or data outdated" },
47
47
{ Status::Settling, " waiting for the system to settle" },
48
48
{ Status::Stable, " the system is stable, the last power limit is still valid" },
49
- { Status::LowerLimitUndercut, " calculated power limit undercuts configured lower limit" }
50
49
};
51
50
52
51
auto iter = texts.find (status);
@@ -73,26 +72,39 @@ void PowerLimiterClass::announceStatus(PowerLimiterClass::Status status)
73
72
_lastStatusPrinted = millis ();
74
73
}
75
74
76
- void PowerLimiterClass::shutdown (PowerLimiterClass::Status status)
75
+ /* *
76
+ * returns true if the inverter state was changed or is about to change, i.e.,
77
+ * if it is actually in need of a shutdown. returns false otherwise, i.e., the
78
+ * inverter is already (assumed to be) shut down.
79
+ */
80
+ bool PowerLimiterClass::shutdown (PowerLimiterClass::Status status)
77
81
{
78
82
announceStatus (status);
79
83
80
- if (_inverter == nullptr || !_inverter->isProducing () || !_inverter->isReachable ()) {
84
+ if (_inverter == nullptr || !_inverter->isProducing () ||
85
+ (_shutdownTimeout > 0 && _shutdownTimeout < millis ()) ) {
86
+ // we are actually (already) done with shutting down the inverter,
87
+ // or a shutdown attempt was initiated but it timed out.
81
88
_inverter = nullptr ;
82
- _shutdownInProgress = false ;
83
- return ;
89
+ _shutdownTimeout = 0 ;
90
+ return false ;
84
91
}
85
92
86
- _shutdownInProgress = true ;
93
+ if (!_inverter->isReachable ()) { return true ; } // retry later (until timeout)
94
+
95
+ // retry shutdown for a maximum amount of time before giving up
96
+ if (_shutdownTimeout == 0 ) { _shutdownTimeout = millis () + 10 * 1000 ; }
87
97
88
98
auto lastLimitCommandState = _inverter->SystemConfigPara ()->getLastLimitCommandSuccess ();
89
- if (CMD_PENDING == lastLimitCommandState) { return ; }
99
+ if (CMD_PENDING == lastLimitCommandState) { return true ; }
90
100
91
101
auto lastPowerCommandState = _inverter->PowerCommand ()->getLastPowerCommandSuccess ();
92
- if (CMD_PENDING == lastPowerCommandState) { return ; }
102
+ if (CMD_PENDING == lastPowerCommandState) { return true ; }
93
103
94
104
CONFIG_T& config = Configuration.get ();
95
105
commitPowerLimit (_inverter, config.PowerLimiter_LowerPowerLimit , false );
106
+
107
+ return true ;
96
108
}
97
109
98
110
void PowerLimiterClass::loop ()
@@ -107,19 +119,22 @@ void PowerLimiterClass::loop()
107
119
return announceStatus (Status::WaitingForValidTimestamp);
108
120
}
109
121
110
- if (_shutdownInProgress ) {
122
+ if (_shutdownTimeout > 0 ) {
111
123
// we transition from SHUTDOWN to OFF when we know the inverter was
112
124
// shut down. until then, we retry shutting it down. in this case we
113
125
// preserve the original status that lead to the decision to shut down.
114
- return shutdown (_lastStatus);
126
+ shutdown ();
127
+ return ;
115
128
}
116
129
117
130
if (!config.PowerLimiter_Enabled ) {
118
- return shutdown (Status::DisabledByConfig);
131
+ shutdown (Status::DisabledByConfig);
132
+ return ;
119
133
}
120
134
121
135
if (PL_MODE_FULL_DISABLE == _mode) {
122
- return shutdown (Status::DisabledByMqtt);
136
+ shutdown (Status::DisabledByMqtt);
137
+ return ;
123
138
}
124
139
125
140
std::shared_ptr<InverterAbstract> currentInverter =
@@ -128,13 +143,15 @@ void PowerLimiterClass::loop()
128
143
// in case of (newly) broken configuration, shut down
129
144
// the last inverter we worked with (if any)
130
145
if (currentInverter == nullptr ) {
131
- return shutdown (Status::InverterInvalid);
146
+ shutdown (Status::InverterInvalid);
147
+ return ;
132
148
}
133
149
134
150
// if the DPL is supposed to manage another inverter now, we first
135
151
// shut down the previous one, if any. then we pick up the new one.
136
152
if (_inverter != nullptr && _inverter->serial () != currentInverter->serial ()) {
137
- return shutdown (Status::InverterChanged);
153
+ shutdown (Status::InverterChanged);
154
+ return ;
138
155
}
139
156
140
157
// update our pointer as the configuration might have changed
@@ -177,11 +194,13 @@ void PowerLimiterClass::loop()
177
194
// the normal mode of operation requires a valid
178
195
// power meter reading to calculate a power limit
179
196
if (!config.PowerMeter_Enabled ) {
180
- return shutdown (Status::PowerMeterDisabled);
197
+ shutdown (Status::PowerMeterDisabled);
198
+ return ;
181
199
}
182
200
183
201
if (millis () - PowerMeter.getLastPowerMeterUpdate () > (30 * 1000 )) {
184
- return shutdown (Status::PowerMeterTimeout);
202
+ shutdown (Status::PowerMeterTimeout);
203
+ return ;
185
204
}
186
205
187
206
// concerns both power limits and start/stop/restart commands and is
@@ -332,7 +351,8 @@ void PowerLimiterClass::unconditionalSolarPassthrough(std::shared_ptr<InverterAb
332
351
CONFIG_T& config = Configuration.get ();
333
352
334
353
if (!config.Vedirect_Enabled || !VeDirect.isDataValid ()) {
335
- return shutdown (Status::NoVeDirect);
354
+ shutdown (Status::NoVeDirect);
355
+ return ;
336
356
}
337
357
338
358
int32_t solarPower = VeDirect.veFrame .V * VeDirect.veFrame .I ;
@@ -487,8 +507,9 @@ bool PowerLimiterClass::setNewPowerLimit(std::shared_ptr<InverterAbstract> inver
487
507
488
508
// Stop the inverter if limit is below threshold.
489
509
if (newPowerLimit < config.PowerLimiter_LowerPowerLimit ) {
490
- shutdown (Status::LowerLimitUndercut);
491
- return true ;
510
+ // the status must not change outside of loop(). this condition is
511
+ // communicated through log messages already.
512
+ return shutdown ();
492
513
}
493
514
494
515
// enforce configured upper power limit
0 commit comments