This commit is contained in:
+47
-4
@@ -761,13 +761,27 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
|||||||
$fahrplanVorgabe = $this->aktuelleNetzfahrplanVorgabe();
|
$fahrplanVorgabe = $this->aktuelleNetzfahrplanVorgabe();
|
||||||
$zielnetzleistung = $betriebsart === ManagerRegler::BETRIEBSART_PEAK
|
$zielnetzleistung = $betriebsart === ManagerRegler::BETRIEBSART_PEAK
|
||||||
? (float) $lastspitzengrenze
|
? (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(
|
$ergebnis = ManagerRegler::verteile(
|
||||||
$netzleistung,
|
$netzleistung,
|
||||||
$zielnetzleistung,
|
$zielnetzleistung,
|
||||||
$verbraucher,
|
$verbraucher,
|
||||||
$betriebsart === ManagerRegler::BETRIEBSART_PEAK ? 'PrioritaetPeak' : 'PrioritaetPV',
|
$betriebsart === ManagerRegler::BETRIEBSART_PEAK ? 'PrioritaetPeak' : 'PrioritaetPV',
|
||||||
$this->bezogeneVerbraucherEnergienWh()
|
$this->bezogeneVerbraucherEnergienWh(),
|
||||||
|
$vorgegebeneSollwerte
|
||||||
);
|
);
|
||||||
$this->setzeDiagnosewert('Verteilbudget', (float) $ergebnis['Verteilbudget_W']);
|
$this->setzeDiagnosewert('Verteilbudget', (float) $ergebnis['Verteilbudget_W']);
|
||||||
$this->setzeDiagnosewert('Verbraucherstatus', json_encode([
|
$this->setzeDiagnosewert('Verbraucherstatus', json_encode([
|
||||||
@@ -2740,7 +2754,8 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
|||||||
&& $this->aktuelleBatterieSoc() !== null;
|
&& $this->aktuelleBatterieSoc() !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function aktuelleNetzfahrplanVorgabe(): ?float
|
/** @return array{Netzleistung_W: float, Batterieleistung_W: ?float}|null */
|
||||||
|
private function aktuelleNetzfahrplanVorgabe(): ?array
|
||||||
{
|
{
|
||||||
if (!$this->ReadPropertyBoolean('NetzfahrplanAktiv')) {
|
if (!$this->ReadPropertyBoolean('NetzfahrplanAktiv')) {
|
||||||
return null;
|
return null;
|
||||||
@@ -2774,14 +2789,42 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
|||||||
}
|
}
|
||||||
$abstand = abs($zeitpunkt - $jetzt);
|
$abstand = abs($zeitpunkt - $jetzt);
|
||||||
if ($abstand < $naechsterAbstand) {
|
if ($abstand < $naechsterAbstand) {
|
||||||
|
$batteryTarget = $punkt['batteryTargetW'] ?? null;
|
||||||
$naechsterAbstand = $abstand;
|
$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;
|
return $naechsterAbstand <= 1800 ? $vorgabe : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<array{InstanzID: int, Daten: array<string, mixed>}> $verbraucher
|
||||||
|
* @return list<int>
|
||||||
|
*/
|
||||||
|
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
|
private function synchronisiereAnlagentopologie(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
+20
-4
@@ -106,7 +106,8 @@ final class ManagerRegler
|
|||||||
float $zielnetzleistung,
|
float $zielnetzleistung,
|
||||||
array $verbraucher,
|
array $verbraucher,
|
||||||
string $prioritaetsfeld,
|
string $prioritaetsfeld,
|
||||||
array $bezogeneEnergieWh = []
|
array $bezogeneEnergieWh = [],
|
||||||
|
array $vorgegebeneSollwerte = []
|
||||||
): array {
|
): array {
|
||||||
if (!in_array($prioritaetsfeld, ['PrioritaetPV', 'PrioritaetPeak'], true)) {
|
if (!in_array($prioritaetsfeld, ['PrioritaetPV', 'PrioritaetPeak'], true)) {
|
||||||
throw new InvalidArgumentException('Unbekanntes Prioritaetsfeld.');
|
throw new InvalidArgumentException('Unbekanntes Prioritaetsfeld.');
|
||||||
@@ -129,7 +130,13 @@ final class ManagerRegler
|
|||||||
|
|
||||||
$festwert = self::istFestwert($daten['Leistungswerte_W']);
|
$festwert = self::istFestwert($daten['Leistungswerte_W']);
|
||||||
$festgehalten = !$daten['AenderungMoeglich'];
|
$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;
|
$nichtSteuerbar += $leistung;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +153,8 @@ final class ManagerRegler
|
|||||||
'Leistungswerte_W' => $daten['Leistungswerte_W'],
|
'Leistungswerte_W' => $daten['Leistungswerte_W'],
|
||||||
'Festwert' => $festwert,
|
'Festwert' => $festwert,
|
||||||
'Festgehalten' => $festgehalten,
|
'Festgehalten' => $festgehalten,
|
||||||
|
'Vorgegeben' => $vorgegeben,
|
||||||
|
'VorgegebenerSollwert' => $vorgegeben ? $vorgegebenerSollwert : null,
|
||||||
'Sollwert' => self::angenommenerSollwert($daten),
|
'Sollwert' => self::angenommenerSollwert($daten),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -167,6 +176,11 @@ final class ManagerRegler
|
|||||||
$sollwerte = [];
|
$sollwerte = [];
|
||||||
$ausgangssumme = 0;
|
$ausgangssumme = 0;
|
||||||
foreach ($steuerbar as $eintrag) {
|
foreach ($steuerbar as $eintrag) {
|
||||||
|
if ($eintrag['Vorgegeben']) {
|
||||||
|
$sollwerte[$eintrag['InstanzID']] = $eintrag['VorgegebenerSollwert'];
|
||||||
|
$ausgangssumme += $eintrag['VorgegebenerSollwert'];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if ($eintrag['Festwert']) {
|
if ($eintrag['Festwert']) {
|
||||||
$sollwerte[$eintrag['InstanzID']] = self::kleinsterLeistungswert(
|
$sollwerte[$eintrag['InstanzID']] = self::kleinsterLeistungswert(
|
||||||
$eintrag['Leistungswerte_W']
|
$eintrag['Leistungswerte_W']
|
||||||
@@ -190,7 +204,7 @@ final class ManagerRegler
|
|||||||
|
|
||||||
$rest = $verteilbudget - $nichtSteuerbar - $ausgangssumme;
|
$rest = $verteilbudget - $nichtSteuerbar - $ausgangssumme;
|
||||||
foreach ($steuerbar as $eintrag) {
|
foreach ($steuerbar as $eintrag) {
|
||||||
if ($eintrag['Festwert'] || $eintrag['Festgehalten']) {
|
if ($eintrag['Vorgegeben'] || $eintrag['Festwert'] || $eintrag['Festgehalten']) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +233,9 @@ final class ManagerRegler
|
|||||||
|
|
||||||
$zugeteilt = $nichtSteuerbar;
|
$zugeteilt = $nichtSteuerbar;
|
||||||
foreach ($steuerbar as $eintrag) {
|
foreach ($steuerbar as $eintrag) {
|
||||||
if (!$eintrag['Festwert'] && !$eintrag['Festgehalten']) {
|
if ($eintrag['Vorgegeben']
|
||||||
|
|| (!$eintrag['Festwert'] && !$eintrag['Festgehalten'])
|
||||||
|
) {
|
||||||
$zugeteilt += $sollwerte[$eintrag['InstanzID']];
|
$zugeteilt += $sollwerte[$eintrag['InstanzID']];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,6 +333,29 @@ final class ManagerReglerTest extends TestCase
|
|||||||
self::assertSame(-3000, $ergebnis['Restabweichung_W']);
|
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
|
public function testMonatlicheGrenzeMussVollstaendigSein(): void
|
||||||
{
|
{
|
||||||
$this->expectException(InvalidArgumentException::class);
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
|||||||
Reference in New Issue
Block a user