This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Belevo\EnelixUtils;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class VirtuelleBatterieRegler
|
||||
{
|
||||
/**
|
||||
* @param list<array{
|
||||
* Index:int,
|
||||
* Name:string,
|
||||
* Kapazitaet_kWh:float,
|
||||
* MaxLaden_W:float,
|
||||
* MaxEntladen_W:float,
|
||||
* Ladezustand_Prozent:float,
|
||||
* Istleistung_W:float
|
||||
* }> $batterien
|
||||
* @return array<string, float>
|
||||
*/
|
||||
public static function aggregiere(
|
||||
array $batterien,
|
||||
float $sdlReserveLadenW,
|
||||
float $sdlReserveEntladenW,
|
||||
float $reservezeitH
|
||||
): array {
|
||||
if ($sdlReserveLadenW < 0.0
|
||||
|| $sdlReserveEntladenW < 0.0
|
||||
|| $reservezeitH < 0.0
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'SDL-Reserven und Reservezeit duerfen nicht negativ sein.'
|
||||
);
|
||||
}
|
||||
|
||||
$kapazitaet = 0.0;
|
||||
$energie = 0.0;
|
||||
$maxLaden = 0.0;
|
||||
$maxEntladen = 0.0;
|
||||
$istleistung = 0.0;
|
||||
|
||||
foreach ($batterien as $batterie) {
|
||||
$kapazitaetKWh = (float) $batterie['Kapazitaet_kWh'];
|
||||
$ladezustand = (float) $batterie['Ladezustand_Prozent'];
|
||||
$ladenW = (float) $batterie['MaxLaden_W'];
|
||||
$entladenW = (float) $batterie['MaxEntladen_W'];
|
||||
|
||||
if ($kapazitaetKWh <= 0.0 || $ladenW < 0.0 || $entladenW < 0.0) {
|
||||
throw new InvalidArgumentException(
|
||||
'Kapazitaet muss positiv und Leistungsgrenzen duerfen nicht negativ sein.'
|
||||
);
|
||||
}
|
||||
if ($ladezustand < 0.0 || $ladezustand > 100.0) {
|
||||
throw new InvalidArgumentException(
|
||||
'Ladezustand muss zwischen 0 und 100 Prozent liegen.'
|
||||
);
|
||||
}
|
||||
|
||||
$kapazitaet += $kapazitaetKWh;
|
||||
$energie += $kapazitaetKWh * $ladezustand / 100.0;
|
||||
$maxLaden += $ladenW;
|
||||
$maxEntladen += $entladenW;
|
||||
$istleistung += (float) $batterie['Istleistung_W'];
|
||||
}
|
||||
|
||||
if ($kapazitaet <= 0.0) {
|
||||
throw new InvalidArgumentException(
|
||||
'Mindestens eine verfuegbare Batterie ist erforderlich.'
|
||||
);
|
||||
}
|
||||
|
||||
$sdlLadenW = min($sdlReserveLadenW, $maxLaden);
|
||||
$sdlEntladenW = min($sdlReserveEntladenW, $maxEntladen);
|
||||
$untereReserve = $sdlEntladenW * $reservezeitH / 1000.0;
|
||||
$obereReserve = $sdlLadenW * $reservezeitH / 1000.0;
|
||||
|
||||
if ($untereReserve + $obereReserve > $kapazitaet) {
|
||||
throw new InvalidArgumentException(
|
||||
'Die SDL-Energiereserven sind groesser als die Gesamtkapazitaet.'
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'Kapazitaet_kWh' => $kapazitaet,
|
||||
'Energie_kWh' => $energie,
|
||||
'Ladezustand_Prozent' => $energie / $kapazitaet * 100.0,
|
||||
'Istleistung_W' => $istleistung,
|
||||
'MaxLaden_W' => $maxLaden,
|
||||
'MaxEntladen_W' => $maxEntladen,
|
||||
'SDLKapazitaet_kWh' => $untereReserve + $obereReserve,
|
||||
'EigenverbrauchKapazitaet_kWh' =>
|
||||
$kapazitaet - $untereReserve - $obereReserve,
|
||||
'SDLMaxLaden_W' => $sdlLadenW,
|
||||
'SDLMaxEntladen_W' => $sdlEntladenW,
|
||||
'EigenverbrauchMaxLaden_W' => max(0.0, $maxLaden - $sdlLadenW),
|
||||
'EigenverbrauchMaxEntladen_W' => max(0.0, $maxEntladen - $sdlEntladenW),
|
||||
'UntereSDLReserve_kWh' => $untereReserve,
|
||||
'ObereSDLReserve_kWh' => $obereReserve,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* SDL wird zuerst begrenzt. Eigenverbrauch erhaelt die verbleibende
|
||||
* physische Leistung derselben Richtung.
|
||||
*
|
||||
* @param array<string, float> $aggregat
|
||||
* @return array{Eigenverbrauch_W:float, SDL_W:float, Gesamt_W:float}
|
||||
*/
|
||||
public static function kombiniere(
|
||||
float $eigenverbrauchW,
|
||||
float $sdlW,
|
||||
array $aggregat
|
||||
): array {
|
||||
$sdl = self::begrenze(
|
||||
$sdlW,
|
||||
(float) $aggregat['SDLMaxLaden_W'],
|
||||
(float) $aggregat['SDLMaxEntladen_W']
|
||||
);
|
||||
$ev = self::begrenze(
|
||||
$eigenverbrauchW,
|
||||
(float) $aggregat['EigenverbrauchMaxLaden_W'],
|
||||
(float) $aggregat['EigenverbrauchMaxEntladen_W']
|
||||
);
|
||||
|
||||
if ($sdl > 0.0 && $ev > 0.0) {
|
||||
$ev = min($ev, max(0.0, (float) $aggregat['MaxLaden_W'] - $sdl));
|
||||
} elseif ($sdl < 0.0 && $ev < 0.0) {
|
||||
$ev = max($ev, -max(
|
||||
0.0,
|
||||
(float) $aggregat['MaxEntladen_W'] - abs($sdl)
|
||||
));
|
||||
}
|
||||
|
||||
$gesamt = self::begrenze(
|
||||
$sdl + $ev,
|
||||
(float) $aggregat['MaxLaden_W'],
|
||||
(float) $aggregat['MaxEntladen_W']
|
||||
);
|
||||
|
||||
return [
|
||||
'Eigenverbrauch_W' => $ev,
|
||||
'SDL_W' => $sdl,
|
||||
'Gesamt_W' => $gesamt,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array{
|
||||
* Index:int,
|
||||
* Name:string,
|
||||
* Kapazitaet_kWh:float,
|
||||
* MaxLaden_W:float,
|
||||
* MaxEntladen_W:float,
|
||||
* Ladezustand_Prozent:float,
|
||||
* Istleistung_W:float
|
||||
* }> $batterien
|
||||
* @return array<int, float>
|
||||
*/
|
||||
public static function verteile(float $gesamtW, array $batterien): array
|
||||
{
|
||||
$ergebnis = [];
|
||||
foreach ($batterien as $batterie) {
|
||||
$ergebnis[(int) $batterie['Index']] = 0.0;
|
||||
}
|
||||
if (abs($gesamtW) < 0.5) {
|
||||
return $ergebnis;
|
||||
}
|
||||
|
||||
$laden = $gesamtW > 0.0;
|
||||
usort(
|
||||
$batterien,
|
||||
static function (array $a, array $b) use ($laden): int {
|
||||
$vergleich = (float) $a['Ladezustand_Prozent']
|
||||
<=> (float) $b['Ladezustand_Prozent'];
|
||||
return $laden ? $vergleich : -$vergleich;
|
||||
}
|
||||
);
|
||||
|
||||
$rest = abs($gesamtW);
|
||||
foreach ($batterien as $batterie) {
|
||||
if ($rest < 0.5) {
|
||||
break;
|
||||
}
|
||||
$grenze = $laden
|
||||
? (float) $batterie['MaxLaden_W']
|
||||
: (float) $batterie['MaxEntladen_W'];
|
||||
$anteil = min($rest, max(0.0, $grenze));
|
||||
$ergebnis[(int) $batterie['Index']] = $laden ? $anteil : -$anteil;
|
||||
$rest -= $anteil;
|
||||
}
|
||||
|
||||
return $ergebnis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die SDL-Leistung wird bilanziell zuerst bedient. Die Differenz zur
|
||||
* gemessenen Gesamtleistung wird dem Eigenverbrauch zugeordnet.
|
||||
*
|
||||
* @return array{Eigenverbrauch_W:float, SDL_W:float}
|
||||
*/
|
||||
public static function ordneIstleistungZu(
|
||||
float $gesamtIstW,
|
||||
float $sdlAuftragW
|
||||
): array {
|
||||
return [
|
||||
'Eigenverbrauch_W' => $gesamtIstW - $sdlAuftragW,
|
||||
'SDL_W' => $sdlAuftragW,
|
||||
];
|
||||
}
|
||||
|
||||
private static function begrenze(
|
||||
float $leistungW,
|
||||
float $maxLadenW,
|
||||
float $maxEntladenW
|
||||
): float {
|
||||
return min($maxLadenW, max(-$maxEntladenW, $leistungW));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user