Lastwechsel ereignisbasiert sperren
Tests / test (push) Failing after 39s

This commit is contained in:
dh
2026-09-17 09:14:15 +00:00
parent 1b4a788f22
commit 355e7163fb
7 changed files with 100 additions and 46 deletions
+2 -8
View File
@@ -33,16 +33,10 @@
}, },
{ {
"type": "NumberSpinner", "type": "NumberSpinner",
"name": "Interval", "name": "LastwechselSperrzeit",
"caption": "Regelintervall", "caption": "Mindestzeit zwischen Lastwechseln",
"suffix": " s", "suffix": " s",
"minimum": 1 "minimum": 1
},
{
"type": "NumberSpinner",
"name": "IdleCounterMax",
"caption": "Zyklen bis Idle",
"minimum": 1
} }
] ]
}, },
+47 -26
View File
@@ -56,8 +56,7 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
$this->RegisterPropertyBoolean('BoilertemperaturGlaetten', false); $this->RegisterPropertyBoolean('BoilertemperaturGlaetten', false);
$this->RegisterPropertyInteger('Boilervolumen', 300); $this->RegisterPropertyInteger('Boilervolumen', 300);
$this->RegisterPropertyString('Zeitplan', '[]'); $this->RegisterPropertyString('Zeitplan', '[]');
$this->RegisterPropertyInteger('Interval', 5); $this->RegisterPropertyInteger('LastwechselSperrzeit', 5);
$this->RegisterPropertyInteger('IdleCounterMax', 2);
$this->RegisterPropertyInteger('TemperaturMaxAlter', 30); $this->RegisterPropertyInteger('TemperaturMaxAlter', 30);
$this->RegisterPropertyFloat('Hysterese', 5.0); $this->RegisterPropertyFloat('Hysterese', 5.0);
$this->RegisterPropertyBoolean('LegionellenfunktionAktiv', true); $this->RegisterPropertyBoolean('LegionellenfunktionAktiv', true);
@@ -74,7 +73,9 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
$this->RegisterAttributeBoolean('InitialwerteGesetzt', false); $this->RegisterAttributeBoolean('InitialwerteGesetzt', false);
$this->RegisterAttributeInteger('RegistrierteFuehlerID', 0); $this->RegisterAttributeInteger('RegistrierteFuehlerID', 0);
$this->RegisterAttributeInteger('LetzteBerechnung', 0); $this->RegisterAttributeInteger('LetzteBerechnung', 0);
$this->RegisterAttributeInteger('LetzteTemperaturberechnung', 0);
$this->RegisterAttributeInteger('LetzteVorgabeZeit', 0); $this->RegisterAttributeInteger('LetzteVorgabeZeit', 0);
$this->RegisterAttributeInteger('LetzterLastwechsel', 0);
$this->RegisterAttributeInteger('LetzteManagerID', 0); $this->RegisterAttributeInteger('LetzteManagerID', 0);
$this->RegisterAttributeInteger('LetzterLegionellenabschluss', 0); $this->RegisterAttributeInteger('LetzterLegionellenabschluss', 0);
$this->RegisterAttributeFloat('Glaettungswert', 0.0); $this->RegisterAttributeFloat('Glaettungswert', 0.0);
@@ -99,9 +100,9 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
$this->RegisterAttributeInteger('ZustandIdleCounter', 0); $this->RegisterAttributeInteger('ZustandIdleCounter', 0);
$this->RegisterTimer( $this->RegisterTimer(
'Regelzyklus', 'LastwechselFreigabe',
0, 0,
"IPS_RequestAction(\$_IPS['TARGET'], 'Regelzyklus', false);" "IPS_RequestAction(\$_IPS['TARGET'], 'LastwechselFreigabe', false);"
); );
$this->RegisterTimer( $this->RegisterTimer(
'Meldezyklus', 'Meldezyklus',
@@ -125,7 +126,7 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
try { try {
$this->pruefeKonfiguration(); $this->pruefeKonfiguration();
} catch (Throwable $fehler) { } catch (Throwable $fehler) {
$this->SetTimerInterval('Regelzyklus', 0); $this->SetTimerInterval('LastwechselFreigabe', 0);
$this->SetTimerInterval('Meldezyklus', 0); $this->SetTimerInterval('Meldezyklus', 0);
$this->SetTimerInterval('RueckmeldungVerzoegert', 0); $this->SetTimerInterval('RueckmeldungVerzoegert', 0);
$this->setzeZustand('Verfuegbar', false); $this->setzeZustand('Verfuegbar', false);
@@ -136,7 +137,7 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
} }
$this->registriereFuehlermeldung(); $this->registriereFuehlermeldung();
$this->SetTimerInterval('Regelzyklus', $this->ReadPropertyInteger('Interval') * 1000); $this->SetTimerInterval('LastwechselFreigabe', 0);
$this->SetTimerInterval('Meldezyklus', $this->ReadPropertyInteger('Meldeintervall') * 1000); $this->SetTimerInterval('Meldezyklus', $this->ReadPropertyInteger('Meldeintervall') * 1000);
if ($this->ReadAttributeInteger('LetzterLegionellenabschluss') <= 0) { if ($this->ReadAttributeInteger('LetzterLegionellenabschluss') <= 0) {
$this->WriteAttributeInteger('LetzterLegionellenabschluss', time()); $this->WriteAttributeInteger('LetzterLegionellenabschluss', time());
@@ -168,7 +169,8 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
$this->setzeTemperaturSollwert((string) $ident, $wert); $this->setzeTemperaturSollwert((string) $ident, $wert);
return; return;
case 'Regelzyklus': case 'LastwechselFreigabe':
$this->SetTimerInterval('LastwechselFreigabe', 0);
$this->regelzyklus(true); $this->regelzyklus(true);
return; return;
@@ -176,6 +178,7 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
if ((bool) $wert) { if ((bool) $wert) {
$this->SetTimerInterval('RueckmeldungVerzoegert', 0); $this->SetTimerInterval('RueckmeldungVerzoegert', 0);
} }
$this->regelzyklus(false);
$this->sendeVerbraucherdaten(); $this->sendeVerbraucherdaten();
return; return;
@@ -233,8 +236,15 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
$temperaturGueltig = $this->aktualisiereTemperatur(); $temperaturGueltig = $this->aktualisiereTemperatur();
$this->aktualisiereLegionellenstatus($temperaturGueltig, $jetzt); $this->aktualisiereLegionellenstatus($temperaturGueltig, $jetzt);
$restzeit = $this->aktualisiereLastwechselstatus($jetzt);
[$angebot, $aenderungMoeglich, $verfuegbar, $zwangsleistung] = [$angebot, $aenderungMoeglich, $verfuegbar, $zwangsleistung] =
$this->bestimmeLeistungsangebot($temperaturGueltig, $jetzt); $this->bestimmeLeistungsangebot($temperaturGueltig, $jetzt);
$aktuelleLeistung = (int) round((float) $this->leseZustand('Istleistung'));
$lastwechselGesperrt = $restzeit > 0 && $verfuegbar && $zwangsleistung !== 0;
if ($lastwechselGesperrt) {
$angebot = [$aktuelleLeistung];
$aenderungMoeglich = false;
}
$this->WriteAttributeString('Leistungsangebot', json_encode($angebot, JSON_THROW_ON_ERROR)); $this->WriteAttributeString('Leistungsangebot', json_encode($angebot, JSON_THROW_ON_ERROR));
$this->setzeZustand('Verfuegbar', $verfuegbar); $this->setzeZustand('Verfuegbar', $verfuegbar);
$this->setzeZustand('AenderungMoeglich', $aenderungMoeglich); $this->setzeZustand('AenderungMoeglich', $aenderungMoeglich);
@@ -247,7 +257,9 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
} }
$ziel = 0; $ziel = 0;
if ($verfuegbar) { if ($lastwechselGesperrt) {
$ziel = $aktuelleLeistung;
} elseif ($verfuegbar) {
if ($zwangsleistung !== null) { if ($zwangsleistung !== null) {
$ziel = $zwangsleistung; $ziel = $zwangsleistung;
} elseif ((bool) $this->leseZustand('SollwertGueltig')) { } elseif ((bool) $this->leseZustand('SollwertGueltig')) {
@@ -259,7 +271,7 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
} }
} }
$this->schalteLeistung($ziel); $this->schalteLeistung($ziel, $jetzt);
$this->setzeZustand('Sollleistung', $ziel); $this->setzeZustand('Sollleistung', $ziel);
$this->aktualisiereStoerungen($temperaturGueltig); $this->aktualisiereStoerungen($temperaturGueltig);
if ($meldungPlanen) { if ($meldungPlanen) {
@@ -296,18 +308,23 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
} }
$temperatur = (float) $rohwert; $temperatur = (float) $rohwert;
$jetzt = time();
$letzteTemperaturberechnung = $this->ReadAttributeInteger('LetzteTemperaturberechnung');
$this->WriteAttributeInteger('LetzteTemperaturberechnung', $jetzt);
if ($this->ReadPropertyBoolean('BoilertemperaturGlaetten')) { if ($this->ReadPropertyBoolean('BoilertemperaturGlaetten')) {
if (!$this->ReadAttributeBoolean('GlaettungInitialisiert')) { if (!$this->ReadAttributeBoolean('GlaettungInitialisiert')) {
$this->WriteAttributeFloat('Glaettungswert', $temperatur); $this->WriteAttributeFloat('Glaettungswert', $temperatur);
$this->WriteAttributeBoolean('GlaettungInitialisiert', true); $this->WriteAttributeBoolean('GlaettungInitialisiert', true);
} else { } elseif ($letzteTemperaturberechnung > 0 && $jetzt > $letzteTemperaturberechnung) {
$temperatur = WarmwasserRegler::pt1( $temperatur = WarmwasserRegler::pt1(
$this->ReadAttributeFloat('Glaettungswert'), $this->ReadAttributeFloat('Glaettungswert'),
$temperatur, $temperatur,
$this->ReadPropertyInteger('Interval'), $jetzt - $letzteTemperaturberechnung,
$this->ReadPropertyInteger('ZeitKonstante') $this->ReadPropertyInteger('ZeitKonstante')
); );
$this->WriteAttributeFloat('Glaettungswert', $temperatur); $this->WriteAttributeFloat('Glaettungswert', $temperatur);
} else {
$temperatur = $this->ReadAttributeFloat('Glaettungswert');
} }
} else { } else {
$this->WriteAttributeBoolean('GlaettungInitialisiert', false); $this->WriteAttributeBoolean('GlaettungInitialisiert', false);
@@ -403,14 +420,13 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
$this->setzeZustand('LegionellenbetriebAktiv', $fruehestensFaellig); $this->setzeZustand('LegionellenbetriebAktiv', $fruehestensFaellig);
} }
private function schalteLeistung(int $zielLeistung): void private function schalteLeistung(int $zielLeistung, int $jetzt): void
{ {
$stufen = WarmwasserRegler::dekodiereLeistungsstufen($this->ReadPropertyString('LeistungsStufen')); $stufen = WarmwasserRegler::dekodiereLeistungsstufen($this->ReadPropertyString('LeistungsStufen'));
$zielStufe = WarmwasserRegler::stufeFuerLeistung($stufen, $zielLeistung); $zielStufe = WarmwasserRegler::stufeFuerLeistung($stufen, $zielLeistung);
$bisherigeLeistung = (int) round((float) $this->leseZustand('Istleistung')); $bisherigeLeistung = (int) round((float) $this->leseZustand('Istleistung'));
if ($zielLeistung === $bisherigeLeistung && $this->kontakteEntsprechenZiel($stufen, $zielLeistung)) { if ($zielLeistung === $bisherigeLeistung && $this->kontakteEntsprechenZiel($stufen, $zielLeistung)) {
$this->WriteAttributeString('Schaltfehler', ''); $this->WriteAttributeString('Schaltfehler', '');
$this->verarbeiteIdle(false);
return; return;
} }
@@ -436,6 +452,8 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
} }
$this->setzeZustand('Istleistung', 0.0); $this->setzeZustand('Istleistung', 0.0);
$this->setzeZustand('AktiveStufe', 0); $this->setzeZustand('AktiveStufe', 0);
$this->WriteAttributeInteger('LetzterLastwechsel', $jetzt);
$this->aktualisiereLastwechselstatus($jetzt);
throw new RuntimeException('Leistungsstufe konnte nicht sicher geschaltet werden: ' . $fehler->getMessage()); throw new RuntimeException('Leistungsstufe konnte nicht sicher geschaltet werden: ' . $fehler->getMessage());
} }
@@ -443,7 +461,8 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
$this->setzeZustand('Istleistung', (float) $zielLeistung); $this->setzeZustand('Istleistung', (float) $zielLeistung);
$this->setzeZustand('Leistungsquelle', Nachrichtenvertrag::LEISTUNGSQUELLE_BERECHNET); $this->setzeZustand('Leistungsquelle', Nachrichtenvertrag::LEISTUNGSQUELLE_BERECHNET);
$this->setzeZustand('AktiveStufe', $zielStufe); $this->setzeZustand('AktiveStufe', $zielStufe);
$this->verarbeiteIdle(true); $this->WriteAttributeInteger('LetzterLastwechsel', $jetzt);
$this->aktualisiereLastwechselstatus($jetzt);
$this->protokolliere('Leistungsstufe', ['Leistung_W' => $zielLeistung, 'Stufe' => $zielStufe]); $this->protokolliere('Leistungsstufe', ['Leistung_W' => $zielLeistung, 'Stufe' => $zielStufe]);
} }
@@ -465,16 +484,18 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
return true; return true;
} }
private function verarbeiteIdle(bool $geaendert): void private function aktualisiereLastwechselstatus(int $jetzt): int
{ {
$counter = (int) $this->leseZustand('IdleCounter'); $restzeit = WarmwasserRegler::restzeitBisLastwechsel(
if ($geaendert) { $this->ReadAttributeInteger('LetzterLastwechsel'),
$counter = $this->ReadPropertyInteger('IdleCounterMax'); $jetzt,
} elseif ($counter > 0) { $this->ReadPropertyInteger('LastwechselSperrzeit')
--$counter; );
} $this->setzeZustand('IdleCounter', $restzeit);
$this->setzeZustand('IdleCounter', $counter); $this->setzeZustand('Idle', $restzeit === 0);
$this->setzeZustand('Idle', $counter === 0); $this->SetTimerInterval('LastwechselFreigabe', $restzeit > 0 ? $restzeit * 1000 : 0);
return $restzeit;
} }
private function aktualisiereEnergie(int $jetzt): void private function aktualisiereEnergie(int $jetzt): void
@@ -674,7 +695,7 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
} }
} }
foreach ([ foreach ([
'Meldeintervall', 'VorgabeTimeout', 'Interval', 'IdleCounterMax', 'TemperaturMaxAlter', 'Meldeintervall', 'VorgabeTimeout', 'LastwechselSperrzeit', 'TemperaturMaxAlter',
'ZeitKonstante', 'Boilervolumen', 'LegionellenMinimalintervallTage', 'ZeitKonstante', 'Boilervolumen', 'LegionellenMinimalintervallTage',
'LegionellenMaximalintervallTage', 'LegionellenMaximalintervallTage',
] as $property) { ] as $property) {
@@ -825,8 +846,8 @@ class VerbraucherWarmwassererwaermer extends IPSModule implements VerbraucherSch
220 220
); );
$this->RegisterVariableInteger('LegioCounter', 'Zeit seit Legionellenabschluss', '', 230); $this->RegisterVariableInteger('LegioCounter', 'Zeit seit Legionellenabschluss', '', 230);
$this->RegisterVariableBoolean('Idle', 'Idle', '~Switch', 240); $this->RegisterVariableBoolean('Idle', 'Lastwechsel freigegeben', '~Switch', 240);
$this->RegisterVariableInteger('IdleCounter', 'Idle Counter', '', 250); $this->RegisterVariableInteger('IdleCounter', 'Restzeit bis Lastwechsel', '', 250);
foreach (self::DIAGNOSE_VARIABLEN as $ident) { foreach (self::DIAGNOSE_VARIABLEN as $ident) {
$wert = $ident === 'Leistungsquelle' $wert = $ident === 'Leistungsquelle'
+3 -3
View File
@@ -5,13 +5,13 @@
| Enelix 1 | Enelix 2 | Hinweis | | Enelix 1 | Enelix 2 | Hinweis |
| --- | --- | --- | | --- | --- | --- |
| `LeistungsStufen` | `LeistungsStufen` | Felder `Stufe`, `Leistung`, `Schaltkontakt_Stufe` bleiben erhalten. | | `LeistungsStufen` | `LeistungsStufen` | Felder `Stufe`, `Leistung`, `Schaltkontakt_Stufe` bleiben erhalten. |
| `ZeitKonstante` | `ZeitKonstante` | PT1-Berechnung verwendet das reale Regelintervall. | | `ZeitKonstante` | `ZeitKonstante` | PT1-Berechnung verwendet die tatsaechlich verstrichene Zeit. |
| `Boilerfuehler_PT1` | `Boilerfuehler_PT1` | Alter und Datentyp werden geprueft. | | `Boilerfuehler_PT1` | `Boilerfuehler_PT1` | Alter und Datentyp werden geprueft. |
| `Boilertemperatur_glätten` | `BoilertemperaturGlaetten` | ASCII-sicherer Ident. | | `Boilertemperatur_glätten` | `BoilertemperaturGlaetten` | ASCII-sicherer Ident. |
| `Boilervolumen` | `Boilervolumen` | Wird fuer die Zeitplanprognose verwendet. | | `Boilervolumen` | `Boilervolumen` | Wird fuer die Zeitplanprognose verwendet. |
| `Zeitplan` | `Zeitplan` | JSON-Liste mit `Uhrzeit` und `Solltemperatur`. | | `Zeitplan` | `Zeitplan` | JSON-Liste mit `Uhrzeit` und `Solltemperatur`. |
| `Interval` | `Interval` | Regelintervall in Sekunden. | | `Interval` | entfaellt | Die Regelung wird ereignisbasiert ausgeloest. |
| `IdleCounterMax` | `IdleCounterMax` | Verhalten bleibt erhalten. | | `IdleCounterMax` | `LastwechselSperrzeit` | Zykluszaehler wird durch eine Sperrzeit in Sekunden ersetzt; Standard `5`. |
Die neuen Properties `TemperaturMaxAlter`, `Hysterese`, die Parameter der Die neuen Properties `TemperaturMaxAlter`, `Hysterese`, die Parameter der
Legionellenfunktion und `DiagnosevariablenAnzeigen` ersetzen feste Werte und Legionellenfunktion und `DiagnosevariablenAnzeigen` ersetzen feste Werte und
+9 -7
View File
@@ -19,13 +19,16 @@ Legionellenprogramm koennen die lokale Warmwassersicherheit priorisieren.
- Mindesttemperatur, vorausberechneter Zeitplan und Legionellenprogramm koennen - Mindesttemperatur, vorausberechneter Zeitplan und Legionellenprogramm koennen
die maximale Stufe lokal erzwingen. Diese Phase wird mit die maximale Stufe lokal erzwingen. Diese Phase wird mit
`AenderungMoeglich=false` gemeldet. `AenderungMoeglich=false` gemeldet.
- Lastwechsel werden fuer `LastwechselSperrzeit` Sekunden gesperrt. Danach
erfolgt genau eine ereignisgesteuerte Neuberechnung; sicherheitsbedingtes
Ausschalten bleibt sofort moeglich.
- `BoilertemperaturGlaetten` aktiviert eine PT1-Glaettung mit konfigurierbarer - `BoilertemperaturGlaetten` aktiviert eine PT1-Glaettung mit konfigurierbarer
Zeitkonstante. Zeitkonstante und der tatsaechlich verstrichenen Zeit.
## Properties ## Properties
Zu den sechs gemeinsamen Verbraucher-Properties aus Zu den sechs gemeinsamen Verbraucher-Properties aus
[`Schnittstelle.md`](../../Schnittstelle.md) kommen 14 Modul-Properties hinzu. [`Schnittstelle.md`](../../Schnittstelle.md) kommen 13 Modul-Properties hinzu.
| Ident | Typ | Standard | Beschreibung | | Ident | Typ | Standard | Beschreibung |
| --- | --- | ---: | --- | | --- | --- | ---: | --- |
@@ -35,8 +38,7 @@ Zu den sechs gemeinsamen Verbraucher-Properties aus
| `BoilertemperaturGlaetten` | Boolean | `false` | PT1-Glaettung aktiv. | | `BoilertemperaturGlaetten` | Boolean | `false` | PT1-Glaettung aktiv. |
| `Boilervolumen` | Integer | `300` | Volumen in Litern fuer die Zeitplanprognose. | | `Boilervolumen` | Integer | `300` | Volumen in Litern fuer die Zeitplanprognose. |
| `Zeitplan` | JSON-Liste | `[]` | Uhrzeit und Solltemperatur. | | `Zeitplan` | JSON-Liste | `[]` | Uhrzeit und Solltemperatur. |
| `Interval` | Integer | `5` | Regelintervall in Sekunden. | | `LastwechselSperrzeit` | Integer | `5` | Mindestzeit zwischen zwei Lastwechseln in Sekunden. |
| `IdleCounterMax` | Integer | `2` | Zyklen bis zum Idle-Status. |
| `TemperaturMaxAlter` | Integer | `30` | Maximal zulaessiges Messwertalter in Sekunden. | | `TemperaturMaxAlter` | Integer | `30` | Maximal zulaessiges Messwertalter in Sekunden. |
| `Hysterese` | Float | `5.0` | Temperaturhysterese in K. | | `Hysterese` | Float | `5.0` | Temperaturhysterese in K. |
| `LegionellenfunktionAktiv` | Boolean | `true` | Zweistufige periodische Aufheizung aktiv. | | `LegionellenfunktionAktiv` | Boolean | `true` | Zweistufige periodische Aufheizung aktiv. |
@@ -74,8 +76,8 @@ Mit `DiagnosevariablenAnzeigen` werden diese 16 Variablen angelegt:
- `NachladenAktiv` - `NachladenAktiv`
- `LegionellenbetriebAktiv` - `LegionellenbetriebAktiv`
- `LegioCounter` - `LegioCounter`
- `Idle` - `Idle` (Lastwechsel freigegeben)
- `IdleCounter` - `IdleCounter` (verbleibende Sperrzeit in Sekunden)
Die Regelung und Managerkommunikation verwenden fuer diese Werte eine interne Die Regelung und Managerkommunikation verwenden fuer diese Werte eine interne
Zustandsablage und funktionieren auch ohne angelegte Diagnosevariablen weiter. Zustandsablage und funktionieren auch ohne angelegte Diagnosevariablen weiter.
@@ -96,7 +98,7 @@ verzoegert, damit keine synchrone Manager-Verbraucher-Endlosschleife entsteht.
`WarmwasserReglerTest.php` prueft Leistungsstufen, PT1, Energieberechnung, `WarmwasserReglerTest.php` prueft Leistungsstufen, PT1, Energieberechnung,
Zeitplan, thermische Prognose und die zweistufigen Legionellengrenzen. Zeitplan, thermische Prognose und die zweistufigen Legionellengrenzen.
`WarmwassererwaermerModulstrukturTest.php` prueft Metadaten, Formular, `WarmwassererwaermerModulstrukturTest.php` prueft Metadaten, Formular,
20 Properties, bedarfsgesteuerte Diagnosevariablen, Schnittstellenvertrag und 19 Properties, bedarfsgesteuerte Diagnosevariablen, Schnittstellenvertrag und
Break-before-make-Schaltung. Break-before-make-Schaltung.
## Migration ## Migration
+17
View File
@@ -109,6 +109,23 @@ final class WarmwasserRegler
return $bisher + $alpha * ($eingang - $bisher); return $bisher + $alpha * ($eingang - $bisher);
} }
public static function restzeitBisLastwechsel(
int $letzterLastwechsel,
int $jetzt,
int $sperrzeitSekunden
): int {
if ($sperrzeitSekunden < 0) {
throw new InvalidArgumentException('Die Lastwechsel-Sperrzeit darf nicht negativ sein.');
}
if ($letzterLastwechsel <= 0) {
return 0;
}
$verstrichen = max(0, $jetzt - $letzterLastwechsel);
return max(0, $sperrzeitSekunden - $verstrichen);
}
public static function energieKWh(float $leistungW, int $dauerSekunden): float public static function energieKWh(float $leistungW, int $dauerSekunden): float
{ {
if ($dauerSekunden < 0) { if ($dauerSekunden < 0) {
+8
View File
@@ -53,6 +53,14 @@ final class WarmwasserReglerTest extends TestCase
self::assertEqualsWithDelta(20.4, WarmwasserRegler::pt1(20.0, 30.0, 5, 120), 0.001); self::assertEqualsWithDelta(20.4, WarmwasserRegler::pt1(20.0, 30.0, 5, 120), 0.001);
} }
public function testLastwechselRestzeitWirdInSekundenBerechnet(): void
{
self::assertSame(0, WarmwasserRegler::restzeitBisLastwechsel(0, 100, 5));
self::assertSame(5, WarmwasserRegler::restzeitBisLastwechsel(100, 100, 5));
self::assertSame(2, WarmwasserRegler::restzeitBisLastwechsel(100, 103, 5));
self::assertSame(0, WarmwasserRegler::restzeitBisLastwechsel(100, 105, 5));
}
public function testEnergieWirdInKWhBerechnet(): void public function testEnergieWirdInKWhBerechnet(): void
{ {
self::assertEqualsWithDelta(3.0, WarmwasserRegler::energieKWh(3000.0, 3600), 0.000001); self::assertEqualsWithDelta(3.0, WarmwasserRegler::energieKWh(3000.0, 3600), 0.000001);
+14 -2
View File
@@ -29,14 +29,14 @@ final class WarmwassererwaermerModulstrukturTest extends TestCase
self::assertMatchesRegularExpression('/^\{[0-9A-F-]{36}\}$/', $metadaten['id']); self::assertMatchesRegularExpression('/^\{[0-9A-F-]{36}\}$/', $metadaten['id']);
} }
public function testAlle20PropertiesSindRegistriertUndImFormularDokumentiert(): void public function testAlle19PropertiesSindRegistriertUndImFormularDokumentiert(): void
{ {
$basis = (string) file_get_contents(__DIR__ . '/../libs/VerbraucherBasisTrait.php'); $basis = (string) file_get_contents(__DIR__ . '/../libs/VerbraucherBasisTrait.php');
$modul = (string) file_get_contents(self::MODULPFAD . 'module.php'); $modul = (string) file_get_contents(self::MODULPFAD . 'module.php');
preg_match_all('/RegisterProperty(?:Integer|Float|Boolean|String)\(\'([^\']+)\'/', $basis . $modul, $treffer); preg_match_all('/RegisterProperty(?:Integer|Float|Boolean|String)\(\'([^\']+)\'/', $basis . $modul, $treffer);
$properties = array_values(array_unique($treffer[1])); $properties = array_values(array_unique($treffer[1]));
self::assertCount(20, $properties); self::assertCount(19, $properties);
$formular = (string) file_get_contents(self::MODULPFAD . 'form.json'); $formular = (string) file_get_contents(self::MODULPFAD . 'form.json');
foreach ($properties as $property) { foreach ($properties as $property) {
@@ -44,6 +44,18 @@ final class WarmwassererwaermerModulstrukturTest extends TestCase
} }
} }
public function testRegelungIstEreignisbasiertUndSperrtLastwechselFuenfSekunden(): void
{
$inhalt = (string) file_get_contents(self::MODULPFAD . 'module.php');
self::assertStringContainsString("RegisterPropertyInteger('LastwechselSperrzeit', 5)", $inhalt);
self::assertStringNotContainsString("RegisterPropertyInteger('Interval'", $inhalt);
self::assertStringNotContainsString("RegisterPropertyInteger('IdleCounterMax'", $inhalt);
self::assertStringNotContainsString("ReadPropertyInteger('Interval')", $inhalt);
self::assertStringContainsString("'LastwechselFreigabe'", $inhalt);
self::assertStringContainsString('LetzteTemperaturberechnung', $inhalt);
}
public function testModulVerwendetAusschliesslichDenVertrag30(): void public function testModulVerwendetAusschliesslichDenVertrag30(): void
{ {
$inhalt = (string) file_get_contents(self::MODULPFAD . 'module.php'); $inhalt = (string) file_get_contents(self::MODULPFAD . 'module.php');