feat: anlagenweite Einspeisebegrenzung und Netzfahrplan
Tests / test (push) Successful in 50s

This commit is contained in:
dh
2026-09-26 09:09:08 +00:00
parent efa8fe698c
commit dcf51bd8be
8 changed files with 737 additions and 17 deletions
+127
View File
@@ -0,0 +1,127 @@
<?php
declare(strict_types=1);
namespace Belevo\EnelixEMS;
use InvalidArgumentException;
/**
* Verteilt eine gemeinsame Einspeisegrenze auf regelbare PV-Wechselrichter.
*
* Netzleistung: positiv Bezug, negativ Einspeisung.
*/
final class EinspeiseRegler
{
/**
* @param list<array{ID: string, Nennleistung_W: float|int, Istleistung_W: float|int}> $wechselrichter
* @param array<string, float|int> $letzteGrenzen
* @return array{
* Netzabweichung_W: float,
* ZielGesamtleistung_W: float,
* Abregelbedarf_W: float,
* Grenzen_W: array<string, float>
* }
*/
public static function verteile(
float $netzleistung,
float $einspeisegrenze,
float $toleranz,
array $wechselrichter,
array $letzteGrenzen = []
): array {
if ($einspeisegrenze < 0.0) {
throw new InvalidArgumentException('Einspeisegrenze darf nicht negativ sein.');
}
if ($toleranz < 0.0) {
throw new InvalidArgumentException('Einspeisetoleranz darf nicht negativ sein.');
}
if ($wechselrichter === []) {
throw new InvalidArgumentException('Mindestens ein regelbarer Wechselrichter ist erforderlich.');
}
$nennleistungGesamt = 0.0;
$istleistungGesamt = 0.0;
$bisherigeGrenzeGesamt = 0.0;
$normalisiert = [];
$kennungen = [];
foreach ($wechselrichter as $eintrag) {
$id = $eintrag['ID'] ?? null;
$nennleistung = $eintrag['Nennleistung_W'] ?? null;
$istleistung = $eintrag['Istleistung_W'] ?? null;
if (!is_string($id) || trim($id) === '' || isset($kennungen[$id])) {
throw new InvalidArgumentException('Wechselrichter-ID fehlt oder ist mehrfach vorhanden.');
}
if (!is_numeric($nennleistung) || (float) $nennleistung <= 0.0) {
throw new InvalidArgumentException('Wechselrichter-Nennleistung muss groesser als 0 sein.');
}
if (!is_numeric($istleistung) || (float) $istleistung < 0.0) {
throw new InvalidArgumentException('Wechselrichter-Istleistung darf nicht negativ sein.');
}
$nennleistung = (float) $nennleistung;
$istleistung = min((float) $istleistung, $nennleistung);
$bisherigeGrenze = $letzteGrenzen[$id] ?? $nennleistung;
if (!is_numeric($bisherigeGrenze)) {
throw new InvalidArgumentException('Gespeicherte Wechselrichtergrenze ist ungueltig.');
}
$bisherigeGrenze = max(0.0, min((float) $bisherigeGrenze, $nennleistung));
$normalisiert[] = [
'ID' => $id,
'Nennleistung_W' => $nennleistung,
'Istleistung_W' => $istleistung,
];
$kennungen[$id] = true;
$nennleistungGesamt += $nennleistung;
$istleistungGesamt += $istleistung;
$bisherigeGrenzeGesamt += $bisherigeGrenze;
}
$netzabweichung = $netzleistung + $einspeisegrenze;
$zielGesamt = $bisherigeGrenzeGesamt;
$nachIstleistungVerteilen = false;
if ($netzabweichung < -$toleranz) {
$nachIstleistungVerteilen = $istleistungGesamt > 0.0;
$wirksameAbweichung = $netzabweichung + $toleranz;
$zielGesamt = min($bisherigeGrenzeGesamt, $istleistungGesamt) + $wirksameAbweichung;
} elseif ($netzabweichung > $toleranz) {
$zielGesamt = $bisherigeGrenzeGesamt + $netzabweichung - $toleranz;
}
$zielGesamt = max(0.0, min($zielGesamt, $nennleistungGesamt));
$grenzen = [];
foreach ($normalisiert as $eintrag) {
$anteil = $nachIstleistungVerteilen
? $eintrag['Istleistung_W'] / $istleistungGesamt
: $eintrag['Nennleistung_W'] / $nennleistungGesamt;
$grenzen[$eintrag['ID']] = round(
min($eintrag['Nennleistung_W'], $zielGesamt * $anteil),
3
);
}
return [
'Netzabweichung_W' => $netzabweichung,
'ZielGesamtleistung_W' => round($zielGesamt, 3),
'Abregelbedarf_W' => round(max(0.0, $istleistungGesamt - $zielGesamt), 3),
'Grenzen_W' => $grenzen,
];
}
public static function stellwert(float $grenzeW, float $nennleistungW, string $art): float
{
if ($nennleistungW <= 0.0 || $grenzeW < 0.0) {
throw new InvalidArgumentException('Leistungsgrenzen sind ungueltig.');
}
$grenzeW = min($grenzeW, $nennleistungW);
if ($art === 'percent') {
return round($grenzeW / $nennleistungW * 100.0, 2);
}
if ($art === 'watt') {
return round($grenzeW, 1);
}
throw new InvalidArgumentException('Begrenzungsart muss percent oder watt sein.');
}
}