diff --git a/docs/Schnittstelle.md b/docs/Schnittstelle.md index 4eec288..9fda990 100644 --- a/docs/Schnittstelle.md +++ b/docs/Schnittstelle.md @@ -90,6 +90,12 @@ melden, ohne dass der Manager seine interne Geraetelogik kennen muss. ## Feste Regeln - Prioritaeten beginnen bei 0; eine kleinere Zahl bedeutet hoehere Prioritaet. +- Jeder verfuegbare, synchronisierte Verbraucher mit einem nicht leeren + `Leistungswerte_W`-Angebot erhaelt einen Sollwert aus genau diesem Angebot. +- `AenderungMoeglich=false` kennzeichnet ein fixes Angebot und ist kein + Ausschlussgrund. Ein Angebot `[11000]` fuehrt deshalb zwingend zu `11000 W`. +- Prioritaeten verteilen nur die ueber den jeweiligen Mindestwert hinaus + verfuegbare Leistung; sie duerfen keinen angebotenen Mindestwert verdrängen. - `Leistungsquelle`: 0 nicht vorhanden, 1 berechnet, 2 gemessen. - Bei Leistungsquelle 0 ist `Istleistung_W` zwingend `null`. - Leistungsbereiche enthalten jeden ganzen Wattwert von `Von_W` bis `Bis_W`. diff --git a/docs/module/Manager/README.md b/docs/module/Manager/README.md index dbb8876..e61e765 100644 --- a/docs/module/Manager/README.md +++ b/docs/module/Manager/README.md @@ -302,14 +302,19 @@ der aktiven Betriebsart und der aktuellen Leistung aller frisch gemeldeten Verbraucher. Eine gültige Istleistung wird bevorzugt; fehlt sie, bleibt der im Zustand bestätigte Sollwert konservativ reserviert. -Nicht verfügbare, nicht änderbare oder aktuell angebotlose Verbraucher behalten -ihre Leistung und erhalten keine neue Vorgabe. Die übrigen Verbraucher werden -nach der gemeldeten PV- beziehungsweise Peak-Priorität und danach stabil nach -Instanz-ID sortiert. Von ihrem jeweils kleinsten erlaubten Leistungswert aus -wird das Budget in dieser Reihenfolge aufgefüllt. Einzelwerte und ganzzahlige -Bereiche werden direkt verarbeitet; Lücken werden nie durch unzulässige Werte -geschlossen. Eine verbleibende Abweichung wird im `Verbraucherstatus` -dokumentiert. +Nicht verfügbare oder aktuell angebotlose Verbraucher behalten ihre Leistung +und erhalten keine neue Vorgabe. Jeder verfügbare, synchronisierte Verbraucher +mit einem nicht leeren Angebot erhält dagegen einen Sollwert aus diesem Angebot. +Das gilt auch bei `AenderungMoeglich=false`: Ein fixes Angebot wie `[11000]` +wird mit genau `11000 W` zugeteilt. + +Die Verbraucher werden nach der gemeldeten PV- beziehungsweise Peak-Priorität +und danach stabil nach Instanz-ID sortiert. Zuerst reserviert der Manager für +jeden Verbraucher den kleinsten erlaubten Leistungswert. Nur das danach +verbleibende Budget wird in Prioritätsreihenfolge aufgefüllt. Einzelwerte und +ganzzahlige Bereiche werden direkt verarbeitet; Lücken werden nie durch +unzulässige Werte geschlossen. Eine verbleibende Abweichung wird im +`Verbraucherstatus` dokumentiert. Die Betriebsart wechselt unterhalb des Solar-Sollwerts zu `PV` und oberhalb der wirksamen Lastspitzengrenze zu `Peak`. Zwischen den beiden Zielwerten diff --git a/libs/ManagerRegler.php b/libs/ManagerRegler.php index bde24b4..7f0d10e 100644 --- a/libs/ManagerRegler.php +++ b/libs/ManagerRegler.php @@ -120,7 +120,7 @@ final class ManagerRegler $aktuelleLeistung += $leistung; $daten = $eintrag['Daten']; - if (!$daten['Verfuegbar'] || !$daten['AenderungMoeglich'] || $daten['Leistungswerte_W'] === []) { + if (!$daten['Verfuegbar'] || $daten['Leistungswerte_W'] === []) { $nichtSteuerbar += $leistung; continue; } diff --git a/tests/ManagerReglerTest.php b/tests/ManagerReglerTest.php index 31c840d..d9ba6b7 100644 --- a/tests/ManagerReglerTest.php +++ b/tests/ManagerReglerTest.php @@ -113,19 +113,19 @@ final class ManagerReglerTest extends TestCase self::assertSame(500, $ergebnis['Restabweichung_W']); } - public function testNichtAenderbarerVerbraucherReserviertSeineIstleistung(): void + public function testFestwertWirdUnabhaengigVonAenderungMoeglichZugewiesen(): void { - $fest = $this->verbraucher(10, 0, [0, 3000], 3000, false); + $fest = $this->verbraucher(10, 0, [3000], 3000, false); $steuerbar = $this->verbraucher(20, 1, [0, ['Von_W' => 1, 'Bis_W' => 4000]], 0); $ergebnis = ManagerRegler::verteile(3000.0, 5000.0, [$fest, $steuerbar], 'PrioritaetPV'); - self::assertSame(3000, $ergebnis['NichtSteuerbar_W']); - self::assertSame([20 => 2000], $ergebnis['Sollwerte']); + self::assertSame(0, $ergebnis['NichtSteuerbar_W']); + self::assertSame([10 => 3000, 20 => 2000], $ergebnis['Sollwerte']); self::assertSame(0, $ergebnis['Restabweichung_W']); } - public function testVeralteterVerbraucherBlockiertUeberschussverteilungNicht(): void + public function testFestwerteWerdenMitHoeherPriorisierterBatterieZugewiesen(): void { $ergebnis = ManagerRegler::verteile( -2411.0, @@ -147,7 +147,13 @@ final class ManagerReglerTest extends TestCase self::assertSame(12562, $ergebnis['Verteilbudget_W']); self::assertSame( - [44234 => 2411, 29743 => 0, 38676 => 0], + [ + 44234 => -12288, + 29743 => 0, + 38676 => 0, + 43878 => 13850, + 46052 => 11000, + ], $ergebnis['Sollwerte'] ); self::assertSame(0, $ergebnis['Restabweichung_W']); @@ -155,7 +161,7 @@ final class ManagerReglerTest extends TestCase public function testFehlendeIstleistungFaelltAufAngenommenenSollwertZurueck(): void { - $verbraucher = $this->verbraucher(10, 0, [0, 3000], null, false); + $verbraucher = $this->verbraucher(10, 0, [3000], null, false); $verbraucher['Daten']['Leistungsquelle'] = Nachrichtenvertrag::LEISTUNGSQUELLE_NICHT_VORHANDEN; $verbraucher['Daten']['Istleistung_W'] = null; $verbraucher['Daten']['Zustand'][0]['Wert'] = 3000; @@ -163,6 +169,7 @@ final class ManagerReglerTest extends TestCase $ergebnis = ManagerRegler::verteile(3000.0, 0.0, [$verbraucher], 'PrioritaetPV'); self::assertSame(3000, $ergebnis['AktuelleVerbraucherleistung_W']); + self::assertSame([10 => 3000], $ergebnis['Sollwerte']); self::assertSame(-3000, $ergebnis['Restabweichung_W']); }