Compare commits
2
Commits
ccecc437e9
...
995b704e36
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
995b704e36 | ||
|
|
5ff0d405d4 |
+5
-7
@@ -2263,12 +2263,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
|||||||
/** @param array<string, mixed> $lease */
|
/** @param array<string, mixed> $lease */
|
||||||
private function prognoseBerechtigungInLease(array $lease): bool
|
private function prognoseBerechtigungInLease(array $lease): bool
|
||||||
{
|
{
|
||||||
return Lizenzpruefung::berechtigungsMenge($lease, Lizenzpruefung::NETZFAHRPLAN) > 0
|
return Lizenzpruefung::hatPrognoseBerechtigung($lease);
|
||||||
|| (Lizenzpruefung::berechtigungsMenge($lease, Lizenzpruefung::PROGNOSE_PV) > 0
|
|
||||||
&& Lizenzpruefung::berechtigungsMenge(
|
|
||||||
$lease,
|
|
||||||
Lizenzpruefung::PROGNOSE_VERBRAUCH
|
|
||||||
) > 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function berechtigungLizenziert(string $kennung, ?string $lizenzcode = null): bool
|
private function berechtigungLizenziert(string $kennung, ?string $lizenzcode = null): bool
|
||||||
@@ -2418,7 +2413,10 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
|||||||
if (!$lizenziert) {
|
if (!$lizenziert) {
|
||||||
return 'Prognosen sind deaktiviert. Fuer die Manager-ID '
|
return 'Prognosen sind deaktiviert. Fuer die Manager-ID '
|
||||||
. $managerID
|
. $managerID
|
||||||
. ' fehlen forecast_pv plus forecast_load oder grid_schedule.';
|
. ' ist keine Prognoseberechtigung vorhanden. Benoetigt wird entweder '
|
||||||
|
. 'PV-Ertragsprognose zusammen mit Verbrauchsprognose oder der '
|
||||||
|
. 'Intelligente Netzfahrplan. Nach der Erweiterung im Lizenzportal '
|
||||||
|
. 'die Lizenz erneut pruefen und binden.';
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($aktiv ? 'Prognosen sind aktiviert. ' : 'Prognosen sind deaktiviert. ')
|
return ($aktiv ? 'Prognosen sind aktiviert. ' : 'Prognosen sind deaktiviert. ')
|
||||||
|
|||||||
@@ -90,6 +90,12 @@ melden, ohne dass der Manager seine interne Geraetelogik kennen muss.
|
|||||||
## Feste Regeln
|
## Feste Regeln
|
||||||
|
|
||||||
- Prioritaeten beginnen bei 0; eine kleinere Zahl bedeutet hoehere Prioritaet.
|
- 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.
|
- `Leistungsquelle`: 0 nicht vorhanden, 1 berechnet, 2 gemessen.
|
||||||
- Bei Leistungsquelle 0 ist `Istleistung_W` zwingend `null`.
|
- Bei Leistungsquelle 0 ist `Istleistung_W` zwingend `null`.
|
||||||
- Leistungsbereiche enthalten jeden ganzen Wattwert von `Von_W` bis `Bis_W`.
|
- Leistungsbereiche enthalten jeden ganzen Wattwert von `Von_W` bis `Bis_W`.
|
||||||
|
|||||||
@@ -279,6 +279,7 @@ stabile ID gebunden werden.
|
|||||||
| HTTP `429` | Zu viele Aktivierungsversuche; vor dem naechsten Versuch warten. |
|
| HTTP `429` | Zu viele Aktivierungsversuche; vor dem naechsten Versuch warten. |
|
||||||
| `Lizenzserver nicht erreichbar` | DNS, Internetzugang, HTTPS und Systemzeit des Symcon-Systems pruefen. Eine bestehende Lease gilt nur bis `offlineUntil`. |
|
| `Lizenzserver nicht erreichbar` | DNS, Internetzugang, HTTPS und Systemzeit des Symcon-Systems pruefen. Eine bestehende Lease gilt nur bis `offlineUntil`. |
|
||||||
| `Offline-Freigabe ist abgelaufen.` | Verbindung zum Lizenzserver wiederherstellen und Lizenz erneut pruefen. |
|
| `Offline-Freigabe ist abgelaufen.` | Verbindung zum Lizenzserver wiederherstellen und Lizenz erneut pruefen. |
|
||||||
|
| `Keine Prognoseberechtigung vorhanden` | Im Lizenzportal entweder PV-Ertragsprognose zusammen mit Verbrauchsprognose oder den Intelligenten Netzfahrplan ergaenzen. Anschliessend im Manager `Lizenz pruefen und binden` ausloesen, die Prognose aktivieren und die Konfiguration speichern. |
|
||||||
|
|
||||||
Fuer eine genauere Diagnose koennen die Diagnosevariablen eingeblendet werden.
|
Fuer eine genauere Diagnose koennen die Diagnosevariablen eingeblendet werden.
|
||||||
`Lizenzstatus` zeigt dann den aktuellen Zustand. Mit aktiviertem Debug-Logging
|
`Lizenzstatus` zeigt dann den aktuellen Zustand. Mit aktiviertem Debug-Logging
|
||||||
@@ -302,14 +303,19 @@ der aktiven Betriebsart und der aktuellen Leistung aller frisch gemeldeten
|
|||||||
Verbraucher. Eine gültige Istleistung wird bevorzugt; fehlt sie, bleibt der im
|
Verbraucher. Eine gültige Istleistung wird bevorzugt; fehlt sie, bleibt der im
|
||||||
Zustand bestätigte Sollwert konservativ reserviert.
|
Zustand bestätigte Sollwert konservativ reserviert.
|
||||||
|
|
||||||
Nicht verfügbare, nicht änderbare oder aktuell angebotlose Verbraucher behalten
|
Nicht verfügbare oder aktuell angebotlose Verbraucher behalten ihre Leistung
|
||||||
ihre Leistung und erhalten keine neue Vorgabe. Die übrigen Verbraucher werden
|
und erhalten keine neue Vorgabe. Jeder verfügbare, synchronisierte Verbraucher
|
||||||
nach der gemeldeten PV- beziehungsweise Peak-Priorität und danach stabil nach
|
mit einem nicht leeren Angebot erhält dagegen einen Sollwert aus diesem Angebot.
|
||||||
Instanz-ID sortiert. Von ihrem jeweils kleinsten erlaubten Leistungswert aus
|
Das gilt auch bei `AenderungMoeglich=false`: Ein fixes Angebot wie `[11000]`
|
||||||
wird das Budget in dieser Reihenfolge aufgefüllt. Einzelwerte und ganzzahlige
|
wird mit genau `11000 W` zugeteilt.
|
||||||
Bereiche werden direkt verarbeitet; Lücken werden nie durch unzulässige Werte
|
|
||||||
geschlossen. Eine verbleibende Abweichung wird im `Verbraucherstatus`
|
Die Verbraucher werden nach der gemeldeten PV- beziehungsweise Peak-Priorität
|
||||||
dokumentiert.
|
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
|
Die Betriebsart wechselt unterhalb des Solar-Sollwerts zu `PV` und oberhalb
|
||||||
der wirksamen Lastspitzengrenze zu `Peak`. Zwischen den beiden Zielwerten
|
der wirksamen Lastspitzengrenze zu `Peak`. Zwischen den beiden Zielwerten
|
||||||
|
|||||||
@@ -117,6 +117,19 @@ final class Lizenzpruefung
|
|||||||
return $menge;
|
return $menge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eine Prognose ist entweder durch den Netzfahrplan oder durch beide
|
||||||
|
* Einzelberechtigungen fuer PV und Verbrauch freigeschaltet.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $lease
|
||||||
|
*/
|
||||||
|
public static function hatPrognoseBerechtigung(array $lease): bool
|
||||||
|
{
|
||||||
|
return self::berechtigungsMenge($lease, self::NETZFAHRPLAN) > 0
|
||||||
|
|| (self::berechtigungsMenge($lease, self::PROGNOSE_PV) > 0
|
||||||
|
&& self::berechtigungsMenge($lease, self::PROGNOSE_VERBRAUCH) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rechnet freie, bestehende Einzellizenzen auf nicht separat lizenzierte Waermepumpen an.
|
* Rechnet freie, bestehende Einzellizenzen auf nicht separat lizenzierte Waermepumpen an.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ final class ManagerRegler
|
|||||||
$aktuelleLeistung += $leistung;
|
$aktuelleLeistung += $leistung;
|
||||||
|
|
||||||
$daten = $eintrag['Daten'];
|
$daten = $eintrag['Daten'];
|
||||||
if (!$daten['Verfuegbar'] || !$daten['AenderungMoeglich'] || $daten['Leistungswerte_W'] === []) {
|
if (!$daten['Verfuegbar'] || $daten['Leistungswerte_W'] === []) {
|
||||||
$nichtSteuerbar += $leistung;
|
$nichtSteuerbar += $leistung;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,6 +228,47 @@ final class LizenzpruefungTest extends TestCase
|
|||||||
Lizenzpruefung::pruefeVerbrauchermengen($lease, $pruefmengen);
|
Lizenzpruefung::pruefeVerbrauchermengen($lease, $pruefmengen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPvUndVerbrauchGebenPrognoseFrei(): void
|
||||||
|
{
|
||||||
|
$lease = $this->lease('manager_standard');
|
||||||
|
$lease['entitlements'][] = [
|
||||||
|
'catalog_key' => Lizenzpruefung::PROGNOSE_PV,
|
||||||
|
'quantity' => 1,
|
||||||
|
'status' => 'active',
|
||||||
|
];
|
||||||
|
$lease['entitlements'][] = [
|
||||||
|
'catalog_key' => Lizenzpruefung::PROGNOSE_VERBRAUCH,
|
||||||
|
'quantity' => 1,
|
||||||
|
'status' => 'active',
|
||||||
|
];
|
||||||
|
|
||||||
|
self::assertTrue(Lizenzpruefung::hatPrognoseBerechtigung($lease));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEinzelnePrognoseBerechtigungReichtNichtAus(): void
|
||||||
|
{
|
||||||
|
$lease = $this->lease('manager_standard');
|
||||||
|
$lease['entitlements'][] = [
|
||||||
|
'catalog_key' => Lizenzpruefung::PROGNOSE_PV,
|
||||||
|
'quantity' => 1,
|
||||||
|
'status' => 'active',
|
||||||
|
];
|
||||||
|
|
||||||
|
self::assertFalse(Lizenzpruefung::hatPrognoseBerechtigung($lease));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNetzfahrplanGibtPrognoseAlleineFrei(): void
|
||||||
|
{
|
||||||
|
$lease = $this->lease('manager_standard');
|
||||||
|
$lease['entitlements'][] = [
|
||||||
|
'catalog_key' => Lizenzpruefung::NETZFAHRPLAN,
|
||||||
|
'quantity' => 1,
|
||||||
|
'status' => 'active',
|
||||||
|
];
|
||||||
|
|
||||||
|
self::assertTrue(Lizenzpruefung::hatPrognoseBerechtigung($lease));
|
||||||
|
}
|
||||||
|
|
||||||
public function testLizenzcodeWirdWieAufDemServerNormalisiert(): void
|
public function testLizenzcodeWirdWieAufDemServerNormalisiert(): void
|
||||||
{
|
{
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
|
|||||||
@@ -113,19 +113,19 @@ final class ManagerReglerTest extends TestCase
|
|||||||
self::assertSame(500, $ergebnis['Restabweichung_W']);
|
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);
|
$steuerbar = $this->verbraucher(20, 1, [0, ['Von_W' => 1, 'Bis_W' => 4000]], 0);
|
||||||
|
|
||||||
$ergebnis = ManagerRegler::verteile(3000.0, 5000.0, [$fest, $steuerbar], 'PrioritaetPV');
|
$ergebnis = ManagerRegler::verteile(3000.0, 5000.0, [$fest, $steuerbar], 'PrioritaetPV');
|
||||||
|
|
||||||
self::assertSame(3000, $ergebnis['NichtSteuerbar_W']);
|
self::assertSame(0, $ergebnis['NichtSteuerbar_W']);
|
||||||
self::assertSame([20 => 2000], $ergebnis['Sollwerte']);
|
self::assertSame([10 => 3000, 20 => 2000], $ergebnis['Sollwerte']);
|
||||||
self::assertSame(0, $ergebnis['Restabweichung_W']);
|
self::assertSame(0, $ergebnis['Restabweichung_W']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testVeralteterVerbraucherBlockiertUeberschussverteilungNicht(): void
|
public function testFestwerteWerdenMitHoeherPriorisierterBatterieZugewiesen(): void
|
||||||
{
|
{
|
||||||
$ergebnis = ManagerRegler::verteile(
|
$ergebnis = ManagerRegler::verteile(
|
||||||
-2411.0,
|
-2411.0,
|
||||||
@@ -147,7 +147,13 @@ final class ManagerReglerTest extends TestCase
|
|||||||
|
|
||||||
self::assertSame(12562, $ergebnis['Verteilbudget_W']);
|
self::assertSame(12562, $ergebnis['Verteilbudget_W']);
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
[44234 => 2411, 29743 => 0, 38676 => 0],
|
[
|
||||||
|
44234 => -12288,
|
||||||
|
29743 => 0,
|
||||||
|
38676 => 0,
|
||||||
|
43878 => 13850,
|
||||||
|
46052 => 11000,
|
||||||
|
],
|
||||||
$ergebnis['Sollwerte']
|
$ergebnis['Sollwerte']
|
||||||
);
|
);
|
||||||
self::assertSame(0, $ergebnis['Restabweichung_W']);
|
self::assertSame(0, $ergebnis['Restabweichung_W']);
|
||||||
@@ -155,7 +161,7 @@ final class ManagerReglerTest extends TestCase
|
|||||||
|
|
||||||
public function testFehlendeIstleistungFaelltAufAngenommenenSollwertZurueck(): void
|
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']['Leistungsquelle'] = Nachrichtenvertrag::LEISTUNGSQUELLE_NICHT_VORHANDEN;
|
||||||
$verbraucher['Daten']['Istleistung_W'] = null;
|
$verbraucher['Daten']['Istleistung_W'] = null;
|
||||||
$verbraucher['Daten']['Zustand'][0]['Wert'] = 3000;
|
$verbraucher['Daten']['Zustand'][0]['Wert'] = 3000;
|
||||||
@@ -163,6 +169,7 @@ final class ManagerReglerTest extends TestCase
|
|||||||
$ergebnis = ManagerRegler::verteile(3000.0, 0.0, [$verbraucher], 'PrioritaetPV');
|
$ergebnis = ManagerRegler::verteile(3000.0, 0.0, [$verbraucher], 'PrioritaetPV');
|
||||||
|
|
||||||
self::assertSame(3000, $ergebnis['AktuelleVerbraucherleistung_W']);
|
self::assertSame(3000, $ergebnis['AktuelleVerbraucherleistung_W']);
|
||||||
|
self::assertSame([10 => 3000], $ergebnis['Sollwerte']);
|
||||||
self::assertSame(-3000, $ergebnis['Restabweichung_W']);
|
self::assertSame(-3000, $ergebnis['Restabweichung_W']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user