This commit is contained in:
+275
-43
@@ -16,6 +16,13 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
private const STATUS_MESSWERT_UNGUELTIG = 201;
|
||||
private const STATUS_KONFIGURATION_UNGUELTIG = 202;
|
||||
private const VM_UPDATE = 10603;
|
||||
private const VERBRAUCHER_MODUL_IDS = [
|
||||
'{B7C54AF4-AD7D-4FE4-B75D-203693906251}',
|
||||
];
|
||||
private const MONATSNAMEN = [
|
||||
1 => 'Januar', 'Februar', 'Maerz', 'April', 'Mai', 'Juni',
|
||||
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember',
|
||||
];
|
||||
|
||||
public function Create(): void
|
||||
{
|
||||
@@ -24,21 +31,18 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$this->RegisterPropertyInteger('Rolle', 0);
|
||||
$this->RegisterPropertyInteger('NetzleistungVariableID', 0);
|
||||
$this->RegisterPropertyFloat('Netzleistungsfaktor', 1.0);
|
||||
$this->RegisterPropertyInteger('MesswertMaxAlter', 30);
|
||||
$this->RegisterPropertyInteger('MesswertMaxAlter', 60);
|
||||
$this->RegisterPropertyString('VerbraucherZuordnung', '[]');
|
||||
$this->RegisterPropertyString('AutomatischeVerbraucherZuordnung', '[]');
|
||||
$this->RegisterPropertyBoolean('AutomatischeSuche', true);
|
||||
$this->RegisterPropertyInteger('SuchbereichID', 0);
|
||||
$this->RegisterPropertyInteger('Regelintervall', 2);
|
||||
$this->RegisterPropertyInteger('Vorgabeintervall', 10);
|
||||
$this->RegisterPropertyInteger('VerbraucherTimeout', 30);
|
||||
$this->RegisterPropertyInteger('KeepAlive', 60);
|
||||
$this->RegisterPropertyInteger('VerbraucherTimeout', 60);
|
||||
$this->RegisterPropertyInteger('Lastspitzenmodus', 0);
|
||||
$this->RegisterPropertyFloat('Lastspitzengrenze', 0.0);
|
||||
$this->RegisterPropertyString('Monatsgrenzen', $this->standardMonatsgrenzen());
|
||||
$this->RegisterPropertyBoolean('MonatsgrenzenVisuBedienbar', false);
|
||||
$this->RegisterPropertyFloat('SollwertSolarladen', 0.0);
|
||||
$this->RegisterPropertyFloat('UmschaltschwelleUeberschuss', 0.0);
|
||||
$this->RegisterPropertyFloat('UmschaltschwelleLastspitze', 0.0);
|
||||
$this->RegisterPropertyFloat('Umschaltdifferenz', 0.0);
|
||||
$this->RegisterPropertyFloat('Umschaltdifferenz', 5.0);
|
||||
$this->RegisterPropertyString('PrognoseAnschluss', '{}');
|
||||
$this->RegisterPropertyString('SDLAnschluss', '{}');
|
||||
$this->RegisterPropertyString('LizenzAnschluss', '{}');
|
||||
@@ -67,13 +71,11 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$this->RegisterAttributeString('LetzteSollwerte', '{}');
|
||||
$this->RegisterAttributeInteger('RegistrierteNetzleistungVariableID', 0);
|
||||
|
||||
// Die alten Timer bleiben deaktiviert registriert, damit bestehende Instanzen sauber migrieren.
|
||||
$this->RegisterTimer('Regelzyklus', 0, '');
|
||||
$this->RegisterTimer('VorgabenErneuern', 0, '');
|
||||
$this->RegisterTimer(
|
||||
'Regelzyklus',
|
||||
0,
|
||||
"IPS_RequestAction(\$_IPS['TARGET'], 'Regeln', false);"
|
||||
);
|
||||
$this->RegisterTimer(
|
||||
'VorgabenErneuern',
|
||||
'KeepAlive',
|
||||
0,
|
||||
"IPS_RequestAction(\$_IPS['TARGET'], 'Regeln', true);"
|
||||
);
|
||||
@@ -89,19 +91,82 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$this->SetStatus(self::STATUS_KONFIGURATION_UNGUELTIG);
|
||||
$this->SetTimerInterval('Regelzyklus', 0);
|
||||
$this->SetTimerInterval('VorgabenErneuern', 0);
|
||||
$this->SetTimerInterval('KeepAlive', 0);
|
||||
$this->protokolliere('Konfiguration', $fehler->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
$this->registriereNetzleistungsmeldung();
|
||||
$this->SetTimerInterval('Regelzyklus', $this->ReadPropertyInteger('Regelintervall') * 1000);
|
||||
$this->SetTimerInterval('VorgabenErneuern', $this->ReadPropertyInteger('Vorgabeintervall') * 1000);
|
||||
$this->SetTimerInterval('Regelzyklus', 0);
|
||||
$this->SetTimerInterval('VorgabenErneuern', 0);
|
||||
$this->SetTimerInterval('KeepAlive', $this->ReadPropertyInteger('KeepAlive') * 1000);
|
||||
$this->SetValue('Monatsgrenzen', $this->ReadPropertyString('Monatsgrenzen'));
|
||||
$this->aktualisiereSichtbarkeit();
|
||||
$this->aktualisiereAnschlussstatus();
|
||||
$this->SetStatus(self::STATUS_AKTIV);
|
||||
}
|
||||
|
||||
public function GetConfigurationForm(): string
|
||||
{
|
||||
$formular = json_decode(
|
||||
(string) file_get_contents(__DIR__ . '/form.json'),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
$automatisch = $this->ReadPropertyBoolean('AutomatischeSuche');
|
||||
$lastspitzenmodus = $this->ReadPropertyInteger('Lastspitzenmodus');
|
||||
$monatsgrenzenSichtbar = $lastspitzenmodus === 2
|
||||
&& $this->GetBuffer('MonatsgrenzenSichtbar') === '1';
|
||||
|
||||
$this->setzeFormularParameter($formular['elements'], 'VerbraucherZuordnung', 'visible', !$automatisch);
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
'AutomatischeVerbraucherZuordnung',
|
||||
'visible',
|
||||
$automatisch
|
||||
);
|
||||
$this->setzeFormularParameter($formular['elements'], 'SuchbereichID', 'visible', $automatisch);
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
'AutomatischeVerbraucherZuordnung',
|
||||
'values',
|
||||
$this->automatischeVerbraucherFuerFormular()
|
||||
);
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
'Lastspitzengrenze',
|
||||
'visible',
|
||||
$lastspitzenmodus === 1
|
||||
);
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
'MonatsgrenzenUmschalten',
|
||||
'visible',
|
||||
$lastspitzenmodus === 2
|
||||
);
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
'MonatsgrenzenUmschalten',
|
||||
'caption',
|
||||
$monatsgrenzenSichtbar ? 'Monatsgrenzen ausblenden' : 'Monatsgrenzen anzeigen'
|
||||
);
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
'Monatsgrenzen',
|
||||
'visible',
|
||||
$monatsgrenzenSichtbar
|
||||
);
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
'Monatsgrenzen',
|
||||
'values',
|
||||
$this->monatsgrenzenFuerFormular()
|
||||
);
|
||||
|
||||
return json_encode($formular, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
public function MessageSink($zeitstempel, $senderID, $nachricht, $daten): void
|
||||
{
|
||||
if ((int) $senderID === $this->ReadPropertyInteger('NetzleistungVariableID')
|
||||
@@ -123,6 +188,39 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$this->regeln((bool) $wert);
|
||||
return;
|
||||
|
||||
case 'FormAutomatischeSuche':
|
||||
$automatisch = (bool) $wert;
|
||||
$this->UpdateFormField('VerbraucherZuordnung', 'visible', !$automatisch);
|
||||
$this->UpdateFormField('AutomatischeVerbraucherZuordnung', 'visible', $automatisch);
|
||||
$this->UpdateFormField('SuchbereichID', 'visible', $automatisch);
|
||||
return;
|
||||
|
||||
case 'FormLastspitzenmodus':
|
||||
$modus = (int) $wert;
|
||||
$this->UpdateFormField('Lastspitzengrenze', 'visible', $modus === 1);
|
||||
$this->UpdateFormField('MonatsgrenzenUmschalten', 'visible', $modus === 2);
|
||||
if ($modus !== 2) {
|
||||
$this->SetBuffer('MonatsgrenzenSichtbar', '0');
|
||||
$this->UpdateFormField('Monatsgrenzen', 'visible', false);
|
||||
$this->UpdateFormField(
|
||||
'MonatsgrenzenUmschalten',
|
||||
'caption',
|
||||
'Monatsgrenzen anzeigen'
|
||||
);
|
||||
}
|
||||
return;
|
||||
|
||||
case 'FormMonatsgrenzenUmschalten':
|
||||
$sichtbar = $this->GetBuffer('MonatsgrenzenSichtbar') !== '1';
|
||||
$this->SetBuffer('MonatsgrenzenSichtbar', $sichtbar ? '1' : '0');
|
||||
$this->UpdateFormField('Monatsgrenzen', 'visible', $sichtbar);
|
||||
$this->UpdateFormField(
|
||||
'MonatsgrenzenUmschalten',
|
||||
'caption',
|
||||
$sichtbar ? 'Monatsgrenzen ausblenden' : 'Monatsgrenzen anzeigen'
|
||||
);
|
||||
return;
|
||||
|
||||
case 'VerbraucherdatenEmpfangen':
|
||||
if (!is_string($wert)) {
|
||||
throw new InvalidArgumentException('Verbraucherdaten muessen als JSON uebergeben werden.');
|
||||
@@ -134,14 +232,6 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$this->VerbraucherdatenEmpfangen($daten);
|
||||
return;
|
||||
|
||||
case 'Monatsgrenzen':
|
||||
if (!$this->ReadPropertyBoolean('MonatsgrenzenVisuBedienbar')) {
|
||||
throw new RuntimeException('Monatsgrenzen sind in der Visualisierung nicht bedienbar.');
|
||||
}
|
||||
$this->dekodiereMonatsgrenzen((string) $wert);
|
||||
IPS_SetProperty($this->InstanceID, 'Monatsgrenzen', (string) $wert);
|
||||
IPS_ApplyChanges($this->InstanceID);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Unbekannte Aktion: ' . $ident);
|
||||
@@ -200,8 +290,6 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$netzleistung,
|
||||
(string) $this->GetValue('Betriebsart'),
|
||||
$lastspitzengrenze !== null,
|
||||
$this->ReadPropertyFloat('UmschaltschwelleUeberschuss'),
|
||||
$this->ReadPropertyFloat('UmschaltschwelleLastspitze'),
|
||||
$this->ReadPropertyFloat('SollwertSolarladen'),
|
||||
$lastspitzengrenze ?? 0.0,
|
||||
$this->ReadPropertyFloat('Umschaltdifferenz')
|
||||
@@ -297,9 +385,30 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
/** @return list<int> */
|
||||
private function aktiveVerbraucherIDs(): array
|
||||
{
|
||||
$zuordnung = json_decode($this->ReadPropertyString('VerbraucherZuordnung'), true, 512, JSON_THROW_ON_ERROR);
|
||||
if (!$this->ReadPropertyBoolean('AutomatischeSuche')) {
|
||||
return $this->leseAktiveVerbraucherIDs('VerbraucherZuordnung');
|
||||
}
|
||||
|
||||
$gespeichert = json_decode(
|
||||
$this->ReadPropertyString('AutomatischeVerbraucherZuordnung'),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
if ($gespeichert === []) {
|
||||
// Bestehende Installationen behalten ihre Auswahl, bis die neue Liste gespeichert wurde.
|
||||
return $this->leseAktiveVerbraucherIDs('VerbraucherZuordnung');
|
||||
}
|
||||
|
||||
return $this->leseAktiveVerbraucherIDs('AutomatischeVerbraucherZuordnung');
|
||||
}
|
||||
|
||||
/** @return list<int> */
|
||||
private function leseAktiveVerbraucherIDs(string $property): array
|
||||
{
|
||||
$zuordnung = json_decode($this->ReadPropertyString($property), true, 512, JSON_THROW_ON_ERROR);
|
||||
if (!is_array($zuordnung)) {
|
||||
throw new InvalidArgumentException('VerbraucherZuordnung muss eine JSON-Liste sein.');
|
||||
throw new InvalidArgumentException($property . ' muss eine JSON-Liste sein.');
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
@@ -319,6 +428,102 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
return array_values(array_unique($ids));
|
||||
}
|
||||
|
||||
/** @return list<array{Name: string, InstanzID: int, Aktiv: bool}> */
|
||||
private function automatischeVerbraucherFuerFormular(): array
|
||||
{
|
||||
$gespeichert = json_decode(
|
||||
$this->ReadPropertyString('AutomatischeVerbraucherZuordnung'),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
$ausgewaehlt = $gespeichert === []
|
||||
? $this->leseAktiveVerbraucherIDs('VerbraucherZuordnung')
|
||||
: $this->leseAktiveVerbraucherIDs('AutomatischeVerbraucherZuordnung');
|
||||
|
||||
$suchbereichID = $this->ReadPropertyInteger('SuchbereichID');
|
||||
$werte = [];
|
||||
foreach (self::VERBRAUCHER_MODUL_IDS as $modulID) {
|
||||
foreach (IPS_GetInstanceListByModuleID($modulID) as $instanzID) {
|
||||
if (!$this->istImSuchbereich($instanzID, $suchbereichID)) {
|
||||
continue;
|
||||
}
|
||||
$werte[] = [
|
||||
'Name' => IPS_GetName($instanzID),
|
||||
'InstanzID' => $instanzID,
|
||||
'Aktiv' => in_array($instanzID, $ausgewaehlt, true),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
usort($werte, static function (array $links, array $rechts): int {
|
||||
$nameVergleich = strcasecmp($links['Name'], $rechts['Name']);
|
||||
|
||||
return $nameVergleich !== 0
|
||||
? $nameVergleich
|
||||
: $links['InstanzID'] <=> $rechts['InstanzID'];
|
||||
});
|
||||
|
||||
return $werte;
|
||||
}
|
||||
|
||||
private function istImSuchbereich(int $objektID, int $suchbereichID): bool
|
||||
{
|
||||
if ($suchbereichID <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while ($objektID > 0) {
|
||||
if ($objektID === $suchbereichID) {
|
||||
return true;
|
||||
}
|
||||
$objektID = IPS_GetParent($objektID);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @return list<array{Monat: string, Grenze_W: float, MonatIndex: int}> */
|
||||
private function monatsgrenzenFuerFormular(): array
|
||||
{
|
||||
$grenzen = $this->dekodiereMonatsgrenzen($this->ReadPropertyString('Monatsgrenzen'));
|
||||
$werte = [];
|
||||
foreach (self::MONATSNAMEN as $monat => $name) {
|
||||
$werte[] = [
|
||||
'Monat' => $name,
|
||||
'Grenze_W' => $grenzen[$monat],
|
||||
'MonatIndex' => $monat,
|
||||
];
|
||||
}
|
||||
|
||||
return $werte;
|
||||
}
|
||||
|
||||
/** @param list<array<string, mixed>> $elemente */
|
||||
private function setzeFormularParameter(
|
||||
array &$elemente,
|
||||
string $name,
|
||||
string $parameter,
|
||||
$wert
|
||||
): bool {
|
||||
foreach ($elemente as &$element) {
|
||||
if (($element['name'] ?? null) === $name) {
|
||||
$element[$parameter] = $wert;
|
||||
|
||||
return true;
|
||||
}
|
||||
if (isset($element['items'])
|
||||
&& is_array($element['items'])
|
||||
&& $this->setzeFormularParameter($element['items'], $name, $parameter, $wert)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
unset($element);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function sendeSollwert(int $instanzID, int $sollleistung): void
|
||||
{
|
||||
if (!IPS_InstanceExists($instanzID)) {
|
||||
@@ -381,13 +586,8 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
] as $ident) {
|
||||
IPS_SetHidden($this->GetIDForIdent($ident), !$logging);
|
||||
}
|
||||
$monatsgrenzenBedienbar = $logging && $this->ReadPropertyBoolean('MonatsgrenzenVisuBedienbar');
|
||||
IPS_SetHidden($this->GetIDForIdent('Monatsgrenzen'), !$monatsgrenzenBedienbar);
|
||||
if ($monatsgrenzenBedienbar) {
|
||||
$this->EnableAction('Monatsgrenzen');
|
||||
} else {
|
||||
$this->DisableAction('Monatsgrenzen');
|
||||
}
|
||||
IPS_SetHidden($this->GetIDForIdent('Monatsgrenzen'), true);
|
||||
$this->DisableAction('Monatsgrenzen');
|
||||
}
|
||||
|
||||
private function aktualisiereAnschlussstatus(): void
|
||||
@@ -406,7 +606,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
|
||||
private function pruefeKonfiguration(): void
|
||||
{
|
||||
foreach (['MesswertMaxAlter', 'Regelintervall', 'Vorgabeintervall', 'VerbraucherTimeout'] as $property) {
|
||||
foreach (['MesswertMaxAlter', 'KeepAlive', 'VerbraucherTimeout'] as $property) {
|
||||
if ($this->ReadPropertyInteger($property) <= 0) {
|
||||
throw new InvalidArgumentException($property . ' muss groesser als 0 sein.');
|
||||
}
|
||||
@@ -416,7 +616,8 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
) {
|
||||
throw new InvalidArgumentException('Umschaltdifferenz muss zwischen 0 und 100 liegen.');
|
||||
}
|
||||
$this->aktiveVerbraucherIDs();
|
||||
$this->leseAktiveVerbraucherIDs('VerbraucherZuordnung');
|
||||
$this->leseAktiveVerbraucherIDs('AutomatischeVerbraucherZuordnung');
|
||||
$this->dekodiereMonatsgrenzen($this->ReadPropertyString('Monatsgrenzen'));
|
||||
foreach (['PrognoseAnschluss', 'SDLAnschluss', 'LizenzAnschluss', 'StoermeldeAnschluss'] as $property) {
|
||||
$wert = json_decode($this->ReadPropertyString($property), true, 512, JSON_THROW_ON_ERROR);
|
||||
@@ -434,13 +635,35 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
throw new InvalidArgumentException('Monatsgrenzen muss ein JSON-Objekt sein.');
|
||||
}
|
||||
$ergebnis = [];
|
||||
for ($monat = 1; $monat <= 12; ++$monat) {
|
||||
$wert = $werte[(string) $monat] ?? $werte[$monat] ?? null;
|
||||
if (!is_int($wert) && !is_float($wert)) {
|
||||
throw new InvalidArgumentException('Monatsgrenzen muss fuer jeden Monat einen Zahlenwert enthalten.');
|
||||
if (isset($werte[0]) && is_array($werte[0])) {
|
||||
foreach ($werte as $eintrag) {
|
||||
if (!is_array($eintrag)) {
|
||||
throw new InvalidArgumentException('Monatsgrenzen muss eine Liste von Monatswerten sein.');
|
||||
}
|
||||
$monat = $eintrag['MonatIndex'] ?? null;
|
||||
$wert = $eintrag['Grenze_W'] ?? null;
|
||||
if (!is_int($monat) || $monat < 1 || $monat > 12
|
||||
|| (!is_int($wert) && !is_float($wert))
|
||||
) {
|
||||
throw new InvalidArgumentException('Jeder Monatswert benoetigt Monatsnummer und Grenze.');
|
||||
}
|
||||
$ergebnis[$monat] = (float) $wert;
|
||||
}
|
||||
} else {
|
||||
for ($monat = 1; $monat <= 12; ++$monat) {
|
||||
$wert = $werte[(string) $monat] ?? $werte[$monat] ?? null;
|
||||
if (!is_int($wert) && !is_float($wert)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Monatsgrenzen muss fuer jeden Monat einen Zahlenwert enthalten.'
|
||||
);
|
||||
}
|
||||
$ergebnis[$monat] = (float) $wert;
|
||||
}
|
||||
$ergebnis[$monat] = (float) $wert;
|
||||
}
|
||||
if (count($ergebnis) !== 12) {
|
||||
throw new InvalidArgumentException('Monatsgrenzen muss genau zwoelf Monatswerte enthalten.');
|
||||
}
|
||||
ksort($ergebnis);
|
||||
|
||||
return $ergebnis;
|
||||
}
|
||||
@@ -455,7 +678,16 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
|
||||
private function standardMonatsgrenzen(): string
|
||||
{
|
||||
return json_encode(array_fill_keys(range(1, 12), 0), JSON_THROW_ON_ERROR);
|
||||
$werte = [];
|
||||
foreach (self::MONATSNAMEN as $monat => $name) {
|
||||
$werte[] = [
|
||||
'Monat' => $name,
|
||||
'Grenze_W' => 0.0,
|
||||
'MonatIndex' => $monat,
|
||||
];
|
||||
}
|
||||
|
||||
return json_encode($werte, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
/** @param mixed $daten */
|
||||
|
||||
Reference in New Issue
Block a user