This commit is contained in:
+6
-4
@@ -8,7 +8,7 @@ IP-Symcon-Registervariablen.
|
|||||||
## Funktionen
|
## Funktionen
|
||||||
|
|
||||||
- positive Leistung bedeutet Laden, negative Leistung bedeutet Entladen
|
- positive Leistung bedeutet Laden, negative Leistung bedeutet Entladen
|
||||||
- 250-W-Grundraster mit 50-W-Feinwerten zwischen -200 W und +200 W
|
- stufenlose Leistungsbereiche in ganzen Watt innerhalb der dynamischen Grenzen
|
||||||
- unterschiedliche Leistungsangebote fuer PV- und Peakbetrieb
|
- unterschiedliche Leistungsangebote fuer PV- und Peakbetrieb
|
||||||
- Reserve- und Mindestladezustand mit konfigurierbarer Hysterese
|
- Reserve- und Mindestladezustand mit konfigurierbarer Hysterese
|
||||||
- direkte Registeransteuerung fuer herstellerunabhaengige Batterien, GoodWe,
|
- direkte Registeransteuerung fuer herstellerunabhaengige Batterien, GoodWe,
|
||||||
@@ -86,9 +86,11 @@ LadezustandHysterese ersetzt die bisher fest im Code hinterlegte
|
|||||||
|
|
||||||
Die bisherigen betriebsartabhaengigen Angebote bleiben erhalten:
|
Die bisherigen betriebsartabhaengigen Angebote bleiben erhalten:
|
||||||
|
|
||||||
- Im PV-Betrieb steht oberhalb der Reserve das gesamte Raster zur Verfuegung.
|
- Im PV-Betrieb steht oberhalb der Reserve der durchgaengige Bereich von der
|
||||||
- Unterhalb der Reserve wird die maximale Ladeleistung angeboten.
|
maximalen Entlade- bis zur maximalen Ladeleistung zur Verfuegung.
|
||||||
- Bei vollem Speicher werden nur Entladung und 0 angeboten.
|
- Unterhalb der Reserve wird die maximale Ladeleistung als fester Wert angeboten.
|
||||||
|
- Bei vollem Speicher wird der durchgaengige Bereich von der maximalen
|
||||||
|
Entladeleistung bis 0 W angeboten.
|
||||||
- Im Peakbetrieb wird oberhalb der Reserve der aus Netz- und aktueller
|
- Im Peakbetrieb wird oberhalb der Reserve der aus Netz- und aktueller
|
||||||
Batterieleistung berechnete Entladewert angeboten.
|
Batterieleistung berechnete Entladewert angeboten.
|
||||||
- Innerhalb der Reserve-Hysterese und unterhalb des Mindestladezustands werden
|
- Innerhalb der Reserve-Hysterese und unterhalb des Mindestladezustands werden
|
||||||
|
|||||||
+10
-9
@@ -293,10 +293,9 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
|||||||
$sollleistung = $daten['Sollleistung_W'];
|
$sollleistung = $daten['Sollleistung_W'];
|
||||||
if ($sollleistung === null) {
|
if ($sollleistung === null) {
|
||||||
if ((bool) $this->leseZustand('SollwertGueltig')
|
if ((bool) $this->leseZustand('SollwertGueltig')
|
||||||
&& !in_array(
|
&& !Nachrichtenvertrag::enthaeltLeistungswert(
|
||||||
(int) $this->leseZustand('Sollleistung'),
|
|
||||||
$this->leseLeistungsangebot(),
|
$this->leseLeistungsangebot(),
|
||||||
true
|
(int) $this->leseZustand('Sollleistung')
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
$this->setzeZustand('SollwertGueltig', false);
|
$this->setzeZustand('SollwertGueltig', false);
|
||||||
@@ -306,7 +305,10 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in_array($sollleistung, $this->leseLeistungsangebot(), true)) {
|
if (!Nachrichtenvertrag::enthaeltLeistungswert(
|
||||||
|
$this->leseLeistungsangebot(),
|
||||||
|
$sollleistung
|
||||||
|
)) {
|
||||||
throw new InvalidArgumentException(
|
throw new InvalidArgumentException(
|
||||||
'Sollleistung_W liegt nicht im aktuell gemeldeten Leistungsangebot.'
|
'Sollleistung_W liegt nicht im aktuell gemeldeten Leistungsangebot.'
|
||||||
);
|
);
|
||||||
@@ -394,10 +396,9 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ((bool) $this->leseZustand('SollwertGueltig')
|
if ((bool) $this->leseZustand('SollwertGueltig')
|
||||||
&& !in_array(
|
&& !Nachrichtenvertrag::enthaeltLeistungswert(
|
||||||
(int) $this->leseZustand('Sollleistung'),
|
|
||||||
$angebot,
|
$angebot,
|
||||||
true
|
(int) $this->leseZustand('Sollleistung')
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
$this->setzeZustand('SollwertGueltig', false);
|
$this->setzeZustand('SollwertGueltig', false);
|
||||||
@@ -1073,7 +1074,7 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return list<int> */
|
/** @return list<int|array{Von_W: int, Bis_W: int}> */
|
||||||
private function leseLeistungsangebot(): array
|
private function leseLeistungsangebot(): array
|
||||||
{
|
{
|
||||||
$angebot = json_decode(
|
$angebot = json_decode(
|
||||||
@@ -1086,7 +1087,7 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
|||||||
return [0];
|
return [0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_values(array_map('intval', $angebot));
|
return array_values($angebot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setzeStoerung(string $text): void
|
private function setzeStoerung(string $text): void
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ Sollleistung_W verwendet folgende Semantik:
|
|||||||
- null: nur Betriebsart synchronisieren und neues Angebot anfordern
|
- null: nur Betriebsart synchronisieren und neues Angebot anfordern
|
||||||
|
|
||||||
Ein konkreter Wert muss im zuletzt fuer dieselbe Betriebsart gemeldeten
|
Ein konkreter Wert muss im zuletzt fuer dieselbe Betriebsart gemeldeten
|
||||||
Leistungsangebot enthalten sein. Waehren der Aenderungssperre wird nur die
|
Einzelwert oder Leistungsbereich enthalten sein. Waehrend der Aenderungssperre
|
||||||
Wiederholung des aktuellen Sollwerts akzeptiert.
|
wird nur die Wiederholung des aktuellen Sollwerts akzeptiert.
|
||||||
|
|
||||||
## Batterie an Manager
|
## Batterie an Manager
|
||||||
|
|
||||||
@@ -56,10 +56,10 @@ Beispiel:
|
|||||||
"EmpfaengerID": 10001,
|
"EmpfaengerID": 10001,
|
||||||
"Zeitpunkt": 1788825602
|
"Zeitpunkt": 1788825602
|
||||||
},
|
},
|
||||||
"Betriebsart": "Peak",
|
"Betriebsart": "PV",
|
||||||
"PrioritaetPV": 0,
|
"PrioritaetPV": 0,
|
||||||
"PrioritaetPeak": 0,
|
"PrioritaetPeak": 0,
|
||||||
"Leistungswerte_W": [-1500],
|
"Leistungswerte_W": [{"Von_W": -5000, "Bis_W": 5000}],
|
||||||
"AenderungMoeglich": true,
|
"AenderungMoeglich": true,
|
||||||
"Verfuegbar": true,
|
"Verfuegbar": true,
|
||||||
"Istleistung_W": -250.0,
|
"Istleistung_W": -250.0,
|
||||||
@@ -68,7 +68,7 @@ Beispiel:
|
|||||||
{
|
{
|
||||||
"Kennung": "Sollleistung_W",
|
"Kennung": "Sollleistung_W",
|
||||||
"Art": "Sollwert",
|
"Art": "Sollwert",
|
||||||
"Wert": -1500,
|
"Wert": -537,
|
||||||
"Einheit": "W"
|
"Einheit": "W"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -83,7 +83,9 @@ Beispiel:
|
|||||||
|
|
||||||
Ist die Leistungsmessung ungueltig, wird Istleistung_W als null und
|
Ist die Leistungsmessung ungueltig, wird Istleistung_W als null und
|
||||||
Leistungsquelle als 0 gemeldet. Bei einem gueltigen Messwert ist
|
Leistungsquelle als 0 gemeldet. Bei einem gueltigen Messwert ist
|
||||||
Leistungsquelle 2.
|
Leistungsquelle 2. Frei regelbare Lade- und Entladeangebote werden als
|
||||||
|
inklusive Bereiche in ganzen Watt gemeldet. Feste Schutz- und Peakvorgaben
|
||||||
|
bleiben einzelne Leistungswerte.
|
||||||
|
|
||||||
## Ereignisse
|
## Ereignisse
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ Verhaltensreferenz.
|
|||||||
- positive Sollleistung fuer Laden und negative Sollleistung fuer Entladen
|
- positive Sollleistung fuer Laden und negative Sollleistung fuer Entladen
|
||||||
- dynamische maximale Lade- und Entladeleistung aus Variablen
|
- dynamische maximale Lade- und Entladeleistung aus Variablen
|
||||||
- Ladezustand, Netzleistung und aktuelle Batterieleistung als Eingaben
|
- Ladezustand, Netzleistung und aktuelle Batterieleistung als Eingaben
|
||||||
- 250-W-Grundraster und 50-W-Feinwerte um 0
|
|
||||||
- Reserve-, Mindestladezustands- und Hystereselogik
|
- Reserve-, Mindestladezustands- und Hystereselogik
|
||||||
- getrennte Angebote fuer PV und Peak
|
- getrennte Angebote fuer PV und Peak
|
||||||
- Wechselrichter- oder Enelix-Steuerung
|
- Wechselrichter- oder Enelix-Steuerung
|
||||||
@@ -25,6 +24,7 @@ Verhaltensreferenz.
|
|||||||
| zyklischer Timer Do_UserCalc | Messwert- und Managerereignisse |
|
| zyklischer Timer Do_UserCalc | Messwert- und Managerereignisse |
|
||||||
| IdleCounter in Zyklen | Aenderungssperre in Sekunden mit Einmaltimer |
|
| IdleCounter in Zyklen | Aenderungssperre in Sekunden mit Einmaltimer |
|
||||||
| PowerSteps als Modulvariable | Leistungswerte_W direkt im Vertrag |
|
| PowerSteps als Modulvariable | Leistungswerte_W direkt im Vertrag |
|
||||||
|
| 250-W-Grundraster mit 50-W-Feinwerten | inklusive Leistungsbereiche in ganzen Watt |
|
||||||
| interne Stellwertvariablen | ausgewaehlte Registervariablen mit RequestAction |
|
| interne Stellwertvariablen | ausgewaehlte Registervariablen mit RequestAction |
|
||||||
| fest verdrahtete Herstellerhilfsvariablen | dynamisch sichtbare Registerauswahl |
|
| fest verdrahtete Herstellerhilfsvariablen | dynamisch sichtbare Registerauswahl |
|
||||||
| fest codierte 2-%-Hysterese | Property LadezustandHysterese |
|
| fest codierte 2-%-Hysterese | Property LadezustandHysterese |
|
||||||
|
|||||||
@@ -73,13 +73,17 @@ zuletzt Management geschrieben.
|
|||||||
|
|
||||||
## Leistungsangebot
|
## Leistungsangebot
|
||||||
|
|
||||||
Das Raster besteht wie in Enelix 1 aus 250-W-Grundschritten. Um 0 werden
|
Die Batterie meldet innerhalb der dynamischen Lade- und Entladegrenzen
|
||||||
zusaetzlich -200, -150, -100, -50, 0, 50, 100, 150 und 200 W angeboten,
|
inklusive Leistungsbereiche in ganzen Watt. Der Manager kann dadurch jeden
|
||||||
soweit diese Werte innerhalb der dynamischen Leistungsgrenzen liegen.
|
ganzzahligen Sollwert innerhalb des aktuell erlaubten Bereichs vorgeben. Die
|
||||||
|
Grenzen werden abgerundet, damit keine dynamische Maximalleistung ueberschritten
|
||||||
|
wird.
|
||||||
|
|
||||||
PV und Peak verwenden die bisherige SoC-Logik. Die Hysterese wird als
|
PV und Peak verwenden weiterhin die bisherige SoC-Logik. Feste Schutz- oder
|
||||||
persistenter Modulzustand gefuehrt. Bei Wechselrichtersteuerung, lokaler
|
Peakvorgaben bleiben Einzelwerte; frei regelbare Angebote werden als Bereiche
|
||||||
Deaktivierung oder ungueltigen Messwerten lautet das Angebot [0].
|
gemeldet. Die Hysterese wird als persistenter Modulzustand gefuehrt. Bei
|
||||||
|
Wechselrichtersteuerung, lokaler Deaktivierung oder ungueltigen Messwerten
|
||||||
|
lautet das Angebot [0].
|
||||||
|
|
||||||
## Variablen
|
## Variablen
|
||||||
|
|
||||||
@@ -115,7 +119,7 @@ Transport und gemeinsame Felder folgen
|
|||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
- BatterieReglerTest: Raster, Hysterese, PV/Peak und Herstellerabbildung
|
- BatterieReglerTest: Leistungsbereiche, Hysterese, PV/Peak und Herstellerabbildung
|
||||||
- BatterieModulstrukturTest: Metadaten, Formular, Ereignismodell und Manager-ID
|
- BatterieModulstrukturTest: Metadaten, Formular, Ereignismodell und Manager-ID
|
||||||
- Symcon/modules/Batterie.php: reale Modulinstanz und Registeraktionen unter
|
- Symcon/modules/Batterie.php: reale Modulinstanz und Registeraktionen unter
|
||||||
IP-Symcon 8.0
|
IP-Symcon 8.0
|
||||||
|
|||||||
+23
-48
@@ -17,38 +17,6 @@ final class BatterieRegler
|
|||||||
public const STEUERUNG_WECHSELRICHTER = 1;
|
public const STEUERUNG_WECHSELRICHTER = 1;
|
||||||
public const STEUERUNG_ENELIX = 2;
|
public const STEUERUNG_ENELIX = 2;
|
||||||
|
|
||||||
/**
|
|
||||||
* Erzeugt das bisherige Raster: 250-W-Grundschritte und
|
|
||||||
* 50-W-Feinschritte im Bereich von -200 W bis +200 W.
|
|
||||||
*
|
|
||||||
* @return list<int>
|
|
||||||
*/
|
|
||||||
public static function leistungsraster(float $maxLadenW, float $maxEntladenW): array
|
|
||||||
{
|
|
||||||
self::pruefeLeistungsgrenzen($maxLadenW, $maxEntladenW);
|
|
||||||
|
|
||||||
$maxLaden = (int) floor($maxLadenW / 50.0) * 50;
|
|
||||||
$maxEntladen = (int) floor($maxEntladenW / 50.0) * 50;
|
|
||||||
$werte = [0];
|
|
||||||
|
|
||||||
for ($leistung = -$maxEntladen; $leistung <= 0; $leistung += 250) {
|
|
||||||
$werte[] = $leistung;
|
|
||||||
}
|
|
||||||
for ($leistung = 0; $leistung <= $maxLaden; $leistung += 250) {
|
|
||||||
$werte[] = $leistung;
|
|
||||||
}
|
|
||||||
for ($leistung = -200; $leistung <= 200; $leistung += 50) {
|
|
||||||
if ($leistung >= -$maxEntladen && $leistung <= $maxLaden) {
|
|
||||||
$werte[] = $leistung;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$werte = array_values(array_unique($werte));
|
|
||||||
sort($werte, SORT_NUMERIC);
|
|
||||||
|
|
||||||
return $werte;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function hystereseAktiv(
|
public static function hystereseAktiv(
|
||||||
float $ladezustand,
|
float $ladezustand,
|
||||||
float $reserve,
|
float $reserve,
|
||||||
@@ -68,9 +36,10 @@ final class BatterieRegler
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bildet das zustands- und betriebsartabhaengige Angebot aus Enelix 1 ab.
|
* Bildet das zustands- und betriebsartabhaengige Angebot als
|
||||||
|
* durchgaengige Leistungsbereiche in ganzen Watt ab.
|
||||||
*
|
*
|
||||||
* @return list<int>
|
* @return list<int|array{Von_W: int, Bis_W: int}>
|
||||||
*/
|
*/
|
||||||
public static function leistungsangebot(
|
public static function leistungsangebot(
|
||||||
bool $aktiv,
|
bool $aktiv,
|
||||||
@@ -97,7 +66,8 @@ final class BatterieRegler
|
|||||||
return [0];
|
return [0];
|
||||||
}
|
}
|
||||||
|
|
||||||
$raster = self::leistungsraster($maxLadenW, $maxEntladenW);
|
$maxLaden = (int) floor($maxLadenW);
|
||||||
|
$maxEntladen = (int) floor($maxEntladenW);
|
||||||
if ($peakbetrieb) {
|
if ($peakbetrieb) {
|
||||||
if ($ladezustand > $reserve && !$hystereseAktiv) {
|
if ($ladezustand > $reserve && !$hystereseAktiv) {
|
||||||
$ziel = $netzleistungW > 0.0
|
$ziel = $netzleistungW > 0.0
|
||||||
@@ -107,26 +77,26 @@ final class BatterieRegler
|
|||||||
return [self::begrenzeLeistung($ziel, $maxLadenW, $maxEntladenW)];
|
return [self::begrenzeLeistung($ziel, $maxLadenW, $maxEntladenW)];
|
||||||
}
|
}
|
||||||
if ($ladezustand > $reserve && $hystereseAktiv) {
|
if ($ladezustand > $reserve && $hystereseAktiv) {
|
||||||
return self::filtere($raster, static fn (int $wert): bool => $wert <= 0);
|
return self::leistungsbereich(-$maxEntladen, 0);
|
||||||
}
|
}
|
||||||
if ($ladezustand > $minimum) {
|
if ($ladezustand > $minimum) {
|
||||||
return $raster;
|
return self::leistungsbereich(-$maxEntladen, $maxLaden);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::filtere($raster, static fn (int $wert): bool => $wert >= 0);
|
return self::leistungsbereich(0, $maxLaden);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($ladezustand > 99.0) {
|
if ($ladezustand > 99.0) {
|
||||||
return self::filtere($raster, static fn (int $wert): bool => $wert <= 0);
|
return self::leistungsbereich(-$maxEntladen, 0);
|
||||||
}
|
}
|
||||||
if ($ladezustand > $reserve && !$hystereseAktiv) {
|
if ($ladezustand > $reserve && !$hystereseAktiv) {
|
||||||
return $raster;
|
return self::leistungsbereich(-$maxEntladen, $maxLaden);
|
||||||
}
|
}
|
||||||
if ($ladezustand >= $reserve && $hystereseAktiv) {
|
if ($ladezustand >= $reserve && $hystereseAktiv) {
|
||||||
return self::filtere($raster, static fn (int $wert): bool => $wert >= 0);
|
return self::leistungsbereich(0, $maxLaden);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [(int) round($maxLadenW)];
|
return [$maxLaden];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -220,12 +190,14 @@ final class BatterieRegler
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param list<int> $werte
|
/** @return list<int|array{Von_W: int, Bis_W: int}> */
|
||||||
* @return list<int>
|
private static function leistungsbereich(int $vonW, int $bisW): array
|
||||||
*/
|
|
||||||
private static function filtere(array $werte, callable $filter): array
|
|
||||||
{
|
{
|
||||||
return array_values(array_filter($werte, $filter));
|
if ($vonW === $bisW) {
|
||||||
|
return [$vonW];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [['Von_W' => $vonW, 'Bis_W' => $bisW]];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function begrenzeLeistung(
|
private static function begrenzeLeistung(
|
||||||
@@ -233,7 +205,10 @@ final class BatterieRegler
|
|||||||
float $maxLadenW,
|
float $maxLadenW,
|
||||||
float $maxEntladenW
|
float $maxEntladenW
|
||||||
): int {
|
): int {
|
||||||
return (int) round(min($maxLadenW, max(-$maxEntladenW, $leistungW)));
|
$maxLaden = (int) floor($maxLadenW);
|
||||||
|
$maxEntladen = (int) floor($maxEntladenW);
|
||||||
|
|
||||||
|
return (int) round(min($maxLaden, max(-$maxEntladen, $leistungW)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function pruefeLeistungsgrenzen(float $maxLadenW, float $maxEntladenW): void
|
private static function pruefeLeistungsgrenzen(float $maxLadenW, float $maxEntladenW): void
|
||||||
|
|||||||
@@ -70,6 +70,33 @@ final class Nachrichtenvertrag
|
|||||||
self::pruefeZustand($daten['Zustand']);
|
self::pruefeZustand($daten['Zustand']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<int|array{Von_W: int, Bis_W: int}> $leistungswerte
|
||||||
|
*/
|
||||||
|
public static function enthaeltLeistungswert(array $leistungswerte, int $leistung): bool
|
||||||
|
{
|
||||||
|
foreach ($leistungswerte as $eintrag) {
|
||||||
|
if (is_int($eintrag)) {
|
||||||
|
if ($eintrag === $leistung) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($eintrag)
|
||||||
|
&& isset($eintrag['Von_W'], $eintrag['Bis_W'])
|
||||||
|
&& is_int($eintrag['Von_W'])
|
||||||
|
&& is_int($eintrag['Bis_W'])
|
||||||
|
&& $leistung >= $eintrag['Von_W']
|
||||||
|
&& $leistung <= $eintrag['Bis_W']
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $kopf
|
* @param mixed $kopf
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ use PHPUnit\Framework\TestCase;
|
|||||||
|
|
||||||
final class BatterieReglerTest extends TestCase
|
final class BatterieReglerTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testLeistungsrasterBehaeltGrobeUndFeineSchritte(): void
|
public function testLeistungsbereichBleibtInnerhalbDynamischerGrenzen(): void
|
||||||
{
|
{
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
[
|
[['Von_W' => -999, 'Bis_W' => 1000]],
|
||||||
-1000, -750, -500, -250, -200, -150, -100, -50,
|
BatterieRegler::leistungsangebot(
|
||||||
0, 50, 100, 150, 200, 250, 500, 750, 1000,
|
true, true, false, 50.0, 20.0, 10.0, false,
|
||||||
],
|
0.0, 0.0, 1000.9, 999.9
|
||||||
BatterieRegler::leistungsraster(1000.0, 1000.0)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,12 +28,10 @@ final class BatterieReglerTest extends TestCase
|
|||||||
self::assertFalse(BatterieRegler::hystereseAktiv(22.1, 20.0, 2.0, true));
|
self::assertFalse(BatterieRegler::hystereseAktiv(22.1, 20.0, 2.0, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPvAngebotEntsprichtDemBisherigenSocVerhalten(): void
|
public function testPvAngebotVerwendetStufenloseBereicheMitBisherigerSocLogik(): void
|
||||||
{
|
{
|
||||||
$voll = BatterieRegler::leistungsraster(1000.0, 1000.0);
|
|
||||||
|
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
$voll,
|
[['Von_W' => -1000, 'Bis_W' => 1000]],
|
||||||
BatterieRegler::leistungsangebot(
|
BatterieRegler::leistungsangebot(
|
||||||
true, true, false, 50.0, 20.0, 10.0, false,
|
true, true, false, 50.0, 20.0, 10.0, false,
|
||||||
0.0, 0.0, 1000.0, 1000.0
|
0.0, 0.0, 1000.0, 1000.0
|
||||||
@@ -47,7 +45,7 @@ final class BatterieReglerTest extends TestCase
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
[-1000, -750, -500, -250, -200, -150, -100, -50, 0],
|
[['Von_W' => -1000, 'Bis_W' => 0]],
|
||||||
BatterieRegler::leistungsangebot(
|
BatterieRegler::leistungsangebot(
|
||||||
true, true, false, 100.0, 20.0, 10.0, false,
|
true, true, false, 100.0, 20.0, 10.0, false,
|
||||||
0.0, 0.0, 1000.0, 1000.0
|
0.0, 0.0, 1000.0, 1000.0
|
||||||
@@ -66,6 +64,24 @@ final class BatterieReglerTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPeakEinschraenkungenWerdenAlsBereicheGemeldet(): void
|
||||||
|
{
|
||||||
|
self::assertSame(
|
||||||
|
[['Von_W' => -1000, 'Bis_W' => 0]],
|
||||||
|
BatterieRegler::leistungsangebot(
|
||||||
|
true, true, true, 21.0, 20.0, 10.0, true,
|
||||||
|
0.0, 0.0, 1000.0, 1000.0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
self::assertSame(
|
||||||
|
[['Von_W' => 0, 'Bis_W' => 1000]],
|
||||||
|
BatterieRegler::leistungsangebot(
|
||||||
|
true, true, true, 10.0, 20.0, 10.0, true,
|
||||||
|
0.0, 0.0, 1000.0, 1000.0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function testWechselrichtersteuerungBietetNurNull(): void
|
public function testWechselrichtersteuerungBietetNurNull(): void
|
||||||
{
|
{
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
@@ -186,6 +202,9 @@ final class BatterieReglerTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->expectException(InvalidArgumentException::class);
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
|
||||||
BatterieRegler::leistungsraster(-1.0, 1000.0);
|
BatterieRegler::leistungsangebot(
|
||||||
|
true, true, false, 50.0, 20.0, 10.0, false,
|
||||||
|
0.0, 0.0, -1.0, 1000.0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,17 @@ final class NachrichtenvertragTest extends TestCase
|
|||||||
self::assertTrue(true);
|
self::assertTrue(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testLeistungswertWirdInEinzelwertenUndBereichenGefunden(): void
|
||||||
|
{
|
||||||
|
$angebot = [0, ['Von_W' => 1000, 'Bis_W' => 2000], 3000];
|
||||||
|
|
||||||
|
self::assertTrue(Nachrichtenvertrag::enthaeltLeistungswert($angebot, 0));
|
||||||
|
self::assertTrue(Nachrichtenvertrag::enthaeltLeistungswert($angebot, 1537));
|
||||||
|
self::assertTrue(Nachrichtenvertrag::enthaeltLeistungswert($angebot, 2000));
|
||||||
|
self::assertFalse(Nachrichtenvertrag::enthaeltLeistungswert($angebot, 999));
|
||||||
|
self::assertFalse(Nachrichtenvertrag::enthaeltLeistungswert($angebot, 2500));
|
||||||
|
}
|
||||||
|
|
||||||
public function testNegativePrioritaetWirdAbgelehnt(): void
|
public function testNegativePrioritaetWirdAbgelehnt(): void
|
||||||
{
|
{
|
||||||
$daten = $this->verbraucherdaten();
|
$daten = $this->verbraucherdaten();
|
||||||
|
|||||||
@@ -119,10 +119,10 @@ PHP
|
|||||||
): void {
|
): void {
|
||||||
IPS_RequestAction($batterieID, 'Aktiv', true);
|
IPS_RequestAction($batterieID, 'Aktiv', true);
|
||||||
$managerdatenSenden('PV', null);
|
$managerdatenSenden('PV', null);
|
||||||
$managerdatenSenden('PV', 500);
|
$managerdatenSenden('PV', 537);
|
||||||
|
|
||||||
$test->waitUntil(
|
$test->waitUntil(
|
||||||
static fn (): bool => (float) GetValue($leistungID) === 500.0
|
static fn (): bool => (float) GetValue($leistungID) === 537.0
|
||||||
&& (int) GetValue($modusID) === 11
|
&& (int) GetValue($modusID) === 11
|
||||||
&& (int) GetValue($managementID) === 2
|
&& (int) GetValue($managementID) === 2
|
||||||
);
|
);
|
||||||
@@ -136,9 +136,9 @@ PHP
|
|||||||
$modusID,
|
$modusID,
|
||||||
$leistungID
|
$leistungID
|
||||||
): void {
|
): void {
|
||||||
$managerdatenSenden('PV', -500);
|
$managerdatenSenden('PV', -843);
|
||||||
$test->waitUntil(
|
$test->waitUntil(
|
||||||
static fn (): bool => (float) GetValue($leistungID) === 500.0
|
static fn (): bool => (float) GetValue($leistungID) === 843.0
|
||||||
&& (int) GetValue($modusID) === 12
|
&& (int) GetValue($modusID) === 12
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user