*/ 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 $stufen * @return list */ public static function leistungswerte(array $stufen): array { return array_merge([0], array_column($stufen, 'Leistung_W')); } /** * @param list $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 $stufen * @return list */ 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 $wert */ private static function istListe(array $wert): bool { return $wert === [] || array_keys($wert) === range(0, count($wert) - 1); } }