215 lines
7.0 KiB
PHP
215 lines
7.0 KiB
PHP
<?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);
|
|
}
|
|
}
|