179 lines
5.5 KiB
PHP
179 lines
5.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Belevo\EnelixEMS;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
final class LadestationRegler
|
|
{
|
|
public const PHASEN_UNBEKANNT = 0;
|
|
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,
|
|
];
|
|
|
|
/**
|
|
* 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
|
|
* @return array<string, int|float|bool>
|
|
*/
|
|
public static function normalisiereStatus(
|
|
array $rohstatus,
|
|
bool $warVerbunden,
|
|
bool $warGeladen = false,
|
|
bool $ladungErwartet = true,
|
|
bool $ladeleistungWurdeErkannt = false
|
|
): array {
|
|
$verbunden = $rohstatus['Rohstatus'] !== 1;
|
|
$leistung = max(0.0, $rohstatus['Istleistung_W']);
|
|
$phasen = self::PHASEN_UNBEKANNT;
|
|
$ladestrom = 0.0;
|
|
$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;
|
|
$ladestrom = isset($rohstatus['Iststrom_A'])
|
|
? max(0.0, (float) $rohstatus['Iststrom_A'])
|
|
: $leistung / $nenner;
|
|
$ermittelterMaximalstrom = 2.5 + $ladestrom;
|
|
$geladen = $warGeladen
|
|
|| ($warVerbunden
|
|
&& $ladungErwartet
|
|
&& $ladeleistungWurdeErkannt
|
|
&& $ermittelterMaximalstrom < 6.0);
|
|
}
|
|
|
|
if (!$verbunden) {
|
|
$fahrzeugstatus = 1;
|
|
} elseif ($geladen) {
|
|
$fahrzeugstatus = 4;
|
|
} elseif ($leistung > 0.0) {
|
|
$fahrzeugstatus = 3;
|
|
} else {
|
|
$fahrzeugstatus = 2;
|
|
}
|
|
|
|
return [
|
|
'FahrzeugVerbunden' => $verbunden,
|
|
'FahrzeugGeladen' => $geladen,
|
|
'Fahrzeugstatus' => $fahrzeugstatus,
|
|
'Phasenzahl' => $phasen,
|
|
'Istleistung_W' => $leistung,
|
|
'Ladestrom_A' => $ladestrom,
|
|
];
|
|
}
|
|
|
|
public static function ermittleFahrzeugMaximalstrom(
|
|
int $vorgegebenerStrom,
|
|
float $gemessenerStrom,
|
|
int $konfigurierterMaximalstrom
|
|
): ?int {
|
|
if ($konfigurierterMaximalstrom < 6 || $konfigurierterMaximalstrom > 32) {
|
|
throw new InvalidArgumentException('MaximalerLadestrom muss zwischen 6 und 32 A liegen.');
|
|
}
|
|
if ($vorgegebenerStrom < 6 || $gemessenerStrom < 5.5) {
|
|
return null;
|
|
}
|
|
|
|
$kandidat = max(6, min($konfigurierterMaximalstrom, (int) round($gemessenerStrom)));
|
|
if ($kandidat > $vorgegebenerStrom - 2 || $kandidat >= $konfigurierterMaximalstrom) {
|
|
return null;
|
|
}
|
|
|
|
return $kandidat;
|
|
}
|
|
|
|
/** @return list<int> */
|
|
public static function leistungsangebot(
|
|
bool $aktiv,
|
|
bool $ladefreigabe,
|
|
bool $verbunden,
|
|
bool $geladen,
|
|
int $phasenzahl,
|
|
int $maximalerLadestrom,
|
|
bool $solarladen,
|
|
bool $peakbetrieb
|
|
): array {
|
|
if (!$aktiv || !$ladefreigabe || !$verbunden || $geladen) {
|
|
return [0];
|
|
}
|
|
|
|
$stufen = self::leistungsstufen($phasenzahl, $maximalerLadestrom);
|
|
if ($stufen === []) {
|
|
return [0];
|
|
}
|
|
if ($solarladen) {
|
|
return $peakbetrieb ? [0] : array_merge([0], $stufen);
|
|
}
|
|
if ($peakbetrieb) {
|
|
return array_merge([0], $stufen);
|
|
}
|
|
|
|
return [$stufen[count($stufen) - 1]];
|
|
}
|
|
|
|
public static function stromFuerLeistung(int $leistung, int $phasenzahl): int
|
|
{
|
|
if ($leistung <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
$stufen = self::stufenFuerPhasen($phasenzahl);
|
|
if ($leistung < $stufen[0]) {
|
|
return 0;
|
|
}
|
|
|
|
$strom = 6;
|
|
foreach ($stufen as $index => $schwelle) {
|
|
if ($leistung < $schwelle) {
|
|
break;
|
|
}
|
|
$strom = 6 + $index;
|
|
}
|
|
|
|
return min(32, $strom);
|
|
}
|
|
|
|
/** @return list<int> */
|
|
private static function leistungsstufen(int $phasenzahl, int $maximalerLadestrom): array
|
|
{
|
|
if ($maximalerLadestrom < 6 || $maximalerLadestrom > 32) {
|
|
throw new InvalidArgumentException('MaximalerLadestrom muss zwischen 6 und 32 A liegen.');
|
|
}
|
|
|
|
return array_slice(self::stufenFuerPhasen($phasenzahl), 0, $maximalerLadestrom - 5);
|
|
}
|
|
|
|
/** @return list<int> */
|
|
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;
|
|
}
|
|
|
|
throw new InvalidArgumentException('Phasenzahl muss 1 oder 3 sein.');
|
|
}
|
|
}
|