From 53fca5e2bb68b5c4914b2e36372036b42ad828f1 Mon Sep 17 00:00:00 2001 From: dh_Agent Date: Tue, 29 Sep 2026 17:23:25 +0000 Subject: [PATCH] feat(manager): Batteriesollwert aus Netzfahrplan anwenden --- Manager/module.php | 51 ++++++++++++++++++++++++++++++++++--- libs/ManagerRegler.php | 24 ++++++++++++++--- tests/ManagerReglerTest.php | 23 +++++++++++++++++ 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/Manager/module.php b/Manager/module.php index eb6e07a..0c9a076 100644 --- a/Manager/module.php +++ b/Manager/module.php @@ -761,13 +761,27 @@ class Manager extends IPSModule implements ManagerSchnittstelle $fahrplanVorgabe = $this->aktuelleNetzfahrplanVorgabe(); $zielnetzleistung = $betriebsart === ManagerRegler::BETRIEBSART_PEAK ? (float) $lastspitzengrenze - : ($fahrplanVorgabe ?? $this->ReadPropertyFloat('SollwertSolarladen')); + : ($fahrplanVorgabe['Netzleistung_W'] + ?? $this->ReadPropertyFloat('SollwertSolarladen')); + $vorgegebeneSollwerte = []; + if ($betriebsart !== ManagerRegler::BETRIEBSART_PEAK + && is_array($fahrplanVorgabe) + && is_float($fahrplanVorgabe['Batterieleistung_W']) + ) { + $batterieIDs = $this->aktiveBatterieVerbraucherIDs($verbraucher); + if (count($batterieIDs) === 1) { + $vorgegebeneSollwerte[$batterieIDs[0]] = (int) round( + $fahrplanVorgabe['Batterieleistung_W'] + ); + } + } $ergebnis = ManagerRegler::verteile( $netzleistung, $zielnetzleistung, $verbraucher, $betriebsart === ManagerRegler::BETRIEBSART_PEAK ? 'PrioritaetPeak' : 'PrioritaetPV', - $this->bezogeneVerbraucherEnergienWh() + $this->bezogeneVerbraucherEnergienWh(), + $vorgegebeneSollwerte ); $this->setzeDiagnosewert('Verteilbudget', (float) $ergebnis['Verteilbudget_W']); $this->setzeDiagnosewert('Verbraucherstatus', json_encode([ @@ -2740,7 +2754,8 @@ class Manager extends IPSModule implements ManagerSchnittstelle && $this->aktuelleBatterieSoc() !== null; } - private function aktuelleNetzfahrplanVorgabe(): ?float + /** @return array{Netzleistung_W: float, Batterieleistung_W: ?float}|null */ + private function aktuelleNetzfahrplanVorgabe(): ?array { if (!$this->ReadPropertyBoolean('NetzfahrplanAktiv')) { return null; @@ -2774,14 +2789,42 @@ class Manager extends IPSModule implements ManagerSchnittstelle } $abstand = abs($zeitpunkt - $jetzt); if ($abstand < $naechsterAbstand) { + $batteryTarget = $punkt['batteryTargetW'] ?? null; $naechsterAbstand = $abstand; - $vorgabe = (float) $punkt['gridTargetW']; + $vorgabe = [ + 'Netzleistung_W' => (float) $punkt['gridTargetW'], + 'Batterieleistung_W' => is_int($batteryTarget) || is_float($batteryTarget) + ? (float) $batteryTarget + : null, + ]; } } return $naechsterAbstand <= 1800 ? $vorgabe : null; } + /** + * @param list}> $verbraucher + * @return list + */ + private function aktiveBatterieVerbraucherIDs(array $verbraucher): array + { + $ids = []; + foreach ($verbraucher as $eintrag) { + $instanzID = $eintrag['InstanzID']; + if (!IPS_InstanceExists($instanzID)) { + continue; + } + $instanz = IPS_GetInstance($instanzID); + $moduleID = strtoupper((string) ($instanz['ModuleInfo']['ModuleID'] ?? '')); + if ((self::VERBRAUCHER_LIZENZEN[$moduleID]['Katalog'] ?? null) === 'battery') { + $ids[] = $instanzID; + } + } + + return $ids; + } + private function synchronisiereAnlagentopologie(): void { try { diff --git a/libs/ManagerRegler.php b/libs/ManagerRegler.php index c7aff6b..e448e14 100644 --- a/libs/ManagerRegler.php +++ b/libs/ManagerRegler.php @@ -106,7 +106,8 @@ final class ManagerRegler float $zielnetzleistung, array $verbraucher, string $prioritaetsfeld, - array $bezogeneEnergieWh = [] + array $bezogeneEnergieWh = [], + array $vorgegebeneSollwerte = [] ): array { if (!in_array($prioritaetsfeld, ['PrioritaetPV', 'PrioritaetPeak'], true)) { throw new InvalidArgumentException('Unbekanntes Prioritaetsfeld.'); @@ -129,7 +130,13 @@ final class ManagerRegler $festwert = self::istFestwert($daten['Leistungswerte_W']); $festgehalten = !$daten['AenderungMoeglich']; - if ($festwert || $festgehalten) { + $vorgegebenerSollwert = $vorgegebeneSollwerte[$eintrag['InstanzID']] ?? null; + $vorgegeben = is_int($vorgegebenerSollwert) + && Nachrichtenvertrag::enthaeltLeistungswert( + $daten['Leistungswerte_W'], + $vorgegebenerSollwert + ); + if (($festwert || $festgehalten) && !$vorgegeben) { $nichtSteuerbar += $leistung; } @@ -146,6 +153,8 @@ final class ManagerRegler 'Leistungswerte_W' => $daten['Leistungswerte_W'], 'Festwert' => $festwert, 'Festgehalten' => $festgehalten, + 'Vorgegeben' => $vorgegeben, + 'VorgegebenerSollwert' => $vorgegeben ? $vorgegebenerSollwert : null, 'Sollwert' => self::angenommenerSollwert($daten), ]; } @@ -167,6 +176,11 @@ final class ManagerRegler $sollwerte = []; $ausgangssumme = 0; foreach ($steuerbar as $eintrag) { + if ($eintrag['Vorgegeben']) { + $sollwerte[$eintrag['InstanzID']] = $eintrag['VorgegebenerSollwert']; + $ausgangssumme += $eintrag['VorgegebenerSollwert']; + continue; + } if ($eintrag['Festwert']) { $sollwerte[$eintrag['InstanzID']] = self::kleinsterLeistungswert( $eintrag['Leistungswerte_W'] @@ -190,7 +204,7 @@ final class ManagerRegler $rest = $verteilbudget - $nichtSteuerbar - $ausgangssumme; foreach ($steuerbar as $eintrag) { - if ($eintrag['Festwert'] || $eintrag['Festgehalten']) { + if ($eintrag['Vorgegeben'] || $eintrag['Festwert'] || $eintrag['Festgehalten']) { continue; } @@ -219,7 +233,9 @@ final class ManagerRegler $zugeteilt = $nichtSteuerbar; foreach ($steuerbar as $eintrag) { - if (!$eintrag['Festwert'] && !$eintrag['Festgehalten']) { + if ($eintrag['Vorgegeben'] + || (!$eintrag['Festwert'] && !$eintrag['Festgehalten']) + ) { $zugeteilt += $sollwerte[$eintrag['InstanzID']]; } } diff --git a/tests/ManagerReglerTest.php b/tests/ManagerReglerTest.php index 31a3bc1..30abd46 100644 --- a/tests/ManagerReglerTest.php +++ b/tests/ManagerReglerTest.php @@ -333,6 +333,29 @@ final class ManagerReglerTest extends TestCase self::assertSame(-3000, $ergebnis['Restabweichung_W']); } + public function testExpliziterBatteriefahrplanBleibtVonAnderenVerbrauchernUnveraendert(): void + { + $ergebnis = ManagerRegler::verteile( + 0.0, + 1000.0, + [ + $this->verbraucher( + 10, + 0, + [['Von_W' => -5000, 'Bis_W' => 5000]], + 0 + ), + $this->verbraucher(20, 1, [0, 3000], 0), + ], + 'PrioritaetPV', + [], + [10 => 1000] + ); + + self::assertSame([10 => 1000, 20 => 0], $ergebnis['Sollwerte']); + self::assertSame(0, $ergebnis['Restabweichung_W']); + } + public function testMonatlicheGrenzeMussVollstaendigSein(): void { $this->expectException(InvalidArgumentException::class);