OpenDTU-old/include/PowerLimiter.h
Bernhard Kirchen 74330a5617 Feature: restart unresponsive inverter
we found that the inverter sometimes stops responding to commands,
especially to the "start producing" command. we now count the number of
consecutive timeouts when trying to send a new limit or power state
commands. after two timeouts were recorded, every additional timeout
will send a restart command to the inverter.

as a last resort, if the counter keeps climbing, the DTU is restarted.

notice that this only targets unresponsive inverters which are
reachable. unreachable inverters are not restarted and do not cause a
DTU reboot. this is important for solar-driven inverters, which are
unreachable during the night. the DPL will not calculate a new limit and
hence the updateInverter() method will do nothing while the target
inverter is unreachable.

publish the timeout counter to MQTT for monitoring purposes.
2024-04-24 16:15:01 +02:00

105 lines
3.6 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include <espMqttClient.h>
#include <Arduino.h>
#include <Hoymiles.h>
#include <memory>
#include <functional>
#include <optional>
#include <TaskSchedulerDeclarations.h>
#include <frozen/string.h>
#define PL_UI_STATE_INACTIVE 0
#define PL_UI_STATE_CHARGING 1
#define PL_UI_STATE_USE_SOLAR_ONLY 2
#define PL_UI_STATE_USE_SOLAR_AND_BATTERY 3
class PowerLimiterClass {
public:
enum class Status : unsigned {
Initializing,
DisabledByConfig,
DisabledByMqtt,
WaitingForValidTimestamp,
PowerMeterPending,
InverterInvalid,
InverterChanged,
InverterOffline,
InverterCommandsDisabled,
InverterLimitPending,
InverterPowerCmdPending,
InverterDevInfoPending,
InverterStatsPending,
CalculatedLimitBelowMinLimit,
UnconditionalSolarPassthrough,
NoVeDirect,
NoEnergy,
HuaweiPsu,
Stable,
};
void init(Scheduler& scheduler);
uint8_t getInverterUpdateTimeouts() const { return _inverterUpdateTimeouts; }
uint8_t getPowerLimiterState();
int32_t getLastRequestedPowerLimit() { return _lastRequestedPowerLimit; }
enum class Mode : unsigned {
Normal = 0,
Disabled = 1,
UnconditionalFullSolarPassthrough = 2
};
void setMode(Mode m) { _mode = m; }
Mode getMode() const { return _mode; }
void calcNextInverterRestart();
private:
void loop();
Task _loopTask;
int32_t _lastRequestedPowerLimit = 0;
bool _shutdownPending = false;
std::optional<uint32_t> _oInverterStatsMillis = std::nullopt;
std::optional<uint32_t> _oUpdateStartMillis = std::nullopt;
std::optional<int32_t> _oTargetPowerLimitWatts = std::nullopt;
std::optional<bool> _oTargetPowerState = std::nullopt;
Status _lastStatus = Status::Initializing;
uint32_t _lastStatusPrinted = 0;
uint32_t _lastCalculation = 0;
static constexpr uint32_t _calculationBackoffMsDefault = 128;
uint32_t _calculationBackoffMs = _calculationBackoffMsDefault;
Mode _mode = Mode::Normal;
std::shared_ptr<InverterAbstract> _inverter = nullptr;
bool _batteryDischargeEnabled = false;
uint32_t _nextInverterRestart = 0; // Values: 0->not calculated / 1->no restart configured / >1->time of next inverter restart in millis()
uint32_t _nextCalculateCheck = 5000; // time in millis for next NTP check to calulate restart
bool _fullSolarPassThroughEnabled = false;
bool _verboseLogging = true;
uint8_t _inverterUpdateTimeouts = 0;
frozen::string const& getStatusText(Status status);
void announceStatus(Status status);
bool shutdown(Status status);
bool shutdown() { return shutdown(_lastStatus); }
float getBatteryVoltage(bool log = false);
int32_t inverterPowerDcToAc(std::shared_ptr<InverterAbstract> inverter, int32_t dcPower);
void unconditionalSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
bool canUseDirectSolarPower();
bool calcPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t solarPower, bool batteryPower);
bool updateInverter();
bool setNewPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t newPowerLimit);
int32_t getSolarPower();
float getLoadCorrectedVoltage();
bool testThreshold(float socThreshold, float voltThreshold,
std::function<bool(float, float)> compare);
bool isStartThresholdReached();
bool isStopThresholdReached();
bool isBelowStopThreshold();
bool useFullSolarPassthrough();
};
extern PowerLimiterClass PowerLimiter;