This commit is contained in:
+154
-52
@@ -33,6 +33,8 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
'Stoerung',
|
||||
'Stoertext',
|
||||
'Rueckmeldefehler',
|
||||
'SchaltbefehlAusstehend',
|
||||
'RestMindestzeit',
|
||||
];
|
||||
|
||||
public function Create(): void
|
||||
@@ -45,7 +47,8 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
$this->RegisterPropertyBoolean('SchaltkontaktInvertiert', false);
|
||||
$this->RegisterPropertyInteger('RueckmeldungVariableID', 0);
|
||||
$this->RegisterPropertyInteger('Mindestlaufzeit', 0);
|
||||
$this->RegisterPropertyInteger('Umschaltabstand', 5);
|
||||
$this->RegisterPropertyInteger('Mindesteinschaltdauer', 5);
|
||||
$this->RegisterPropertyInteger('Mindestausschaltdauer', 5);
|
||||
$this->RegisterPropertyBoolean('DiagnosevariablenAnzeigen', false);
|
||||
|
||||
$this->RegisterVariableBoolean('Schaltzustand', 'Schaltzustand', '~Switch', 100);
|
||||
@@ -55,7 +58,8 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
$this->RegisterAttributeInteger('RegistrierteRueckmeldung', 0);
|
||||
$this->RegisterAttributeInteger('LetzteVorgabeZeit', 0);
|
||||
$this->RegisterAttributeInteger('LetzteManagerID', 0);
|
||||
$this->RegisterAttributeInteger('GesperrtBis', 0);
|
||||
$this->RegisterAttributeInteger('BestaetigtSeit', 0);
|
||||
$this->RegisterAttributeInteger('FreigabeAb', 0);
|
||||
$this->RegisterAttributeInteger('LaufzeitStandZeit', 0);
|
||||
$this->RegisterAttributeString('LaufzeitTag', '');
|
||||
$this->RegisterAttributeString('Leistungsangebot', '[0]');
|
||||
@@ -75,6 +79,8 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
$this->RegisterAttributeBoolean('ZustandSchaltzustand', false);
|
||||
$this->RegisterAttributeInteger('ZustandTageslaufzeit', 0);
|
||||
$this->RegisterAttributeBoolean('ZustandRueckmeldefehler', false);
|
||||
$this->RegisterAttributeBoolean('ZustandSchaltbefehlAusstehend', false);
|
||||
$this->RegisterAttributeInteger('ZustandRestMindestzeit', 0);
|
||||
|
||||
$this->RegisterTimer(
|
||||
'Meldezyklus',
|
||||
@@ -86,10 +92,12 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
0,
|
||||
"IPS_RequestAction(\$_IPS['TARGET'], 'Melden', true);"
|
||||
);
|
||||
// Bestehende Instanzen behalten den alten Timer deaktiviert.
|
||||
$this->RegisterTimer('Umschaltfreigabe', 0, '');
|
||||
$this->RegisterTimer(
|
||||
'Umschaltfreigabe',
|
||||
'MindestzeitAbgelaufen',
|
||||
0,
|
||||
"IPS_RequestAction(\$_IPS['TARGET'], 'Umschaltfreigabe', 0);"
|
||||
"IPS_RequestAction(\$_IPS['TARGET'], 'MindestzeitAbgelaufen', 0);"
|
||||
);
|
||||
$this->RegisterTimer(
|
||||
'VorgabeTimeout',
|
||||
@@ -162,7 +170,7 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
$this->sendeVerbraucherdaten();
|
||||
return;
|
||||
|
||||
case 'Umschaltfreigabe':
|
||||
case 'MindestzeitAbgelaufen':
|
||||
case 'VorgabeTimeout':
|
||||
case 'Tagesplanung':
|
||||
$this->SetTimerInterval((string) $ident, 0);
|
||||
@@ -223,14 +231,8 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
$schaltzustand = $this->leseSchaltzustand();
|
||||
|
||||
if ($schaltzustand !== $vorherigerSchaltzustand) {
|
||||
$this->setzeSchaltzustand($schaltzustand);
|
||||
if ($this->ReadAttributeBoolean('Initialisiert')
|
||||
&& !$this->ReadAttributeBoolean('SchaltpruefungAusstehend')
|
||||
) {
|
||||
$this->starteUmschaltsperre($jetzt);
|
||||
}
|
||||
$this->bestaetigeZustandswechsel($schaltzustand, $jetzt);
|
||||
}
|
||||
|
||||
$this->pruefeAusstehendenSchaltvorgang($schaltzustand, $jetzt);
|
||||
$this->pruefeVorgabeTimeout($jetzt);
|
||||
if ((bool) $this->leseZustand('SollwertGueltig')
|
||||
@@ -244,12 +246,23 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
}
|
||||
|
||||
$aktiv = (bool) $this->GetValue('Aktiv');
|
||||
$wartetAufRueckmeldung = $this->ReadAttributeBoolean('SchaltpruefungAusstehend');
|
||||
$rueckmeldefehler = $this->ReadPropertyInteger('RueckmeldungVariableID') > 0
|
||||
&& !$wartetAufRueckmeldung
|
||||
&& $schaltzustand !== $this->leseAktorSchaltzustand();
|
||||
$this->setzeZustand('Rueckmeldefehler', $rueckmeldefehler);
|
||||
|
||||
$fehlerfrei = $this->ReadAttributeString('Schaltfehler') === ''
|
||||
&& !(bool) $this->leseZustand('Rueckmeldefehler');
|
||||
&& !$rueckmeldefehler;
|
||||
$verfuegbar = $aktiv && $fehlerfrei;
|
||||
$mindestlaufzeitErzwungen = $verfuegbar
|
||||
&& $this->mussMindestlaufzeitErzwingen($jetzt);
|
||||
$gesperrt = $jetzt < $this->ReadAttributeInteger('GesperrtBis');
|
||||
$restMindestzeit = $wartetAufRueckmeldung
|
||||
? 0
|
||||
: EinStufigRegler::restMindestzeit(
|
||||
$this->ReadAttributeInteger('FreigabeAb'),
|
||||
$jetzt
|
||||
);
|
||||
|
||||
$ziel = 0;
|
||||
if ($verfuegbar) {
|
||||
@@ -261,20 +274,37 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
}
|
||||
|
||||
$istleistung = $this->leistungFuerSchaltzustand($schaltzustand);
|
||||
if ($ziel !== $istleistung && (!$aktiv || (!$gesperrt && $fehlerfrei))) {
|
||||
$this->schalte($ziel > 0, $jetzt);
|
||||
$zielzustand = $ziel > 0;
|
||||
$befehlNoetig = $ziel !== $istleistung
|
||||
|| ($wartetAufRueckmeldung
|
||||
&& $zielzustand !== $this->ReadAttributeBoolean('Schaltziel'));
|
||||
// Die lokale Deaktivierung bleibt der einzige bewusste Mindestzeit-Override.
|
||||
$sicheresAusschalten = !$zielzustand && !$aktiv;
|
||||
if ($befehlNoetig
|
||||
&& ($sicheresAusschalten
|
||||
|| ($restMindestzeit === 0 && !$wartetAufRueckmeldung && $fehlerfrei))
|
||||
) {
|
||||
$this->schalte($zielzustand, $jetzt);
|
||||
$schaltzustand = $this->leseSchaltzustand();
|
||||
$this->setzeSchaltzustand($schaltzustand);
|
||||
$this->pruefeAusstehendenSchaltvorgang($schaltzustand, $jetzt);
|
||||
$istleistung = $this->leistungFuerSchaltzustand($schaltzustand);
|
||||
$gesperrt = $jetzt < $this->ReadAttributeInteger('GesperrtBis');
|
||||
$wartetAufRueckmeldung = $this->ReadAttributeBoolean('SchaltpruefungAusstehend');
|
||||
$restMindestzeit = $wartetAufRueckmeldung
|
||||
? 0
|
||||
: EinStufigRegler::restMindestzeit(
|
||||
$this->ReadAttributeInteger('FreigabeAb'),
|
||||
$jetzt
|
||||
);
|
||||
}
|
||||
|
||||
$this->setzeZustand('Sollleistung', $ziel);
|
||||
$this->setzeZustand('Verfuegbar', $verfuegbar);
|
||||
$this->setzeZustand('SchaltbefehlAusstehend', $wartetAufRueckmeldung);
|
||||
$this->setzeZustand('RestMindestzeit', $restMindestzeit);
|
||||
$aenderungMoeglich = $verfuegbar
|
||||
&& !$mindestlaufzeitErzwungen
|
||||
&& !$gesperrt
|
||||
&& !$this->ReadAttributeBoolean('SchaltpruefungAusstehend');
|
||||
&& $restMindestzeit === 0
|
||||
&& !$wartetAufRueckmeldung;
|
||||
$this->setzeZustand('AenderungMoeglich', $aenderungMoeglich);
|
||||
|
||||
$angebot = EinStufigRegler::leistungsangebot(
|
||||
@@ -314,58 +344,77 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
|
||||
$this->WriteAttributeBoolean('Schaltziel', $einschalten);
|
||||
$this->WriteAttributeBoolean('SchaltpruefungAusstehend', true);
|
||||
$this->setzeZustand('SchaltbefehlAusstehend', true);
|
||||
$this->WriteAttributeBoolean('SchaltvorgangLaeuft', true);
|
||||
try {
|
||||
RequestAction($kontaktID, $aktorwert);
|
||||
} catch (Throwable $fehler) {
|
||||
$this->WriteAttributeBoolean('SchaltpruefungAusstehend', false);
|
||||
$this->setzeZustand('SchaltbefehlAusstehend', false);
|
||||
throw new RuntimeException('Schaltkontakt konnte nicht gesetzt werden: ' . $fehler->getMessage());
|
||||
} finally {
|
||||
$this->WriteAttributeBoolean('SchaltvorgangLaeuft', false);
|
||||
}
|
||||
|
||||
$this->starteUmschaltsperre($jetzt);
|
||||
$istzustand = $this->leseSchaltzustand();
|
||||
if ($istzustand === $einschalten) {
|
||||
$this->WriteAttributeBoolean('SchaltpruefungAusstehend', false);
|
||||
$this->setzeZustand('Rueckmeldefehler', false);
|
||||
$this->bestaetigeZustandswechsel($istzustand, $jetzt);
|
||||
} elseif ($this->ReadPropertyInteger('RueckmeldungVariableID') <= 0) {
|
||||
$this->WriteAttributeBoolean('SchaltpruefungAusstehend', false);
|
||||
$this->setzeZustand('SchaltbefehlAusstehend', false);
|
||||
throw new RuntimeException('Schaltkontakt hat den angeforderten Zustand nicht uebernommen.');
|
||||
} elseif ($this->ReadPropertyInteger('Umschaltabstand') === 0) {
|
||||
$this->WriteAttributeBoolean('SchaltpruefungAusstehend', false);
|
||||
$this->setzeZustand('Rueckmeldefehler', true);
|
||||
} else {
|
||||
// Die physische Rueckmeldung bestimmt Zustand und Beginn der Mindestzeit.
|
||||
$this->setzeZustand('Rueckmeldefehler', false);
|
||||
}
|
||||
|
||||
$this->WriteAttributeString('Schaltfehler', '');
|
||||
$this->protokolliere('Lastwechsel', ['Eingeschaltet' => $einschalten]);
|
||||
$this->protokolliere('Schaltbefehl', ['Eingeschaltet' => $einschalten]);
|
||||
}
|
||||
|
||||
private function pruefeAusstehendenSchaltvorgang(bool $schaltzustand, int $jetzt): void
|
||||
{
|
||||
if (!$this->ReadAttributeBoolean('SchaltpruefungAusstehend')) {
|
||||
if ((bool) $this->leseZustand('Rueckmeldefehler')
|
||||
&& $schaltzustand === $this->ReadAttributeBoolean('Schaltziel')
|
||||
) {
|
||||
$this->setzeZustand('Rueckmeldefehler', false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($schaltzustand === $this->ReadAttributeBoolean('Schaltziel')) {
|
||||
$this->WriteAttributeBoolean('SchaltpruefungAusstehend', false);
|
||||
$this->setzeZustand('Rueckmeldefehler', false);
|
||||
return;
|
||||
}
|
||||
if ($jetzt >= $this->ReadAttributeInteger('GesperrtBis')) {
|
||||
$this->WriteAttributeBoolean('SchaltpruefungAusstehend', false);
|
||||
$this->setzeZustand('Rueckmeldefehler', true);
|
||||
if ($this->ReadAttributeBoolean('SchaltpruefungAusstehend')
|
||||
&& $schaltzustand === $this->ReadAttributeBoolean('Schaltziel')
|
||||
) {
|
||||
$this->bestaetigeZustandswechsel($schaltzustand, $jetzt);
|
||||
}
|
||||
|
||||
$this->setzeZustand(
|
||||
'SchaltbefehlAusstehend',
|
||||
$this->ReadAttributeBoolean('SchaltpruefungAusstehend')
|
||||
);
|
||||
}
|
||||
|
||||
private function starteUmschaltsperre(int $jetzt): void
|
||||
private function bestaetigeZustandswechsel(bool $schaltzustand, int $jetzt): void
|
||||
{
|
||||
$gesperrtBis = $jetzt + $this->ReadPropertyInteger('Umschaltabstand');
|
||||
$this->WriteAttributeInteger('GesperrtBis', $gesperrtBis);
|
||||
$vorherigerZustand = (bool) $this->leseZustand('Schaltzustand');
|
||||
$zustandGeaendert = $schaltzustand !== $vorherigerZustand
|
||||
|| $this->ReadAttributeInteger('BestaetigtSeit') <= 0;
|
||||
|
||||
$this->setzeSchaltzustand($schaltzustand);
|
||||
if ($zustandGeaendert) {
|
||||
$this->WriteAttributeInteger('BestaetigtSeit', $jetzt);
|
||||
$this->WriteAttributeInteger(
|
||||
'FreigabeAb',
|
||||
EinStufigRegler::freigabezeitpunkt(
|
||||
$schaltzustand,
|
||||
$jetzt,
|
||||
$this->ReadPropertyInteger('Mindesteinschaltdauer'),
|
||||
$this->ReadPropertyInteger('Mindestausschaltdauer')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($schaltzustand === $this->ReadAttributeBoolean('Schaltziel')) {
|
||||
$this->WriteAttributeBoolean('SchaltpruefungAusstehend', false);
|
||||
}
|
||||
$this->setzeZustand(
|
||||
'SchaltbefehlAusstehend',
|
||||
$this->ReadAttributeBoolean('SchaltpruefungAusstehend')
|
||||
);
|
||||
$this->setzeZustand('Rueckmeldefehler', false);
|
||||
$this->protokolliere('Schaltzustand bestaetigt', ['Eingeschaltet' => $schaltzustand]);
|
||||
}
|
||||
|
||||
private function aktualisiereTageslaufzeit(int $jetzt): void
|
||||
@@ -410,10 +459,14 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
|
||||
private function planeTimer(int $jetzt): void
|
||||
{
|
||||
$gesperrtBis = $this->ReadAttributeInteger('GesperrtBis');
|
||||
$this->SetTimerInterval('Umschaltfreigabe', 0);
|
||||
$freigabeAb = $this->ReadAttributeInteger('FreigabeAb');
|
||||
$wartetAufRueckmeldung = $this->ReadAttributeBoolean('SchaltpruefungAusstehend');
|
||||
$this->SetTimerInterval(
|
||||
'Umschaltfreigabe',
|
||||
$gesperrtBis > $jetzt ? max(1, ($gesperrtBis - $jetzt) * 1000) : 0
|
||||
'MindestzeitAbgelaufen',
|
||||
!$wartetAufRueckmeldung && $freigabeAb > $jetzt
|
||||
? max(1, ($freigabeAb - $jetzt) * 1000)
|
||||
: 0
|
||||
);
|
||||
$this->planeVorgabeTimeout($jetzt);
|
||||
|
||||
@@ -457,7 +510,19 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
$this->setzeZustand('Rueckmeldefehler', false);
|
||||
$this->WriteAttributeBoolean('Initialisiert', true);
|
||||
}
|
||||
$this->setzeSchaltzustand($schaltzustand);
|
||||
|
||||
if ($this->ReadAttributeInteger('BestaetigtSeit') <= 0) {
|
||||
// Migration bestehender Instanzen ohne bestaetigten Zeitbezug.
|
||||
$this->WriteAttributeBoolean('Schaltziel', $schaltzustand);
|
||||
$this->WriteAttributeBoolean('SchaltpruefungAusstehend', false);
|
||||
$this->bestaetigeZustandswechsel($schaltzustand, $jetzt);
|
||||
} else {
|
||||
$this->setzeSchaltzustand($schaltzustand);
|
||||
$this->setzeZustand(
|
||||
'SchaltbefehlAusstehend',
|
||||
$this->ReadAttributeBoolean('SchaltpruefungAusstehend')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function setzeSchaltzustand(bool $eingeschaltet): void
|
||||
@@ -474,6 +539,11 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
return (bool) GetValue($rueckmeldungID);
|
||||
}
|
||||
|
||||
return $this->leseAktorSchaltzustand();
|
||||
}
|
||||
|
||||
private function leseAktorSchaltzustand(): bool
|
||||
{
|
||||
$kontaktwert = (bool) GetValue($this->ReadPropertyInteger('SchaltkontaktVariableID'));
|
||||
|
||||
return $kontaktwert !== $this->ReadPropertyBoolean('SchaltkontaktInvertiert');
|
||||
@@ -551,6 +621,24 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
'Wert' => (bool) $this->leseZustand('Schaltzustand'),
|
||||
'Einheit' => '',
|
||||
],
|
||||
[
|
||||
'Kennung' => 'SchaltbefehlAusstehend',
|
||||
'Art' => 'Status',
|
||||
'Wert' => (bool) $this->leseZustand('SchaltbefehlAusstehend'),
|
||||
'Einheit' => '',
|
||||
],
|
||||
[
|
||||
'Kennung' => 'Schaltbereit',
|
||||
'Art' => 'Status',
|
||||
'Wert' => (bool) $this->leseZustand('AenderungMoeglich'),
|
||||
'Einheit' => '',
|
||||
],
|
||||
[
|
||||
'Kennung' => 'RestMindestzeit_s',
|
||||
'Art' => 'Istwert',
|
||||
'Wert' => (int) $this->leseZustand('RestMindestzeit'),
|
||||
'Einheit' => 's',
|
||||
],
|
||||
[
|
||||
'Kennung' => 'Tageslaufzeit_s',
|
||||
'Art' => 'Istwert',
|
||||
@@ -636,8 +724,10 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
) {
|
||||
throw new InvalidArgumentException('Mindestlaufzeit muss zwischen 0 und 86400 Sekunden liegen.');
|
||||
}
|
||||
if ($this->ReadPropertyInteger('Umschaltabstand') < 0) {
|
||||
throw new InvalidArgumentException('Umschaltabstand darf nicht negativ sein.');
|
||||
foreach (['Mindesteinschaltdauer', 'Mindestausschaltdauer'] as $property) {
|
||||
if ($this->ReadPropertyInteger($property) < 0) {
|
||||
throw new InvalidArgumentException($property . ' darf nicht negativ sein.');
|
||||
}
|
||||
}
|
||||
|
||||
$kontaktID = $this->ReadPropertyInteger('SchaltkontaktVariableID');
|
||||
@@ -713,6 +803,13 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
if ($this->ReadPropertyBoolean('DiagnosevariablenAnzeigen')) {
|
||||
$this->registriereVerbraucherDiagnose();
|
||||
$this->RegisterVariableBoolean('Rueckmeldefehler', 'Rueckmeldefehler', '~Alert', 200);
|
||||
$this->RegisterVariableBoolean(
|
||||
'SchaltbefehlAusstehend',
|
||||
'Schaltbefehl ausstehend',
|
||||
'~Switch',
|
||||
210
|
||||
);
|
||||
$this->RegisterVariableInteger('RestMindestzeit', 'Restliche Mindestzeit', '', 220);
|
||||
foreach (self::DIAGNOSE_VARIABLEN as $ident) {
|
||||
$wert = $ident === 'Leistungsquelle'
|
||||
? Nachrichtenvertrag::LEISTUNGSQUELLE_BERECHNET
|
||||
@@ -739,10 +836,12 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
'Stoerung' => 'ZustandStoerung',
|
||||
'Schaltzustand' => 'ZustandSchaltzustand',
|
||||
'Rueckmeldefehler' => 'ZustandRueckmeldefehler',
|
||||
'SchaltbefehlAusstehend' => 'ZustandSchaltbefehlAusstehend',
|
||||
];
|
||||
$integerAttribute = [
|
||||
'Sollleistung' => 'ZustandSollleistung',
|
||||
'Tageslaufzeit' => 'ZustandTageslaufzeit',
|
||||
'RestMindestzeit' => 'ZustandRestMindestzeit',
|
||||
];
|
||||
$floatAttribute = [
|
||||
'Istleistung' => 'ZustandIstleistung',
|
||||
@@ -783,10 +882,12 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
'Stoerung' => 'ZustandStoerung',
|
||||
'Schaltzustand' => 'ZustandSchaltzustand',
|
||||
'Rueckmeldefehler' => 'ZustandRueckmeldefehler',
|
||||
'SchaltbefehlAusstehend' => 'ZustandSchaltbefehlAusstehend',
|
||||
];
|
||||
$integerAttribute = [
|
||||
'Sollleistung' => 'ZustandSollleistung',
|
||||
'Tageslaufzeit' => 'ZustandTageslaufzeit',
|
||||
'RestMindestzeit' => 'ZustandRestMindestzeit',
|
||||
];
|
||||
$floatAttribute = [
|
||||
'Istleistung' => 'ZustandIstleistung',
|
||||
@@ -838,6 +939,7 @@ class VerbraucherEinStufig extends IPSModule implements VerbraucherSchnittstelle
|
||||
'Meldezyklus',
|
||||
'RueckmeldungVerzoegert',
|
||||
'Umschaltfreigabe',
|
||||
'MindestzeitAbgelaufen',
|
||||
'VorgabeTimeout',
|
||||
'Tagesplanung',
|
||||
] as $timer) {
|
||||
|
||||
Reference in New Issue
Block a user