1130 lines
45 KiB
PHP
1130 lines
45 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/LadestationAdapter.php';
|
|
require_once __DIR__ . '/../libs/LadestationRegler.php';
|
|
|
|
use Belevo\EnelixEMS\LadestationAdapter;
|
|
use Belevo\EnelixEMS\LadestationRegler;
|
|
use Belevo\EnelixEMS\LadestationTransport;
|
|
use Belevo\EnelixEMS\Nachrichtenvertrag;
|
|
use Belevo\EnelixEMS\VerbraucherBasisTrait;
|
|
use Belevo\EnelixEMS\VerbraucherSchnittstelle;
|
|
|
|
class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstelle
|
|
{
|
|
use VerbraucherBasisTrait;
|
|
|
|
private const MANAGER_MODULE_ID = '{6F771B18-59D4-4C8A-B951-3B2FE9F6A2C4}';
|
|
private const STATUS_AKTIV = 102;
|
|
private const STATUS_KONFIGURATION_UNGUELTIG = 201;
|
|
private const STATUS_KOMMUNIKATIONSFEHLER = 202;
|
|
private const REGELUEBERGANG_TIMEOUT_MS = 2000;
|
|
private const PHASENERKENNUNGSSTROM = 6;
|
|
|
|
/** @var list<string> */
|
|
private const DIAGNOSE_VARIABLEN = [
|
|
'Leistungsquelle',
|
|
'SollwertGueltig',
|
|
'Verfuegbar',
|
|
'AenderungMoeglich',
|
|
'Stoerung',
|
|
'Stoertext',
|
|
'Fahrzeugstatus',
|
|
'FahrzeugMaximalstrom',
|
|
'LetzterGeraetebefehl',
|
|
'LeistungsangebotDiagnose',
|
|
];
|
|
|
|
public function Create(): void
|
|
{
|
|
parent::Create();
|
|
|
|
$this->registriereVerbraucherBasis();
|
|
$this->RegisterPropertyInteger('Geraetetyp', 0);
|
|
$this->RegisterPropertyString('Geraeteadresse', '');
|
|
$this->RegisterPropertyString('GeraeteID', '');
|
|
$this->RegisterPropertyString('Seriennummer', '');
|
|
$this->RegisterPropertyString('Benutzername', '');
|
|
$this->RegisterPropertyString('Passwort', '');
|
|
$this->RegisterPropertyInteger('MaximalerLadestrom', 16);
|
|
$this->RegisterPropertyInteger('Phasenerkennungszeit', 60);
|
|
$this->RegisterPropertyInteger('FahrzeugstromErkennungszeit', 90);
|
|
$this->RegisterPropertyInteger('ZeitZwischenZustandswechseln', 1);
|
|
$this->RegisterPropertyInteger('Mindesteinschaltdauer', 0);
|
|
$this->RegisterPropertyInteger('Mindestausschaltdauer', 0);
|
|
$this->RegisterPropertyInteger('Abfrageintervall', 5);
|
|
$this->RegisterPropertyBoolean('Ladefreigabe', true);
|
|
$this->RegisterPropertyBoolean('Solarladen', true);
|
|
$this->RegisterPropertyBoolean('DiagnosevariablenAnzeigen', false);
|
|
|
|
// Ausschliesslich fuer den automatisierten Symcon-Funktionstest.
|
|
$this->RegisterPropertyBoolean('Testmodus', false);
|
|
$this->RegisterPropertyString('Testantwort', '');
|
|
|
|
$this->RegisterVariableBoolean('FahrzeugVerbunden', 'Fahrzeug verbunden', '~Switch', 100);
|
|
$this->RegisterVariableBoolean('FahrzeugGeladen', 'Fahrzeug geladen', '~Switch', 110);
|
|
$this->RegisterVariableFloat('Ladestrom', 'Ladestrom', '', 130);
|
|
$this->RegisterVariableInteger('Phasenzahl', 'Phasenzahl', '', 140);
|
|
|
|
$this->RegisterAttributeBoolean('InitialwerteGesetzt', false);
|
|
$this->RegisterAttributeBoolean('KonfigurationLadefreigabe', false);
|
|
$this->RegisterAttributeBoolean('KonfigurationSolarladen', true);
|
|
$this->RegisterAttributeBoolean('ZustandLadefreigabe', false);
|
|
$this->RegisterAttributeBoolean('ZustandSolarladen', true);
|
|
$this->RegisterAttributeBoolean('ZustandFahrzeugVerbunden', false);
|
|
$this->RegisterAttributeBoolean('ZustandFahrzeugGeladen', false);
|
|
$this->RegisterAttributeInteger('ZustandFahrzeugstatus', 0);
|
|
$this->RegisterAttributeFloat('ZustandLadestrom', 0.0);
|
|
$this->RegisterAttributeInteger('ZustandPhasenzahl', 0);
|
|
$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->RegisterAttributeString('Leistungsangebot', '[0]');
|
|
$this->RegisterAttributeString('Betriebsart', Nachrichtenvertrag::BETRIEBSART_PV);
|
|
$this->RegisterAttributeInteger('LetzteManagerID', 0);
|
|
$this->RegisterAttributeInteger('LetzteVorgabeZeit', 0);
|
|
$this->RegisterAttributeInteger('LetzterGesetzterStrom', -1);
|
|
$this->RegisterAttributeInteger('LetzteGesetzteLeistung', 0);
|
|
$this->RegisterAttributeInteger('LeistungswechselGesperrtBis', 0);
|
|
$this->RegisterAttributeInteger('AusschaltenGesperrtBis', 0);
|
|
$this->RegisterAttributeInteger('EinschaltenGesperrtBis', 0);
|
|
$this->RegisterAttributeInteger('PhasenerkennungSeit', 0);
|
|
$this->RegisterAttributeInteger('PhasenerkennungVersion', 0);
|
|
$this->RegisterAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
|
$this->RegisterAttributeInteger('GeraeteMaximalstrom', 32);
|
|
$this->RegisterAttributeInteger('ErkannterMaximalstrom', 32);
|
|
$this->RegisterAttributeInteger('FahrzeugstromKandidat', 0);
|
|
$this->RegisterAttributeInteger('FahrzeugstromKandidatSeit', 0);
|
|
$this->RegisterAttributeBoolean('RegeluebergangAktiv', false);
|
|
|
|
$this->RegisterTimer(
|
|
'Statuszyklus',
|
|
0,
|
|
"IPS_RequestAction(\$_IPS['TARGET'], 'Statuszyklus', false);"
|
|
);
|
|
$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', false);"
|
|
);
|
|
$this->RegisterTimer(
|
|
'RegeluebergangTimeout',
|
|
0,
|
|
"IPS_RequestAction(\$_IPS['TARGET'], 'RegeluebergangTimeout', false);"
|
|
);
|
|
}
|
|
|
|
public function ApplyChanges(): void
|
|
{
|
|
parent::ApplyChanges();
|
|
|
|
$this->initialisiereLokaleEinstellungen();
|
|
$this->aktualisiereVariablen();
|
|
$this->bereinigeUngueltigenSollwert();
|
|
|
|
// Ein persistiertes Ladeende nach Neueinrichtung oder Modulupdate einmal neu pruefen.
|
|
$this->setzeZustand('FahrzeugGeladen', false);
|
|
$this->WriteAttributeInteger('LetzterGesetzterStrom', -1);
|
|
$this->WriteAttributeInteger('LetzteGesetzteLeistung', 0);
|
|
$this->WriteAttributeInteger('LeistungswechselGesperrtBis', 0);
|
|
$this->WriteAttributeInteger('AusschaltenGesperrtBis', 0);
|
|
$this->WriteAttributeInteger('EinschaltenGesperrtBis', 0);
|
|
$this->WriteAttributeInteger('PhasenerkennungSeit', 0);
|
|
if ($this->ReadAttributeInteger('PhasenerkennungVersion') < 1) {
|
|
$this->WriteAttributeInteger(
|
|
'ZustandPhasenzahl',
|
|
LadestationRegler::PHASEN_UNBEKANNT
|
|
);
|
|
$this->WriteAttributeInteger('PhasenerkennungVersion', 1);
|
|
}
|
|
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
|
$this->WriteAttributeInteger(
|
|
'ErkannterMaximalstrom',
|
|
$this->ReadPropertyInteger('MaximalerLadestrom')
|
|
);
|
|
$this->setzeFahrzeugstromKandidatZurueck();
|
|
$this->beendeRegeluebergang();
|
|
|
|
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('Statuszyklus', $this->ReadPropertyInteger('Abfrageintervall') * 1000);
|
|
$this->SetTimerInterval('Meldezyklus', $this->ReadPropertyInteger('Meldeintervall') * 1000);
|
|
$this->aktualisiere(true);
|
|
}
|
|
|
|
public function RequestAction($ident, $wert): void
|
|
{
|
|
switch ($ident) {
|
|
case 'Aktiv':
|
|
$aktiv = (bool) $wert;
|
|
$this->SetValue('Aktiv', $aktiv);
|
|
if ($aktiv) {
|
|
$this->starteRegeluebergang();
|
|
} else {
|
|
$this->beendeRegeluebergang();
|
|
$this->verwerfeSollwert();
|
|
}
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'Ladefreigabe':
|
|
$ladefreigabe = (bool) $wert;
|
|
$this->WriteAttributeBoolean('ZustandLadefreigabe', $ladefreigabe);
|
|
$this->setzeSichtbareVariable('Ladefreigabe', $ladefreigabe);
|
|
if ($ladefreigabe) {
|
|
$this->starteRegeluebergang();
|
|
} else {
|
|
$this->beendeRegeluebergang();
|
|
$this->verwerfeSollwert();
|
|
}
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'Solarladen':
|
|
$solarladen = (bool) $wert;
|
|
if ($solarladen !== $this->ReadAttributeBoolean('ZustandSolarladen')) {
|
|
$this->starteRegeluebergang();
|
|
}
|
|
$this->WriteAttributeBoolean('ZustandSolarladen', $solarladen);
|
|
$this->setzeSichtbareVariable('Solarladen', $solarladen);
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'Statuszyklus':
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'VorgabeTimeout':
|
|
$this->SetTimerInterval('VorgabeTimeout', 0);
|
|
$this->verwerfeSollwert();
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'RegeluebergangTimeout':
|
|
$this->beendeRegeluebergang();
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'Melden':
|
|
if ((bool) $wert) {
|
|
$this->SetTimerInterval('RueckmeldungVerzoegert', 0);
|
|
}
|
|
$this->sendeVerbraucherdaten();
|
|
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
|
|
{
|
|
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 Ladestation nicht aktiv zugeordnet.');
|
|
}
|
|
|
|
$this->WriteAttributeString('Betriebsart', $daten['Betriebsart']);
|
|
$this->aktualisiere(false, false, false);
|
|
$sollleistung = $daten['Sollleistung_W'];
|
|
if ($sollleistung === null) {
|
|
if ((bool) $this->leseZustand('SollwertGueltig')
|
|
&& !in_array((int) $this->leseZustand('Sollleistung'), $this->leseLeistungsangebot(), true)
|
|
) {
|
|
$this->verwerfeSollwert();
|
|
$this->aktualisiere(false, false);
|
|
}
|
|
$this->SetTimerInterval('RueckmeldungVerzoegert', 100);
|
|
return;
|
|
}
|
|
if (!in_array($sollleistung, $this->leseLeistungsangebot(), true)) {
|
|
throw new InvalidArgumentException(
|
|
'Sollleistung_W liegt nicht im aktuell gemeldeten Leistungsangebot.'
|
|
);
|
|
}
|
|
|
|
$this->beendeRegeluebergang();
|
|
$this->WriteAttributeInteger('LetzteManagerID', $managerID);
|
|
$this->WriteAttributeInteger('LetzteVorgabeZeit', time());
|
|
$this->setzeZustand('Sollleistung', $sollleistung);
|
|
$this->setzeZustand('SollwertGueltig', true);
|
|
$this->SetTimerInterval('VorgabeTimeout', $this->ReadPropertyInteger('VorgabeTimeout') * 1000);
|
|
$this->aktualisiere(false, false);
|
|
$this->SetTimerInterval('RueckmeldungVerzoegert', 100);
|
|
}
|
|
|
|
private function aktualisiere(
|
|
bool $meldungPlanen,
|
|
bool $statusLesen = true,
|
|
bool $steuerungAusfuehren = true
|
|
): void
|
|
{
|
|
try {
|
|
if ($statusLesen) {
|
|
$this->leseGeraetestatus();
|
|
}
|
|
|
|
$aktiv = (bool) $this->GetValue('Aktiv');
|
|
$ladefreigabe = $this->ReadAttributeBoolean('ZustandLadefreigabe');
|
|
$verbunden = $this->ReadAttributeBoolean('ZustandFahrzeugVerbunden');
|
|
$geladen = $this->ReadAttributeBoolean('ZustandFahrzeugGeladen');
|
|
$phasenzahl = $this->ReadAttributeInteger('ZustandPhasenzahl');
|
|
$maximalstrom = min(
|
|
$this->ReadPropertyInteger('MaximalerLadestrom'),
|
|
max(6, $this->ReadAttributeInteger('GeraeteMaximalstrom')),
|
|
max(6, $this->ReadAttributeInteger('ErkannterMaximalstrom'))
|
|
);
|
|
$this->setzeSichtbareVariable('FahrzeugMaximalstrom', $maximalstrom);
|
|
$solarladen = $this->ReadAttributeBoolean('ZustandSolarladen');
|
|
$peakbetrieb = $this->ReadAttributeString('Betriebsart')
|
|
=== Nachrichtenvertrag::BETRIEBSART_PEAK;
|
|
$verfuegbar = LadestationRegler::leistungsangebot(
|
|
$aktiv,
|
|
$ladefreigabe,
|
|
$verbunden,
|
|
$geladen,
|
|
$phasenzahl,
|
|
$maximalstrom,
|
|
$solarladen,
|
|
false
|
|
) !== [0];
|
|
$angebot = LadestationRegler::leistungsangebot(
|
|
$aktiv,
|
|
$ladefreigabe,
|
|
$verbunden,
|
|
$geladen,
|
|
$phasenzahl,
|
|
$maximalstrom,
|
|
$solarladen,
|
|
$peakbetrieb
|
|
);
|
|
if ($aktiv
|
|
&& $ladefreigabe
|
|
&& $verbunden
|
|
&& !$geladen
|
|
&& !($peakbetrieb && $solarladen)
|
|
&& $phasenzahl !== LadestationRegler::PHASEN_UNBEKANNT
|
|
) {
|
|
$jetzt = time();
|
|
$letzteLeistung = $this->ReadAttributeInteger('LetzteGesetzteLeistung');
|
|
if ($jetzt < $this->ReadAttributeInteger('LeistungswechselGesperrtBis')) {
|
|
$angebot = [$letzteLeistung];
|
|
} elseif ($letzteLeistung > 0
|
|
&& $jetzt < $this->ReadAttributeInteger('AusschaltenGesperrtBis')
|
|
) {
|
|
$positiveStufen = array_values(array_filter(
|
|
LadestationRegler::leistungsangebot(
|
|
$aktiv,
|
|
$ladefreigabe,
|
|
$verbunden,
|
|
$geladen,
|
|
$phasenzahl,
|
|
$maximalstrom,
|
|
$solarladen,
|
|
false
|
|
),
|
|
static fn (int $wert): bool => $wert > 0
|
|
));
|
|
$angebot = $positiveStufen !== [] ? $positiveStufen : [$letzteLeistung];
|
|
} elseif ($letzteLeistung <= 0
|
|
&& $jetzt < $this->ReadAttributeInteger('EinschaltenGesperrtBis')
|
|
) {
|
|
$angebot = [0];
|
|
}
|
|
}
|
|
$angebotJson = json_encode($angebot, JSON_THROW_ON_ERROR);
|
|
$this->WriteAttributeString('Leistungsangebot', $angebotJson);
|
|
$this->setzeSichtbareVariable('LeistungsangebotDiagnose', $angebotJson);
|
|
$this->setzeZustand('Verfuegbar', $verfuegbar);
|
|
$this->setzeZustand('AenderungMoeglich', $verfuegbar && count($angebot) > 1);
|
|
|
|
$sollleistung = $this->bestimmeWirksameSollleistung($angebot);
|
|
$phasenerkennungAktiv = $aktiv
|
|
&& $ladefreigabe
|
|
&& $verbunden
|
|
&& !$geladen
|
|
&& $phasenzahl === LadestationRegler::PHASEN_UNBEKANNT;
|
|
$regeluebergangAktiv = $aktiv
|
|
&& $ladefreigabe
|
|
&& $verbunden
|
|
&& !$geladen
|
|
&& $this->ReadAttributeBoolean('RegeluebergangAktiv');
|
|
if ($steuerungAusfuehren && !$phasenerkennungAktiv && !$regeluebergangAktiv) {
|
|
$this->setzeLadestrom($sollleistung);
|
|
}
|
|
$this->setzeStoerung('');
|
|
$this->SetStatus(self::STATUS_AKTIV);
|
|
} catch (Throwable $fehler) {
|
|
$this->setzeZustand('Verfuegbar', false);
|
|
$this->setzeZustand('AenderungMoeglich', false);
|
|
$this->setzeStoerung($fehler->getMessage());
|
|
$this->SetStatus(self::STATUS_KOMMUNIKATIONSFEHLER);
|
|
$this->protokolliere('Geraetekommunikation', $fehler->getMessage());
|
|
}
|
|
|
|
if ($meldungPlanen) {
|
|
$this->SetTimerInterval('RueckmeldungVerzoegert', 100);
|
|
}
|
|
}
|
|
|
|
private function leseGeraetestatus(): void
|
|
{
|
|
$transport = new LadestationTransport(
|
|
fn (array $anfrage): string => $this->fuehreHttpAnfrageAus($anfrage)
|
|
);
|
|
$antwort = $transport->statusLesen(
|
|
$this->ReadPropertyInteger('Geraetetyp'),
|
|
$this->geraetekonfiguration()
|
|
);
|
|
$rohstatus = LadestationAdapter::dekodiereStatus(
|
|
$this->ReadPropertyInteger('Geraetetyp'),
|
|
$antwort
|
|
);
|
|
if (isset($rohstatus['Sollstrom_A'])) {
|
|
$geraeteSollstrom = (int) $rohstatus['Sollstrom_A'];
|
|
if ($geraeteSollstrom === 0 || ($geraeteSollstrom >= 6 && $geraeteSollstrom <= 32)) {
|
|
$this->WriteAttributeInteger('LetzterGesetzterStrom', $geraeteSollstrom);
|
|
}
|
|
}
|
|
if (isset($rohstatus['Maximalstrom_A'])) {
|
|
$this->WriteAttributeInteger('GeraeteMaximalstrom', (int) $rohstatus['Maximalstrom_A']);
|
|
}
|
|
|
|
$warVerbunden = $this->ReadAttributeBoolean('ZustandFahrzeugVerbunden');
|
|
$verbunden = $rohstatus['Rohstatus'] !== 1;
|
|
$bekanntePhasenzahl = $warVerbunden
|
|
? $this->ReadAttributeInteger('ZustandPhasenzahl')
|
|
: LadestationRegler::PHASEN_UNBEKANNT;
|
|
|
|
if (!$verbunden) {
|
|
$bekanntePhasenzahl = LadestationRegler::PHASEN_UNBEKANNT;
|
|
$this->WriteAttributeInteger('PhasenerkennungSeit', 0);
|
|
$this->WriteAttributeInteger('LeistungswechselGesperrtBis', 0);
|
|
$this->WriteAttributeInteger('AusschaltenGesperrtBis', 0);
|
|
$this->WriteAttributeInteger('EinschaltenGesperrtBis', 0);
|
|
} elseif ($bekanntePhasenzahl === LadestationRegler::PHASEN_UNBEKANNT) {
|
|
$direktErkanntePhasen = (int) ($rohstatus['Phasenzahl']
|
|
?? LadestationRegler::PHASEN_UNBEKANNT);
|
|
if (in_array(
|
|
$direktErkanntePhasen,
|
|
[LadestationRegler::PHASEN_EIN, LadestationRegler::PHASEN_DREI],
|
|
true
|
|
)) {
|
|
$bekanntePhasenzahl = $direktErkanntePhasen;
|
|
$this->WriteAttributeInteger('PhasenerkennungSeit', 0);
|
|
} elseif ($rohstatus['Istleistung_W'] > 7500.0) {
|
|
$bekanntePhasenzahl = LadestationRegler::PHASEN_DREI;
|
|
$this->WriteAttributeInteger('PhasenerkennungSeit', 0);
|
|
} elseif ((bool) $this->GetValue('Aktiv')
|
|
&& $this->ReadAttributeBoolean('ZustandLadefreigabe')
|
|
) {
|
|
$erkennungSeit = $this->ReadAttributeInteger('PhasenerkennungSeit');
|
|
if ($erkennungSeit <= 0) {
|
|
$this->sendeLadestromAnGeraet(self::PHASENERKENNUNGSSTROM);
|
|
$erkennungSeit = time();
|
|
$this->WriteAttributeInteger('PhasenerkennungSeit', $erkennungSeit);
|
|
} elseif ((time() - $erkennungSeit)
|
|
>= $this->ReadPropertyInteger('Phasenerkennungszeit')
|
|
) {
|
|
$bekanntePhasenzahl = LadestationRegler::erkennePhasenzahlAusLeistung(
|
|
(float) $rohstatus['Istleistung_W'],
|
|
$this->ReadAttributeInteger('LetzterGesetzterStrom')
|
|
);
|
|
$this->WriteAttributeInteger('PhasenerkennungSeit', 0);
|
|
}
|
|
} else {
|
|
$this->WriteAttributeInteger('PhasenerkennungSeit', 0);
|
|
}
|
|
}
|
|
|
|
$status = LadestationRegler::normalisiereStatus(
|
|
$rohstatus,
|
|
$warVerbunden,
|
|
$this->ReadAttributeBoolean('ZustandFahrzeugGeladen'),
|
|
$this->ReadAttributeInteger('LetzterGesetzterStrom') >= 6,
|
|
$this->ReadAttributeBoolean('LadeleistungSeitFreigabeErkannt'),
|
|
$bekanntePhasenzahl
|
|
);
|
|
if (!$status['FahrzeugVerbunden']) {
|
|
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
|
} elseif ($status['Ladestrom_A'] >= 3.5) {
|
|
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', true);
|
|
}
|
|
if (!$status['FahrzeugVerbunden']
|
|
|| $status['Phasenzahl'] !== LadestationRegler::PHASEN_UNBEKANNT
|
|
) {
|
|
$this->aktualisiereFahrzeugstromgrenze($status);
|
|
}
|
|
|
|
foreach ($status as $ident => $wert) {
|
|
$this->setzeZustand($ident, $wert);
|
|
}
|
|
}
|
|
|
|
/** @param array<string, int|float|bool> $status */
|
|
private function aktualisiereFahrzeugstromgrenze(array $status): void
|
|
{
|
|
$konfigurierterMaximalstrom = $this->ReadPropertyInteger('MaximalerLadestrom');
|
|
if (!$status['FahrzeugVerbunden']) {
|
|
$this->WriteAttributeInteger('ErkannterMaximalstrom', $konfigurierterMaximalstrom);
|
|
$this->setzeSichtbareVariable('FahrzeugMaximalstrom', $konfigurierterMaximalstrom);
|
|
$this->setzeFahrzeugstromKandidatZurueck();
|
|
return;
|
|
}
|
|
|
|
$kandidat = LadestationRegler::ermittleFahrzeugMaximalstrom(
|
|
$this->ReadAttributeInteger('LetzterGesetzterStrom'),
|
|
(float) $status['Ladestrom_A'],
|
|
$konfigurierterMaximalstrom
|
|
);
|
|
if ($kandidat === null
|
|
|| $kandidat >= $this->ReadAttributeInteger('ErkannterMaximalstrom')
|
|
) {
|
|
$this->setzeFahrzeugstromKandidatZurueck();
|
|
return;
|
|
}
|
|
|
|
$jetzt = time();
|
|
if ($this->ReadAttributeInteger('FahrzeugstromKandidat') !== $kandidat) {
|
|
$this->WriteAttributeInteger('FahrzeugstromKandidat', $kandidat);
|
|
$this->WriteAttributeInteger('FahrzeugstromKandidatSeit', $jetzt);
|
|
return;
|
|
}
|
|
if (($jetzt - $this->ReadAttributeInteger('FahrzeugstromKandidatSeit'))
|
|
< $this->ReadPropertyInteger('FahrzeugstromErkennungszeit')
|
|
) {
|
|
return;
|
|
}
|
|
|
|
$this->WriteAttributeInteger('ErkannterMaximalstrom', $kandidat);
|
|
$this->WriteAttributeInteger('LeistungswechselGesperrtBis', 0);
|
|
$this->setzeSichtbareVariable('FahrzeugMaximalstrom', $kandidat);
|
|
$this->setzeFahrzeugstromKandidatZurueck();
|
|
}
|
|
|
|
private function setzeFahrzeugstromKandidatZurueck(): void
|
|
{
|
|
$this->WriteAttributeInteger('FahrzeugstromKandidat', 0);
|
|
$this->WriteAttributeInteger('FahrzeugstromKandidatSeit', 0);
|
|
}
|
|
|
|
/** @param list<int> $angebot */
|
|
private function bestimmeWirksameSollleistung(array $angebot): int
|
|
{
|
|
if ((bool) $this->leseZustand('SollwertGueltig')) {
|
|
$sollleistung = (int) $this->leseZustand('Sollleistung');
|
|
if (in_array($sollleistung, $angebot, true)) {
|
|
return $sollleistung;
|
|
}
|
|
$this->verwerfeSollwert();
|
|
$ersatz = 0;
|
|
foreach ($angebot as $leistungswert) {
|
|
if ($leistungswert > $sollleistung) {
|
|
break;
|
|
}
|
|
$ersatz = $leistungswert;
|
|
}
|
|
return $ersatz;
|
|
}
|
|
|
|
if (!$this->ReadAttributeBoolean('ZustandSolarladen') && $angebot !== [0]) {
|
|
return $angebot[count($angebot) - 1];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private function setzeLadestrom(int $leistung): void
|
|
{
|
|
$phasenzahl = $this->ReadAttributeInteger('ZustandPhasenzahl');
|
|
$strom = $leistung === 0
|
|
? 0
|
|
: LadestationRegler::stromFuerLeistung($leistung, $phasenzahl);
|
|
$bisherigeLeistung = $this->ReadAttributeInteger('LetzteGesetzteLeistung');
|
|
$stromUnveraendert = $strom === $this->ReadAttributeInteger('LetzterGesetzterStrom');
|
|
|
|
if ($stromUnveraendert && $leistung === $bisherigeLeistung) {
|
|
return;
|
|
}
|
|
if (!$stromUnveraendert) {
|
|
$this->sendeLadestromAnGeraet($strom);
|
|
}
|
|
if ($leistung === $bisherigeLeistung) {
|
|
return;
|
|
}
|
|
|
|
if (!$this->ReadAttributeBoolean('ZustandFahrzeugVerbunden')) {
|
|
$leistungswechselGesperrtBis = 0;
|
|
$this->WriteAttributeInteger('AusschaltenGesperrtBis', 0);
|
|
$this->WriteAttributeInteger('EinschaltenGesperrtBis', 0);
|
|
} else {
|
|
$jetzt = time();
|
|
$wechselzeit = $this->ReadPropertyInteger('ZeitZwischenZustandswechseln');
|
|
$leistungswechselGesperrtBis = $wechselzeit > 0
|
|
? $jetzt + ($wechselzeit * 60)
|
|
: 0;
|
|
|
|
if ($bisherigeLeistung <= 0 && $leistung > 0) {
|
|
$mindestzeit = $this->ReadPropertyInteger('Mindesteinschaltdauer');
|
|
$this->WriteAttributeInteger(
|
|
'AusschaltenGesperrtBis',
|
|
$mindestzeit > 0 ? $jetzt + ($mindestzeit * 60) : 0
|
|
);
|
|
$this->WriteAttributeInteger('EinschaltenGesperrtBis', 0);
|
|
} elseif ($bisherigeLeistung > 0 && $leistung <= 0) {
|
|
$mindestzeit = $this->ReadPropertyInteger('Mindestausschaltdauer');
|
|
$this->WriteAttributeInteger(
|
|
'EinschaltenGesperrtBis',
|
|
$mindestzeit > 0 ? $jetzt + ($mindestzeit * 60) : 0
|
|
);
|
|
$this->WriteAttributeInteger('AusschaltenGesperrtBis', 0);
|
|
}
|
|
}
|
|
|
|
$this->WriteAttributeInteger('LetzteGesetzteLeistung', $leistung);
|
|
$this->WriteAttributeInteger(
|
|
'LeistungswechselGesperrtBis',
|
|
$leistungswechselGesperrtBis
|
|
);
|
|
}
|
|
|
|
private function sendeLadestromAnGeraet(int $strom): void
|
|
{
|
|
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
|
$this->setzeFahrzeugstromKandidatZurueck();
|
|
|
|
$transport = new LadestationTransport(
|
|
fn (array $anfrage): string => $this->fuehreHttpAnfrageAus($anfrage, true)
|
|
);
|
|
$transport->ladestromSetzen(
|
|
$this->ReadPropertyInteger('Geraetetyp'),
|
|
$this->geraetekonfiguration(),
|
|
$strom
|
|
);
|
|
$this->WriteAttributeInteger('LetzterGesetzterStrom', $strom);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $anfrage
|
|
*/
|
|
private function fuehreHttpAnfrageAus(
|
|
array $anfrage,
|
|
bool $alsGeraetebefehl = false
|
|
): string
|
|
{
|
|
if ($alsGeraetebefehl) {
|
|
$protokoll = [
|
|
'Methode' => $anfrage['Methode'],
|
|
'URL' => $anfrage['URL'],
|
|
];
|
|
$this->setzeSichtbareVariable(
|
|
'LetzterGeraetebefehl',
|
|
json_encode($protokoll, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR)
|
|
);
|
|
}
|
|
|
|
if ($this->ReadPropertyBoolean('Testmodus')) {
|
|
$url = (string) $anfrage['URL'];
|
|
if (str_ends_with($url, '/api/status')
|
|
|| str_ends_with($url, '/mqtt?payload=')
|
|
|| str_contains($url, '/pico/charging/')
|
|
) {
|
|
return $this->ReadPropertyString('Testantwort');
|
|
}
|
|
return '{}';
|
|
}
|
|
|
|
$curl = curl_init();
|
|
if ($curl === false) {
|
|
throw new RuntimeException('HTTP-Anfrage konnte nicht initialisiert werden.');
|
|
}
|
|
|
|
curl_setopt($curl, CURLOPT_URL, (string) $anfrage['URL']);
|
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
|
|
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
|
|
if ($anfrage['Methode'] === 'POST') {
|
|
curl_setopt($curl, CURLOPT_POST, true);
|
|
}
|
|
if (isset($anfrage['Benutzername'], $anfrage['Passwort'])) {
|
|
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
|
curl_setopt(
|
|
$curl,
|
|
CURLOPT_USERPWD,
|
|
(string) $anfrage['Benutzername'] . ':' . (string) $anfrage['Passwort']
|
|
);
|
|
}
|
|
|
|
$antwort = curl_exec($curl);
|
|
if ($antwort === false) {
|
|
$fehler = curl_error($curl);
|
|
curl_close($curl);
|
|
throw new RuntimeException('HTTP-Anfrage fehlgeschlagen: ' . $fehler);
|
|
}
|
|
$status = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
curl_close($curl);
|
|
if ($status >= 400) {
|
|
throw new RuntimeException('Geraet antwortet mit HTTP-Status ' . $status . '.');
|
|
}
|
|
|
|
return (string) $antwort;
|
|
}
|
|
|
|
/** @return array<string, string> */
|
|
private function geraetekonfiguration(): array
|
|
{
|
|
return [
|
|
'Geraeteadresse' => $this->ReadPropertyString('Geraeteadresse'),
|
|
'GeraeteID' => $this->ReadPropertyString('GeraeteID'),
|
|
'Seriennummer' => $this->ReadPropertyString('Seriennummer'),
|
|
'Benutzername' => $this->ReadPropertyString('Benutzername'),
|
|
'Passwort' => $this->ReadPropertyString('Passwort'),
|
|
];
|
|
}
|
|
|
|
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
|
|
{
|
|
$stoerung = (bool) $this->leseZustand('Stoerung');
|
|
|
|
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' => (float) $this->leseZustand('Istleistung'),
|
|
'Leistungsquelle' => Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN,
|
|
'Zustand' => [
|
|
[
|
|
'Kennung' => 'Sollleistung_W',
|
|
'Art' => 'Sollwert',
|
|
'Wert' => (bool) $this->leseZustand('SollwertGueltig')
|
|
? (int) $this->leseZustand('Sollleistung')
|
|
: null,
|
|
'Einheit' => 'W',
|
|
],
|
|
[
|
|
'Kennung' => 'FahrzeugVerbunden',
|
|
'Art' => 'Status',
|
|
'Wert' => (bool) $this->leseZustand('FahrzeugVerbunden'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'FahrzeugGeladen',
|
|
'Art' => 'Status',
|
|
'Wert' => (bool) $this->leseZustand('FahrzeugGeladen'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'Fahrzeugstatus',
|
|
'Art' => 'Status',
|
|
'Wert' => (int) $this->leseZustand('Fahrzeugstatus'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'Phasenzahl',
|
|
'Art' => 'Status',
|
|
'Wert' => (int) $this->leseZustand('Phasenzahl'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'Ladestrom_A',
|
|
'Art' => 'Istwert',
|
|
'Wert' => (float) $this->leseZustand('Ladestrom'),
|
|
'Einheit' => 'A',
|
|
],
|
|
[
|
|
'Kennung' => 'Ladefreigabe',
|
|
'Art' => 'Status',
|
|
'Wert' => $this->ReadAttributeBoolean('ZustandLadefreigabe'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'Solarladen',
|
|
'Art' => 'Status',
|
|
'Wert' => $this->ReadAttributeBoolean('ZustandSolarladen'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'Ladefehler',
|
|
'Art' => 'Stoerung',
|
|
'Wert' => $stoerung,
|
|
'Einheit' => '',
|
|
'Text' => (string) $this->leseZustand('Stoertext'),
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/** @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 starteRegeluebergang(): void
|
|
{
|
|
if ($this->zugeordneteManagerIDs() === []) {
|
|
$this->beendeRegeluebergang();
|
|
return;
|
|
}
|
|
|
|
$this->WriteAttributeBoolean('RegeluebergangAktiv', true);
|
|
$this->SetTimerInterval('RegeluebergangTimeout', self::REGELUEBERGANG_TIMEOUT_MS);
|
|
}
|
|
|
|
private function beendeRegeluebergang(): void
|
|
{
|
|
$this->WriteAttributeBoolean('RegeluebergangAktiv', false);
|
|
$this->SetTimerInterval('RegeluebergangTimeout', 0);
|
|
}
|
|
|
|
private function pruefeKonfiguration(): void
|
|
{
|
|
foreach (['PrioritaetPV', 'PrioritaetPeak'] as $property) {
|
|
if ($this->ReadPropertyInteger($property) < 0) {
|
|
throw new InvalidArgumentException($property . ' muss mindestens 0 sein.');
|
|
}
|
|
}
|
|
foreach ([
|
|
'Meldeintervall',
|
|
'VorgabeTimeout',
|
|
'Abfrageintervall',
|
|
'Phasenerkennungszeit',
|
|
'FahrzeugstromErkennungszeit',
|
|
] as $property) {
|
|
if ($this->ReadPropertyInteger($property) <= 0) {
|
|
throw new InvalidArgumentException($property . ' muss groesser als 0 sein.');
|
|
}
|
|
}
|
|
foreach ([
|
|
'ZeitZwischenZustandswechseln',
|
|
'Mindesteinschaltdauer',
|
|
'Mindestausschaltdauer',
|
|
] as $property) {
|
|
if ($this->ReadPropertyInteger($property) < 0) {
|
|
throw new InvalidArgumentException($property . ' darf nicht negativ sein.');
|
|
}
|
|
}
|
|
|
|
$maximalstrom = $this->ReadPropertyInteger('MaximalerLadestrom');
|
|
if ($maximalstrom < 6 || $maximalstrom > 32) {
|
|
throw new InvalidArgumentException('MaximalerLadestrom muss zwischen 6 und 32 A liegen.');
|
|
}
|
|
|
|
$geraetetyp = $this->ReadPropertyInteger('Geraetetyp');
|
|
$konfiguration = $this->geraetekonfiguration();
|
|
LadestationAdapter::statusAnfrage($geraetetyp, $konfiguration);
|
|
LadestationAdapter::steuerAnfragen($geraetetyp, $konfiguration, 0);
|
|
}
|
|
|
|
private function initialisiereLokaleEinstellungen(): void
|
|
{
|
|
$initialisiert = $this->ReadAttributeBoolean('InitialwerteGesetzt');
|
|
$ladefreigabe = $this->ReadPropertyBoolean('Ladefreigabe');
|
|
$solarladen = $this->ReadPropertyBoolean('Solarladen');
|
|
|
|
if (!$initialisiert
|
|
|| $ladefreigabe !== $this->ReadAttributeBoolean('KonfigurationLadefreigabe')
|
|
) {
|
|
$this->WriteAttributeBoolean('ZustandLadefreigabe', $ladefreigabe);
|
|
}
|
|
if (!$initialisiert
|
|
|| $solarladen !== $this->ReadAttributeBoolean('KonfigurationSolarladen')
|
|
) {
|
|
$this->WriteAttributeBoolean('ZustandSolarladen', $solarladen);
|
|
}
|
|
|
|
$this->WriteAttributeBoolean('KonfigurationLadefreigabe', $ladefreigabe);
|
|
$this->WriteAttributeBoolean('KonfigurationSolarladen', $solarladen);
|
|
$this->WriteAttributeBoolean('InitialwerteGesetzt', true);
|
|
}
|
|
|
|
private function aktualisiereVariablen(): void
|
|
{
|
|
if ($this->ReadPropertyBoolean('DiagnosevariablenAnzeigen')) {
|
|
$this->registriereVerbraucherDiagnose();
|
|
$this->RegisterVariableInteger('Fahrzeugstatus', 'Fahrzeugstatus', '', 200);
|
|
$this->RegisterVariableString('LetzterGeraetebefehl', 'Letzter Geraetebefehl', '', 210);
|
|
$this->RegisterVariableString('LeistungsangebotDiagnose', 'Leistungsangebot', '', 220);
|
|
$this->RegisterVariableInteger(
|
|
'FahrzeugMaximalstrom',
|
|
'Erkannter Fahrzeug-Maximalstrom',
|
|
'',
|
|
230
|
|
);
|
|
$this->SetValue(
|
|
'FahrzeugMaximalstrom',
|
|
$this->ReadAttributeInteger('ErkannterMaximalstrom')
|
|
);
|
|
$this->SetValue(
|
|
'LeistungsangebotDiagnose',
|
|
$this->ReadAttributeString('Leistungsangebot')
|
|
);
|
|
foreach (self::DIAGNOSE_VARIABLEN as $ident) {
|
|
if (!in_array($ident, [
|
|
'LetzterGeraetebefehl',
|
|
'LeistungsangebotDiagnose',
|
|
'FahrzeugMaximalstrom',
|
|
], true)) {
|
|
$this->setzeSichtbareVariable($ident, $this->leseZustand($ident));
|
|
}
|
|
}
|
|
} else {
|
|
foreach (self::DIAGNOSE_VARIABLEN as $ident) {
|
|
$this->entferneVariable($ident);
|
|
}
|
|
}
|
|
|
|
if ($this->ReadPropertyBoolean('EinstellungenInVisu')) {
|
|
$this->RegisterVariableBoolean('Ladefreigabe', 'Ladefreigabe', '~Switch', 150);
|
|
$this->RegisterVariableBoolean('Solarladen', 'Solarladen', '~Switch', 160);
|
|
$this->RegisterVariableFloat('Istleistung', 'Istleistung', '', 170);
|
|
$this->RegisterVariableInteger('Sollleistung', 'Sollleistung', '', 180);
|
|
$this->EnableAction('Ladefreigabe');
|
|
$this->EnableAction('Solarladen');
|
|
$this->SetValue('Ladefreigabe', $this->ReadAttributeBoolean('ZustandLadefreigabe'));
|
|
$this->SetValue('Solarladen', $this->ReadAttributeBoolean('ZustandSolarladen'));
|
|
$this->SetValue('Istleistung', $this->ReadAttributeFloat('ZustandIstleistung'));
|
|
$this->SetValue('Sollleistung', $this->ReadAttributeInteger('ZustandSollleistung'));
|
|
} else {
|
|
$this->entferneVariable('Ladefreigabe');
|
|
$this->entferneVariable('Solarladen');
|
|
$this->entferneVariable('Istleistung');
|
|
$this->entferneVariable('Sollleistung');
|
|
}
|
|
}
|
|
|
|
private function entferneVariable(string $ident): void
|
|
{
|
|
$id = @$this->GetIDForIdent($ident);
|
|
if (is_int($id) && $id > 0 && IPS_VariableExists($id)) {
|
|
$this->UnregisterVariable($ident);
|
|
}
|
|
}
|
|
|
|
/** @param mixed $wert */
|
|
private function setzeZustand(string $ident, $wert): void
|
|
{
|
|
switch ($ident) {
|
|
case 'FahrzeugVerbunden':
|
|
case 'FahrzeugGeladen':
|
|
case 'SollwertGueltig':
|
|
case 'Verfuegbar':
|
|
case 'AenderungMoeglich':
|
|
case 'Stoerung':
|
|
$this->WriteAttributeBoolean('Zustand' . $ident, (bool) $wert);
|
|
break;
|
|
case 'Fahrzeugstatus':
|
|
case 'Phasenzahl':
|
|
case 'Sollleistung':
|
|
$this->WriteAttributeInteger('Zustand' . $ident, (int) $wert);
|
|
break;
|
|
case 'Ladestrom_A':
|
|
$ident = 'Ladestrom';
|
|
$this->WriteAttributeFloat('ZustandLadestrom', (float) $wert);
|
|
break;
|
|
case 'Istleistung_W':
|
|
$ident = 'Istleistung';
|
|
$this->WriteAttributeFloat('ZustandIstleistung', (float) $wert);
|
|
break;
|
|
case 'Stoertext':
|
|
$this->WriteAttributeString('ZustandStoertext', (string) $wert);
|
|
break;
|
|
default:
|
|
throw new LogicException('Unbekannter Zustand: ' . $ident);
|
|
}
|
|
$this->setzeSichtbareVariable($ident, $wert);
|
|
if ($ident === 'Istleistung') {
|
|
$this->setzeSichtbareVariable(
|
|
'Leistungsquelle',
|
|
Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN
|
|
);
|
|
}
|
|
}
|
|
|
|
/** @return mixed */
|
|
private function leseZustand(string $ident)
|
|
{
|
|
if ($ident === 'Ladefreigabe' || $ident === 'Solarladen') {
|
|
return $this->ReadAttributeBoolean('Zustand' . $ident);
|
|
}
|
|
if (in_array($ident, [
|
|
'FahrzeugVerbunden', 'FahrzeugGeladen', 'SollwertGueltig',
|
|
'Verfuegbar', 'AenderungMoeglich', 'Stoerung',
|
|
], true)) {
|
|
return $this->ReadAttributeBoolean('Zustand' . $ident);
|
|
}
|
|
if (in_array($ident, ['Fahrzeugstatus', 'Phasenzahl', 'Sollleistung'], true)) {
|
|
return $this->ReadAttributeInteger('Zustand' . $ident);
|
|
}
|
|
if ($ident === 'Ladestrom' || $ident === 'Istleistung') {
|
|
return $this->ReadAttributeFloat('Zustand' . $ident);
|
|
}
|
|
if ($ident === 'Stoertext') {
|
|
return $this->ReadAttributeString('ZustandStoertext');
|
|
}
|
|
if ($ident === 'Leistungsquelle') {
|
|
return Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN;
|
|
}
|
|
throw new LogicException('Unbekannter Zustand: ' . $ident);
|
|
}
|
|
|
|
/** @param mixed $wert */
|
|
private function setzeSichtbareVariable(string $ident, $wert): void
|
|
{
|
|
$id = @$this->GetIDForIdent($ident);
|
|
if (is_int($id) && $id > 0 && IPS_VariableExists($id)) {
|
|
$this->SetValue($ident, $wert);
|
|
}
|
|
}
|
|
|
|
/** @return list<int> */
|
|
private function leseLeistungsangebot(): array
|
|
{
|
|
$angebot = json_decode(
|
|
$this->ReadAttributeString('Leistungsangebot'),
|
|
true,
|
|
512,
|
|
JSON_THROW_ON_ERROR
|
|
);
|
|
|
|
return is_array($angebot) ? array_map('intval', $angebot) : [0];
|
|
}
|
|
|
|
private function setzeStoerung(string $text): void
|
|
{
|
|
$this->setzeZustand('Stoerung', $text !== '');
|
|
$this->setzeZustand('Stoertext', $text);
|
|
}
|
|
|
|
private function deaktiviereTimer(): void
|
|
{
|
|
foreach ([
|
|
'Statuszyklus',
|
|
'Meldezyklus',
|
|
'RueckmeldungVerzoegert',
|
|
'VorgabeTimeout',
|
|
'RegeluebergangTimeout',
|
|
] as $timer) {
|
|
$this->SetTimerInterval($timer, 0);
|
|
}
|
|
}
|
|
|
|
private function protokolliere(string $bereich, string $nachricht): void
|
|
{
|
|
if (!$this->ReadPropertyBoolean('LoggingEin')) {
|
|
return;
|
|
}
|
|
$this->SendDebug($bereich, $nachricht, 0);
|
|
}
|
|
}
|