diff --git a/VerbraucherEinStufig/README.md b/VerbraucherEinStufig/README.md new file mode 100644 index 0000000..5d7a884 --- /dev/null +++ b/VerbraucherEinStufig/README.md @@ -0,0 +1,172 @@ +# Verbraucher 1-Stufig + +IP-Symcon-Modul fuer einen elektrischen Verbraucher, der genau zwei +Leistungszustaende kennt: aus (`0 W`) und ein (`Nennleistung`). + +Das Modul ist fuer IP-Symcon ab Version 8.0 und den Enelix-Nachrichtenvertrag +`4.0` ausgelegt. Es arbeitet ereignisbasiert und besitzt weder einen +Regelzyklus noch eine konfigurierbare Zyklusanzahl oder Zykluszeit. + +## Funktionen + +- Schalten eines Boolean-Aktors auf `0 W` oder die konfigurierte Nennleistung +- optionale separate Schaltzustands-Rueckmeldung +- Mindest-Einschalt- und Mindest-Ausschaltdauer +- taegliche Mindestlaufzeit +- getrennte Prioritaeten fuer PV- und Peakbetrieb +- ereignisbasierte Zustandsmeldung an den Enelix Manager +- Diagnosevariablen und optionales Debug-Logging +- sichere lokale Deaktivierung ueber die Variable `Aktiv` + +## Installation + +1. Im IP-Symcon Module Control die Bibliothek + `https://git.belevo.ch/ENELIX/Enelix-EMS.git` installieren. +2. Fuer Entwicklung und Tests den Kanal beziehungsweise Branch `develop` + verwenden. +3. Unter **Instanz hinzufuegen** nach **Verbraucher 1-Stufig** suchen. +4. Eine Instanz anlegen und mindestens Nennleistung und Schaltkontakt + konfigurieren. + +## Konfiguration + +| Einstellung | Standard | Beschreibung | +| --- | ---: | --- | +| `PrioritaetPV` | `0` | Reihenfolge bei der PV-Leistungsverteilung. | +| `PrioritaetPeak` | `0` | Reihenfolge bei der Lastspitzenregelung. | +| `Meldeintervall` | `10 s` | Periodische Vollmeldung an zugeordnete Manager. | +| `VorgabeTimeout` | `120 s` | Gueltigkeitsdauer einer Manager-Vorgabe. | +| `Mindesteinschaltdauer` | `5 s` | Mindestdauer eines bestaetigten Ein-Zustands. | +| `Mindestausschaltdauer` | `5 s` | Mindestdauer eines bestaetigten Aus-Zustands. | +| `Nennleistung` | `0 W` | Leistungsaufnahme im eingeschalteten Zustand. | +| `SchaltkontaktVariableID` | `0` | Booleanvariable des zu schaltenden Aktors. | +| `SchaltkontaktInvertiert` | `false` | Invertiert die Aktorlogik. | +| `RueckmeldungVariableID` | `0` | Optionale Booleanvariable fuer den physischen Zustand. | +| `Mindestlaufzeit` | `0 s` | Geforderte Laufzeit pro lokalem Kalendertag. | +| `PeakSperreBeiMindestlaufzeitAnbieten` | `true` | Erlaubt im Peakbetrieb eine Sperre trotz faelliger Tagesmindestlaufzeit. | +| `DiagnosevariablenAnzeigen` | `false` | Blendet technische Diagnosevariablen ein. | +| `LoggingEin` | `false` | Aktiviert zusaetzliche Debug-Ausgaben. | + +`Nennleistung` muss groesser als `0` sein. Der Schaltkontakt muss eine +Booleanvariable mit funktionsfaehiger Standard- oder benutzerdefinierter +Aktion sein. Beide Mindestzeiten duerfen auf `0` gesetzt werden. + +Der fruehere allgemeine `Umschaltabstand` sowie Properties fuer Zyklusanzahl +und Zykluszeit existieren nicht. + +## Verhalten ohne separate Rueckmeldung + +Ist keine `RueckmeldungVariableID` konfiguriert, wird die Aktorvariable als +unmittelbare Schaltbestaetigung verwendet. + +Nach einem erfolgreichen Schaltbefehl beginnt ab dem uebernommenen +Aktorzustand: + +- beim Einschalten die `Mindesteinschaltdauer`, +- beim Ausschalten die `Mindestausschaltdauer`. + +Uebernimmt die Aktorvariable den angeforderten Wert nicht, meldet das Modul +einen Schaltfehler und stellt sich dem Manager nicht als schaltbereit dar. + +## Verhalten mit separater Rueckmeldung + +Ist eine `RueckmeldungVariableID` konfiguriert, bestimmt ausschliesslich deren +Booleanwert den bestaetigten Schaltzustand. `true` muss dabei physisch +eingeschaltet bedeuten. + +Zwischen Aktorbefehl und passender Rueckmeldung meldet das Modul: + +- den bisherigen bestaetigten Schaltzustand und die bisherige Istleistung, +- `SchaltbefehlAusstehend=true`, +- `Schaltbereit=false`, +- `AenderungMoeglich=false`. + +Die jeweilige Mindestzeit beginnt erst, sobald die Rueckmeldung den neuen +Zustand bestaetigt. Bleibt die Rueckmeldung aus, bleibt der Schaltbefehl +sichtbar ausstehend. Eine abweichende Rueckmeldung ohne laufenden +Schaltvorgang wird als Rueckmeldefehler gemeldet. + +## Mindestzeiten und Leistungsangebot + +Waerend einer Mindest-Ein- oder Mindest-Aus-Zeit bietet das Modul nur die +bestaetigte aktuelle Leistung an. Ein regulaerer Lastwechsel ist erst nach +Ablauf der Mindestzeit wieder moeglich. + +Der Manager erhaelt dazu unter anderem: + +| Zustand | Bedeutung | +| --- | --- | +| `Schaltzustand` | Bestaetigter Ein-/Aus-Zustand. | +| `SchaltbefehlAusstehend` | Aktorbefehl wartet auf physische Bestaetigung. | +| `Schaltbereit` | Ein weiterer regulaerer Lastwechsel ist moeglich. | +| `RestMindestzeit_s` | Verbleibende Mindestzeit in Sekunden. | +| `Tageslaufzeit_s` | Bestaetigte Laufzeit des aktuellen Tages. | +| `Rueckmeldefehler` | Aktor und Rueckmeldung widersprechen sich unerwartet. | + +Die lokale Aktion `Aktiv=false` ist ein bewusster Sicherheits-Override. Sie +schaltet den Verbraucher auch waehrend einer laufenden +Mindesteinschaltdauer aus. + +## Sichtbare Variablen + +Immer vorhanden sind: + +- `Aktiv`: lokale Freigabe fuer das Energiemanagement +- `Schaltzustand`: bestaetigter oder aus dem Aktor abgeleiteter Zustand +- `Tageslaufzeit`: bestaetigte Laufzeit des aktuellen Tages in Sekunden + +Bei aktivierter Diagnose werden zusaetzlich Soll- und Istleistung, +Verfuegbarkeit, Schaltbereitschaft, Stoerung, Rueckmeldefehler, +ausstehender Schaltbefehl und verbleibende Mindestzeit angezeigt. + +## Inbetriebnahme + +1. Nennleistung und Schaltkontakt konfigurieren. +2. Falls vorhanden, die separate Rueckmeldung auswaehlen und ihre + `true`-Semantik pruefen. +3. Mindest-Ein- und Mindest-Ausschaltdauer passend zum angeschlossenen + Geraet festlegen. +4. Den Verbraucher im Enelix Manager manuell oder automatisch aktiv + zuordnen. +5. Fuer die Erstpruefung Diagnosevariablen und bei Bedarf Logging aktivieren. +6. Die Variable `Aktiv` einschalten. +7. Unter Aufsicht je eine Ein- und Aus-Vorgabe durch den Manager ausfuehren. +8. Kontrollieren, dass Rueckmeldung, Mindestzeiten und `Schaltbereit` + erwartungsgemaess wechseln. + +## Fehlersuche + +- **Konfiguration ungueltig:** Nennleistung, Variablentyp und Aktoraktion + pruefen. +- **Schaltbefehl bleibt ausstehend:** Separate Rueckmeldung und deren + `true`-Semantik pruefen. +- **Rueckmeldefehler:** Aktor- und Rueckmeldewert stimmen ausserhalb eines + laufenden Schaltvorgangs nicht ueberein. +- **Kein Lastwechsel moeglich:** `RestMindestzeit`, `Aktiv`, + `SollwertGueltig` und die Managerzuordnung kontrollieren. +- **Keine Manager-Vorgabe akzeptiert:** Der Verbraucher muss beim sendenden + Manager aktiv zugeordnet sein. + +## Tests + +Nur dieses Modul: + +~~~bash +composer check:verbraucher-einstufig +~~~ + +Gesamtes Repository inklusive dieser Testsuite: + +~~~bash +composer check +~~~ + +Die Modulsuite kann damit unabhaengig weiterentwickelt werden und bleibt +gleichzeitig Bestandteil des allgemeinen Tests. + +## Weiterfuehrende Dokumentation + +- [Ausfuehrliche Modulbeschreibung](../docs/module/Verbraucher-1-Stufig/README.md) +- [Manager-Verbraucher-Schnittstelle](../docs/Schnittstelle.md) +- [Migration von Enelix 1](../docs/migration/Verbraucher-1-Stufig.md) +- [Aufbau der separaten Testsuite](../tests/VerbraucherEinStufig/README.md) diff --git a/tests/DokumentationsstrukturTest.php b/tests/DokumentationsstrukturTest.php index 17b7129..d00ceea 100644 --- a/tests/DokumentationsstrukturTest.php +++ b/tests/DokumentationsstrukturTest.php @@ -43,7 +43,7 @@ final class DokumentationsstrukturTest extends TestCase { $inhalt = file_get_contents(__DIR__ . '/../docs/module/Ladestation-Stand-Alone/README.md'); self::assertNotFalse($inhalt); - self::assertStringContainsString('Status: Implementiert', $inhalt); + self::assertStringContainsStringIgnoringCase('Status: implementiert', $inhalt); self::assertStringContainsString('Properties', $inhalt); self::assertStringContainsString('Fake-HTTP-Transport', $inhalt); } @@ -65,11 +65,22 @@ final class DokumentationsstrukturTest extends TestCase public function testVerbraucherEinStufigDokumentiertDenImplementiertenStand(): void { - $inhalt = file_get_contents(__DIR__ . '/../docs/module/Verbraucher-1-Stufig/README.md'); - self::assertNotFalse($inhalt); - self::assertStringContainsString('Status: implementiert', $inhalt); - self::assertStringContainsString('Umschaltabstand', $inhalt); - self::assertStringContainsString('Installation und Inbetriebnahme', $inhalt); + $detailInhalt = file_get_contents( + __DIR__ . '/../docs/module/Verbraucher-1-Stufig/README.md' + ); + self::assertNotFalse($detailInhalt); + self::assertStringContainsString('Status: implementiert', $detailInhalt); + self::assertStringContainsString('Umschaltabstand', $detailInhalt); + self::assertStringContainsString('Installation und Inbetriebnahme', $detailInhalt); + + $modulInhalt = file_get_contents(__DIR__ . '/../VerbraucherEinStufig/README.md'); + self::assertNotFalse($modulInhalt); + self::assertStringContainsString('Verhalten ohne separate Rueckmeldung', $modulInhalt); + self::assertStringContainsString('Mindesteinschaltdauer', $modulInhalt); + self::assertStringContainsString( + 'composer check:verbraucher-einstufig', + $modulInhalt + ); } public function testWarmwassererwaermerDokumentiertDenImplementiertenStand(): void