fix(charger): stabilize Go-E setpoint control
Tests / test (push) Successful in 47s

This commit is contained in:
dh
2026-10-06 11:33:59 +00:00
parent 37addf4242
commit ec7182b833
6 changed files with 117 additions and 7 deletions
+11 -1
View File
@@ -100,6 +100,7 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
$this->RegisterAttributeInteger('PhasenerkennungSeit', 0); $this->RegisterAttributeInteger('PhasenerkennungSeit', 0);
$this->RegisterAttributeInteger('PhasenerkennungVersion', 0); $this->RegisterAttributeInteger('PhasenerkennungVersion', 0);
$this->RegisterAttributeBoolean('LadeleistungSeitFreigabeErkannt', false); $this->RegisterAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
$this->RegisterAttributeInteger('GeraeteMaximalstrom', 32);
$this->RegisterAttributeInteger('ErkannterMaximalstrom', 32); $this->RegisterAttributeInteger('ErkannterMaximalstrom', 32);
$this->RegisterAttributeInteger('FahrzeugstromKandidat', 0); $this->RegisterAttributeInteger('FahrzeugstromKandidat', 0);
$this->RegisterAttributeInteger('FahrzeugstromKandidatSeit', 0); $this->RegisterAttributeInteger('FahrzeugstromKandidatSeit', 0);
@@ -236,7 +237,6 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
if ((bool) $wert) { if ((bool) $wert) {
$this->SetTimerInterval('RueckmeldungVerzoegert', 0); $this->SetTimerInterval('RueckmeldungVerzoegert', 0);
} }
$this->aktualisiere(false);
$this->sendeVerbraucherdaten(); $this->sendeVerbraucherdaten();
return; return;
@@ -315,6 +315,7 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
$phasenzahl = $this->ReadAttributeInteger('ZustandPhasenzahl'); $phasenzahl = $this->ReadAttributeInteger('ZustandPhasenzahl');
$maximalstrom = min( $maximalstrom = min(
$this->ReadPropertyInteger('MaximalerLadestrom'), $this->ReadPropertyInteger('MaximalerLadestrom'),
max(6, $this->ReadAttributeInteger('GeraeteMaximalstrom')),
max(6, $this->ReadAttributeInteger('ErkannterMaximalstrom')) max(6, $this->ReadAttributeInteger('ErkannterMaximalstrom'))
); );
$this->setzeSichtbareVariable('FahrzeugMaximalstrom', $maximalstrom); $this->setzeSichtbareVariable('FahrzeugMaximalstrom', $maximalstrom);
@@ -423,6 +424,15 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
$this->ReadPropertyInteger('Geraetetyp'), $this->ReadPropertyInteger('Geraetetyp'),
$antwort $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'); $warVerbunden = $this->ReadAttributeBoolean('ZustandFahrzeugVerbunden');
$verbunden = $rohstatus['Rohstatus'] !== 1; $verbunden = $rohstatus['Rohstatus'] !== 1;
+15 -3
View File
@@ -219,6 +219,11 @@ class Manager extends IPSModule implements ManagerSchnittstelle
0, 0,
"IPS_RequestAction(\$_IPS['TARGET'], 'Regeln', true);" "IPS_RequestAction(\$_IPS['TARGET'], 'Regeln', true);"
); );
$this->RegisterTimer(
'RegelungVerzoegert',
0,
"IPS_RequestAction(\$_IPS['TARGET'], 'RegelnVerzoegert', false);"
);
$this->RegisterTimer( $this->RegisterTimer(
'PrognoseSenden', 'PrognoseSenden',
0, 0,
@@ -252,6 +257,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle
$this->SetTimerInterval('Regelzyklus', 0); $this->SetTimerInterval('Regelzyklus', 0);
$this->SetTimerInterval('VorgabenErneuern', 0); $this->SetTimerInterval('VorgabenErneuern', 0);
$this->SetTimerInterval('KeepAlive', 0); $this->SetTimerInterval('KeepAlive', 0);
$this->SetTimerInterval('RegelungVerzoegert', 0);
$this->SetTimerInterval('PrognoseSenden', 0); $this->SetTimerInterval('PrognoseSenden', 0);
$this->SetTimerInterval('StoerungenSenden', 0); $this->SetTimerInterval('StoerungenSenden', 0);
$this->SetTimerInterval('MesswerteAufzeichnen', 0); $this->SetTimerInterval('MesswerteAufzeichnen', 0);
@@ -263,6 +269,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle
$this->SetTimerInterval('Regelzyklus', 0); $this->SetTimerInterval('Regelzyklus', 0);
$this->SetTimerInterval('VorgabenErneuern', 0); $this->SetTimerInterval('VorgabenErneuern', 0);
$this->SetTimerInterval('KeepAlive', $this->ReadPropertyInteger('KeepAlive') * 1000); $this->SetTimerInterval('KeepAlive', $this->ReadPropertyInteger('KeepAlive') * 1000);
$this->SetTimerInterval('RegelungVerzoegert', 0);
$this->SetTimerInterval('PrognoseSenden', 0); $this->SetTimerInterval('PrognoseSenden', 0);
$this->SetTimerInterval( $this->SetTimerInterval(
'StoerungenSenden', 'StoerungenSenden',
@@ -456,6 +463,11 @@ class Manager extends IPSModule implements ManagerSchnittstelle
$this->regeln((bool) $wert); $this->regeln((bool) $wert);
return; return;
case 'RegelnVerzoegert':
$this->SetTimerInterval('RegelungVerzoegert', 0);
$this->regeln(false);
return;
case 'PrognoseSenden': case 'PrognoseSenden':
$this->sendePrognoseTelemetrie(); $this->sendePrognoseTelemetrie();
return; return;
@@ -624,9 +636,9 @@ class Manager extends IPSModule implements ManagerSchnittstelle
$this->WriteAttributeString('VerbraucherCache', json_encode($cache, JSON_THROW_ON_ERROR)); $this->WriteAttributeString('VerbraucherCache', json_encode($cache, JSON_THROW_ON_ERROR));
$this->aktualisiereEnergieflusswerte(); $this->aktualisiereEnergieflusswerte();
$this->aktualisiereDiagrammMesswerte(); $this->aktualisiereDiagrammMesswerte();
if ($this->GetBuffer('VerbraucherSynchronisierungAktiv') !== '1') {
$this->regeln(false); // Den synchronen Rueckruf in die noch meldende Verbraucherinstanz vermeiden.
} $this->SetTimerInterval('RegelungVerzoegert', 100);
} }
/** @param array<string, mixed> $daten */ /** @param array<string, mixed> $daten */
+15 -1
View File
@@ -91,7 +91,7 @@ final class LadestationAdapter
} }
/** /**
* @return array{Rohstatus: int, Istleistung_W: float, Iststrom_A?: float, Phasenzahl?: int} * @return array{Rohstatus: int, Istleistung_W: float, Sollstrom_A?: int, Maximalstrom_A?: int, Iststrom_A?: float, Phasenzahl?: int}
*/ */
public static function dekodiereStatus(int $geraetetyp, string $antwort): array public static function dekodiereStatus(int $geraetetyp, string $antwort): array
{ {
@@ -115,6 +115,20 @@ final class LadestationAdapter
'Rohstatus' => (int) $daten['car'], 'Rohstatus' => (int) $daten['car'],
'Istleistung_W' => (float) $daten['nrg'][11] * $leistungsfaktor, 'Istleistung_W' => (float) $daten['nrg'][11] * $leistungsfaktor,
]; ];
if (isset($daten['amp']) && is_numeric($daten['amp'])) {
$sollstrom = (int) $daten['amp'];
$freigabeEntzogen = isset($daten['alw']) && $daten['alw'] === false;
$erzwungenAus = $geraetetyp === self::GO_E_GEMINI
&& isset($daten['frc'])
&& (int) $daten['frc'] === 1;
$status['Sollstrom_A'] = $freigabeEntzogen || $erzwungenAus ? 0 : $sollstrom;
}
if (isset($daten['ama']) && is_numeric($daten['ama'])) {
$maximalstrom = (int) $daten['ama'];
if ($maximalstrom >= 6 && $maximalstrom <= 32) {
$status['Maximalstrom_A'] = $maximalstrom;
}
}
if (isset($daten['nrg'][4], $daten['nrg'][5], $daten['nrg'][6]) if (isset($daten['nrg'][4], $daten['nrg'][5], $daten['nrg'][6])
&& is_numeric($daten['nrg'][4]) && is_numeric($daten['nrg'][4])
&& is_numeric($daten['nrg'][5]) && is_numeric($daten['nrg'][5])
+27 -2
View File
@@ -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, Phasenzahl?: int} $erwartet * @param array{Rohstatus: int, Istleistung_W: float, Sollstrom_A?: int, Maximalstrom_A?: int, Iststrom_A?: float, Phasenzahl?: int} $erwartet
*/ */
public function testStatusaufrufUndDekodierungFuerAlleGeraete( public function testStatusaufrufUndDekodierungFuerAlleGeraete(
int $geraetetyp, int $geraetetyp,
@@ -52,6 +52,8 @@ final class LadestationAdapterTest extends TestCase
[ [
'Rohstatus' => 2, 'Rohstatus' => 2,
'Istleistung_W' => 11000.0, 'Istleistung_W' => 11000.0,
'Sollstrom_A' => 16,
'Maximalstrom_A' => 16,
'Iststrom_A' => 16.0, 'Iststrom_A' => 16.0,
'Phasenzahl' => 3, 'Phasenzahl' => 3,
], ],
@@ -64,6 +66,8 @@ final class LadestationAdapterTest extends TestCase
[ [
'Rohstatus' => 2, 'Rohstatus' => 2,
'Istleistung_W' => 2300.0, 'Istleistung_W' => 2300.0,
'Sollstrom_A' => 16,
'Maximalstrom_A' => 16,
'Iststrom_A' => 9.8, 'Iststrom_A' => 9.8,
'Phasenzahl' => 1, 'Phasenzahl' => 1,
], ],
@@ -81,6 +85,20 @@ final class LadestationAdapterTest extends TestCase
]; ];
} }
public function testGoEAusFreigabeWirdAlsNullSollstromDekodiert(): void
{
$daten = json_decode($this->goEAntwort(2, 0, [0, 0, 0]), true, 512, JSON_THROW_ON_ERROR);
$daten['alw'] = false;
$daten['frc'] = 1;
$status = LadestationAdapter::dekodiereStatus(
LadestationAdapter::GO_E_GEMINI,
json_encode($daten, JSON_THROW_ON_ERROR)
);
self::assertSame(0, $status['Sollstrom_A']);
}
/** /**
* @dataProvider steuerFaelle * @dataProvider steuerFaelle
* @param array<string, string> $konfiguration * @param array<string, string> $konfiguration
@@ -180,6 +198,13 @@ final class LadestationAdapterTest extends TestCase
} }
$nrg[11] = $leistung; $nrg[11] = $leistung;
return json_encode(['car' => $status, 'nrg' => $nrg], JSON_THROW_ON_ERROR); return json_encode([
'car' => $status,
'nrg' => $nrg,
'amp' => 16,
'ama' => 16,
'alw' => true,
'frc' => 2,
], JSON_THROW_ON_ERROR);
} }
} }
@@ -104,4 +104,32 @@ final class LadestationStandAloneModulstrukturTest extends TestCase
self::assertStringContainsString('PHASENERKENNUNGSSTROM = 6', $modul); self::assertStringContainsString('PHASENERKENNUNGSSTROM = 6', $modul);
self::assertStringContainsString('UnregisterVariable($ident)', $modul); self::assertStringContainsString('UnregisterVariable($ident)', $modul);
} }
public function testMeldezyklusSendetDenBereitsGelesenenZustandOhneZusaetzlichenHttpAbruf(): void
{
$modul = (string) file_get_contents(self::MODULPFAD . 'module.php');
self::assertSame(1, preg_match(
"/case 'Melden':(?<block>.*?)case 'ManagerdatenEmpfangen':/s",
$modul,
$treffer
));
self::assertStringContainsString('$this->sendeVerbraucherdaten();', $treffer['block']);
self::assertStringNotContainsString('$this->aktualisiere(', $treffer['block']);
}
public function testVomGeraetBestaetigterSollstromWirdFuerWiederholungVerwendet(): void
{
$modul = (string) file_get_contents(self::MODULPFAD . 'module.php');
self::assertStringContainsString("\$rohstatus['Sollstrom_A']", $modul);
self::assertStringContainsString(
"WriteAttributeInteger('LetzterGesetzterStrom', \$geraeteSollstrom)",
$modul
);
self::assertStringContainsString("RegisterAttributeInteger('GeraeteMaximalstrom', 32)", $modul);
self::assertStringContainsString(
"WriteAttributeInteger('GeraeteMaximalstrom', (int) \$rohstatus['Maximalstrom_A'])",
$modul
);
}
} }
+21
View File
@@ -380,4 +380,25 @@ final class ManagerModulstrukturTest extends TestCase
self::assertStringContainsString('VerbraucherdatenEmpfangen(array $daten)', $inhalt); self::assertStringContainsString('VerbraucherdatenEmpfangen(array $daten)', $inhalt);
self::assertStringContainsString("'ManagerdatenEmpfangen'", $inhalt); self::assertStringContainsString("'ManagerdatenEmpfangen'", $inhalt);
} }
public function testVerbrauchermeldungPlantRegelungOhneSynchronenRueckruf(): void
{
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
self::assertStringContainsString(
"RegisterTimer(\n 'RegelungVerzoegert'",
$inhalt
);
self::assertStringContainsString("case 'RegelnVerzoegert':", $inhalt);
self::assertSame(1, preg_match(
'/public function VerbraucherdatenEmpfangen\(array \$daten\): void(?<block>.*?)'
. 'private function aktualisiereVerbraucherEnergie/s',
$inhalt,
$treffer
));
self::assertStringContainsString(
"SetTimerInterval('RegelungVerzoegert', 100)",
$treffer['block']
);
self::assertStringNotContainsString('$this->regeln(false);', $treffer['block']);
}
} }