Verbrauchermodule und betriebsartabhängige Angebote ergänzen
Tests / test (push) Successful in 42s

This commit is contained in:
dh
2026-09-21 18:46:17 +00:00
parent 7b6d1b2f07
commit 3c71626b5f
46 changed files with 4513 additions and 311 deletions
+214
View File
@@ -0,0 +1,214 @@
<?php
declare(strict_types=1);
namespace Belevo\EnelixEMS;
use InvalidArgumentException;
final class PufferspeicherRegler
{
public const MINIMUM_AUS = 0;
public const MINIMUM_STATISCH = 1;
public const MINIMUM_DIFFERENZ = 2;
/**
* @return list<array{Stufe: int, Leistung_W: int, KontaktID: int}>
*/
public static function dekodiereLeistungsstufen(string $json): array
{
$werte = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
if (!is_array($werte) || !self::istListe($werte)) {
throw new InvalidArgumentException('LeistungsStufen muss eine JSON-Liste sein.');
}
$ergebnis = [];
$leistungen = [];
$kontakte = [];
foreach ($werte as $index => $eintrag) {
if (!is_array($eintrag)) {
throw new InvalidArgumentException('Jede Leistungsstufe muss ein Objekt sein.');
}
$stufe = $eintrag['Stufe'] ?? $index + 1;
$leistung = $eintrag['Leistung'] ?? null;
$kontakt = $eintrag['Schaltkontakt_Stufe'] ?? null;
if (!is_int($stufe) || $stufe <= 0) {
throw new InvalidArgumentException('Stufe muss eine positive Ganzzahl sein.');
}
if (!is_int($leistung) || $leistung <= 0) {
throw new InvalidArgumentException('Leistung muss eine positive Ganzzahl sein.');
}
if (!is_int($kontakt) || $kontakt <= 0) {
throw new InvalidArgumentException('Schaltkontakt_Stufe muss eine positive Variablen-ID sein.');
}
if (isset($leistungen[$leistung])) {
throw new InvalidArgumentException('Leistungen muessen eindeutig sein.');
}
if (isset($kontakte[$kontakt])) {
throw new InvalidArgumentException('Schaltkontakte duerfen nur einer Stufe zugeordnet sein.');
}
$leistungen[$leistung] = true;
$kontakte[$kontakt] = true;
$ergebnis[] = [
'Stufe' => $stufe,
'Leistung_W' => $leistung,
'KontaktID' => $kontakt,
];
}
usort(
$ergebnis,
static fn (array $links, array $rechts): int => $links['Leistung_W'] <=> $rechts['Leistung_W']
);
return $ergebnis;
}
/**
* @param list<array{Stufe: int, Leistung_W: int, KontaktID: int}> $stufen
* @return list<int>
*/
public static function leistungswerte(array $stufen): array
{
return array_merge([0], array_column($stufen, 'Leistung_W'));
}
/**
* @param list<array{Stufe: int, Leistung_W: int, KontaktID: int}> $stufen
*/
public static function stufeFuerLeistung(array $stufen, int $leistung): int
{
if ($leistung === 0) {
return 0;
}
foreach ($stufen as $stufe) {
if ($stufe['Leistung_W'] === $leistung) {
return $stufe['Stufe'];
}
}
throw new InvalidArgumentException('Die Sollleistung entspricht keiner konfigurierten Leistungsstufe.');
}
public static function solltemperatur(
float $aussentemperatur,
float $fusspunkt,
float $steigung,
float $minimum,
float $maximum
): float {
if ($steigung < 0.0) {
throw new InvalidArgumentException('HeizkurvenSteigung darf nicht negativ sein.');
}
if ($minimum > $maximum) {
throw new InvalidArgumentException('Heizkurvenminimum darf nicht groesser als das Maximum sein.');
}
$soll = $fusspunkt + $steigung * (20.0 - $aussentemperatur);
return min($maximum, max($minimum, $soll));
}
public static function einschaltschwelle(
float $solltemperatur,
float $hysterese,
int $minimumModus,
float $statischeMindesttemperatur,
float $mindesttemperaturDifferenz
): float {
if ($hysterese <= 0.0) {
throw new InvalidArgumentException('Hysterese muss groesser als 0 sein.');
}
if ($minimumModus === self::MINIMUM_AUS) {
return $solltemperatur - $hysterese;
}
if ($minimumModus === self::MINIMUM_STATISCH) {
return min($solltemperatur, $statischeMindesttemperatur);
}
if ($minimumModus === self::MINIMUM_DIFFERENZ) {
if ($mindesttemperaturDifferenz < 0.0) {
throw new InvalidArgumentException('MindesttemperaturDifferenz darf nicht negativ sein.');
}
return $solltemperatur - $mindesttemperaturDifferenz;
}
throw new InvalidArgumentException('MindesttemperaturModus muss 0, 1 oder 2 sein.');
}
/**
* @param list<array{Stufe: int, Leistung_W: int, KontaktID: int}> $stufen
* @return list<int>
*/
public static function leistungsangebot(
bool $aktiv,
bool $peakbetrieb,
bool $temperaturenGueltig,
float $puffertemperatur,
float $einschaltschwelle,
array $stufen
): array {
if (!$aktiv || $peakbetrieb || !$temperaturenGueltig || $stufen === []) {
return [0];
}
if ($puffertemperatur < $einschaltschwelle) {
return self::leistungswerte($stufen);
}
return [0];
}
public static function fusspunktAusWaermepumpenwert(
float $waermepumpenSolltemperatur,
float $aussentemperatur,
float $steigung
): float {
if ($steigung < 0.0) {
throw new InvalidArgumentException('HeizkurvenSteigung darf nicht negativ sein.');
}
return $waermepumpenSolltemperatur - $steigung * (20.0 - $aussentemperatur);
}
public static function pt1(float $bisher, float $eingang, int $deltaSekunden, int $zeitkonstante): float
{
if ($deltaSekunden <= 0 || $zeitkonstante <= 0) {
throw new InvalidArgumentException('PT1-Zeiten muessen groesser als 0 sein.');
}
$alpha = $deltaSekunden / ($zeitkonstante + $deltaSekunden);
return $bisher + $alpha * ($eingang - $bisher);
}
public static function restzeitBisLastwechsel(
int $letzterLastwechsel,
int $jetzt,
int $sperrzeitSekunden
): int {
if ($sperrzeitSekunden < 0) {
throw new InvalidArgumentException('LastwechselSperrzeit darf nicht negativ sein.');
}
if ($letzterLastwechsel <= 0) {
return 0;
}
return max(0, $sperrzeitSekunden - max(0, $jetzt - $letzterLastwechsel));
}
public static function energieKWh(float $leistungW, int $dauerSekunden): float
{
if ($dauerSekunden < 0) {
throw new InvalidArgumentException('Dauer darf nicht negativ sein.');
}
return $leistungW * $dauerSekunden / 3600000.0;
}
/** @param array<mixed> $wert */
private static function istListe(array $wert): bool
{
return $wert === [] || array_keys($wert) === range(0, count($wert) - 1);
}
}