DPL: clean up automatic inverter restart

This commit is contained in:
Bernhard Kirchen 2024-11-15 23:33:37 +01:00 committed by Bernhard Kirchen
parent cbffafaa28
commit a8f57d98a4
3 changed files with 50 additions and 50 deletions

View File

@ -54,7 +54,6 @@ public:
Mode getMode() const { return _mode; } Mode getMode() const { return _mode; }
bool usesBatteryPoweredInverter(); bool usesBatteryPoweredInverter();
bool isGovernedInverterProducing(); bool isGovernedInverterProducing();
void calcNextInverterRestart();
private: private:
void loop(); void loop();
@ -73,8 +72,7 @@ private:
std::deque<std::unique_ptr<PowerLimiterInverter>> _inverters; std::deque<std::unique_ptr<PowerLimiterInverter>> _inverters;
bool _batteryDischargeEnabled = false; bool _batteryDischargeEnabled = false;
bool _nighttimeDischarging = false; bool _nighttimeDischarging = false;
uint32_t _nextInverterRestart = 0; // Values: 0->not calculated / 1->no restart configured / >1->time of next inverter restart in millis() std::pair<bool, uint32_t> _nextInverterRestart = { false, 0 };
uint32_t _nextCalculateCheck = 5000; // time in millis for next NTP check to calulate restart
bool _fullSolarPassThroughEnabled = false; bool _fullSolarPassThroughEnabled = false;
bool _verboseLogging = true; bool _verboseLogging = true;
@ -100,6 +98,7 @@ private:
bool isStartThresholdReached(); bool isStartThresholdReached();
bool isStopThresholdReached(); bool isStopThresholdReached();
bool isBelowStopThreshold(); bool isBelowStopThreshold();
void calcNextInverterRestart();
bool isFullSolarPassthroughActive(); bool isFullSolarPassthroughActive();
}; };

View File

@ -135,6 +135,8 @@ void PowerLimiterClass::reloadConfig()
if (upInv) { _inverters.push_back(std::move(upInv)); } if (upInv) { _inverters.push_back(std::move(upInv)); }
} }
calcNextInverterRestart();
_reloadConfigFlag = false; _reloadConfigFlag = false;
} }
@ -215,29 +217,22 @@ void PowerLimiterClass::loop()
MessageOutput.println("[DPL::loop] ******************* ENTER **********************"); MessageOutput.println("[DPL::loop] ******************* ENTER **********************");
} }
// TODO(schlimmchen): comparison breaks when millis() wraps around. auto autoRestartInverters = [this]() -> void {
// Check if next inverter restart time is reached if (!_nextInverterRestart.first) { return; } // no automatic restarts
if ((_nextInverterRestart > 1) && (_nextInverterRestart <= millis())) {
MessageOutput.println("[DPL::loop] send inverter restart"); auto constexpr halfOfAllMillis = std::numeric_limits<uint32_t>::max() / 2;
auto diff = _nextInverterRestart.second - millis();
if (diff < halfOfAllMillis) { return; }
MessageOutput.println("[DPL::loop] send inverter restart command");
for (auto& upInv : _inverters) { for (auto& upInv : _inverters) {
if (!upInv->isSolarPowered()) { upInv->restart(); } if (!upInv->isSolarPowered()) { upInv->restart(); }
} }
calcNextInverterRestart();
}
// Check if NTP time is set and next inverter restart not calculated yet
if ((config.PowerLimiter.RestartHour >= 0) && (_nextInverterRestart == 0) ) {
// check every 5 seconds
if (_nextCalculateCheck < millis()) {
struct tm timeinfo;
if (getLocalTime(&timeinfo, 5)) {
calcNextInverterRestart(); calcNextInverterRestart();
} else { };
MessageOutput.println("[DPL::loop] inverter restart calculation: NTP not ready");
_nextCalculateCheck += 5000; autoRestartInverters();
}
}
}
auto getBatteryPower = [this,&config]() -> bool { auto getBatteryPower = [this,&config]() -> bool {
if (!usesBatteryPoweredInverter()) { return false; } if (!usesBatteryPoweredInverter()) { return false; }
@ -775,44 +770,51 @@ bool PowerLimiterClass::isBelowStopThreshold()
); );
} }
/// @brief calculate next inverter restart in millis
void PowerLimiterClass::calcNextInverterRestart() void PowerLimiterClass::calcNextInverterRestart()
{ {
auto const& config = Configuration.get(); auto const& config = Configuration.get();
// first check if restart is configured at all
if (config.PowerLimiter.RestartHour < 0) { if (config.PowerLimiter.RestartHour < 0) {
_nextInverterRestart = 1; _nextInverterRestart = { false, 0 };
MessageOutput.println("[DPL::calcNextInverterRestart] _nextInverterRestart disabled"); MessageOutput.println("[DPL::calcNextInverterRestart] automatic inverter restart disabled");
return; return;
} }
// read time from timeserver, if time is not synced then return
struct tm timeinfo; struct tm timeinfo;
if (getLocalTime(&timeinfo, 5)) { getLocalTime(&timeinfo, 5); // always succeeds as we call this method only
// from the DPL loop *after* we already made
// sure that time information is available.
// calculation first step is offset to next restart in minutes // calculation first step is offset to next restart in minutes
uint16_t dayMinutes = timeinfo.tm_hour * 60 + timeinfo.tm_min; uint16_t dayMinutes = timeinfo.tm_hour * 60 + timeinfo.tm_min;
uint16_t targetMinutes = config.PowerLimiter.RestartHour * 60; uint16_t targetMinutes = config.PowerLimiter.RestartHour * 60;
uint32_t restartMillis = 0;
if (config.PowerLimiter.RestartHour > timeinfo.tm_hour) { if (config.PowerLimiter.RestartHour > timeinfo.tm_hour) {
// next restart is on the same day // next restart is on the same day
_nextInverterRestart = targetMinutes - dayMinutes; restartMillis = targetMinutes - dayMinutes;
} else { } else {
// next restart is on next day // next restart is on next day
_nextInverterRestart = 1440 - dayMinutes + targetMinutes; restartMillis = 1440 - dayMinutes + targetMinutes;
} }
if (_verboseLogging) { if (_verboseLogging) {
MessageOutput.printf("[DPL::calcNextInverterRestart] Localtime read %d %d / configured RestartHour %d\r\n", timeinfo.tm_hour, timeinfo.tm_min, config.PowerLimiter.RestartHour); MessageOutput.printf("[DPL::calcNextInverterRestart] Localtime "
MessageOutput.printf("[DPL::calcNextInverterRestart] dayMinutes %d / targetMinutes %d\r\n", dayMinutes, targetMinutes); "read %02d:%02d / configured RestartHour %d\r\n", timeinfo.tm_hour,
MessageOutput.printf("[DPL::calcNextInverterRestart] next inverter restart in %d minutes\r\n", _nextInverterRestart); timeinfo.tm_min, config.PowerLimiter.RestartHour);
MessageOutput.printf("[DPL::calcNextInverterRestart] dayMinutes %d / "
"targetMinutes %d\r\n", dayMinutes, targetMinutes);
MessageOutput.printf("[DPL::calcNextInverterRestart] next inverter "
"restart in %d minutes\r\n", restartMillis);
} }
// then convert unit for next restart to milliseconds and add current uptime millis()
_nextInverterRestart *= 60000; // convert unit for next restart to milliseconds and add current uptime
_nextInverterRestart += millis(); restartMillis *= 60000;
} else { restartMillis += millis();
MessageOutput.println("[DPL::calcNextInverterRestart] getLocalTime not successful, no calculation");
_nextInverterRestart = 0; MessageOutput.printf("[DPL::calcNextInverterRestart] next inverter "
} "restart @ %d millis\r\n", restartMillis);
MessageOutput.printf("[DPL::calcNextInverterRestart] _nextInverterRestart @ %d millis\r\n", _nextInverterRestart);
_nextInverterRestart = { true, restartMillis };
} }
bool PowerLimiterClass::isFullSolarPassthroughActive() bool PowerLimiterClass::isFullSolarPassthroughActive()

View File

@ -109,7 +109,6 @@ void WebApiPowerLimiterClass::onAdminPost(AsyncWebServerRequest* request)
request->send(response); request->send(response);
PowerLimiter.triggerReloadingConfig(); PowerLimiter.triggerReloadingConfig();
PowerLimiter.calcNextInverterRestart();
// potentially make thresholds auto-discoverable // potentially make thresholds auto-discoverable
MqttHandlePowerLimiterHass.forceUpdate(); MqttHandlePowerLimiterHass.forceUpdate();