Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
schlimmchen committed Feb 28, 2024
1 parent 110526b commit faa15f3
Showing 1 changed file with 16 additions and 13 deletions.
29 changes: 16 additions & 13 deletions src/PowerLimiter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,23 +527,27 @@ int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr<InverterAbstract> inve

/**
* updates the inverter state (power production and limit). returns true if a
* change to its state was requested.
*
* TODO(schlimmchen): there is no guarantee that the limit update will complete
* successfully before the power control request enabled the inverter. this
* should be a two-step process, where the limit is updated und repeated until
* set as desired, and then the inverter is started.
* change to its state was requested. this function only requests one change
* (limit value or production on/off) at a time.
*/
bool PowerLimiterClass::commitPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t newLimitAbs, bool enablePowerProduction)
{
bool change = false;

// disable power production as soon as possible.
// setting the power limit is less important.
if (!enablePowerProduction && inverter->isProducing()) {
MessageOutput.println("[DPL::commitPowerLimit] Stopping inverter...");
inverter->sendPowerControlRequest(false);
change = true;
return true;
}

auto const& config = Configuration.get();

// make sure that the inverter starts up with its limit set to the lower
// power limit. if the DPL was in control of the inverter and if the DPL
// shut down the inverter previously, the limit should already be set to
// this value (unless a transmit error occurred).
if (enablePowerProduction && !inverter->isProducing()) {
newLimitAbs = config.PowerLimiter.LowerPowerLimit;
}

// early in the loop we make it a pre-requisite that this
Expand All @@ -554,7 +558,6 @@ bool PowerLimiterClass::commitPowerLimit(std::shared_ptr<InverterAbstract> inver
auto currentLimitAbs = static_cast<int32_t>(currentLimitPercent * maxPower / 100);
auto diff = std::abs(currentLimitAbs - newLimitAbs);

auto const& config = Configuration.get();
auto hysteresis = config.PowerLimiter.TargetPowerConsumptionHysteresis;

if (hysteresis < (maxPower / 100)) {
Expand All @@ -574,18 +577,18 @@ bool PowerLimiterClass::commitPowerLimit(std::shared_ptr<InverterAbstract> inver
PowerLimitControlType::AbsolutNonPersistent);

_lastRequestedPowerLimit = newLimitAbs;
change = true;
return true;
}

// enable power production only after setting the desired limit,
// such that an older, greater limit will not cause power spikes.
if (enablePowerProduction && !inverter->isProducing()) {
MessageOutput.println("[DPL::commitPowerLimit] Starting up inverter...");
inverter->sendPowerControlRequest(true);
change = true;
return true;
}

return change;
return false;
}

/**
Expand Down

4 comments on commit faa15f3

@spcqike
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,

ich hoffe, es ist ok wenn ich meine Gedanken zu den Anpassungen hier äußere :), auch wenn es (ich vermute) Work In Progress ist? :D

Kann es sein, dass die Abschaltung so nicht mehr funktioniert, wie gedacht?
der PowerLimiterClass::shutdown() ruft am Ende den commitPowerLimit auf. damit setzte er bisher, da der commitPowerLimit mehrere Befehle in die Warteschlange schickte, sowohl das Limit auf das untere Leistungslimit, als auch den Wechselrichter in den "stopp".

Mit der Umstellung auf "1 Befehl pro Durchlauf", die ich gut finde, würde das so aktuell nicht mehr funktionieren.

eventuell ist das aber gar nicht wichtig, wenn wir beim Starten sicherstellen, dass vor dem starten das untere Leistungslimit als Limit gesetzt ist. oder?

ein weiterer Punkt der mir auffällt, wo ich mir aber nicht sicher bin wie es sich im Einsatz verhalten wird:
if (diff > hysteresis) setzt immer einen neues Limit und verlässt anschließend die Funktion. Wenn man nun einen schwankenden Verbrauch hat, würde er ggf. mehrmals in Folge nur das Limit anpassen, aber nie starten.

Meine Idee und Reihenfolge der Umsetzung wäre:

  1. stoppe, wenn gestoppt werden soll
  2. starte, wenn gestarte werden soll, prüfe dabei, ob das Limit dem untersten Limit entspricht, wenn nicht, setze zuerst das unterste Limit (damit würde er im zweiten Durchlauf starten, oder?)
  3. wenn der Inverter läuft und diff > hysteresis wahr ist, dann passe das aktuelle Limit an.

Wie siehst du das?

Gruß

@schlimmchen
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ich seh das so, dass ich es zwar spannend finde, dass du dich für meine Arbeit interessierst, aber das ist wirklich nur ein "oh, ich committe das schnell, weil ich was anderes tun muss", und ich hab noch nicht einmal sichergestellt, dass es wenigstens kompiliert. Es war auch insbesondere mitten in einem Gedankengang.

Wenn man nun einen schwankenden Verbrauch hat, würde er ggf. mehrmals in Folge nur das Limit anpassen, aber nie starten.

Das stimmt nicht, wegen

    if (enablePowerProduction && !inverter->isProducing()) {
        newLimitAbs = config.PowerLimiter.LowerPowerLimit;
    }

Wenn der Inverter gestartet werden soll, dann wird das lower power limit gesetzt. Egal wie der Verbrauch schwankt.

Ich versuche seit gestern Abend ein neues Konzept einzuführen. Erwähnt hatte ich es schon: In die loop eingreifen. Ideal wäre es, wenn diese Funktion (lokal heißt sie seit gestern Abend anders) versprechen würde, dass sie sich darum kümmert, dass die zuvor angeforderten Zustände tatsächlich erreicht werden und entsprechend Rückmeldung gibt. Diese Funktion wird dann mehr oder weniger als erstes aufgerufen in der DPL loop, und solange die sagt "ich brauch noch einen Aufruf, der Inverter ist noch nicht so eingestellt wie vorhin gefordert" passiert im DPL nichts außer dass im nächsten loop wieder diese Funktion aufgerufen wird.

Da stecke ich jetzt mitten drin. Das größte Problem ist zu entscheiden, ob ein Limit wie gewünscht gesetzt wurde, oder noch nicht. Das hat damit zu tun, dass die Hoymiles lib nur relative Limits speichert, die auch noch berechnet sind wenn man zuvor absolute Limits angefordert hat. Da gibt es also mindestens Rundungsfehler zu berücksichtigen. Vermutlich werde ich jedes absolute Limit in ein relatives umrechnen und dann prüfen, ob das relative Limit genau wie gefordert zurückgemeldet wird vom Inverter.

@spcqike
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Das stimmt nicht, wegen

oh, das habe ich wohl tatsächlich (ebenfalls) übersehen .... du hast natürlich Recht :)

Da gibt es also mindestens Rundungsfehler zu berücksichtigen. Vermutlich werde ich jedes absolute Limit in ein relatives umrechnen und dann prüfen, ob das relative Limit genau wie gefordert zurückgemeldet wird vom Inverter.

kann man relative Limits als Float übergeben? zumindest in der Weboberfläche geht es nicht. sowohl "45,1" als auch "45.1" werden als "45" übergeben.

bei absoluten 271W hingegen werden nach dem Übertragen 45.1% bzw. 270.6W angezeigt (bei meinem HM-600)
grafik

2 Nachkommastellen scheint es nicht zu geben. 271W wären sonst 45.16666.

@schlimmchen
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: Mein HM-1500 beispielsweise erhält zwar von der Hoymiles Lib einen Wert in Promille, aber er rundet dann selbstständig (scheinbar immer nach unten) auf ganze Prozent. Das meldet er dann auch so in Promille. Jedenfalls sagt der Code der Hoymiles Lib das. Wer weiß, wie sich die anderen Inverter so verhalten... In helgeerbe#708 habe ich implementiert, dass wir annehmen, dass ein Limit Command, das erfolgreich war und nach einem Update-Zyklus verschickt wurde, jenes vom DPL war und dass das Limit dann auch übernommen wurde.

Please sign in to comment.