feat(ladestation): Phasenerkennung und Schaltsperren erweitern
Tests / test (push) Successful in 45s

This commit is contained in:
dh
2026-09-29 22:49:26 +00:00
parent 53fca5e2bb
commit faa5dfb33f
10 changed files with 593 additions and 113 deletions
+14 -6
View File
@@ -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
{
@@ -121,11 +121,19 @@ final class LadestationAdapter
&& is_numeric($daten['nrg'][6])
) {
$stromfaktor = $geraetetyp === self::GO_E_ALT ? 0.1 : 1.0;
$status['Iststrom_A'] = max(
(float) $daten['nrg'][4],
(float) $daten['nrg'][5],
(float) $daten['nrg'][6]
) * $stromfaktor;
$phasenstroeme = [
(float) $daten['nrg'][4] * $stromfaktor,
(float) $daten['nrg'][5] * $stromfaktor,
(float) $daten['nrg'][6] * $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;
+36 -25
View File
@@ -12,27 +12,15 @@ final class LadestationRegler
public const PHASEN_EIN = 1;
public const PHASEN_DREI = 3;
/** @var list<int> */
private const LEISTUNGEN_EINPHASIG = [
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,
];
private const LEISTUNG_PRO_AMPERE_EINPHASIG = 230;
private const LEISTUNG_PRO_AMPERE_DREIPHASIG = 684;
/**
* 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
* 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>
*/
public static function normalisiereStatus(
@@ -40,7 +28,8 @@ final class LadestationRegler
bool $warVerbunden,
bool $warGeladen = false,
bool $ladungErwartet = true,
bool $ladeleistungWurdeErkannt = false
bool $ladeleistungWurdeErkannt = false,
int $bekanntePhasenzahl = self::PHASEN_UNBEKANNT
): array {
$verbunden = $rohstatus['Rohstatus'] !== 1;
$leistung = max(0.0, $rohstatus['Istleistung_W']);
@@ -49,11 +38,22 @@ final class LadestationRegler
$geladen = false;
if ($verbunden) {
$phasen = $leistung > 7500.0 ? self::PHASEN_DREI : self::PHASEN_EIN;
$nenner = $phasen === self::PHASEN_EIN ? 230.0 : 1.71 * 400.0;
if (in_array($bekanntePhasenzahl, [self::PHASEN_EIN, self::PHASEN_DREI], true)) {
$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'])
? max(0.0, (float) $rohstatus['Iststrom_A'])
: $leistung / $nenner;
: ($phasen === self::PHASEN_UNBEKANNT ? 0.0 : $leistung / $nenner);
$ermittelterMaximalstrom = 2.5 + $ladestrom;
$geladen = $warGeladen
|| ($warVerbunden
@@ -94,7 +94,10 @@ final class LadestationRegler
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) {
return null;
}
@@ -117,6 +120,10 @@ final class LadestationRegler
return [0];
}
if ($phasenzahl === self::PHASEN_UNBEKANNT) {
return [0];
}
$stufen = self::leistungsstufen($phasenzahl, $maximalerLadestrom);
if ($stufen === []) {
return [0];
@@ -167,12 +174,16 @@ final class LadestationRegler
private static function stufenFuerPhasen(int $phasenzahl): array
{
if ($phasenzahl === self::PHASEN_EIN) {
return self::LEISTUNGEN_EINPHASIG;
}
if ($phasenzahl === self::PHASEN_DREI) {
return self::LEISTUNGEN_DREIPHASIG;
$leistungProAmpere = self::LEISTUNG_PRO_AMPERE_EINPHASIG;
} elseif ($phasenzahl === self::PHASEN_DREI) {
$leistungProAmpere = self::LEISTUNG_PRO_AMPERE_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)
);
}
}