1264 lines
46 KiB
PHP
1264 lines
46 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../libs/VerbraucherSchnittstelle.php';
|
|
require_once __DIR__ . '/../libs/VerbraucherBasisTrait.php';
|
|
require_once __DIR__ . '/../libs/Nachrichtenvertrag.php';
|
|
require_once __DIR__ . '/../libs/BatterieRegler.php';
|
|
require_once __DIR__ . '/../libs/BatterieNetzfahrplanV4TestTrait.php';
|
|
|
|
use Belevo\EnelixEMS\BatterieRegler;
|
|
use Belevo\EnelixEMS\Nachrichtenvertrag;
|
|
use Belevo\EnelixEMS\VerbraucherBasisTrait;
|
|
use Belevo\EnelixEMS\VerbraucherSchnittstelle;
|
|
|
|
class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
|
{
|
|
use VerbraucherBasisTrait;
|
|
use \Belevo\EnelixEMS\BatterieNetzfahrplanV4TestTrait;
|
|
|
|
private const MANAGER_MODULE_ID = '{6F771B18-59D4-4C8A-B951-3B2FE9F6A2C4}';
|
|
private const STATUS_AKTIV = 102;
|
|
private const STATUS_KONFIGURATION_UNGUELTIG = 201;
|
|
private const STATUS_MESSWERTE_UNGUELTIG = 202;
|
|
private const STATUS_REGISTERFEHLER = 203;
|
|
private const VM_UPDATE = 10603;
|
|
|
|
/** @var array<string, string> */
|
|
private const REGISTER_PROPERTIES = [
|
|
'Management' => 'ManagementRegisterVariableID',
|
|
'Modus' => 'ModusRegisterVariableID',
|
|
'Leistung' => 'LeistungsRegisterVariableID',
|
|
'Ladeleistung' => 'LadeleistungRegisterVariableID',
|
|
'Entladeleistung' => 'EntladeleistungRegisterVariableID',
|
|
];
|
|
|
|
/** @var list<string> */
|
|
private const MESSWERT_PROPERTIES = [
|
|
'MaxLadeleistungVariableID',
|
|
'MaxEntladeleistungVariableID',
|
|
'LadezustandVariableID',
|
|
'NetzleistungVariableID',
|
|
'IstleistungVariableID',
|
|
];
|
|
|
|
/** @var list<string> */
|
|
private const DIAGNOSE_VARIABLEN = [
|
|
'Istleistung',
|
|
'Leistungsquelle',
|
|
'Sollleistung',
|
|
'SollwertGueltig',
|
|
'Verfuegbar',
|
|
'AenderungMoeglich',
|
|
'Stoerung',
|
|
'Stoertext',
|
|
'Ladezustand',
|
|
'LadezustandGueltig',
|
|
'Netzleistung',
|
|
'MaxLadeleistung',
|
|
'MaxEntladeleistung',
|
|
'HystereseAktiv',
|
|
'Steuerungsmodus',
|
|
'BezogeneEnergie',
|
|
'LeistungsangebotDiagnose',
|
|
'LetzterRegisterbefehl',
|
|
];
|
|
|
|
public function Create(): void
|
|
{
|
|
parent::Create();
|
|
|
|
$this->registriereVerbraucherBasis();
|
|
$this->registriereV4BatterieTest();
|
|
$this->RegisterPropertyInteger('Batterietyp', BatterieRegler::TYP_UNKONFIGURIERT);
|
|
$this->RegisterPropertyInteger(
|
|
'Batteriemanagement',
|
|
BatterieRegler::STEUERUNG_WECHSELRICHTER
|
|
);
|
|
$this->RegisterPropertyInteger('MaxLadeleistungVariableID', 0);
|
|
$this->RegisterPropertyInteger('MaxEntladeleistungVariableID', 0);
|
|
$this->RegisterPropertyInteger('LadezustandVariableID', 0);
|
|
$this->RegisterPropertyInteger('NetzleistungVariableID', 0);
|
|
$this->RegisterPropertyInteger('IstleistungVariableID', 0);
|
|
$this->RegisterPropertyInteger('ManagementRegisterVariableID', 0);
|
|
$this->RegisterPropertyInteger('ModusRegisterVariableID', 0);
|
|
$this->RegisterPropertyInteger('LeistungsRegisterVariableID', 0);
|
|
$this->RegisterPropertyInteger('LadeleistungRegisterVariableID', 0);
|
|
$this->RegisterPropertyInteger('EntladeleistungRegisterVariableID', 0);
|
|
$this->RegisterPropertyFloat('ReserveLadezustand', 20.0);
|
|
$this->RegisterPropertyFloat('MindestLadezustand', 10.0);
|
|
$this->RegisterPropertyFloat('LadezustandHysterese', 2.0);
|
|
$this->RegisterPropertyBoolean('NachladenMitMaximalleistung', true);
|
|
$this->RegisterPropertyInteger('MaximaleNachladeleistung', 0);
|
|
$this->RegisterPropertyInteger('MesswertMaxAlter', 60);
|
|
$this->RegisterPropertyInteger('Aenderungssperre', 4);
|
|
$this->RegisterPropertyBoolean('DiagnosevariablenAnzeigen', false);
|
|
|
|
$this->RegisterVariableInteger('Ladestatus', 'Ladestatus', '', 100);
|
|
|
|
$this->RegisterAttributeString('RegistrierteMesswerte', '[]');
|
|
$this->RegisterAttributeInteger('LetzteVorgabeZeit', 0);
|
|
$this->RegisterAttributeInteger('LetzteManagerID', 0);
|
|
$this->RegisterAttributeInteger('AenderungFreigabeAb', 0);
|
|
$this->RegisterAttributeInteger('EnergieStandZeit', 0);
|
|
$this->RegisterAttributeInteger('LetzterSollwert', 0);
|
|
$this->RegisterAttributeString('Leistungsangebot', '[0]');
|
|
$this->RegisterAttributeString(
|
|
'Betriebsart',
|
|
Nachrichtenvertrag::BETRIEBSART_PV
|
|
);
|
|
$this->RegisterAttributeString('LetzterRegisterbefehl', '{}');
|
|
$this->RegisterAttributeString('Registerfehler', '');
|
|
$this->RegisterAttributeBoolean('RegisterInitialisiert', false);
|
|
$this->RegisterAttributeBoolean('RegisterSchreiben', false);
|
|
|
|
$this->RegisterAttributeFloat('ZustandIstleistung', 0.0);
|
|
$this->RegisterAttributeInteger('ZustandSollleistung', 0);
|
|
$this->RegisterAttributeBoolean('ZustandSollwertGueltig', false);
|
|
$this->RegisterAttributeBoolean('ZustandVerfuegbar', false);
|
|
$this->RegisterAttributeBoolean('ZustandAenderungMoeglich', false);
|
|
$this->RegisterAttributeBoolean('ZustandStoerung', false);
|
|
$this->RegisterAttributeString('ZustandStoertext', '');
|
|
$this->RegisterAttributeFloat('ZustandLadezustand', 0.0);
|
|
$this->RegisterAttributeBoolean('ZustandLadezustandGueltig', false);
|
|
$this->RegisterAttributeFloat('ZustandNetzleistung', 0.0);
|
|
$this->RegisterAttributeFloat('ZustandMaxLadeleistung', 0.0);
|
|
$this->RegisterAttributeFloat('ZustandMaxEntladeleistung', 0.0);
|
|
$this->RegisterAttributeBoolean('ZustandHystereseAktiv', false);
|
|
$this->RegisterAttributeInteger('ZustandSteuerungsmodus', 0);
|
|
$this->RegisterAttributeFloat('ZustandBezogeneEnergie', 0.0);
|
|
|
|
$this->RegisterTimer(
|
|
'Meldezyklus',
|
|
0,
|
|
"IPS_RequestAction(\$_IPS['TARGET'], 'Melden', false);"
|
|
);
|
|
$this->RegisterTimer(
|
|
'RueckmeldungVerzoegert',
|
|
0,
|
|
"IPS_RequestAction(\$_IPS['TARGET'], 'Melden', true);"
|
|
);
|
|
$this->RegisterTimer(
|
|
'VorgabeTimeout',
|
|
0,
|
|
"IPS_RequestAction(\$_IPS['TARGET'], 'VorgabeTimeout', 0);"
|
|
);
|
|
$this->RegisterTimer(
|
|
'AenderungFreigabe',
|
|
0,
|
|
"IPS_RequestAction(\$_IPS['TARGET'], 'AenderungFreigabe', 0);"
|
|
);
|
|
}
|
|
|
|
public function ApplyChanges(): void
|
|
{
|
|
parent::ApplyChanges();
|
|
|
|
$this->V4BatterieTestStoppen();
|
|
$this->aktualisiereVariablen();
|
|
$this->registriereMesswertmeldungen();
|
|
$this->WriteAttributeBoolean('RegisterInitialisiert', false);
|
|
|
|
try {
|
|
$this->pruefeKonfiguration();
|
|
} catch (Throwable $fehler) {
|
|
$this->deaktiviereTimer();
|
|
$this->setzeZustand('Verfuegbar', false);
|
|
$this->setzeZustand('AenderungMoeglich', false);
|
|
$this->setzeStoerung($fehler->getMessage());
|
|
$this->SetStatus(self::STATUS_KONFIGURATION_UNGUELTIG);
|
|
$this->protokolliere('Konfiguration', $fehler->getMessage());
|
|
return;
|
|
}
|
|
|
|
$this->SetTimerInterval(
|
|
'Meldezyklus',
|
|
$this->ReadPropertyInteger('Meldeintervall') * 1000
|
|
);
|
|
$this->aktualisiere(true);
|
|
}
|
|
|
|
public function GetConfigurationForm(): string
|
|
{
|
|
$formular = json_decode(
|
|
(string) file_get_contents(__DIR__ . '/form.json'),
|
|
true,
|
|
512,
|
|
JSON_THROW_ON_ERROR
|
|
);
|
|
$this->setzeRegisterfelderSichtbar(
|
|
$formular['elements'],
|
|
$this->ReadPropertyInteger('Batterietyp')
|
|
);
|
|
$this->setzeFormfeldSichtbar(
|
|
$formular['elements'],
|
|
'MaximaleNachladeleistung',
|
|
!$this->ReadPropertyBoolean('NachladenMitMaximalleistung')
|
|
);
|
|
|
|
return json_encode($formular, JSON_THROW_ON_ERROR);
|
|
}
|
|
|
|
public function MessageSink($zeitstempel, $senderID, $nachricht, $daten): void
|
|
{
|
|
if ((int) $nachricht !== self::VM_UPDATE
|
|
|| $this->ReadAttributeBoolean('RegisterSchreiben')
|
|
) {
|
|
return;
|
|
}
|
|
|
|
$messwerte = json_decode(
|
|
$this->ReadAttributeString('RegistrierteMesswerte'),
|
|
true
|
|
);
|
|
if (is_array($messwerte) && in_array((int) $senderID, $messwerte, true)) {
|
|
$this->aktualisiere(true);
|
|
}
|
|
}
|
|
|
|
public function RequestAction($ident, $wert): void
|
|
{
|
|
switch ($ident) {
|
|
case 'Aktiv':
|
|
$this->SetValue('Aktiv', (bool) $wert);
|
|
if (!(bool) $wert) {
|
|
$this->setzeZustand('SollwertGueltig', false);
|
|
$this->SetTimerInterval('VorgabeTimeout', 0);
|
|
}
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'Melden':
|
|
if ((bool) $wert) {
|
|
$this->SetTimerInterval('RueckmeldungVerzoegert', 0);
|
|
}
|
|
$this->aktualisiere(false);
|
|
$this->sendeVerbraucherdaten();
|
|
return;
|
|
|
|
case 'VorgabeTimeout':
|
|
$this->SetTimerInterval('VorgabeTimeout', 0);
|
|
$this->setzeZustand('SollwertGueltig', false);
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'AenderungFreigabe':
|
|
$this->SetTimerInterval('AenderungFreigabe', 0);
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'Aktualisieren':
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'NetzfahrplanV4TestStart':
|
|
if (!is_string($wert)) throw new InvalidArgumentException('JSON-Testanmeldung erforderlich.');
|
|
$this->V4BatterieTestStarten($wert);
|
|
return;
|
|
case 'NetzfahrplanV4TestCommand':
|
|
if (!is_string($wert)) throw new InvalidArgumentException('JSON-Testbefehl erforderlich.');
|
|
$this->V4BatterieTestBefehl($wert);
|
|
return;
|
|
case 'NetzfahrplanV4TestStop':
|
|
$this->V4BatterieTestStoppen();
|
|
return;
|
|
case 'NetzfahrplanV4TestWatchdog':
|
|
$this->aktualisiere(true);
|
|
return;
|
|
case 'SichererZustand':
|
|
$this->V4BatterieTestStoppen();
|
|
$this->setzeZustand('SollwertGueltig', false);
|
|
$this->schreibeRegister(0, true);
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'FormBatterietyp':
|
|
$this->aktualisiereFormRegisterfelder((int) $wert);
|
|
return;
|
|
|
|
case 'FormNachladenMitMaximalleistung':
|
|
$this->UpdateFormField(
|
|
'MaximaleNachladeleistung',
|
|
'visible',
|
|
!(bool) $wert
|
|
);
|
|
return;
|
|
|
|
case 'ManagerdatenEmpfangen':
|
|
if (!is_string($wert)) {
|
|
throw new InvalidArgumentException(
|
|
'Managerdaten muessen als JSON uebergeben werden.'
|
|
);
|
|
}
|
|
$daten = json_decode($wert, true, 512, JSON_THROW_ON_ERROR);
|
|
if (!is_array($daten)) {
|
|
throw new InvalidArgumentException(
|
|
'Managerdaten muessen ein JSON-Objekt sein.'
|
|
);
|
|
}
|
|
$this->ManagerdatenEmpfangen($daten);
|
|
return;
|
|
}
|
|
|
|
throw new InvalidArgumentException('Unbekannte Aktion: ' . $ident);
|
|
}
|
|
|
|
/** @param array<string, mixed> $daten */
|
|
public function ManagerdatenEmpfangen(array $daten): void
|
|
{
|
|
$lock = 'ENELIX.V4.Battery.' . $this->InstanceID;
|
|
if (!IPS_SemaphoreEnter($lock, 1000)) throw new RuntimeException('Batterieannahme beschaeftigt.');
|
|
try { $this->ManagerdatenEmpfangenIntern($daten); }
|
|
finally { IPS_SemaphoreLeave($lock); }
|
|
}
|
|
|
|
private function ManagerdatenEmpfangenIntern(array $daten): void
|
|
{
|
|
Nachrichtenvertrag::pruefeManagerdaten($daten);
|
|
if ($daten['Kopf']['EmpfaengerID'] !== $this->InstanceID) {
|
|
throw new InvalidArgumentException(
|
|
'Managerdaten sind an eine andere Instanz adressiert.'
|
|
);
|
|
}
|
|
|
|
$managerID = $daten['Kopf']['AbsenderID'];
|
|
if (!in_array($managerID, $this->zugeordneteManagerIDs(), true)) {
|
|
throw new InvalidArgumentException(
|
|
'Der Manager hat diese Batterie nicht aktiv zugeordnet.'
|
|
);
|
|
}
|
|
|
|
if ($this->v4BatterieTestAktiv()) {
|
|
// Ordinary manager messages cannot overwrite or renew the independent short test lease.
|
|
$this->planeMeldung();
|
|
return;
|
|
}
|
|
$this->WriteAttributeString('Betriebsart', $daten['Betriebsart']);
|
|
$this->aktualisiereIntern(false);
|
|
|
|
$sollleistung = $daten['Sollleistung_W'];
|
|
if ($sollleistung === null) {
|
|
if ((bool) $this->leseZustand('SollwertGueltig')
|
|
&& !Nachrichtenvertrag::enthaeltLeistungswert(
|
|
$this->leseLeistungsangebot(),
|
|
(int) $this->leseZustand('Sollleistung')
|
|
)
|
|
) {
|
|
$this->setzeZustand('SollwertGueltig', false);
|
|
$this->aktualisiereIntern(false);
|
|
}
|
|
$this->planeMeldung();
|
|
return;
|
|
}
|
|
|
|
if (!Nachrichtenvertrag::enthaeltLeistungswert(
|
|
$this->leseLeistungsangebot(),
|
|
$sollleistung
|
|
)) {
|
|
throw new InvalidArgumentException(
|
|
'Sollleistung_W liegt nicht im aktuell gemeldeten Leistungsangebot.'
|
|
);
|
|
}
|
|
if (!(bool) $this->leseZustand('AenderungMoeglich')
|
|
&& $sollleistung !== (int) $this->leseZustand('Sollleistung')
|
|
) {
|
|
throw new RuntimeException(
|
|
'Die Aenderungssperre der Batterie ist noch aktiv.'
|
|
);
|
|
}
|
|
|
|
$this->WriteAttributeInteger('LetzteManagerID', $managerID);
|
|
$this->WriteAttributeInteger('LetzteVorgabeZeit', time());
|
|
$this->setzeZustand('Sollleistung', $sollleistung);
|
|
$this->setzeZustand('SollwertGueltig', true);
|
|
$this->planeVorgabeTimeout(time());
|
|
$this->aktualisiereIntern(false);
|
|
$this->planeMeldung();
|
|
}
|
|
|
|
private function aktualisiere(bool $meldungPlanen): void
|
|
{
|
|
$lock = 'ENELIX.V4.Battery.' . $this->InstanceID;
|
|
if (!IPS_SemaphoreEnter($lock, 0)) return;
|
|
try { $this->aktualisiereIntern($meldungPlanen); }
|
|
finally { IPS_SemaphoreLeave($lock); }
|
|
}
|
|
|
|
private function aktualisiereIntern(bool $meldungPlanen): void
|
|
{
|
|
try {
|
|
$jetzt = time();
|
|
$this->aktualisiereEnergie($jetzt);
|
|
$messwerte = $this->leseMesswerte();
|
|
$messwerteGueltig = $messwerte['Gueltig'];
|
|
|
|
$this->setzeZustand('Ladezustand', $messwerte['Ladezustand']);
|
|
$this->setzeZustand('LadezustandGueltig', $messwerteGueltig);
|
|
$this->setzeZustand('Netzleistung', $messwerte['Netzleistung']);
|
|
$this->setzeZustand('MaxLadeleistung', $messwerte['MaxLaden']);
|
|
$this->setzeZustand('MaxEntladeleistung', $messwerte['MaxEntladen']);
|
|
$this->setzeZustand('Istleistung', $messwerte['Istleistung']);
|
|
$this->setzeSichtbareVariable(
|
|
'Leistungsquelle',
|
|
$messwerteGueltig
|
|
? Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN
|
|
: Nachrichtenvertrag::LEISTUNGSQUELLE_NICHT_VORHANDEN
|
|
);
|
|
$this->aktualisiereLadestatus(
|
|
$messwerte['Istleistung'],
|
|
$messwerteGueltig
|
|
);
|
|
|
|
if ($messwerteGueltig) {
|
|
$hysterese = BatterieRegler::hystereseAktiv(
|
|
$messwerte['Ladezustand'],
|
|
$this->ReadPropertyFloat('ReserveLadezustand'),
|
|
$this->ReadPropertyFloat('LadezustandHysterese'),
|
|
(bool) $this->leseZustand('HystereseAktiv')
|
|
);
|
|
$this->setzeZustand('HystereseAktiv', $hysterese);
|
|
}
|
|
|
|
$enelixSteuert = $this->ReadPropertyInteger('Batteriemanagement')
|
|
=== BatterieRegler::STEUERUNG_ENELIX;
|
|
$aktiv = (bool) $this->GetValue('Aktiv');
|
|
$verfuegbar = $aktiv && $enelixSteuert && $messwerteGueltig;
|
|
$angebot = $messwerteGueltig
|
|
? BatterieRegler::leistungsangebot(
|
|
$aktiv,
|
|
$enelixSteuert,
|
|
$this->ReadAttributeString('Betriebsart')
|
|
=== Nachrichtenvertrag::BETRIEBSART_PEAK,
|
|
$messwerte['Ladezustand'],
|
|
$this->ReadPropertyFloat('ReserveLadezustand'),
|
|
$this->ReadPropertyFloat('MindestLadezustand'),
|
|
(bool) $this->leseZustand('HystereseAktiv'),
|
|
$messwerte['Netzleistung'],
|
|
$messwerte['Istleistung'],
|
|
$messwerte['MaxLaden'],
|
|
$messwerte['MaxEntladen'],
|
|
$this->ReadPropertyBoolean('NachladenMitMaximalleistung')
|
|
? null
|
|
: (float) $this->ReadPropertyInteger('MaximaleNachladeleistung')
|
|
)
|
|
: [0];
|
|
|
|
$this->WriteAttributeString(
|
|
'Leistungsangebot',
|
|
json_encode($angebot, JSON_THROW_ON_ERROR)
|
|
);
|
|
$this->setzeSichtbareVariable(
|
|
'LeistungsangebotDiagnose',
|
|
json_encode($angebot, JSON_THROW_ON_ERROR)
|
|
);
|
|
|
|
if ((bool) $this->leseZustand('SollwertGueltig')
|
|
&& !Nachrichtenvertrag::enthaeltLeistungswert(
|
|
$angebot,
|
|
(int) $this->leseZustand('Sollleistung')
|
|
)
|
|
) {
|
|
$this->setzeZustand('SollwertGueltig', false);
|
|
}
|
|
$this->pruefeVorgabeTimeout($jetzt);
|
|
|
|
$ziel = $verfuegbar && (bool) $this->leseZustand('SollwertGueltig')
|
|
? (int) $this->leseZustand('Sollleistung')
|
|
: 0;
|
|
$v4Ziel = $this->v4BatterieTestZiel($messwerte, $verfuegbar, $jetzt);
|
|
if ($v4Ziel !== null) $ziel = $v4Ziel;
|
|
$this->schreibeRegister($ziel, false);
|
|
$this->setzeZustand('Sollleistung', $ziel);
|
|
$this->setzeZustand('Verfuegbar', $verfuegbar);
|
|
$aenderungMoeglich = $verfuegbar
|
|
&& $jetzt >= $this->ReadAttributeInteger('AenderungFreigabeAb');
|
|
$this->setzeZustand('AenderungMoeglich', $aenderungMoeglich);
|
|
$this->setzeZustand(
|
|
'Steuerungsmodus',
|
|
$this->ReadPropertyInteger('Batteriemanagement')
|
|
);
|
|
$this->setzeStoerung(
|
|
$messwerteGueltig
|
|
? ''
|
|
: 'Batteriemesswerte fehlen, sind veraltet oder ungueltig.'
|
|
);
|
|
$this->SetStatus(
|
|
$messwerteGueltig
|
|
? self::STATUS_AKTIV
|
|
: self::STATUS_MESSWERTE_UNGUELTIG
|
|
);
|
|
$this->planeAenderungsfreigabe($jetzt);
|
|
$this->planeVorgabeTimeout($jetzt);
|
|
|
|
if ($meldungPlanen) {
|
|
$this->planeMeldung();
|
|
}
|
|
} catch (Throwable $fehler) {
|
|
$this->WriteAttributeString('Registerfehler', $fehler->getMessage());
|
|
$this->setzeZustand('Verfuegbar', false);
|
|
$this->setzeZustand('AenderungMoeglich', false);
|
|
$this->setzeStoerung($fehler->getMessage());
|
|
$this->SetStatus(self::STATUS_REGISTERFEHLER);
|
|
$this->protokolliere('Aktualisierung', $fehler->getMessage());
|
|
if ($meldungPlanen) {
|
|
$this->planeMeldung();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @return array{Gueltig: bool, MaxLaden: float, MaxEntladen: float, Ladezustand: float, Netzleistung: float, Istleistung: float} */
|
|
private function leseMesswerte(): array
|
|
{
|
|
$werte = [
|
|
'MaxLaden' => 0.0,
|
|
'MaxEntladen' => 0.0,
|
|
'Ladezustand' => 0.0,
|
|
'Netzleistung' => 0.0,
|
|
'Istleistung' => 0.0,
|
|
];
|
|
$zuordnung = [
|
|
'MaxLadeleistungVariableID' => 'MaxLaden',
|
|
'MaxEntladeleistungVariableID' => 'MaxEntladen',
|
|
'LadezustandVariableID' => 'Ladezustand',
|
|
'NetzleistungVariableID' => 'Netzleistung',
|
|
'IstleistungVariableID' => 'Istleistung',
|
|
];
|
|
|
|
$gueltig = true;
|
|
foreach ($zuordnung as $property => $schluessel) {
|
|
$variablenID = $this->ReadPropertyInteger($property);
|
|
if (!$this->istMesswertAktuell($variablenID)) {
|
|
$gueltig = false;
|
|
continue;
|
|
}
|
|
$werte[$schluessel] = (float) GetValue($variablenID);
|
|
}
|
|
|
|
if ($werte['MaxLaden'] <= 0.0
|
|
|| $werte['MaxEntladen'] <= 0.0
|
|
|| $werte['Ladezustand'] < 0.0
|
|
|| $werte['Ladezustand'] > 100.0
|
|
) {
|
|
$gueltig = false;
|
|
}
|
|
|
|
return ['Gueltig' => $gueltig] + $werte;
|
|
}
|
|
|
|
private function istMesswertAktuell(int $variablenID): bool
|
|
{
|
|
if ($variablenID <= 0 || !IPS_VariableExists($variablenID)) {
|
|
return false;
|
|
}
|
|
$variable = IPS_GetVariable($variablenID);
|
|
$aktualisiert = (int) ($variable['VariableUpdated'] ?? 0);
|
|
|
|
return $aktualisiert > 0
|
|
&& time() - $aktualisiert <= $this->ReadPropertyInteger('MesswertMaxAlter');
|
|
}
|
|
|
|
private function schreibeRegister(int $sollleistungW, bool $erzwingen): void
|
|
{
|
|
$werte = BatterieRegler::registerwerte(
|
|
$this->ReadPropertyInteger('Batterietyp'),
|
|
$this->ReadPropertyInteger('Batteriemanagement'),
|
|
$sollleistungW
|
|
);
|
|
$befehl = json_encode($werte, JSON_THROW_ON_ERROR);
|
|
if (!$erzwingen
|
|
&& $this->ReadAttributeBoolean('RegisterInitialisiert')
|
|
&& $befehl === $this->ReadAttributeString('LetzterRegisterbefehl')
|
|
) {
|
|
return;
|
|
}
|
|
|
|
$this->WriteAttributeBoolean('RegisterSchreiben', true);
|
|
try {
|
|
foreach ($werte as $register => $wert) {
|
|
$property = self::REGISTER_PROPERTIES[$register];
|
|
RequestAction($this->ReadPropertyInteger($property), $wert);
|
|
}
|
|
} catch (Throwable $fehler) {
|
|
throw new RuntimeException(
|
|
'Batterieregister konnten nicht geschrieben werden: '
|
|
. $fehler->getMessage()
|
|
);
|
|
} finally {
|
|
$this->WriteAttributeBoolean('RegisterSchreiben', false);
|
|
}
|
|
|
|
$alterSollwert = $this->ReadAttributeInteger('LetzterSollwert');
|
|
$this->WriteAttributeString('LetzterRegisterbefehl', $befehl);
|
|
$this->WriteAttributeBoolean('RegisterInitialisiert', true);
|
|
$this->setzeSichtbareVariable('LetzterRegisterbefehl', $befehl);
|
|
$this->WriteAttributeString('Registerfehler', '');
|
|
if ($alterSollwert !== $sollleistungW
|
|
&& $this->ReadPropertyInteger('Batteriemanagement')
|
|
=== BatterieRegler::STEUERUNG_ENELIX
|
|
) {
|
|
$this->WriteAttributeInteger(
|
|
'AenderungFreigabeAb',
|
|
time() + $this->ReadPropertyInteger('Aenderungssperre')
|
|
);
|
|
}
|
|
$this->WriteAttributeInteger('LetzterSollwert', $sollleistungW);
|
|
$this->protokolliere('Registerbefehl', $werte);
|
|
}
|
|
|
|
private function aktualisiereEnergie(int $jetzt): void
|
|
{
|
|
$standZeit = $this->ReadAttributeInteger('EnergieStandZeit');
|
|
if ($standZeit > 0 && $jetzt >= $standZeit) {
|
|
$energie = (float) $this->leseZustand('BezogeneEnergie')
|
|
+ (float) $this->leseZustand('Istleistung')
|
|
* ($jetzt - $standZeit) / 3600000.0;
|
|
$this->setzeZustand('BezogeneEnergie', $energie);
|
|
}
|
|
$this->WriteAttributeInteger('EnergieStandZeit', $jetzt);
|
|
}
|
|
|
|
private function aktualisiereLadestatus(
|
|
float $istleistungW,
|
|
bool $gueltig
|
|
): void {
|
|
$status = 0;
|
|
if ($gueltig) {
|
|
$status = $istleistungW > 0.0 ? 2 : ($istleistungW < 0.0 ? 3 : 1);
|
|
}
|
|
$this->SetValue('Ladestatus', $status);
|
|
}
|
|
|
|
private function pruefeVorgabeTimeout(int $jetzt): void
|
|
{
|
|
if (!(bool) $this->leseZustand('SollwertGueltig')) {
|
|
return;
|
|
}
|
|
$letzteVorgabe = $this->ReadAttributeInteger('LetzteVorgabeZeit');
|
|
if ($letzteVorgabe <= 0
|
|
|| $jetzt - $letzteVorgabe >= $this->ReadPropertyInteger('VorgabeTimeout')
|
|
) {
|
|
$this->setzeZustand('SollwertGueltig', false);
|
|
$this->SetTimerInterval('VorgabeTimeout', 0);
|
|
}
|
|
}
|
|
|
|
private function planeVorgabeTimeout(int $jetzt): void
|
|
{
|
|
if (!(bool) $this->leseZustand('SollwertGueltig')) {
|
|
$this->SetTimerInterval('VorgabeTimeout', 0);
|
|
return;
|
|
}
|
|
$ablauf = $this->ReadAttributeInteger('LetzteVorgabeZeit')
|
|
+ $this->ReadPropertyInteger('VorgabeTimeout');
|
|
$this->SetTimerInterval(
|
|
'VorgabeTimeout',
|
|
max(1, ($ablauf - $jetzt) * 1000)
|
|
);
|
|
}
|
|
|
|
private function planeAenderungsfreigabe(int $jetzt): void
|
|
{
|
|
$freigabe = $this->ReadAttributeInteger('AenderungFreigabeAb');
|
|
$this->SetTimerInterval(
|
|
'AenderungFreigabe',
|
|
$freigabe > $jetzt ? max(1, ($freigabe - $jetzt) * 1000) : 0
|
|
);
|
|
}
|
|
|
|
private function planeMeldung(): void
|
|
{
|
|
$this->SetTimerInterval('RueckmeldungVerzoegert', 100);
|
|
}
|
|
|
|
private function sendeVerbraucherdaten(): void
|
|
{
|
|
foreach ($this->zugeordneteManagerIDs() as $managerID) {
|
|
$daten = $this->baueVerbraucherdaten($managerID);
|
|
Nachrichtenvertrag::pruefeVerbraucherdaten($daten);
|
|
try {
|
|
IPS_RequestAction(
|
|
$managerID,
|
|
'VerbraucherdatenEmpfangen',
|
|
json_encode($daten, JSON_THROW_ON_ERROR)
|
|
);
|
|
} catch (Throwable $fehler) {
|
|
$this->protokolliere(
|
|
'Managerkommunikation',
|
|
$fehler->getMessage()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @return array<string, mixed> */
|
|
private function baueVerbraucherdaten(int $managerID): array
|
|
{
|
|
$messwerteGueltig = (bool) $this->leseZustand('LadezustandGueltig');
|
|
$registerfehler = $this->ReadAttributeString('Registerfehler');
|
|
|
|
return [
|
|
'Kopf' => [
|
|
'Version' => Nachrichtenvertrag::VERSION,
|
|
'AbsenderID' => $this->InstanceID,
|
|
'EmpfaengerID' => $managerID,
|
|
'Zeitpunkt' => time(),
|
|
],
|
|
'Betriebsart' => $this->ReadAttributeString('Betriebsart'),
|
|
'PrioritaetPV' => $this->ReadPropertyInteger('PrioritaetPV'),
|
|
'PrioritaetPeak' => $this->ReadPropertyInteger('PrioritaetPeak'),
|
|
'Leistungswerte_W' => $this->leseLeistungsangebot(),
|
|
'AenderungMoeglich' => (bool) $this->leseZustand('AenderungMoeglich'),
|
|
'Verfuegbar' => (bool) $this->leseZustand('Verfuegbar'),
|
|
'Istleistung_W' => $messwerteGueltig
|
|
? (float) $this->leseZustand('Istleistung')
|
|
: null,
|
|
'Leistungsquelle' => $messwerteGueltig
|
|
? Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN
|
|
: Nachrichtenvertrag::LEISTUNGSQUELLE_NICHT_VORHANDEN,
|
|
'Zustand' => [
|
|
[
|
|
'Kennung' => 'Sollleistung_W',
|
|
'Art' => 'Sollwert',
|
|
'Wert' => (bool) $this->leseZustand('SollwertGueltig')
|
|
? (int) $this->leseZustand('Sollleistung')
|
|
: null,
|
|
'Einheit' => 'W',
|
|
],
|
|
[
|
|
'Kennung' => 'Ladezustand_Prozent',
|
|
'Art' => 'Istwert',
|
|
'Wert' => $messwerteGueltig
|
|
? (float) $this->leseZustand('Ladezustand')
|
|
: null,
|
|
'Einheit' => '%',
|
|
],
|
|
[
|
|
'Kennung' => 'HystereseAktiv',
|
|
'Art' => 'Status',
|
|
'Wert' => (bool) $this->leseZustand('HystereseAktiv'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'Batteriesteuerung',
|
|
'Art' => 'Status',
|
|
'Wert' => $this->ReadPropertyInteger('Batteriemanagement'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'Messwertfehler',
|
|
'Art' => 'Stoerung',
|
|
'Wert' => !$messwerteGueltig,
|
|
'Einheit' => '',
|
|
'Text' => $messwerteGueltig
|
|
? ''
|
|
: 'Batteriemesswerte fehlen, sind veraltet oder ungueltig.',
|
|
],
|
|
[
|
|
'Kennung' => 'Registerfehler',
|
|
'Art' => 'Stoerung',
|
|
'Wert' => $registerfehler !== '',
|
|
'Einheit' => '',
|
|
'Text' => $registerfehler,
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/** @return list<int> */
|
|
private function zugeordneteManagerIDs(): array
|
|
{
|
|
$ergebnis = [];
|
|
foreach (IPS_GetInstanceListByModuleID(self::MANAGER_MODULE_ID) as $managerID) {
|
|
try {
|
|
$automatisch = (bool) IPS_GetProperty(
|
|
$managerID,
|
|
'AutomatischeSuche'
|
|
);
|
|
$property = $automatisch
|
|
? 'AutomatischeVerbraucherZuordnung'
|
|
: 'VerbraucherZuordnung';
|
|
$zuordnung = $this->leseManagerZuordnung($managerID, $property);
|
|
if ($automatisch && $zuordnung === []) {
|
|
$zuordnung = $this->leseManagerZuordnung(
|
|
$managerID,
|
|
'VerbraucherZuordnung'
|
|
);
|
|
}
|
|
} catch (Throwable $fehler) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($zuordnung as $eintrag) {
|
|
if (!is_array($eintrag) || ($eintrag['Aktiv'] ?? false) !== true) {
|
|
continue;
|
|
}
|
|
$instanzID = $eintrag['InstanzID']
|
|
?? $eintrag['Verbraucher']
|
|
?? null;
|
|
if ($instanzID === $this->InstanceID) {
|
|
$ergebnis[] = (int) $managerID;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return array_values(array_unique($ergebnis));
|
|
}
|
|
|
|
/** @return array<mixed> */
|
|
private function leseManagerZuordnung(int $managerID, string $property): array
|
|
{
|
|
$zuordnung = json_decode(
|
|
(string) IPS_GetProperty($managerID, $property),
|
|
true,
|
|
512,
|
|
JSON_THROW_ON_ERROR
|
|
);
|
|
|
|
return is_array($zuordnung) ? $zuordnung : [];
|
|
}
|
|
|
|
private function pruefeKonfiguration(): void
|
|
{
|
|
foreach (['PrioritaetPV', 'PrioritaetPeak', 'Aenderungssperre'] as $property) {
|
|
if ($this->ReadPropertyInteger($property) < 0) {
|
|
throw new InvalidArgumentException(
|
|
$property . ' darf nicht negativ sein.'
|
|
);
|
|
}
|
|
}
|
|
foreach (['Meldeintervall', 'VorgabeTimeout', 'MesswertMaxAlter'] as $property) {
|
|
if ($this->ReadPropertyInteger($property) <= 0) {
|
|
throw new InvalidArgumentException(
|
|
$property . ' muss groesser als 0 sein.'
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!$this->ReadPropertyBoolean('NachladenMitMaximalleistung')
|
|
&& $this->ReadPropertyInteger('MaximaleNachladeleistung') <= 0
|
|
) {
|
|
throw new InvalidArgumentException(
|
|
'MaximaleNachladeleistung muss groesser als 0 W sein.'
|
|
);
|
|
}
|
|
|
|
BatterieRegler::hystereseAktiv(
|
|
50.0,
|
|
$this->ReadPropertyFloat('ReserveLadezustand'),
|
|
$this->ReadPropertyFloat('LadezustandHysterese'),
|
|
false
|
|
);
|
|
BatterieRegler::leistungsangebot(
|
|
true,
|
|
true,
|
|
false,
|
|
50.0,
|
|
$this->ReadPropertyFloat('ReserveLadezustand'),
|
|
$this->ReadPropertyFloat('MindestLadezustand'),
|
|
false,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
1.0,
|
|
$this->ReadPropertyBoolean('NachladenMitMaximalleistung')
|
|
? null
|
|
: (float) $this->ReadPropertyInteger('MaximaleNachladeleistung')
|
|
);
|
|
|
|
foreach (self::MESSWERT_PROPERTIES as $property) {
|
|
$this->pruefeNumerischeVariable(
|
|
$this->ReadPropertyInteger($property),
|
|
$property,
|
|
false
|
|
);
|
|
}
|
|
|
|
$registerIDs = [];
|
|
foreach (BatterieRegler::erforderlicheRegister(
|
|
$this->ReadPropertyInteger('Batterietyp')
|
|
) as $register) {
|
|
$property = self::REGISTER_PROPERTIES[$register];
|
|
$variablenID = $this->ReadPropertyInteger($property);
|
|
$this->pruefeNumerischeVariable($variablenID, $property, true);
|
|
if (isset($registerIDs[$variablenID])) {
|
|
throw new InvalidArgumentException(
|
|
'Jedes Batterieregister muss eine eigene Variable verwenden.'
|
|
);
|
|
}
|
|
$registerIDs[$variablenID] = true;
|
|
}
|
|
|
|
BatterieRegler::registerwerte(
|
|
$this->ReadPropertyInteger('Batterietyp'),
|
|
$this->ReadPropertyInteger('Batteriemanagement'),
|
|
0
|
|
);
|
|
}
|
|
|
|
private function pruefeNumerischeVariable(
|
|
int $variablenID,
|
|
string $bezeichnung,
|
|
bool $aktionErforderlich
|
|
): void {
|
|
if ($variablenID <= 0 || !IPS_VariableExists($variablenID)) {
|
|
throw new InvalidArgumentException(
|
|
$bezeichnung . ' ist nicht eingerichtet.'
|
|
);
|
|
}
|
|
$variable = IPS_GetVariable($variablenID);
|
|
if (!in_array((int) $variable['VariableType'], [1, 2], true)) {
|
|
throw new InvalidArgumentException(
|
|
$bezeichnung . ' muss eine Integer- oder Floatvariable sein.'
|
|
);
|
|
}
|
|
if ($aktionErforderlich
|
|
&& (int) $variable['VariableAction'] <= 0
|
|
&& (int) $variable['VariableCustomAction'] <= 0
|
|
) {
|
|
throw new InvalidArgumentException(
|
|
$bezeichnung
|
|
. ' benoetigt eine Standard- oder benutzerdefinierte Aktion.'
|
|
);
|
|
}
|
|
}
|
|
|
|
private function registriereMesswertmeldungen(): void
|
|
{
|
|
$alteIDs = json_decode(
|
|
$this->ReadAttributeString('RegistrierteMesswerte'),
|
|
true
|
|
);
|
|
if (!is_array($alteIDs)) {
|
|
$alteIDs = [];
|
|
}
|
|
$neueIDs = [];
|
|
foreach (self::MESSWERT_PROPERTIES as $property) {
|
|
$variablenID = $this->ReadPropertyInteger($property);
|
|
if ($variablenID > 0) {
|
|
$neueIDs[] = $variablenID;
|
|
}
|
|
}
|
|
$neueIDs = array_values(array_unique($neueIDs));
|
|
|
|
foreach ($alteIDs as $alteID) {
|
|
if (!in_array((int) $alteID, $neueIDs, true)) {
|
|
$this->UnregisterMessage((int) $alteID, self::VM_UPDATE);
|
|
}
|
|
}
|
|
foreach ($neueIDs as $neueID) {
|
|
if (IPS_VariableExists($neueID)) {
|
|
$this->RegisterMessage($neueID, self::VM_UPDATE);
|
|
}
|
|
}
|
|
$this->WriteAttributeString(
|
|
'RegistrierteMesswerte',
|
|
json_encode($neueIDs, JSON_THROW_ON_ERROR)
|
|
);
|
|
}
|
|
|
|
private function aktualisiereVariablen(): void
|
|
{
|
|
if ($this->ReadPropertyBoolean('DiagnosevariablenAnzeigen')) {
|
|
$this->registriereVerbraucherDiagnose();
|
|
$this->RegisterVariableFloat('Ladezustand', 'Ladezustand', '', 200);
|
|
$this->RegisterVariableBoolean(
|
|
'LadezustandGueltig',
|
|
'Ladezustand gueltig',
|
|
'~Switch',
|
|
210
|
|
);
|
|
$this->RegisterVariableFloat('Netzleistung', 'Netzleistung', '', 220);
|
|
$this->RegisterVariableFloat(
|
|
'MaxLadeleistung',
|
|
'Maximale Ladeleistung',
|
|
'',
|
|
230
|
|
);
|
|
$this->RegisterVariableFloat(
|
|
'MaxEntladeleistung',
|
|
'Maximale Entladeleistung',
|
|
'',
|
|
240
|
|
);
|
|
$this->RegisterVariableBoolean(
|
|
'HystereseAktiv',
|
|
'Hysterese aktiv',
|
|
'~Switch',
|
|
250
|
|
);
|
|
$this->RegisterVariableInteger(
|
|
'Steuerungsmodus',
|
|
'Steuerungsmodus',
|
|
'',
|
|
260
|
|
);
|
|
$this->RegisterVariableFloat(
|
|
'BezogeneEnergie',
|
|
'Bezogene Energie',
|
|
'~Electricity',
|
|
270
|
|
);
|
|
$this->RegisterVariableString(
|
|
'LeistungsangebotDiagnose',
|
|
'Leistungsangebot',
|
|
'',
|
|
280
|
|
);
|
|
$this->RegisterVariableString(
|
|
'LetzterRegisterbefehl',
|
|
'Letzter Registerbefehl',
|
|
'',
|
|
290
|
|
);
|
|
$this->synchronisiereDiagnosevariablen();
|
|
return;
|
|
}
|
|
|
|
foreach (self::DIAGNOSE_VARIABLEN as $ident) {
|
|
$variablenID = @IPS_GetObjectIDByIdent($ident, $this->InstanceID);
|
|
if (is_int($variablenID)
|
|
&& $variablenID > 0
|
|
&& IPS_VariableExists($variablenID)
|
|
) {
|
|
$this->UnregisterVariable($ident);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function synchronisiereDiagnosevariablen(): void
|
|
{
|
|
foreach (self::DIAGNOSE_VARIABLEN as $ident) {
|
|
if ($ident === 'Leistungsquelle') {
|
|
continue;
|
|
}
|
|
if ($ident === 'LeistungsangebotDiagnose') {
|
|
$wert = $this->ReadAttributeString('Leistungsangebot');
|
|
} elseif ($ident === 'LetzterRegisterbefehl') {
|
|
$wert = $this->ReadAttributeString('LetzterRegisterbefehl');
|
|
} else {
|
|
$wert = $this->leseZustand($ident);
|
|
}
|
|
$this->setzeSichtbareVariable($ident, $wert);
|
|
}
|
|
}
|
|
|
|
/** @param bool|int|float|string $wert */
|
|
private function setzeZustand(string $ident, $wert): void
|
|
{
|
|
$booleanAttribute = [
|
|
'SollwertGueltig' => 'ZustandSollwertGueltig',
|
|
'Verfuegbar' => 'ZustandVerfuegbar',
|
|
'AenderungMoeglich' => 'ZustandAenderungMoeglich',
|
|
'Stoerung' => 'ZustandStoerung',
|
|
'LadezustandGueltig' => 'ZustandLadezustandGueltig',
|
|
'HystereseAktiv' => 'ZustandHystereseAktiv',
|
|
];
|
|
$integerAttribute = [
|
|
'Sollleistung' => 'ZustandSollleistung',
|
|
'Steuerungsmodus' => 'ZustandSteuerungsmodus',
|
|
];
|
|
$floatAttribute = [
|
|
'Istleistung' => 'ZustandIstleistung',
|
|
'Ladezustand' => 'ZustandLadezustand',
|
|
'Netzleistung' => 'ZustandNetzleistung',
|
|
'MaxLadeleistung' => 'ZustandMaxLadeleistung',
|
|
'MaxEntladeleistung' => 'ZustandMaxEntladeleistung',
|
|
'BezogeneEnergie' => 'ZustandBezogeneEnergie',
|
|
];
|
|
$stringAttribute = ['Stoertext' => 'ZustandStoertext'];
|
|
|
|
if (isset($booleanAttribute[$ident])) {
|
|
$wert = (bool) $wert;
|
|
$this->WriteAttributeBoolean($booleanAttribute[$ident], $wert);
|
|
} elseif (isset($integerAttribute[$ident])) {
|
|
$wert = (int) $wert;
|
|
$this->WriteAttributeInteger($integerAttribute[$ident], $wert);
|
|
} elseif (isset($floatAttribute[$ident])) {
|
|
$wert = (float) $wert;
|
|
$this->WriteAttributeFloat($floatAttribute[$ident], $wert);
|
|
} elseif (isset($stringAttribute[$ident])) {
|
|
$wert = (string) $wert;
|
|
$this->WriteAttributeString($stringAttribute[$ident], $wert);
|
|
} else {
|
|
throw new LogicException(
|
|
'Unbekannter interner Batteriezustand: ' . $ident
|
|
);
|
|
}
|
|
|
|
$this->setzeSichtbareVariable($ident, $wert);
|
|
}
|
|
|
|
/** @return bool|int|float|string */
|
|
private function leseZustand(string $ident)
|
|
{
|
|
$booleanAttribute = [
|
|
'SollwertGueltig' => 'ZustandSollwertGueltig',
|
|
'Verfuegbar' => 'ZustandVerfuegbar',
|
|
'AenderungMoeglich' => 'ZustandAenderungMoeglich',
|
|
'Stoerung' => 'ZustandStoerung',
|
|
'LadezustandGueltig' => 'ZustandLadezustandGueltig',
|
|
'HystereseAktiv' => 'ZustandHystereseAktiv',
|
|
];
|
|
$integerAttribute = [
|
|
'Sollleistung' => 'ZustandSollleistung',
|
|
'Steuerungsmodus' => 'ZustandSteuerungsmodus',
|
|
];
|
|
$floatAttribute = [
|
|
'Istleistung' => 'ZustandIstleistung',
|
|
'Ladezustand' => 'ZustandLadezustand',
|
|
'Netzleistung' => 'ZustandNetzleistung',
|
|
'MaxLadeleistung' => 'ZustandMaxLadeleistung',
|
|
'MaxEntladeleistung' => 'ZustandMaxEntladeleistung',
|
|
'BezogeneEnergie' => 'ZustandBezogeneEnergie',
|
|
];
|
|
$stringAttribute = ['Stoertext' => 'ZustandStoertext'];
|
|
|
|
if (isset($booleanAttribute[$ident])) {
|
|
return $this->ReadAttributeBoolean($booleanAttribute[$ident]);
|
|
}
|
|
if (isset($integerAttribute[$ident])) {
|
|
return $this->ReadAttributeInteger($integerAttribute[$ident]);
|
|
}
|
|
if (isset($floatAttribute[$ident])) {
|
|
return $this->ReadAttributeFloat($floatAttribute[$ident]);
|
|
}
|
|
if (isset($stringAttribute[$ident])) {
|
|
return $this->ReadAttributeString($stringAttribute[$ident]);
|
|
}
|
|
|
|
throw new LogicException(
|
|
'Unbekannter interner Batteriezustand: ' . $ident
|
|
);
|
|
}
|
|
|
|
/** @param bool|int|float|string $wert */
|
|
private function setzeSichtbareVariable(string $ident, $wert): void
|
|
{
|
|
$variablenID = @IPS_GetObjectIDByIdent($ident, $this->InstanceID);
|
|
if (is_int($variablenID)
|
|
&& $variablenID > 0
|
|
&& IPS_VariableExists($variablenID)
|
|
) {
|
|
SetValue($variablenID, $wert);
|
|
}
|
|
}
|
|
|
|
/** @return list<int|array{Von_W: int, Bis_W: int}> */
|
|
private function leseLeistungsangebot(): array
|
|
{
|
|
$angebot = json_decode(
|
|
$this->ReadAttributeString('Leistungsangebot'),
|
|
true,
|
|
512,
|
|
JSON_THROW_ON_ERROR
|
|
);
|
|
if (!is_array($angebot)) {
|
|
return [0];
|
|
}
|
|
|
|
return array_values($angebot);
|
|
}
|
|
|
|
private function setzeStoerung(string $text): void
|
|
{
|
|
$this->setzeZustand('Stoerung', $text !== '');
|
|
$this->setzeZustand('Stoertext', $text);
|
|
}
|
|
|
|
/** @param array<int, mixed> $elemente */
|
|
private function setzeFormfeldSichtbar(
|
|
array &$elemente,
|
|
string $name,
|
|
bool $sichtbar
|
|
): void {
|
|
foreach ($elemente as &$element) {
|
|
if (($element['name'] ?? '') === $name) {
|
|
$element['visible'] = $sichtbar;
|
|
}
|
|
if (isset($element['items']) && is_array($element['items'])) {
|
|
$this->setzeFormfeldSichtbar($element['items'], $name, $sichtbar);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @param array<int, mixed> $elemente */
|
|
private function setzeRegisterfelderSichtbar(
|
|
array &$elemente,
|
|
int $batterietyp
|
|
): void {
|
|
$sichtbar = $this->registerfeldSichtbarkeit($batterietyp);
|
|
foreach ($elemente as &$element) {
|
|
if (isset($element['name'], $sichtbar[$element['name']])) {
|
|
$element['visible'] = $sichtbar[$element['name']];
|
|
}
|
|
if (isset($element['items']) && is_array($element['items'])) {
|
|
$this->setzeRegisterfelderSichtbar(
|
|
$element['items'],
|
|
$batterietyp
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function aktualisiereFormRegisterfelder(int $batterietyp): void
|
|
{
|
|
foreach ($this->registerfeldSichtbarkeit($batterietyp) as $name => $sichtbar) {
|
|
$this->UpdateFormField($name, 'visible', $sichtbar);
|
|
}
|
|
}
|
|
|
|
/** @return array<string, bool> */
|
|
private function registerfeldSichtbarkeit(int $batterietyp): array
|
|
{
|
|
$konfiguriert = in_array($batterietyp, [
|
|
BatterieRegler::TYP_HERSTELLERUNABHAENGIG,
|
|
BatterieRegler::TYP_GOODWE,
|
|
BatterieRegler::TYP_SOLAREDGE,
|
|
BatterieRegler::TYP_SIGENERGY,
|
|
], true);
|
|
$goodwe = $batterietyp === BatterieRegler::TYP_GOODWE;
|
|
$getrennt = in_array($batterietyp, [
|
|
BatterieRegler::TYP_HERSTELLERUNABHAENGIG,
|
|
BatterieRegler::TYP_SOLAREDGE,
|
|
BatterieRegler::TYP_SIGENERGY,
|
|
], true);
|
|
|
|
return [
|
|
'ManagementRegisterVariableID' => $konfiguriert,
|
|
'ModusRegisterVariableID' => $konfiguriert,
|
|
'LeistungsRegisterVariableID' => $goodwe,
|
|
'LadeleistungRegisterVariableID' => $getrennt,
|
|
'EntladeleistungRegisterVariableID' => $getrennt,
|
|
];
|
|
}
|
|
|
|
private function deaktiviereTimer(): void
|
|
{
|
|
foreach ([
|
|
'Meldezyklus',
|
|
'RueckmeldungVerzoegert',
|
|
'VorgabeTimeout',
|
|
'AenderungFreigabe',
|
|
] as $timer) {
|
|
$this->SetTimerInterval($timer, 0);
|
|
}
|
|
}
|
|
|
|
/** @param mixed $daten */
|
|
private function protokolliere(string $bezeichnung, $daten): void
|
|
{
|
|
if (!$this->ReadPropertyBoolean('LoggingEin')) {
|
|
return;
|
|
}
|
|
$this->SendDebug(
|
|
$bezeichnung,
|
|
is_string($daten)
|
|
? $daten
|
|
: json_encode($daten, JSON_THROW_ON_ERROR),
|
|
0
|
|
);
|
|
}
|
|
}
|