$lease */ public static function pruefeLease( array $lease, string $installationID, int $lastspitzenmodus, ?int $zeitpunkt = null ): void { if (($lease['status'] ?? null) !== 'active') { throw new InvalidArgumentException('Die Lizenz ist nicht aktiv.'); } if (($lease['installationId'] ?? null) !== $installationID) { throw new InvalidArgumentException('Die Lizenz gehoert zu einer anderen Installation.'); } if (isset($lease['development']) && !is_bool($lease['development'])) { throw new InvalidArgumentException('Der Entwicklungsstatus der Lizenz ist ungueltig.'); } $ausgestelltAm = self::zeitstempel($lease, 'issuedAt'); $erneuernAb = self::zeitstempel($lease, 'refreshAfter'); $offlineBis = self::zeitstempel($lease, 'offlineUntil'); if ($ausgestelltAm > $erneuernAb || $erneuernAb > $offlineBis) { throw new InvalidArgumentException('Die Lizenzzeitpunkte sind widerspruechlich.'); } if (($zeitpunkt ?? time()) >= $offlineBis) { throw new InvalidArgumentException('Die Offline-Freigabe ist abgelaufen.'); } $berechtigungen = $lease['entitlements'] ?? null; if (!is_array($berechtigungen)) { throw new InvalidArgumentException('Die Lizenz enthaelt keine Berechtigungen.'); } $standard = false; $peak = false; foreach ($berechtigungen as $berechtigung) { if (!is_array($berechtigung) || ($berechtigung['status'] ?? null) !== 'active' || !is_int($berechtigung['quantity'] ?? null) || $berechtigung['quantity'] < 1 ) { continue; } $kennung = $berechtigung['catalog_key'] ?? $berechtigung['sku'] ?? null; $standard = $standard || $kennung === self::MANAGER_STANDARD; $peak = $peak || $kennung === self::MANAGER_PEAK; } if (!$standard && !$peak) { throw new InvalidArgumentException('Die Manager-Berechtigung fehlt.'); } if ($lastspitzenmodus !== 0 && !$peak) { throw new InvalidArgumentException('Peak Shaving ist nicht lizenziert.'); } } /** * Liefert die aufsummierte aktive Berechtigung fuer einen Katalogeintrag. * * @param array $lease */ public static function berechtigungsMenge(array $lease, string $kennung): int { $berechtigungen = $lease['entitlements'] ?? null; if (!is_array($berechtigungen)) { return 0; } $menge = 0; foreach ($berechtigungen as $berechtigung) { if (!is_array($berechtigung) || ($berechtigung['status'] ?? null) !== 'active' || !is_int($berechtigung['quantity'] ?? null) || $berechtigung['quantity'] < 1 ) { continue; } $berechtigungsKennung = $berechtigung['catalog_key'] ?? $berechtigung['sku'] ?? null; if ($berechtigungsKennung === $kennung) { $menge += $berechtigung['quantity']; } } return $menge; } /** * Eine Prognose ist entweder durch den Netzfahrplan oder durch beide * Einzelberechtigungen fuer PV und Verbrauch freigeschaltet. * * @param array $lease */ public static function hatPrognoseBerechtigung(array $lease): bool { return self::berechtigungsMenge($lease, self::NETZFAHRPLAN) > 0 || (self::berechtigungsMenge($lease, self::PROGNOSE_PV) > 0 && self::berechtigungsMenge($lease, self::PROGNOSE_VERBRAUCH) > 0); } /** * Rechnet freie, bestehende Einzellizenzen auf nicht separat lizenzierte Waermepumpen an. * * @param array $lease * @param array $verwendeteMengen * @return array */ public static function beruecksichtigeBestandslizenzFuerWaermepumpen( array $lease, array $verwendeteMengen ): array { $waermepumpen = $verwendeteMengen['heat_pump'] ?? 0; if ($waermepumpen <= 0) { return $verwendeteMengen; } $nichtSeparatLizenziert = max( 0, $waermepumpen - self::berechtigungsMenge($lease, 'heat_pump') ); $verbraucher = $verwendeteMengen['consumer_single'] ?? 0; $freieBestandslizenzen = max( 0, self::berechtigungsMenge($lease, 'consumer_single') - $verbraucher ); $angerechnet = min($nichtSeparatLizenziert, $freieBestandslizenzen); if ($angerechnet === 0) { return $verwendeteMengen; } $verwendeteMengen['heat_pump'] = $waermepumpen - $angerechnet; $verwendeteMengen['consumer_single'] = $verbraucher + $angerechnet; return $verwendeteMengen; } /** * @param array $lease * @param array $verwendeteMengen */ public static function pruefeVerbrauchermengen(array $lease, array $verwendeteMengen): void { foreach ($verwendeteMengen as $kennung => $verwendet) { if ($verwendet < 0) { throw new InvalidArgumentException( 'Die verwendete Menge fuer ' . $kennung . ' darf nicht negativ sein.' ); } $lizenziert = self::berechtigungsMenge($lease, $kennung); if ($verwendet > $lizenziert) { throw new InvalidArgumentException(sprintf( 'Lizenzkontingent fuer %s ueberschritten: %d verwendet, %d lizenziert.', $kennung, $verwendet, $lizenziert )); } } } /** * @param array $lease */ public static function istLeaseGueltig( array $lease, string $installationID, int $lastspitzenmodus, ?int $zeitpunkt = null ): bool { try { self::pruefeLease($lease, $installationID, $lastspitzenmodus, $zeitpunkt); return true; } catch (Throwable $fehler) { return false; } } /** * @param array $lease */ public static function erneuerungFaellig(array $lease, ?int $zeitpunkt = null): bool { try { return ($zeitpunkt ?? time()) >= self::zeitstempel($lease, 'refreshAfter'); } catch (Throwable $fehler) { return true; } } /** * @param array $lease */ public static function offlineBis(array $lease): int { return self::zeitstempel($lease, 'offlineUntil'); } /** * @param array $lease */ public static function erneuernAb(array $lease): int { return self::zeitstempel($lease, 'refreshAfter'); } /** * @param array $lease */ private static function zeitstempel(array $lease, string $feld): int { $wert = $lease[$feld] ?? null; if (!is_string($wert) || trim($wert) === '') { throw new InvalidArgumentException($feld . ' fehlt in der Lizenz.'); } try { return (new DateTimeImmutable($wert))->getTimestamp(); } catch (Throwable $fehler) { throw new InvalidArgumentException($feld . ' ist kein gueltiger ISO-8601-Zeitpunkt.', 0, $fehler); } } }