feat(ladestation): Phasenerkennung und Schaltsperren erweitern
Tests / test (push) Successful in 45s
Tests / test (push) Successful in 45s
This commit is contained in:
@@ -38,12 +38,40 @@
|
|||||||
"suffix": " s",
|
"suffix": " s",
|
||||||
"minimum": 1
|
"minimum": 1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "NumberSpinner",
|
||||||
|
"name": "Phasenerkennungszeit",
|
||||||
|
"caption": "Phasen erkennen nach",
|
||||||
|
"suffix": " s",
|
||||||
|
"minimum": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "NumberSpinner",
|
"type": "NumberSpinner",
|
||||||
"name": "FahrzeugstromErkennungszeit",
|
"name": "FahrzeugstromErkennungszeit",
|
||||||
"caption": "Fahrzeug-Maximalstrom erkennen nach",
|
"caption": "Fahrzeug-Maximalstrom erkennen nach",
|
||||||
"suffix": " s",
|
"suffix": " s",
|
||||||
"minimum": 30
|
"minimum": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "NumberSpinner",
|
||||||
|
"name": "ZeitZwischenZustandswechseln",
|
||||||
|
"caption": "Zeit zwischen Leistungsaenderungen",
|
||||||
|
"suffix": " min",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "NumberSpinner",
|
||||||
|
"name": "Mindesteinschaltdauer",
|
||||||
|
"caption": "Mindesteinschaltdauer",
|
||||||
|
"suffix": " min",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "NumberSpinner",
|
||||||
|
"name": "Mindestausschaltdauer",
|
||||||
|
"caption": "Mindestausschaltdauer",
|
||||||
|
"suffix": " min",
|
||||||
|
"minimum": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -51,7 +51,11 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
$this->RegisterPropertyString('Benutzername', '');
|
$this->RegisterPropertyString('Benutzername', '');
|
||||||
$this->RegisterPropertyString('Passwort', '');
|
$this->RegisterPropertyString('Passwort', '');
|
||||||
$this->RegisterPropertyInteger('MaximalerLadestrom', 16);
|
$this->RegisterPropertyInteger('MaximalerLadestrom', 16);
|
||||||
|
$this->RegisterPropertyInteger('Phasenerkennungszeit', 60);
|
||||||
$this->RegisterPropertyInteger('FahrzeugstromErkennungszeit', 90);
|
$this->RegisterPropertyInteger('FahrzeugstromErkennungszeit', 90);
|
||||||
|
$this->RegisterPropertyInteger('ZeitZwischenZustandswechseln', 1);
|
||||||
|
$this->RegisterPropertyInteger('Mindesteinschaltdauer', 0);
|
||||||
|
$this->RegisterPropertyInteger('Mindestausschaltdauer', 0);
|
||||||
$this->RegisterPropertyInteger('Abfrageintervall', 5);
|
$this->RegisterPropertyInteger('Abfrageintervall', 5);
|
||||||
$this->RegisterPropertyBoolean('Ladefreigabe', true);
|
$this->RegisterPropertyBoolean('Ladefreigabe', true);
|
||||||
$this->RegisterPropertyBoolean('Solarladen', true);
|
$this->RegisterPropertyBoolean('Solarladen', true);
|
||||||
@@ -89,6 +93,12 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
$this->RegisterAttributeInteger('LetzteManagerID', 0);
|
$this->RegisterAttributeInteger('LetzteManagerID', 0);
|
||||||
$this->RegisterAttributeInteger('LetzteVorgabeZeit', 0);
|
$this->RegisterAttributeInteger('LetzteVorgabeZeit', 0);
|
||||||
$this->RegisterAttributeInteger('LetzterGesetzterStrom', -1);
|
$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->RegisterAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
||||||
$this->RegisterAttributeInteger('ErkannterMaximalstrom', 32);
|
$this->RegisterAttributeInteger('ErkannterMaximalstrom', 32);
|
||||||
$this->RegisterAttributeInteger('FahrzeugstromKandidat', 0);
|
$this->RegisterAttributeInteger('FahrzeugstromKandidat', 0);
|
||||||
@@ -126,6 +136,18 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
// Ein persistiertes Ladeende nach Neueinrichtung oder Modulupdate einmal neu pruefen.
|
// Ein persistiertes Ladeende nach Neueinrichtung oder Modulupdate einmal neu pruefen.
|
||||||
$this->setzeZustand('FahrzeugGeladen', false);
|
$this->setzeZustand('FahrzeugGeladen', false);
|
||||||
$this->WriteAttributeInteger('LetzterGesetzterStrom', -1);
|
$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->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
||||||
$this->WriteAttributeInteger(
|
$this->WriteAttributeInteger(
|
||||||
'ErkannterMaximalstrom',
|
'ErkannterMaximalstrom',
|
||||||
@@ -283,6 +305,40 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
$solarladen,
|
$solarladen,
|
||||||
$peakbetrieb
|
$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);
|
$angebotJson = json_encode($angebot, JSON_THROW_ON_ERROR);
|
||||||
$this->WriteAttributeString('Leistungsangebot', $angebotJson);
|
$this->WriteAttributeString('Leistungsangebot', $angebotJson);
|
||||||
$this->setzeSichtbareVariable('LeistungsangebotDiagnose', $angebotJson);
|
$this->setzeSichtbareVariable('LeistungsangebotDiagnose', $angebotJson);
|
||||||
@@ -290,7 +346,14 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
$this->setzeZustand('AenderungMoeglich', $verfuegbar && count($angebot) > 1);
|
$this->setzeZustand('AenderungMoeglich', $verfuegbar && count($angebot) > 1);
|
||||||
|
|
||||||
$sollleistung = $this->bestimmeWirksameSollleistung($angebot);
|
$sollleistung = $this->bestimmeWirksameSollleistung($angebot);
|
||||||
$this->setzeLadestrom($sollleistung);
|
if (!($aktiv
|
||||||
|
&& $ladefreigabe
|
||||||
|
&& $verbunden
|
||||||
|
&& !$geladen
|
||||||
|
&& $phasenzahl === LadestationRegler::PHASEN_UNBEKANNT
|
||||||
|
)) {
|
||||||
|
$this->setzeLadestrom($sollleistung);
|
||||||
|
}
|
||||||
$this->setzeStoerung('');
|
$this->setzeStoerung('');
|
||||||
$this->SetStatus(self::STATUS_AKTIV);
|
$this->SetStatus(self::STATUS_AKTIV);
|
||||||
} catch (Throwable $fehler) {
|
} catch (Throwable $fehler) {
|
||||||
@@ -319,19 +382,73 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
$this->ReadPropertyInteger('Geraetetyp'),
|
$this->ReadPropertyInteger('Geraetetyp'),
|
||||||
$antwort
|
$antwort
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$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(
|
||||||
|
$this->ReadPropertyInteger('MaximalerLadestrom')
|
||||||
|
);
|
||||||
|
$erkennungSeit = time();
|
||||||
|
$this->WriteAttributeInteger('PhasenerkennungSeit', $erkennungSeit);
|
||||||
|
} elseif ((time() - $erkennungSeit)
|
||||||
|
>= $this->ReadPropertyInteger('Phasenerkennungszeit')
|
||||||
|
) {
|
||||||
|
$bekanntePhasenzahl = $rohstatus['Istleistung_W'] > 7500.0
|
||||||
|
? LadestationRegler::PHASEN_DREI
|
||||||
|
: LadestationRegler::PHASEN_EIN;
|
||||||
|
$this->WriteAttributeInteger('PhasenerkennungSeit', 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->WriteAttributeInteger('PhasenerkennungSeit', 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$status = LadestationRegler::normalisiereStatus(
|
$status = LadestationRegler::normalisiereStatus(
|
||||||
$rohstatus,
|
$rohstatus,
|
||||||
$this->ReadAttributeBoolean('ZustandFahrzeugVerbunden'),
|
$warVerbunden,
|
||||||
$this->ReadAttributeBoolean('ZustandFahrzeugGeladen'),
|
$this->ReadAttributeBoolean('ZustandFahrzeugGeladen'),
|
||||||
$this->ReadAttributeInteger('LetzterGesetzterStrom') >= 6,
|
$this->ReadAttributeInteger('LetzterGesetzterStrom') >= 6,
|
||||||
$this->ReadAttributeBoolean('LadeleistungSeitFreigabeErkannt')
|
$this->ReadAttributeBoolean('LadeleistungSeitFreigabeErkannt'),
|
||||||
|
$bekanntePhasenzahl
|
||||||
);
|
);
|
||||||
if (!$status['FahrzeugVerbunden']) {
|
if (!$status['FahrzeugVerbunden']) {
|
||||||
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
||||||
} elseif ($status['Ladestrom_A'] >= 3.5) {
|
} elseif ($status['Ladestrom_A'] >= 3.5) {
|
||||||
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', true);
|
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', true);
|
||||||
}
|
}
|
||||||
$this->aktualisiereFahrzeugstromgrenze($status);
|
if (!$status['FahrzeugVerbunden']
|
||||||
|
|| $status['Phasenzahl'] !== LadestationRegler::PHASEN_UNBEKANNT
|
||||||
|
) {
|
||||||
|
$this->aktualisiereFahrzeugstromgrenze($status);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($status as $ident => $wert) {
|
foreach ($status as $ident => $wert) {
|
||||||
$this->setzeZustand($ident, $wert);
|
$this->setzeZustand($ident, $wert);
|
||||||
@@ -374,6 +491,7 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->WriteAttributeInteger('ErkannterMaximalstrom', $kandidat);
|
$this->WriteAttributeInteger('ErkannterMaximalstrom', $kandidat);
|
||||||
|
$this->WriteAttributeInteger('LeistungswechselGesperrtBis', 0);
|
||||||
$this->setzeSichtbareVariable('FahrzeugMaximalstrom', $kandidat);
|
$this->setzeSichtbareVariable('FahrzeugMaximalstrom', $kandidat);
|
||||||
$this->setzeFahrzeugstromKandidatZurueck();
|
$this->setzeFahrzeugstromKandidatZurueck();
|
||||||
}
|
}
|
||||||
@@ -416,9 +534,56 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
$strom = $leistung === 0
|
$strom = $leistung === 0
|
||||||
? 0
|
? 0
|
||||||
: LadestationRegler::stromFuerLeistung($leistung, $phasenzahl);
|
: LadestationRegler::stromFuerLeistung($leistung, $phasenzahl);
|
||||||
if ($strom === $this->ReadAttributeInteger('LetzterGesetzterStrom')) {
|
$bisherigeLeistung = $this->ReadAttributeInteger('LetzteGesetzteLeistung');
|
||||||
|
$stromUnveraendert = $strom === $this->ReadAttributeInteger('LetzterGesetzterStrom');
|
||||||
|
|
||||||
|
if ($stromUnveraendert && $leistung === $bisherigeLeistung) {
|
||||||
return;
|
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->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
||||||
$this->setzeFahrzeugstromKandidatZurueck();
|
$this->setzeFahrzeugstromKandidatZurueck();
|
||||||
|
|
||||||
@@ -660,12 +825,23 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
'Meldeintervall',
|
'Meldeintervall',
|
||||||
'VorgabeTimeout',
|
'VorgabeTimeout',
|
||||||
'Abfrageintervall',
|
'Abfrageintervall',
|
||||||
|
'Phasenerkennungszeit',
|
||||||
'FahrzeugstromErkennungszeit',
|
'FahrzeugstromErkennungszeit',
|
||||||
] as $property) {
|
] as $property) {
|
||||||
if ($this->ReadPropertyInteger($property) <= 0) {
|
if ($this->ReadPropertyInteger($property) <= 0) {
|
||||||
throw new InvalidArgumentException($property . ' muss groesser als 0 sein.');
|
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');
|
$maximalstrom = $this->ReadPropertyInteger('MaximalerLadestrom');
|
||||||
if ($maximalstrom < 6 || $maximalstrom > 32) {
|
if ($maximalstrom < 6 || $maximalstrom > 32) {
|
||||||
throw new InvalidArgumentException('MaximalerLadestrom muss zwischen 6 und 32 A liegen.');
|
throw new InvalidArgumentException('MaximalerLadestrom muss zwischen 6 und 32 A liegen.');
|
||||||
|
|||||||
@@ -40,13 +40,20 @@ Die Auswertung folgt bewusst dem Verhalten der bisherigen Enelix-1-Ladestation:
|
|||||||
wird entsprechend `State != 1` ausgewertet.
|
wird entsprechend `State != 1` ausgewertet.
|
||||||
2. Die gemessene Leistung wird bei der alten go-e-API mit Faktor 10, bei
|
2. Die gemessene Leistung wird bei der alten go-e-API mit Faktor 10, bei
|
||||||
Gemini direkt in Watt und bei Pico von kW in Watt umgerechnet.
|
Gemini direkt in Watt und bei Pico von kW in Watt umgerechnet.
|
||||||
3. Bei einer Leistung ueber 7500 W wird dreiphasiges Laden erkannt. Darunter
|
3. go-e meldet die drei Phasenstroeme einzeln. Sobald mindestens zwei Phasen
|
||||||
wird bei verbundenem Fahrzeug einphasiges Laden angenommen.
|
Strom fuehren, wird dreiphasiges Laden direkt erkannt.
|
||||||
4. Der Ladestrom wird aus der Leistung mit `230 V` einphasig beziehungsweise
|
4. Sind beim Anstecken noch keine belastbaren Phasenwerte vorhanden, gibt das
|
||||||
`1.71 * 400 V` dreiphasig berechnet.
|
Modul zunaechst den konfigurierten Maximalstrom als Erkennungsstrom vor. Bis
|
||||||
5. War das Fahrzeug bereits im vorherigen Zyklus verbunden und liegt der
|
zur abgeschlossenen Erkennung bleibt `Phasenzahl=0` und das EMS-Angebot
|
||||||
ermittelte Maximalstrom aus Ladestrom plus `2.5 A` unter `6 A`, wird
|
`[0]`.
|
||||||
`FahrzeugGeladen=true` gesetzt.
|
5. Nach `Phasenerkennungszeit` gilt wie in Enelix 1: ueber 7500 W werden drei
|
||||||
|
Phasen erkannt, andernfalls eine Phase. Die erkannte Phasenzahl bleibt bis
|
||||||
|
zum Ausstecken erhalten und kippt waehrend einer Ladepause nicht zurueck.
|
||||||
|
6. Der Ladestrom wird aus der Leistung mit `230 W/A` einphasig beziehungsweise
|
||||||
|
`684 W/A` dreiphasig berechnet.
|
||||||
|
7. Ein Ladeende wird nur nach zuvor tatsaechlich gemessener Ladeleistung
|
||||||
|
erkannt. Eine vom EMS angeordnete Solarpause gilt deshalb nicht als
|
||||||
|
`FahrzeugGeladen`.
|
||||||
|
|
||||||
Der normalisierte `Fahrzeugstatus` verwendet folgende Werte:
|
Der normalisierte `Fahrzeugstatus` verwendet folgende Werte:
|
||||||
|
|
||||||
@@ -72,10 +79,18 @@ stoppt die Ladestation.
|
|||||||
| Peak | ein | ausschliesslich `[0]`; die Ladestation wird gestoppt |
|
| Peak | ein | ausschliesslich `[0]`; die Ladestation wird gestoppt |
|
||||||
| Peak | aus | `0 W` und alle Ladestufen von 6 A bis zum konfigurierten Maximum |
|
| Peak | aus | `0 W` und alle Ladestufen von 6 A bis zum konfigurierten Maximum |
|
||||||
|
|
||||||
Die festen ein- und dreiphasigen Leistungsstufen sowie deren Rundung wurden aus
|
Die Leistungsstufen entsprechen Enelix 1: `230 W` pro Ampere einphasig und
|
||||||
Enelix 1 uebernommen. Ohne gueltige Manager-Vorgabe faehrt das Modul bei
|
`684 W` pro Ampere dreiphasig. Ohne gueltige Manager-Vorgabe faehrt das Modul
|
||||||
aktivem Solarladen mit `0 A`; bei ausgeschaltetem Solarladen verwendet es die
|
bei aktivem Solarladen mit `0 A`; bei ausgeschaltetem Solarladen verwendet es
|
||||||
maximale angebotene Leistung.
|
die maximale angebotene Leistung.
|
||||||
|
|
||||||
|
Nach einer Leistungsaenderung wird die aktuelle Stufe fuer
|
||||||
|
`ZeitZwischenZustandswechseln` gehalten. Waehrend der
|
||||||
|
`Mindesteinschaltdauer` bleiben positive Leistungsstufen regelbar, `0 W`
|
||||||
|
wird jedoch nicht angeboten. Nach dem Abschalten meldet das Modul waehrend der
|
||||||
|
`Mindestausschaltdauer` ausschliesslich `[0]`. Sicherheitsabschaltungen
|
||||||
|
durch Deaktivierung, fehlende Ladefreigabe, Ausstecken oder ein erkanntes
|
||||||
|
Ladeende greifen sofort.
|
||||||
|
|
||||||
## Bedienung
|
## Bedienung
|
||||||
|
|
||||||
@@ -114,8 +129,13 @@ gueltige Manager-Vorgabe und berechnet das Leistungsangebot sofort neu.
|
|||||||
| `Benutzername` | String | leer | Benutzername fuer die Pico-API. |
|
| `Benutzername` | String | leer | Benutzername fuer die Pico-API. |
|
||||||
| `Passwort` | String | leer | Passwort fuer die Pico-API; im Formular als Passwortfeld dargestellt. |
|
| `Passwort` | String | leer | Passwort fuer die Pico-API; im Formular als Passwortfeld dargestellt. |
|
||||||
| `MaximalerLadestrom` | Integer | `16 A` | Obergrenze der angebotenen Ladestufen; zulaessig sind 6 bis 32 A. |
|
| `MaximalerLadestrom` | Integer | `16 A` | Obergrenze der angebotenen Ladestufen; zulaessig sind 6 bis 32 A. |
|
||||||
|
| `Phasenerkennungszeit` | Integer | `60 s` | Dauer der Maximalstrom-Probe vor der leistungsbasierten Phasenerkennung. |
|
||||||
|
| `FahrzeugstromErkennungszeit` | Integer | `90 s` | Zeit, die eine stabile Stromunterschreitung anliegen muss, bevor die Fahrzeuggrenze mit 2,5 A Reserve uebernommen wird. |
|
||||||
|
| `ZeitZwischenZustandswechseln` | Integer | `1 min` | Mindestabstand zwischen zwei Leistungsaenderungen. |
|
||||||
|
| `Mindesteinschaltdauer` | Integer | `0 min` | Mindestdauer nach dem Einschalten, in der kein Abschalten auf 0 W angeboten wird. |
|
||||||
|
| `Mindestausschaltdauer` | Integer | `0 min` | Mindestpause nach dem Abschalten, in der nur 0 W angeboten werden. |
|
||||||
| `Abfrageintervall` | Integer | `5 s` | Intervall der Geraetestatusabfrage. |
|
| `Abfrageintervall` | Integer | `5 s` | Intervall der Geraetestatusabfrage. |
|
||||||
| `Ladefreigabe` | Boolean | `false` | Startwert der lokalen Ladefreigabe. |
|
| `Ladefreigabe` | Boolean | `true` | Startwert der lokalen Ladefreigabe. |
|
||||||
| `Solarladen` | Boolean | `true` | Startwert der lokalen Solarlogik. |
|
| `Solarladen` | Boolean | `true` | Startwert der lokalen Solarlogik. |
|
||||||
| `DiagnosevariablenAnzeigen` | Boolean | `false` | Blendet gemeinsame und ladestationsspezifische Diagnosevariablen ein. |
|
| `DiagnosevariablenAnzeigen` | Boolean | `false` | Blendet gemeinsame und ladestationsspezifische Diagnosevariablen ein. |
|
||||||
|
|
||||||
@@ -232,16 +252,22 @@ niemals in Repository-Dateien, Skripte oder Screenshots.
|
|||||||
- Die Instanz erreicht Status `102`, und `Stoerung` bleibt `false`.
|
- Die Instanz erreicht Status `102`, und `Stoerung` bleibt `false`.
|
||||||
- Ohne Fahrzeug sind `FahrzeugVerbunden=false`, `Phasenzahl=0` und das
|
- Ohne Fahrzeug sind `FahrzeugVerbunden=false`, `Phasenzahl=0` und das
|
||||||
Leistungsangebot `[0]`.
|
Leistungsangebot `[0]`.
|
||||||
- Nach dem Anstecken wird das Fahrzeug erkannt und die gemessene Leistung
|
- Nach dem Anstecken wird das Fahrzeug erkannt und bei unbekannten Phasen
|
||||||
plausibel in `Istleistung` und `Ladestrom` abgebildet.
|
zunaechst der konfigurierte Maximalstrom zur Erkennung angefordert.
|
||||||
- Bei einer Ladeleistung bis 7500 W wird eine Phase, darueber werden drei
|
- Nach der Erkennungszeit werden bis 7500 W eine Phase, darueber drei Phasen
|
||||||
Phasen angezeigt.
|
gespeichert. Bei go-e koennen die einzelnen Phasenstroeme die Erkennung
|
||||||
|
bereits vorher abschliessen.
|
||||||
|
- Die erkannte Phasenzahl bleibt bei einer anschliessenden Ladepause stabil.
|
||||||
|
- Die gemessene Leistung wird plausibel in `Istleistung` und `Ladestrom`
|
||||||
|
abgebildet.
|
||||||
- `Aktiv=false` oder `Ladefreigabe=false` stoppt die Ladestation.
|
- `Aktiv=false` oder `Ladefreigabe=false` stoppt die Ladestation.
|
||||||
- PV mit `Solarladen=true` bietet `0 W` und die Ladestufen an.
|
- PV mit `Solarladen=true` bietet `0 W` und die Ladestufen an.
|
||||||
- PV mit `Solarladen=false` bietet nur die maximale Leistung an.
|
- PV mit `Solarladen=false` bietet nur die maximale Leistung an.
|
||||||
- Peak mit `Solarladen=true` stoppt die Ladestation und bietet nur `[0]` an.
|
- Peak mit `Solarladen=true` stoppt die Ladestation und bietet nur `[0]` an.
|
||||||
- Peak mit `Solarladen=false` bietet `0 W` und die Ladestufen an.
|
- Peak mit `Solarladen=false` bietet `0 W` und die Ladestufen an.
|
||||||
- Eine Manager-Vorgabe ausserhalb des gemeldeten Angebots wird abgewiesen.
|
- Eine Manager-Vorgabe ausserhalb des gemeldeten Angebots wird abgewiesen.
|
||||||
|
- Leistungswechsel-, Mindest-Ein- und Mindest-Aus-Zeiten werden im gemeldeten
|
||||||
|
Angebot sichtbar und vom Manager eingehalten.
|
||||||
- Nach Ablauf des Vorgabe-Timeouts gilt wieder das lokale Ersatzverhalten.
|
- Nach Ablauf des Vorgabe-Timeouts gilt wieder das lokale Ersatzverhalten.
|
||||||
- Im Debug-Protokoll und in `LetzterGeraetebefehl` erscheinen keine
|
- Im Debug-Protokoll und in `LetzterGeraetebefehl` erscheinen keine
|
||||||
Zugangsdaten.
|
Zugangsdaten.
|
||||||
@@ -256,9 +282,11 @@ niemals in Repository-Dateien, Skripte oder Screenshots.
|
|||||||
- Fahrzeug wird nicht erkannt: Rohstatus der Geraete-API kontrollieren. Der
|
- Fahrzeug wird nicht erkannt: Rohstatus der Geraete-API kontrollieren. Der
|
||||||
Adapter erwartet bei go-e `car` und `nrg[11]`, bei Pico `State` und
|
Adapter erwartet bei go-e `car` und `nrg[11]`, bei Pico `State` und
|
||||||
`ActiveChargingPower`.
|
`ActiveChargingPower`.
|
||||||
- Falsche Phasenzahl bei sehr kleiner oder stehender Ladung: Die kompatible
|
- Phasenzahl bleibt laenger auf `0`: Waehrend der Erkennungsphase muessen
|
||||||
Enelix-1-Erkennung basiert auf der momentanen Leistung. Fuer eine sichere
|
Ladestation und Fahrzeug den angeforderten Maximalstrom tatsaechlich
|
||||||
Dreiphasenerkennung muss waehrend der Erkennung mehr als 7500 W anliegen.
|
freigeben. Bei Pico muss fuer eine sichere Dreiphasenerkennung waehrend der
|
||||||
|
Erkennung mehr als 7500 W anliegen; go-e kann zusaetzlich anhand der
|
||||||
|
einzelnen Phasenstroeme erkennen.
|
||||||
- Manager-Vorgabe wird abgewiesen: aktive Zuordnung im Manager sowie
|
- Manager-Vorgabe wird abgewiesen: aktive Zuordnung im Manager sowie
|
||||||
`LeistungsangebotDiagnose` und die Betriebsart kontrollieren.
|
`LeistungsangebotDiagnose` und die Betriebsart kontrollieren.
|
||||||
|
|
||||||
@@ -270,9 +298,10 @@ URL, HTTP-Methode, Authentisierung und Steueraufrufe ohne reale Hardware
|
|||||||
geprueft.
|
geprueft.
|
||||||
|
|
||||||
Der Symcon-Funktionstest verwendet den internen `Testmodus` mit simulierten
|
Der Symcon-Funktionstest verwendet den internen `Testmodus` mit simulierten
|
||||||
API-Antworten. Er prueft alle drei Geraetevarianten, Fahrzeug- und
|
API-Antworten. Er prueft alle drei Geraetevarianten, die Maximalstrom-Probe
|
||||||
Phasenerkennung, die Leistungsangebote in PV und Peak sowie die Umschaltung
|
beim Anstecken, stabile Phasen waehrend einer Ladepause, die Schaltsperren,
|
||||||
ueber den `Solarladen`-Button.
|
Fahrzeugstrom-Erkennung, die Leistungsangebote in PV und Peak sowie die
|
||||||
|
Umschaltung ueber den `Solarladen`-Button.
|
||||||
|
|
||||||
PHP-Syntax, Struktur- und Unit-Tests des gesamten Repositorys:
|
PHP-Syntax, Struktur- und Unit-Tests des gesamten Repositorys:
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ final class LadestationAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{Rohstatus: int, Istleistung_W: float, Iststrom_A?: float}
|
* @return array{Rohstatus: int, Istleistung_W: float, Iststrom_A?: float, Phasenzahl?: int}
|
||||||
*/
|
*/
|
||||||
public static function dekodiereStatus(int $geraetetyp, string $antwort): array
|
public static function dekodiereStatus(int $geraetetyp, string $antwort): array
|
||||||
{
|
{
|
||||||
@@ -121,11 +121,19 @@ final class LadestationAdapter
|
|||||||
&& is_numeric($daten['nrg'][6])
|
&& is_numeric($daten['nrg'][6])
|
||||||
) {
|
) {
|
||||||
$stromfaktor = $geraetetyp === self::GO_E_ALT ? 0.1 : 1.0;
|
$stromfaktor = $geraetetyp === self::GO_E_ALT ? 0.1 : 1.0;
|
||||||
$status['Iststrom_A'] = max(
|
$phasenstroeme = [
|
||||||
(float) $daten['nrg'][4],
|
(float) $daten['nrg'][4] * $stromfaktor,
|
||||||
(float) $daten['nrg'][5],
|
(float) $daten['nrg'][5] * $stromfaktor,
|
||||||
(float) $daten['nrg'][6]
|
(float) $daten['nrg'][6] * $stromfaktor,
|
||||||
) * $stromfaktor;
|
];
|
||||||
|
$status['Iststrom_A'] = max($phasenstroeme);
|
||||||
|
$aktivePhasen = count(array_filter(
|
||||||
|
$phasenstroeme,
|
||||||
|
static fn (float $strom): bool => $strom >= 1.0
|
||||||
|
));
|
||||||
|
if ($aktivePhasen > 0) {
|
||||||
|
$status['Phasenzahl'] = $aktivePhasen >= 2 ? 3 : 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $status;
|
return $status;
|
||||||
|
|||||||
+36
-25
@@ -12,27 +12,15 @@ final class LadestationRegler
|
|||||||
public const PHASEN_EIN = 1;
|
public const PHASEN_EIN = 1;
|
||||||
public const PHASEN_DREI = 3;
|
public const PHASEN_DREI = 3;
|
||||||
|
|
||||||
/** @var list<int> */
|
private const LEISTUNG_PRO_AMPERE_EINPHASIG = 230;
|
||||||
private const LEISTUNGEN_EINPHASIG = [
|
private const LEISTUNG_PRO_AMPERE_DREIPHASIG = 684;
|
||||||
1450, 1690, 1930, 2170, 2410, 2650, 2890, 3130, 3370,
|
|
||||||
3610, 3850, 4090, 4330, 4570, 4810, 5050, 5290, 5530,
|
|
||||||
5770, 6010, 6250, 6490, 6730, 6970, 7210, 7450, 7690,
|
|
||||||
];
|
|
||||||
|
|
||||||
/** @var list<int> */
|
|
||||||
private const LEISTUNGEN_DREIPHASIG = [
|
|
||||||
4150, 4850, 5550, 6250, 6950, 7600, 8300, 9000, 9700,
|
|
||||||
10300, 11000, 11750, 12450, 13150, 13850, 14550, 15250,
|
|
||||||
15900, 16600, 17300, 18000, 18700, 19400, 20100, 20800,
|
|
||||||
21500, 22000,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Schwelle und der Zuschlag von 2.5 A stammen aus der Enelix-1-Ladestation.
|
* Die Schwelle und der Zuschlag von 2.5 A stammen aus der Enelix-1-Ladestation.
|
||||||
* Ein Ladeende wird erst nach zuvor gemessener Ladeleistung angenommen, damit
|
* Ein Ladeende wird erst nach zuvor gemessener Ladeleistung angenommen, damit
|
||||||
* die Anlaufzeit nach einer neuen Stromvorgabe nicht als volles Fahrzeug gilt.
|
* die Anlaufzeit nach einer neuen Stromvorgabe nicht als volles Fahrzeug gilt.
|
||||||
*
|
*
|
||||||
* @param array{Rohstatus: int, Istleistung_W: float, Iststrom_A?: float} $rohstatus
|
* @param array{Rohstatus: int, Istleistung_W: float, Iststrom_A?: float, Phasenzahl?: int} $rohstatus
|
||||||
* @return array<string, int|float|bool>
|
* @return array<string, int|float|bool>
|
||||||
*/
|
*/
|
||||||
public static function normalisiereStatus(
|
public static function normalisiereStatus(
|
||||||
@@ -40,7 +28,8 @@ final class LadestationRegler
|
|||||||
bool $warVerbunden,
|
bool $warVerbunden,
|
||||||
bool $warGeladen = false,
|
bool $warGeladen = false,
|
||||||
bool $ladungErwartet = true,
|
bool $ladungErwartet = true,
|
||||||
bool $ladeleistungWurdeErkannt = false
|
bool $ladeleistungWurdeErkannt = false,
|
||||||
|
int $bekanntePhasenzahl = self::PHASEN_UNBEKANNT
|
||||||
): array {
|
): array {
|
||||||
$verbunden = $rohstatus['Rohstatus'] !== 1;
|
$verbunden = $rohstatus['Rohstatus'] !== 1;
|
||||||
$leistung = max(0.0, $rohstatus['Istleistung_W']);
|
$leistung = max(0.0, $rohstatus['Istleistung_W']);
|
||||||
@@ -49,11 +38,22 @@ final class LadestationRegler
|
|||||||
$geladen = false;
|
$geladen = false;
|
||||||
|
|
||||||
if ($verbunden) {
|
if ($verbunden) {
|
||||||
$phasen = $leistung > 7500.0 ? self::PHASEN_DREI : self::PHASEN_EIN;
|
if (in_array($bekanntePhasenzahl, [self::PHASEN_EIN, self::PHASEN_DREI], true)) {
|
||||||
$nenner = $phasen === self::PHASEN_EIN ? 230.0 : 1.71 * 400.0;
|
$phasen = $bekanntePhasenzahl;
|
||||||
|
} elseif (isset($rohstatus['Phasenzahl'])
|
||||||
|
&& in_array($rohstatus['Phasenzahl'], [self::PHASEN_EIN, self::PHASEN_DREI], true)
|
||||||
|
) {
|
||||||
|
$phasen = (int) $rohstatus['Phasenzahl'];
|
||||||
|
} elseif ($leistung > 7500.0) {
|
||||||
|
$phasen = self::PHASEN_DREI;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nenner = $phasen === self::PHASEN_DREI
|
||||||
|
? self::LEISTUNG_PRO_AMPERE_DREIPHASIG
|
||||||
|
: self::LEISTUNG_PRO_AMPERE_EINPHASIG;
|
||||||
$ladestrom = isset($rohstatus['Iststrom_A'])
|
$ladestrom = isset($rohstatus['Iststrom_A'])
|
||||||
? max(0.0, (float) $rohstatus['Iststrom_A'])
|
? max(0.0, (float) $rohstatus['Iststrom_A'])
|
||||||
: $leistung / $nenner;
|
: ($phasen === self::PHASEN_UNBEKANNT ? 0.0 : $leistung / $nenner);
|
||||||
$ermittelterMaximalstrom = 2.5 + $ladestrom;
|
$ermittelterMaximalstrom = 2.5 + $ladestrom;
|
||||||
$geladen = $warGeladen
|
$geladen = $warGeladen
|
||||||
|| ($warVerbunden
|
|| ($warVerbunden
|
||||||
@@ -94,7 +94,10 @@ final class LadestationRegler
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$kandidat = max(6, min($konfigurierterMaximalstrom, (int) round($gemessenerStrom)));
|
$kandidat = max(
|
||||||
|
6,
|
||||||
|
min($konfigurierterMaximalstrom, (int) floor($gemessenerStrom + 2.5))
|
||||||
|
);
|
||||||
if ($kandidat > $vorgegebenerStrom - 2 || $kandidat >= $konfigurierterMaximalstrom) {
|
if ($kandidat > $vorgegebenerStrom - 2 || $kandidat >= $konfigurierterMaximalstrom) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -117,6 +120,10 @@ final class LadestationRegler
|
|||||||
return [0];
|
return [0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($phasenzahl === self::PHASEN_UNBEKANNT) {
|
||||||
|
return [0];
|
||||||
|
}
|
||||||
|
|
||||||
$stufen = self::leistungsstufen($phasenzahl, $maximalerLadestrom);
|
$stufen = self::leistungsstufen($phasenzahl, $maximalerLadestrom);
|
||||||
if ($stufen === []) {
|
if ($stufen === []) {
|
||||||
return [0];
|
return [0];
|
||||||
@@ -167,12 +174,16 @@ final class LadestationRegler
|
|||||||
private static function stufenFuerPhasen(int $phasenzahl): array
|
private static function stufenFuerPhasen(int $phasenzahl): array
|
||||||
{
|
{
|
||||||
if ($phasenzahl === self::PHASEN_EIN) {
|
if ($phasenzahl === self::PHASEN_EIN) {
|
||||||
return self::LEISTUNGEN_EINPHASIG;
|
$leistungProAmpere = self::LEISTUNG_PRO_AMPERE_EINPHASIG;
|
||||||
}
|
} elseif ($phasenzahl === self::PHASEN_DREI) {
|
||||||
if ($phasenzahl === self::PHASEN_DREI) {
|
$leistungProAmpere = self::LEISTUNG_PRO_AMPERE_DREIPHASIG;
|
||||||
return self::LEISTUNGEN_DREIPHASIG;
|
} else {
|
||||||
|
throw new InvalidArgumentException('Phasenzahl muss 1 oder 3 sein.');
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidArgumentException('Phasenzahl muss 1 oder 3 sein.');
|
return array_map(
|
||||||
|
static fn (int $strom): int => $strom * $leistungProAmpere,
|
||||||
|
range(6, 32)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ final class LadestationAdapterTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @dataProvider statusFaelle
|
* @dataProvider statusFaelle
|
||||||
* @param array<string, string> $konfiguration
|
* @param array<string, string> $konfiguration
|
||||||
* @param array{Rohstatus: int, Istleistung_W: float, Iststrom_A?: float} $erwartet
|
* @param array{Rohstatus: int, Istleistung_W: float, Iststrom_A?: float, Phasenzahl?: int} $erwartet
|
||||||
*/
|
*/
|
||||||
public function testStatusaufrufUndDekodierungFuerAlleGeraete(
|
public function testStatusaufrufUndDekodierungFuerAlleGeraete(
|
||||||
int $geraetetyp,
|
int $geraetetyp,
|
||||||
@@ -49,14 +49,24 @@ final class LadestationAdapterTest extends TestCase
|
|||||||
$this->lokaleKonfiguration(),
|
$this->lokaleKonfiguration(),
|
||||||
$this->goEAntwort(2, 1100, [160, 158, 159]),
|
$this->goEAntwort(2, 1100, [160, 158, 159]),
|
||||||
'http://192.0.2.10/mqtt?payload=',
|
'http://192.0.2.10/mqtt?payload=',
|
||||||
['Rohstatus' => 2, 'Istleistung_W' => 11000.0, 'Iststrom_A' => 16.0],
|
[
|
||||||
|
'Rohstatus' => 2,
|
||||||
|
'Istleistung_W' => 11000.0,
|
||||||
|
'Iststrom_A' => 16.0,
|
||||||
|
'Phasenzahl' => 3,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
yield 'go-e Gemini' => [
|
yield 'go-e Gemini' => [
|
||||||
LadestationAdapter::GO_E_GEMINI,
|
LadestationAdapter::GO_E_GEMINI,
|
||||||
$this->lokaleKonfiguration(),
|
$this->lokaleKonfiguration(),
|
||||||
$this->goEAntwort(2, 2300, [9.8, 0, 0]),
|
$this->goEAntwort(2, 2300, [9.8, 0, 0]),
|
||||||
'http://192.0.2.10/api/status',
|
'http://192.0.2.10/api/status',
|
||||||
['Rohstatus' => 2, 'Istleistung_W' => 2300.0, 'Iststrom_A' => 9.8],
|
[
|
||||||
|
'Rohstatus' => 2,
|
||||||
|
'Istleistung_W' => 2300.0,
|
||||||
|
'Iststrom_A' => 9.8,
|
||||||
|
'Phasenzahl' => 1,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
yield 'Smart-me Pico' => [
|
yield 'Smart-me Pico' => [
|
||||||
LadestationAdapter::SMART_ME_PICO,
|
LadestationAdapter::SMART_ME_PICO,
|
||||||
|
|||||||
@@ -22,17 +22,49 @@ final class LadestationReglerTest extends TestCase
|
|||||||
self::assertSame(LadestationRegler::PHASEN_DREI, $status['Phasenzahl']);
|
self::assertSame(LadestationRegler::PHASEN_DREI, $status['Phasenzahl']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEinphasigesFahrzeugWirdUnterhalb7500WErkannt(): void
|
public function testDirektGemeldeteEinphasigkeitWirdAuchUnter7500WErkannt(): void
|
||||||
{
|
{
|
||||||
$status = LadestationRegler::normalisiereStatus([
|
$status = LadestationRegler::normalisiereStatus([
|
||||||
'Rohstatus' => 2,
|
'Rohstatus' => 2,
|
||||||
'Istleistung_W' => 2300.0,
|
'Istleistung_W' => 2300.0,
|
||||||
|
'Phasenzahl' => 1,
|
||||||
], false);
|
], false);
|
||||||
|
|
||||||
self::assertSame(LadestationRegler::PHASEN_EIN, $status['Phasenzahl']);
|
self::assertSame(LadestationRegler::PHASEN_EIN, $status['Phasenzahl']);
|
||||||
self::assertEqualsWithDelta(10.0, $status['Ladestrom_A'], 0.01);
|
self::assertEqualsWithDelta(10.0, $status['Ladestrom_A'], 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testErkannteDreiphasigkeitBleibtWaehrendLadepauseErhalten(): void
|
||||||
|
{
|
||||||
|
$status = LadestationRegler::normalisiereStatus([
|
||||||
|
'Rohstatus' => 2,
|
||||||
|
'Istleistung_W' => 0.0,
|
||||||
|
], true, false, false, true, LadestationRegler::PHASEN_DREI);
|
||||||
|
|
||||||
|
self::assertSame(LadestationRegler::PHASEN_DREI, $status['Phasenzahl']);
|
||||||
|
self::assertSame(0.0, $status['Ladestrom_A']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPhasenBleibenOhneMessungBisZurErkennungUnbekannt(): void
|
||||||
|
{
|
||||||
|
$status = LadestationRegler::normalisiereStatus([
|
||||||
|
'Rohstatus' => 2,
|
||||||
|
'Istleistung_W' => 0.0,
|
||||||
|
], false);
|
||||||
|
|
||||||
|
self::assertSame(LadestationRegler::PHASEN_UNBEKANNT, $status['Phasenzahl']);
|
||||||
|
self::assertSame([0], LadestationRegler::leistungsangebot(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
LadestationRegler::PHASEN_UNBEKANNT,
|
||||||
|
16,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
public function testAnlaufOhneGemesseneLadeleistungGiltNichtAlsLadeende(): void
|
public function testAnlaufOhneGemesseneLadeleistungGiltNichtAlsLadeende(): void
|
||||||
{
|
{
|
||||||
$status = LadestationRegler::normalisiereStatus([
|
$status = LadestationRegler::normalisiereStatus([
|
||||||
@@ -90,7 +122,7 @@ final class LadestationReglerTest extends TestCase
|
|||||||
|
|
||||||
public function testFahrzeugMaximalstromWirdMitZweiAmpereAbstandErkannt(): void
|
public function testFahrzeugMaximalstromWirdMitZweiAmpereAbstandErkannt(): void
|
||||||
{
|
{
|
||||||
self::assertSame(16, LadestationRegler::ermittleFahrzeugMaximalstrom(20, 15.9, 20));
|
self::assertSame(18, LadestationRegler::ermittleFahrzeugMaximalstrom(20, 15.9, 20));
|
||||||
self::assertNull(LadestationRegler::ermittleFahrzeugMaximalstrom(17, 15.9, 20));
|
self::assertNull(LadestationRegler::ermittleFahrzeugMaximalstrom(17, 15.9, 20));
|
||||||
self::assertNull(LadestationRegler::ermittleFahrzeugMaximalstrom(20, 0.0, 20));
|
self::assertNull(LadestationRegler::ermittleFahrzeugMaximalstrom(20, 0.0, 20));
|
||||||
}
|
}
|
||||||
@@ -104,7 +136,7 @@ final class LadestationReglerTest extends TestCase
|
|||||||
|
|
||||||
self::assertSame(0, $solarPV[0]);
|
self::assertSame(0, $solarPV[0]);
|
||||||
self::assertCount(12, $solarPV);
|
self::assertCount(12, $solarPV);
|
||||||
self::assertSame([3850], $normalPV);
|
self::assertSame([3680], $normalPV);
|
||||||
self::assertSame([0], $solarPeak);
|
self::assertSame([0], $solarPeak);
|
||||||
self::assertSame($solarPV, $normalPeak);
|
self::assertSame($solarPV, $normalPeak);
|
||||||
}
|
}
|
||||||
@@ -112,7 +144,7 @@ final class LadestationReglerTest extends TestCase
|
|||||||
public function testPeakKannJedesFahrzeugAufNullSetzen(): void
|
public function testPeakKannJedesFahrzeugAufNullSetzen(): void
|
||||||
{
|
{
|
||||||
self::assertSame(0, LadestationRegler::stromFuerLeistung(0, 3));
|
self::assertSame(0, LadestationRegler::stromFuerLeistung(0, 3));
|
||||||
self::assertSame(6, LadestationRegler::stromFuerLeistung(4150, 3));
|
self::assertSame(6, LadestationRegler::stromFuerLeistung(4104, 3));
|
||||||
self::assertSame(16, LadestationRegler::stromFuerLeistung(11000, 3));
|
self::assertSame(16, LadestationRegler::stromFuerLeistung(10944, 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,22 @@ final class LadestationStandAloneModulstrukturTest extends TestCase
|
|||||||
"RegisterPropertyBoolean('Ladefreigabe', true)",
|
"RegisterPropertyBoolean('Ladefreigabe', true)",
|
||||||
$modul
|
$modul
|
||||||
);
|
);
|
||||||
|
self::assertStringContainsString(
|
||||||
|
"RegisterPropertyInteger('Phasenerkennungszeit', 60)",
|
||||||
|
$modul
|
||||||
|
);
|
||||||
|
self::assertStringContainsString(
|
||||||
|
"RegisterPropertyInteger('ZeitZwischenZustandswechseln', 1)",
|
||||||
|
$modul
|
||||||
|
);
|
||||||
|
self::assertStringContainsString(
|
||||||
|
"RegisterPropertyInteger('Mindesteinschaltdauer', 0)",
|
||||||
|
$modul
|
||||||
|
);
|
||||||
|
self::assertStringContainsString(
|
||||||
|
"RegisterPropertyInteger('Mindestausschaltdauer', 0)",
|
||||||
|
$modul
|
||||||
|
);
|
||||||
self::assertStringContainsString("EnableAction('Ladefreigabe')", $modul);
|
self::assertStringContainsString("EnableAction('Ladefreigabe')", $modul);
|
||||||
self::assertStringContainsString("EnableAction('Solarladen')", $modul);
|
self::assertStringContainsString("EnableAction('Solarladen')", $modul);
|
||||||
self::assertStringContainsString("ReadPropertyBoolean('LoggingEin')", $modul);
|
self::assertStringContainsString("ReadPropertyBoolean('LoggingEin')", $modul);
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ return static function (TestContext $test): void {
|
|||||||
) use ($station): void {
|
) use ($station): void {
|
||||||
IPS_RequestAction($station, 'Aktiv', true);
|
IPS_RequestAction($station, 'Aktiv', true);
|
||||||
$test->assertSame(
|
$test->assertSame(
|
||||||
'[0,4150,4850,5550,6250,6950,7600,8300,9000,9700,10300,11000]',
|
'[0,4104,4788,5472,6156,6840,7524,8208,8892,9576,10260,10944]',
|
||||||
(string) GetValue(
|
(string) GetValue(
|
||||||
$test->objectByIdent('LeistungsangebotDiagnose', $station)
|
$test->objectByIdent('LeistungsangebotDiagnose', $station)
|
||||||
)
|
)
|
||||||
@@ -125,7 +125,7 @@ return static function (TestContext $test): void {
|
|||||||
);
|
);
|
||||||
$test->assertInstanceStatus($station);
|
$test->assertInstanceStatus($station);
|
||||||
$test->assertSame(
|
$test->assertSame(
|
||||||
'[3850]',
|
'[3680]',
|
||||||
(string) GetValue(
|
(string) GetValue(
|
||||||
$test->objectByIdent('LeistungsangebotDiagnose', $station)
|
$test->objectByIdent('LeistungsangebotDiagnose', $station)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ return static function (TestContext $test): void {
|
|||||||
foreach ($definitionen as $name => $definition) {
|
foreach ($definitionen as $name => $definition) {
|
||||||
$properties = array_merge($definition, [
|
$properties = array_merge($definition, [
|
||||||
'MaximalerLadestrom' => 16,
|
'MaximalerLadestrom' => 16,
|
||||||
|
'ZeitZwischenZustandswechseln' => 0,
|
||||||
'Ladefreigabe' => true,
|
'Ladefreigabe' => true,
|
||||||
'Solarladen' => true,
|
'Solarladen' => true,
|
||||||
'EinstellungenInVisu' => true,
|
'EinstellungenInVisu' => true,
|
||||||
@@ -71,6 +72,7 @@ return static function (TestContext $test): void {
|
|||||||
'Geraetetyp' => 1,
|
'Geraetetyp' => 1,
|
||||||
'Geraeteadresse' => '192.0.2.12',
|
'Geraeteadresse' => '192.0.2.12',
|
||||||
'MaximalerLadestrom' => 16,
|
'MaximalerLadestrom' => 16,
|
||||||
|
'ZeitZwischenZustandswechseln' => 0,
|
||||||
'Ladefreigabe' => true,
|
'Ladefreigabe' => true,
|
||||||
'Solarladen' => true,
|
'Solarladen' => true,
|
||||||
'EinstellungenInVisu' => true,
|
'EinstellungenInVisu' => true,
|
||||||
@@ -88,12 +90,85 @@ return static function (TestContext $test): void {
|
|||||||
'Geraeteadresse' => '192.0.2.13',
|
'Geraeteadresse' => '192.0.2.13',
|
||||||
'MaximalerLadestrom' => 20,
|
'MaximalerLadestrom' => 20,
|
||||||
'FahrzeugstromErkennungszeit' => 1,
|
'FahrzeugstromErkennungszeit' => 1,
|
||||||
|
'ZeitZwischenZustandswechseln' => 0,
|
||||||
'Ladefreigabe' => true,
|
'Ladefreigabe' => true,
|
||||||
'Solarladen' => true,
|
'Solarladen' => true,
|
||||||
'EinstellungenInVisu' => true,
|
'EinstellungenInVisu' => true,
|
||||||
'DiagnosevariablenAnzeigen' => true,
|
'DiagnosevariablenAnzeigen' => true,
|
||||||
'Testmodus' => true,
|
'Testmodus' => true,
|
||||||
'Testantwort' => $goEAntwort(2, 10263, [15.9, 15.8, 15.7]),
|
'Testantwort' => $goEAntwort(2, 10260, [15.9, 15.8, 15.7]),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$phasenerkennung = $test->createInstance(
|
||||||
|
'{0D94913C-0F31-4C29-A685-6EB4AE58E55D}',
|
||||||
|
'Ladestation Phasenerkennung',
|
||||||
|
[
|
||||||
|
'Geraetetyp' => 2,
|
||||||
|
'Geraeteadresse' => '192.0.2.14',
|
||||||
|
'MaximalerLadestrom' => 16,
|
||||||
|
'Phasenerkennungszeit' => 1,
|
||||||
|
'ZeitZwischenZustandswechseln' => 0,
|
||||||
|
'Ladefreigabe' => true,
|
||||||
|
'Solarladen' => true,
|
||||||
|
'EinstellungenInVisu' => true,
|
||||||
|
'DiagnosevariablenAnzeigen' => true,
|
||||||
|
'Testmodus' => true,
|
||||||
|
'Testantwort' => $goEAntwort(2, 0),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$phasenhaltung = $test->createInstance(
|
||||||
|
'{0D94913C-0F31-4C29-A685-6EB4AE58E55D}',
|
||||||
|
'Ladestation Phasenhaltung',
|
||||||
|
[
|
||||||
|
'Geraetetyp' => 2,
|
||||||
|
'Geraeteadresse' => '192.0.2.14',
|
||||||
|
'MaximalerLadestrom' => 16,
|
||||||
|
'ZeitZwischenZustandswechseln' => 0,
|
||||||
|
'Ladefreigabe' => true,
|
||||||
|
'Solarladen' => true,
|
||||||
|
'EinstellungenInVisu' => true,
|
||||||
|
'DiagnosevariablenAnzeigen' => true,
|
||||||
|
'Testmodus' => true,
|
||||||
|
'Testantwort' => $goEAntwort(2, 10944, [16, 16, 16]),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$schaltsperre = $test->createInstance(
|
||||||
|
'{0D94913C-0F31-4C29-A685-6EB4AE58E55D}',
|
||||||
|
'Ladestation Schaltsperre',
|
||||||
|
[
|
||||||
|
'Geraetetyp' => 2,
|
||||||
|
'Geraeteadresse' => '192.0.2.15',
|
||||||
|
'MaximalerLadestrom' => 16,
|
||||||
|
'ZeitZwischenZustandswechseln' => 1,
|
||||||
|
'Mindesteinschaltdauer' => 2,
|
||||||
|
'Ladefreigabe' => true,
|
||||||
|
'Solarladen' => true,
|
||||||
|
'EinstellungenInVisu' => true,
|
||||||
|
'DiagnosevariablenAnzeigen' => true,
|
||||||
|
'Testmodus' => true,
|
||||||
|
'Testantwort' => $goEAntwort(2, 10944, [16, 16, 16]),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$mindestzeiten = $test->createInstance(
|
||||||
|
'{0D94913C-0F31-4C29-A685-6EB4AE58E55D}',
|
||||||
|
'Ladestation Mindestzeiten',
|
||||||
|
[
|
||||||
|
'Geraetetyp' => 2,
|
||||||
|
'Geraeteadresse' => '192.0.2.17',
|
||||||
|
'MaximalerLadestrom' => 16,
|
||||||
|
'ZeitZwischenZustandswechseln' => 0,
|
||||||
|
'Mindesteinschaltdauer' => 1,
|
||||||
|
'Mindestausschaltdauer' => 1,
|
||||||
|
'Ladefreigabe' => true,
|
||||||
|
'Solarladen' => true,
|
||||||
|
'EinstellungenInVisu' => true,
|
||||||
|
'DiagnosevariablenAnzeigen' => true,
|
||||||
|
'Testmodus' => true,
|
||||||
|
'Testantwort' => $goEAntwort(2, 10944, [16, 16, 16]),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -104,7 +179,16 @@ return static function (TestContext $test): void {
|
|||||||
'AutomatischeSuche' => false,
|
'AutomatischeSuche' => false,
|
||||||
'VerbraucherZuordnung' => json_encode(array_map(
|
'VerbraucherZuordnung' => json_encode(array_map(
|
||||||
static fn (int $id): array => ['InstanzID' => $id, 'Aktiv' => true],
|
static fn (int $id): array => ['InstanzID' => $id, 'Aktiv' => true],
|
||||||
array_merge(array_values($stationen), [$stromerkennung])
|
array_merge(
|
||||||
|
array_values($stationen),
|
||||||
|
[
|
||||||
|
$stromerkennung,
|
||||||
|
$phasenerkennung,
|
||||||
|
$phasenhaltung,
|
||||||
|
$schaltsperre,
|
||||||
|
$mindestzeiten,
|
||||||
|
]
|
||||||
|
)
|
||||||
), JSON_THROW_ON_ERROR),
|
), JSON_THROW_ON_ERROR),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -156,6 +240,94 @@ return static function (TestContext $test): void {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$test->runCase('Erkannte Phasen bleiben waehrend einer Ladepause stabil', static function (
|
||||||
|
TestContext $test
|
||||||
|
) use ($goEAntwort, $phasenhaltung): void {
|
||||||
|
IPS_RequestAction($phasenhaltung, 'Aktiv', true);
|
||||||
|
$test->assertSame(
|
||||||
|
3,
|
||||||
|
GetValue($test->objectByIdent('Phasenzahl', $phasenhaltung))
|
||||||
|
);
|
||||||
|
|
||||||
|
IPS_SetProperty($phasenhaltung, 'Testantwort', $goEAntwort(2, 0));
|
||||||
|
IPS_ApplyChanges($phasenhaltung);
|
||||||
|
IPS_RequestAction($phasenhaltung, 'Statuszyklus', false);
|
||||||
|
|
||||||
|
$test->assertSame(
|
||||||
|
3,
|
||||||
|
GetValue($test->objectByIdent('Phasenzahl', $phasenhaltung))
|
||||||
|
);
|
||||||
|
$test->assertSame(
|
||||||
|
false,
|
||||||
|
GetValue($test->objectByIdent('FahrzeugGeladen', $phasenhaltung))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$test->runCase('Schaltsperre haelt die aktuelle Leistung stabil', static function (
|
||||||
|
TestContext $test
|
||||||
|
) use ($schaltsperre, $anforderungSenden): void {
|
||||||
|
IPS_RequestAction($schaltsperre, 'Aktiv', true);
|
||||||
|
IPS_RequestAction($schaltsperre, 'Solarladen', true);
|
||||||
|
$anforderungSenden($schaltsperre, 4104);
|
||||||
|
IPS_RequestAction($schaltsperre, 'Statuszyklus', false);
|
||||||
|
|
||||||
|
$diagnose = json_encode([
|
||||||
|
'Angebot' => GetValue($test->objectByIdent('LeistungsangebotDiagnose', $schaltsperre)),
|
||||||
|
'Verbunden' => GetValue($test->objectByIdent('FahrzeugVerbunden', $schaltsperre)),
|
||||||
|
'Geladen' => GetValue($test->objectByIdent('FahrzeugGeladen', $schaltsperre)),
|
||||||
|
'Phasen' => GetValue($test->objectByIdent('Phasenzahl', $schaltsperre)),
|
||||||
|
'Sollleistung' => GetValue($test->objectByIdent('Sollleistung', $schaltsperre)),
|
||||||
|
'SollwertGueltig' => GetValue($test->objectByIdent('SollwertGueltig', $schaltsperre)),
|
||||||
|
'Befehl' => GetValue($test->objectByIdent('LetzterGeraetebefehl', $schaltsperre)),
|
||||||
|
], JSON_THROW_ON_ERROR);
|
||||||
|
$test->assertSame(
|
||||||
|
'[4104]',
|
||||||
|
(string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $schaltsperre)),
|
||||||
|
'Diagnose: ' . $diagnose
|
||||||
|
);
|
||||||
|
|
||||||
|
$abgewiesen = false;
|
||||||
|
try {
|
||||||
|
$anforderungSenden($schaltsperre, 4788);
|
||||||
|
} catch (Throwable $fehler) {
|
||||||
|
$abgewiesen = true;
|
||||||
|
}
|
||||||
|
if (!$abgewiesen) {
|
||||||
|
$abgewiesen = GetValue(
|
||||||
|
$test->objectByIdent('SollwertGueltig', $schaltsperre)
|
||||||
|
) !== true || GetValue(
|
||||||
|
$test->objectByIdent('Sollleistung', $schaltsperre)
|
||||||
|
) !== 4788;
|
||||||
|
}
|
||||||
|
$test->assertSame(true, $abgewiesen);
|
||||||
|
});
|
||||||
|
|
||||||
|
$test->runCase('Mindestzeiten erlauben Modulation aber verhindern Takten', static function (
|
||||||
|
TestContext $test
|
||||||
|
) use ($mindestzeiten, $anforderungSenden): void {
|
||||||
|
IPS_RequestAction($mindestzeiten, 'Aktiv', true);
|
||||||
|
$anforderungSenden($mindestzeiten, 4104);
|
||||||
|
IPS_RequestAction($mindestzeiten, 'Statuszyklus', false);
|
||||||
|
|
||||||
|
$angebot = json_decode(
|
||||||
|
(string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $mindestzeiten)),
|
||||||
|
true,
|
||||||
|
512,
|
||||||
|
JSON_THROW_ON_ERROR
|
||||||
|
);
|
||||||
|
$test->assertTrue(!in_array(0, $angebot, true));
|
||||||
|
$test->assertTrue(in_array(4788, $angebot, true));
|
||||||
|
|
||||||
|
$anforderungSenden($mindestzeiten, 4788);
|
||||||
|
IPS_RequestAction($mindestzeiten, 'Ladefreigabe', false);
|
||||||
|
IPS_RequestAction($mindestzeiten, 'Ladefreigabe', true);
|
||||||
|
|
||||||
|
$test->assertSame(
|
||||||
|
'[0]',
|
||||||
|
(string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $mindestzeiten))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
$test->runCase('Solarpause wird nicht als Ladeende erkannt', static function (
|
$test->runCase('Solarpause wird nicht als Ladeende erkannt', static function (
|
||||||
TestContext $test
|
TestContext $test
|
||||||
) use ($solarPause): void {
|
) use ($solarPause): void {
|
||||||
@@ -171,35 +343,12 @@ return static function (TestContext $test): void {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$test->runCase('ApplyChanges prueft ein gespeichertes Ladeende neu', static function (
|
|
||||||
TestContext $test
|
|
||||||
) use ($goEAntwort, $solarPause): void {
|
|
||||||
IPS_RequestAction($solarPause, 'Solarladen', false);
|
|
||||||
IPS_RequestAction($solarPause, 'Statuszyklus', false);
|
|
||||||
$test->assertSame(
|
|
||||||
true,
|
|
||||||
GetValue($test->objectByIdent('FahrzeugGeladen', $solarPause))
|
|
||||||
);
|
|
||||||
|
|
||||||
IPS_SetProperty($solarPause, 'Testantwort', $goEAntwort(2, 2300, [100, 100, 100]));
|
|
||||||
IPS_ApplyChanges($solarPause);
|
|
||||||
|
|
||||||
$test->assertSame(
|
|
||||||
false,
|
|
||||||
GetValue($test->objectByIdent('FahrzeugGeladen', $solarPause))
|
|
||||||
);
|
|
||||||
$test->assertSame(
|
|
||||||
3,
|
|
||||||
GetValue($test->objectByIdent('Fahrzeugstatus', $solarPause))
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
$test->runCase('Solar- und Peakbetrieb liefern die Enelix-1-Angebote', static function (
|
$test->runCase('Solar- und Peakbetrieb liefern die Enelix-1-Angebote', static function (
|
||||||
TestContext $test
|
TestContext $test
|
||||||
) use ($stationen, $anforderungSenden): void {
|
) use ($stationen, $anforderungSenden): void {
|
||||||
$stationId = $stationen['go-e alt'];
|
$stationId = $stationen['go-e alt'];
|
||||||
IPS_RequestAction($stationId, 'Solarladen', true);
|
IPS_RequestAction($stationId, 'Solarladen', true);
|
||||||
$anforderungSenden($stationId, 4150);
|
$anforderungSenden($stationId, 4104);
|
||||||
$befehl = (string) GetValue($test->objectByIdent('LetzterGeraetebefehl', $stationId));
|
$befehl = (string) GetValue($test->objectByIdent('LetzterGeraetebefehl', $stationId));
|
||||||
$diagnose = json_encode([
|
$diagnose = json_encode([
|
||||||
'Sollleistung' => GetValue($test->objectByIdent('Sollleistung', $stationId)),
|
'Sollleistung' => GetValue($test->objectByIdent('Sollleistung', $stationId)),
|
||||||
@@ -214,22 +363,22 @@ return static function (TestContext $test): void {
|
|||||||
|
|
||||||
IPS_RequestAction($stationId, 'Solarladen', false);
|
IPS_RequestAction($stationId, 'Solarladen', false);
|
||||||
$angebot = (string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $stationId));
|
$angebot = (string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $stationId));
|
||||||
$test->assertSame('[11000]', $angebot, 'PV ohne Solarladen muss nur das Maximum anbieten.');
|
$test->assertSame('[10944]', $angebot, 'PV ohne Solarladen muss nur das Maximum anbieten.');
|
||||||
$abgewiesen = false;
|
$abgewiesen = false;
|
||||||
try {
|
try {
|
||||||
$anforderungSenden($stationId, 4150);
|
$anforderungSenden($stationId, 4104);
|
||||||
} catch (Throwable $fehler) {
|
} catch (Throwable $fehler) {
|
||||||
$abgewiesen = true;
|
$abgewiesen = true;
|
||||||
}
|
}
|
||||||
if (!$abgewiesen) {
|
if (!$abgewiesen) {
|
||||||
$abgewiesen = GetValue($test->objectByIdent('SollwertGueltig', $stationId)) !== true
|
$abgewiesen = GetValue($test->objectByIdent('SollwertGueltig', $stationId)) !== true
|
||||||
|| GetValue($test->objectByIdent('Sollleistung', $stationId)) !== 4150;
|
|| GetValue($test->objectByIdent('Sollleistung', $stationId)) !== 4104;
|
||||||
}
|
}
|
||||||
$test->assertSame(true, $abgewiesen, 'Normalbetrieb muss eine Zwischenstufe ablehnen.');
|
$test->assertSame(true, $abgewiesen, 'Normalbetrieb muss eine Zwischenstufe ablehnen.');
|
||||||
|
|
||||||
$anforderungSenden($stationId, 4150, 'Peak');
|
$anforderungSenden($stationId, 4104, 'Peak');
|
||||||
$test->assertSame(
|
$test->assertSame(
|
||||||
'[0,4150,4850,5550,6250,6950,7600,8300,9000,9700,10300,11000]',
|
'[0,4104,4788,5472,6156,6840,7524,8208,8892,9576,10260,10944]',
|
||||||
(string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $stationId))
|
(string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $stationId))
|
||||||
);
|
);
|
||||||
$test->assertTrue(str_contains(
|
$test->assertTrue(str_contains(
|
||||||
@@ -254,37 +403,58 @@ return static function (TestContext $test): void {
|
|||||||
) use ($stromerkennung, $anforderungSenden): void {
|
) use ($stromerkennung, $anforderungSenden): void {
|
||||||
IPS_RequestAction($stromerkennung, 'Aktiv', true);
|
IPS_RequestAction($stromerkennung, 'Aktiv', true);
|
||||||
IPS_RequestAction($stromerkennung, 'Solarladen', true);
|
IPS_RequestAction($stromerkennung, 'Solarladen', true);
|
||||||
$anforderungSenden($stromerkennung, 13850);
|
$anforderungSenden($stromerkennung, 13680);
|
||||||
IPS_RequestAction($stromerkennung, 'Statuszyklus', false);
|
IPS_RequestAction($stromerkennung, 'Statuszyklus', false);
|
||||||
IPS_Sleep(2100);
|
IPS_Sleep(2100);
|
||||||
IPS_RequestAction($stromerkennung, 'Statuszyklus', false);
|
IPS_RequestAction($stromerkennung, 'Statuszyklus', false);
|
||||||
|
|
||||||
$test->assertSame(
|
$test->assertSame(
|
||||||
16,
|
18,
|
||||||
GetValue($test->objectByIdent('FahrzeugMaximalstrom', $stromerkennung))
|
GetValue($test->objectByIdent('FahrzeugMaximalstrom', $stromerkennung))
|
||||||
);
|
);
|
||||||
$test->assertSame(
|
$test->assertSame(
|
||||||
'[0,4150,4850,5550,6250,6950,7600,8300,9000,9700,10300,11000]',
|
'[0,4104,4788,5472,6156,6840,7524,8208,8892,9576,10260,10944,11628,12312]',
|
||||||
(string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $stromerkennung))
|
(string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $stromerkennung))
|
||||||
);
|
);
|
||||||
$test->assertTrue(str_contains(
|
$test->assertTrue(str_contains(
|
||||||
(string) GetValue($test->objectByIdent('LetzterGeraetebefehl', $stromerkennung)),
|
(string) GetValue($test->objectByIdent('LetzterGeraetebefehl', $stromerkennung)),
|
||||||
'amp=16'
|
'amp=18'
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
$test->runCase('Sollwert bleibt waehrend der Ladeanlaufzeit gueltig', static function (
|
$test->runCase('Neue Ladestation wird erst nach Freigabe mit Maximalstrom erkannt', static function (
|
||||||
TestContext $test
|
TestContext $test
|
||||||
) use ($goEAntwort, $stationen, $anforderungSenden): void {
|
) use ($phasenerkennung, $anforderungSenden): void {
|
||||||
$stationId = $stationen['go-e Gemini'];
|
$stationId = $phasenerkennung;
|
||||||
IPS_SetProperty($stationId, 'Testantwort', $goEAntwort(2, 0));
|
$test->assertSame(
|
||||||
IPS_ApplyChanges($stationId);
|
0,
|
||||||
IPS_RequestAction($stationId, 'Aktiv', true);
|
GetValue($test->objectByIdent('Phasenzahl', $stationId))
|
||||||
IPS_RequestAction($stationId, 'Solarladen', true);
|
);
|
||||||
|
$test->assertTrue(!str_contains(
|
||||||
|
(string) GetValue($test->objectByIdent('LetzterGeraetebefehl', $stationId)),
|
||||||
|
'amp=16'
|
||||||
|
), 'Eine inaktive Station darf keine Maximalstrom-Probe starten.');
|
||||||
|
|
||||||
$anforderungSenden($stationId, 3850);
|
IPS_RequestAction($stationId, 'Aktiv', true);
|
||||||
|
|
||||||
|
$test->assertSame(
|
||||||
|
0,
|
||||||
|
GetValue($test->objectByIdent('Phasenzahl', $stationId))
|
||||||
|
);
|
||||||
|
$test->assertTrue(str_contains(
|
||||||
|
(string) GetValue($test->objectByIdent('LetzterGeraetebefehl', $stationId)),
|
||||||
|
'amp=16'
|
||||||
|
), 'Nach der Freigabe muss die Maximalstrom-Probe starten.');
|
||||||
|
|
||||||
|
IPS_Sleep(2100);
|
||||||
|
IPS_RequestAction($stationId, 'Statuszyklus', false);
|
||||||
|
$anforderungSenden($stationId, 3680);
|
||||||
IPS_RequestAction($stationId, 'Statuszyklus', false);
|
IPS_RequestAction($stationId, 'Statuszyklus', false);
|
||||||
|
|
||||||
|
$test->assertSame(
|
||||||
|
1,
|
||||||
|
GetValue($test->objectByIdent('Phasenzahl', $stationId))
|
||||||
|
);
|
||||||
$test->assertSame(
|
$test->assertSame(
|
||||||
false,
|
false,
|
||||||
GetValue($test->objectByIdent('FahrzeugGeladen', $stationId))
|
GetValue($test->objectByIdent('FahrzeugGeladen', $stationId))
|
||||||
@@ -294,11 +464,11 @@ return static function (TestContext $test): void {
|
|||||||
GetValue($test->objectByIdent('SollwertGueltig', $stationId))
|
GetValue($test->objectByIdent('SollwertGueltig', $stationId))
|
||||||
);
|
);
|
||||||
$test->assertSame(
|
$test->assertSame(
|
||||||
3850,
|
3680,
|
||||||
GetValue($test->objectByIdent('Sollleistung', $stationId))
|
GetValue($test->objectByIdent('Sollleistung', $stationId))
|
||||||
);
|
);
|
||||||
$test->assertSame(
|
$test->assertSame(
|
||||||
'[0,1450,1690,1930,2170,2410,2650,2890,3130,3370,3610,3850]',
|
'[0,1380,1610,1840,2070,2300,2530,2760,2990,3220,3450,3680]',
|
||||||
(string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $stationId))
|
(string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $stationId))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user