diff --git a/Bat_EV_SDL _V2/module.php b/Bat_EV_SDL _V2/module.php index 356c010..8aa9506 100644 --- a/Bat_EV_SDL _V2/module.php +++ b/Bat_EV_SDL _V2/module.php @@ -490,197 +490,186 @@ class Bat_EV_SDL_V2 extends IPSModule + private function CalculateBatteryDistribution(float $pEvW, float $pSdlW): array - { - $calcJsonId = $this->GetIDForIdent("CalcJSON"); - // Fallback, falls Variable leer ist oder nicht existiert - if (!IPS_VariableExists($calcJsonId)) { - return []; - } - $rawJson = (string)GetValue($calcJsonId); - if (empty($rawJson)) { - return []; - } - $calc = json_decode($rawJson, true); - $batteries = $calc['batteries'] ?? []; - - // --------------------------------------------------------- - // Hilfsfunktion: Verteilungslogik (Closure) - // --------------------------------------------------------- - $distributePower = function(float $targetPower, string $mode) use ($batteries): array { - // Initialisierung des Ergebnis-Arrays (Key = idx, Value = zugewiesene Watt) - $result = []; - foreach ($batteries as $bat) { - $result[$bat['idx']] = 0.0; - } - - if (abs($targetPower) < 0.01) { - return $result; // Nichts zu tun - } - - $isCharge = ($targetPower > 0); - $absPower = abs($targetPower); - // Relevante Keys basierend auf Modus (EV oder SDL) und Richtung (Laden/Entladen) - $socKey = ($mode === 'EV') ? 'EV_SOC' : 'SDL_SOC'; - // Achtung: JSON Limits sind in kW, wir rechnen in Watt -> * 1000 - $limitKey = $isCharge ? $mode . '_Charge_kW' : $mode . '_Discharge_kW'; - - // 1. Batterien vorbereiten und gruppieren nach gerundetem SoC - $groups = []; - foreach ($batteries as $bat) { - $soc = (int)round($bat[$socKey]); // Auf ganze Zahl runden - $maxW = ((float)$bat[$limitKey]) * 1000.0; // kW in Watt umrechnen - $groups[$soc][] = [ - 'idx' => $bat['idx'], - 'maxW' => $maxW - ]; - } - - // 2. Sortieren der Gruppen - // Laden: Wenig SoC zuerst (ASC) -> leere füllen - // Entladen: Viel SoC zuerst (DESC) -> volle leeren - if ($isCharge) { - ksort($groups); - } else { - krsort($groups); - } - - // 3. Verteilung - $remainingNeeded = $absPower; - - foreach ($groups as $soc => $groupBatteries) { - if ($remainingNeeded <= 0.01) break; - - // Gesamte verfügbare Leistung in dieser SoC-Gruppe ermitteln - $groupTotalCapacity = 0.0; - foreach ($groupBatteries as $gb) { - $groupTotalCapacity += $gb['maxW']; - } - - // Wie viel können wir dieser Gruppe zuteilen? - // Entweder alles was die Gruppe kann, oder den Restbedarf - $powerForThisGroup = min($remainingNeeded, $groupTotalCapacity); - - // Proportionale Aufteilung innerhalb der Gruppe - // Falls Gruppe Kapazität 0 hat (Defekt/Voll), verhindern wir DivByZero - if ($groupTotalCapacity > 0) { - $ratio = $powerForThisGroup / $groupTotalCapacity; - foreach ($groupBatteries as $gb) { - $assigned = $gb['maxW'] * $ratio; - $result[$gb['idx']] = $assigned; - } - } - - $remainingNeeded -= $powerForThisGroup; - } - - // Wenn wir entladen, müssen die Werte negativ sein - if (!$isCharge) { - foreach ($result as $idx => $val) { - $result[$idx] = -$val; - } - } - - return $result; - }; - - // --------------------------------------------------------- - // Hauptablauf - // --------------------------------------------------------- - - // 1. Berechnung für EV und SDL getrennt durchführen - $evDistribution = $distributePower($pEvW, 'EV'); - $sdlDistribution = $distributePower($pSdlW, 'SDL'); - - // 2. Ergebnisse zusammenführen und Output formatieren - $finalOutput = []; - +{ + $calcJsonId = $this->GetIDForIdent("CalcJSON"); + // Fallback, falls Variable leer ist oder nicht existiert + if (!IPS_VariableExists($calcJsonId)) { + return []; + } + $rawJson = (string)GetValue($calcJsonId); + if (empty($rawJson)) { + return []; + } + $calc = json_decode($rawJson, true); + $batteries = $calc['batteries'] ?? []; + + // --------------------------------------------------------- + // Hilfsfunktion: Verteilungslogik (Closure) + // --------------------------------------------------------- + $distributePower = function(float $targetPower, string $mode) use ($batteries): array { + // Initialisierung des Ergebnis-Arrays (Key = idx, Value = zugewiesene Watt) + $result = []; foreach ($batteries as $bat) { - $idx = $bat['idx']; - // Summe der beiden Anforderungen (kann sich gegenseitig aufheben) - $valEv = $evDistribution[$idx] ?? 0.0; - $valSdl = $sdlDistribution[$idx] ?? 0.0; - $totalW = $valEv + $valSdl; - - // Aufteilen in Charge / Discharge für das Return-Format - $chargeW = 0.0; - $dischargeW = 0.0; - - if ($totalW > 0) { - $chargeW = abs($totalW); - } else { - $dischargeW = abs($totalW); - } - - // JSON Objekt erstellen - $finalOutput[] = [ - "idx" => $idx, - "typ" => (string)$bat['typ'], // Typ als String beibehalten - "chargeW" => round($chargeW, 0), // Optional: runden für sauberes JSON - "dischargeW" => round($dischargeW, 0) + $result[$bat['idx']] = 0.0; + } + + if (abs($targetPower) < 0.01) { + return $result; // Nichts zu tun + } + + $isCharge = ($targetPower > 0); + $absPower = abs($targetPower); + // Relevante Keys basierend auf Modus (EV oder SDL) und Richtung (Laden/Entladen) + $socKey = ($mode === 'EV') ? 'EV_SOC' : 'SDL_SOC'; + // Achtung: JSON Limits sind in kW, wir rechnen in Watt -> * 1000 + $limitKey = $isCharge ? $mode . '_Charge_kW' : $mode . '_Discharge_kW'; + + // 1. Batterien vorbereiten und gruppieren nach gerundetem SoC + $groups = []; + foreach ($batteries as $bat) { + $soc = (int)round($bat[$socKey]); // Auf ganze Zahl runden + $maxW = ((float)$bat[$limitKey]) * 1000.0; // kW in Watt umrechnen + $groups[$soc][] = [ + 'idx' => $bat['idx'], + 'maxW' => $maxW ]; } - - - $batteriesRaw = json_decode($this->ReadPropertyString("Batteries")); - - $totalPower_ist = 0; - - foreach ($batteriesRaw as $bat) { - - $totalPower_ist += GetValue($bat->register_bat_power); - - } - - -$sumReq = (abs($pEvW) + abs($pSdlW)); -$sumReqRel = ($pEvW + $pSdlW); - -if($sumReq==0){ - - $this->SetValue("Aktuelle_Leistung_EV", $totalPower_ist / 2); - $this->SetValue("Aktuelle_Leistung_SDL", $totalPower_ist / 2); - -}else{ - - if($pEvW>=0){ - - $this->SetValue("Aktuelle_Leistung_EV",((1+($totalPower_ist-$sumReqRel) / $sumReq)) * $pEvW); - - }else{ - $this->SetValue("Aktuelle_Leistung_EV",((1-($totalPower_ist-$sumReqRel) / $sumReq)) * $pEvW); - - } - - if($pSdlW>=0){ - $this->SetValue("Aktuelle_Leistung_SDL",((1+($totalPower_ist-$sumReqRel) / $sumReq)) * $pSdlW); - - }else{ - $this->SetValue("Aktuelle_Leistung_SDL",((1-($totalPower_ist-$sumReqRel) / $sumReq)) * $pSdlW); - } - -} -/* - $sumReq = (float)($pEvW + $pSdlW); - - if (!is_finite($sumReq) || abs($sumReq) < 0.01) { - - $this->SetValue("Aktuelle_Leistung_EV", $totalPower_ist / 2); - $this->SetValue("Aktuelle_Leistung_SDL", $totalPower_ist / 2); + // 2. Sortieren der Gruppen + // Laden: Wenig SoC zuerst (ASC) -> leere füllen + // Entladen: Viel SoC zuerst (DESC) -> volle leeren + if ($isCharge) { + ksort($groups); } else { - - $this->SetValue("Aktuelle_Leistung_EV",($totalPower_ist / $sumReq) * $pEvW); - - $this->SetValue("Aktuelle_Leistung_SDL",($totalPower_ist / $sumReq) * $pSdlW); + krsort($groups); } - */ + // 3. Verteilung + $remainingNeeded = $absPower; + foreach ($groups as $soc => $groupBatteries) { + if ($remainingNeeded <= 0.01) break; - return $finalOutput; + // Gesamte verfügbare Leistung in dieser SoC-Gruppe ermitteln + $groupTotalCapacity = 0.0; + foreach ($groupBatteries as $gb) { + $groupTotalCapacity += $gb['maxW']; + } + + // Wie viel können wir dieser Gruppe zuteilen? + // Entweder alles was die Gruppe kann, oder den Restbedarf + $powerForThisGroup = min($remainingNeeded, $groupTotalCapacity); + + // Proportionale Aufteilung innerhalb der Gruppe + // Falls Gruppe Kapazität 0 hat (Defekt/Voll), verhindern wir DivByZero + if ($groupTotalCapacity > 0) { + $ratio = $powerForThisGroup / $groupTotalCapacity; + foreach ($groupBatteries as $gb) { + $assigned = $gb['maxW'] * $ratio; + $result[$gb['idx']] = $assigned; + } + } + + $remainingNeeded -= $powerForThisGroup; + } + + // Wenn wir entladen, müssen die Werte negativ sein + if (!$isCharge) { + foreach ($result as $idx => $val) { + $result[$idx] = -$val; + } + } + + return $result; + }; + + // --------------------------------------------------------- + // Hauptablauf + // --------------------------------------------------------- + + // 1. Berechnung für EV und SDL getrennt durchführen + $evDistribution = $distributePower($pEvW, 'EV'); + $sdlDistribution = $distributePower($pSdlW, 'SDL'); + + // 2. Ergebnisse zusammenführen und Output formatieren + $finalOutput = []; + + foreach ($batteries as $bat) { + $idx = $bat['idx']; + // Summe der beiden Anforderungen (kann sich gegenseitig aufheben) + $valEv = $evDistribution[$idx] ?? 0.0; + $valSdl = $sdlDistribution[$idx] ?? 0.0; + $totalW = $valEv + $valSdl; + + // Aufteilen in Charge / Discharge für das Return-Format + $chargeW = 0.0; + $dischargeW = 0.0; + + if ($totalW > 0) { + $chargeW = abs($totalW); + } else { + $dischargeW = abs($totalW); + } + + // JSON Objekt erstellen + $finalOutput[] = [ + "idx" => $idx, + "typ" => (string)$bat['typ'], // Typ als String beibehalten + "chargeW" => round($chargeW, 0), // Optional: runden für sauberes JSON + "dischargeW" => round($dischargeW, 0) + ]; } + $batteriesRaw = json_decode($this->ReadPropertyString("Batteries")); + + $totalPower_ist = 0; + + foreach ($batteriesRaw as $bat) { + $totalPower_ist += GetValue($bat->register_bat_power); + } + + // ---------------------------- + // ALT (nicht mehr gebraucht) -> AUSKOMMENTIERT + // ---------------------------- + /* + $sumReq = (abs($pEvW) + abs($pSdlW)); + $sumReqRel = ($pEvW + $pSdlW); + + if($sumReq==0){ + + $this->SetValue("Aktuelle_Leistung_EV", $totalPower_ist / 2); + $this->SetValue("Aktuelle_Leistung_SDL", $totalPower_ist / 2); + + }else{ + + if($pEvW>=0){ + $this->SetValue("Aktuelle_Leistung_EV",((1+($totalPower_ist-$sumReqRel) / $sumReq)) * $pEvW); + }else{ + $this->SetValue("Aktuelle_Leistung_EV",((1-($totalPower_ist-$sumReqRel) / $sumReq)) * $pEvW); + } + + if($pSdlW>=0){ + $this->SetValue("Aktuelle_Leistung_SDL",((1+($totalPower_ist-$sumReqRel) / $sumReq)) * $pSdlW); + }else{ + $this->SetValue("Aktuelle_Leistung_SDL",((1-($totalPower_ist-$sumReqRel) / $sumReq)) * $pSdlW); + } + } + */ + + // ---------------------------- + // NEU (genau deine gewünschte Logik) + // aktuelle - EV_SOLL = aktuelle SDL + // aktuelle - SDL_SOLL = aktuelle EV + // ---------------------------- + $this->SetValue("Aktuelle_Leistung_SDL", $totalPower_ist - $pEvW); + $this->SetValue("Aktuelle_Leistung_EV", $totalPower_ist - $pSdlW); + + return $finalOutput; +} + + private function WriteBatteryPowerSetpoints(array $distribution): void { IPS_LogMessage(