From 9b87523f61eb52476771add610266bdeefcf3bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fliger?= Date: Fri, 28 Feb 2025 11:18:15 +0100 Subject: [PATCH] =?UTF-8?q?erste=20Logik=20hinzugef=C3=BChgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HauptManager/form.json | 39 +++--- HauptManager/module.php | 289 +++++++++++++++++++++++++++++++--------- Manager/form.json | 20 +++ Manager/module.php | 210 ++++++++++++++++++++++++++++- library.json | 2 +- 5 files changed, 470 insertions(+), 90 deletions(-) diff --git a/HauptManager/form.json b/HauptManager/form.json index c0722dc..292da1d 100644 --- a/HauptManager/form.json +++ b/HauptManager/form.json @@ -16,11 +16,6 @@ "caption": "Sollwertvorgabe für Solarladen", "suffix": "Watt" }, - { - "type": "SelectVariable", - "name": "Netzbezug", - "caption": "Variable mit dem zu regelnden Netzbezug" - }, { "type": "List", "name": "Verbraucher_Liste", @@ -30,30 +25,26 @@ "sortable": true, "columns": [ { - "caption": "Energieverbraucher", - "name": "Verbraucher", + "caption": "Verbraucher Lesen", + "name": "User_Up", "width": "auto", "add": 0, "edit": { - "type": "SelectInstance", - "filter": "Verbraucher" + "type": "SelectVariable", + "filter": "String" + } + }, + { + "caption": "Verbraucher Steuern", + "name": "User_Down", + "width": "auto", + "add": 0, + "edit": { + "type": "SelectVariable", + "filter": "String" } } - ] - }, - { - "type": "List", - "name": "ClientList", - "caption": "Eingehende Verbindungen", - "add": false, - "delete": false, - "sortable": false, - "columns": [ - { - "caption": "Client", - "name": "Client", - "width": "auto" - } + ] } ] diff --git a/HauptManager/module.php b/HauptManager/module.php index 9407957..1159146 100644 --- a/HauptManager/module.php +++ b/HauptManager/module.php @@ -11,23 +11,15 @@ class HauptManager extends IPSModule $this->RegisterPropertyInteger("Ueberschussleistung", 0); $this->RegisterPropertyInteger("Netzbezug", 0); // Initialisierung mit 0 $this->RegisterPropertyString("Verbraucher_Liste", "[]"); - $this->RegisterVariableString("Clients", "Clients", "", 0); // Timer registrieren - $this->RegisterTimer("Timer_DistributeEnergy", 5000, "IPS_RequestAction(" .$this->InstanceID .', "DistributeEnergy", "");'); + $this->RegisterTimer("Timer_DistributeEnergy",5000,"IPS_RequestAction(" .$this->InstanceID .', "DistributeEnergy", "");'); } public function ApplyChanges() { parent::ApplyChanges(); - // Liste aller Verbraucher einlesen - $Verbraucher_Liste = $this->ReadPropertyString("Verbraucher_Liste"); - - // WebSocket-Server starten - $this->StartWebSocketServer(); - - // Initialisiere die Client-Liste im Formular - $this->UpdateClientList(); + //Liste aller Verbraucher einlesen } public function RequestAction($Ident, $Value) @@ -46,65 +38,234 @@ class HauptManager extends IPSModule public function DistributeEnergy() { - // Energieverteilung implementieren - } + $Verbraucher_Liste = json_decode($this->ReadPropertyString("Verbraucher_Liste"), true); + $currentTime = time(); + $Netzbezug = 0; - private function StartWebSocketServer() - { - $server = new PHPWebSocket(); - $server->bind('message', function($clientID, $message, $messageLength, $binary) { - $this->onMessage($clientID, $message); - }); - $server->bind('open', function($clientID) { - $this->AddClient($clientID); - }); - $server->bind('close', function($clientID) { - $this->RemoveClient($clientID); - }); - $server->startServer('localhost', 8080); - } - - public function AddClient($clientID) - { - $clients = json_decode($this->GetValue("Clients"), true); - if ($clients === null) { - $clients = []; - } - $clients[] = (string)$clientID; - $this->SetValue("Clients", json_encode($clients)); - $this->UpdateClientList(); - } - - public function RemoveClient($clientID) - { - $clients = json_decode($this->GetValue("Clients"), true); - if ($clients !== null) { - $index = array_search((string)$clientID, $clients); - if ($index !== false) { - unset($clients[$index]); - $clients = array_values($clients); // Reindizieren des Arrays - $this->SetValue("Clients", json_encode($clients)); - $this->UpdateClientList(); + foreach ($Verbraucher_Liste as $key => $user) { + if (isset($user["Timestamp"]) && ($currentTime - $user["Timestamp"]) < 30) { + $summeNetzbezug += $user["Netzbezug"]; + } else { + unset($Verbraucher_Liste[$key]); } } - } - private function UpdateClientList() - { - $clients = json_decode($this->GetValue("Clients"), true); - if ($clients === null) { - $clients = []; - } - $clientList = []; - foreach ($clients as $client) { - $clientList[] = ['Client' => $client]; - } - $this->UpdateFormField('ClientList', 'values', json_encode($clientList)); - } - public function onMessage($clientID, $message) - { - // Nachrichtenverarbeitung implementieren + + + $Peakleistung = $this->ReadPropertyInteger("Peakleistung"); + $Ueberschussleistung = $this->ReadPropertyInteger("Ueberschussleistung"); + + // Fallunterscheidung ob auf Solarladen oder Peakshaving gerregelt wird. + if ($Netzbezug < ($Peakleistung - $Ueberschussleistung) / 2) { + $remainingPower = -1 * (-1 * $Ueberschussleistung + $Netzbezug); + $Is_Peak_Shaving = false; + } else { + $remainingPower = $Peakleistung - $Netzbezug; + $Is_Peak_Shaving = true; + } + + // Alle Energieverbraucher auslesen und dekodieren + + if (empty($Verbraucher_Liste)) { + // Liste ist leer, daher nichts zu tun + IPS_LogMessage("Manager", "aufgerufen leere liste"); + + return; + } + + // Frage alle Energieverbraucher ab, was sie für Leistungen benötigen könnten + foreach ($Verbraucher_Liste as $user) { + if (IPS_InstanceExists($user["Verbraucher"])) { + IPS_RequestAction($user["Verbraucher"],"GetCurrentData", $Is_Peak_Shaving); + IPS_LogMessage("Manager", "aufgerufen getcurrentdata"); + + } + } + + $filteredVerbraucher = []; // Array das später mit allen Verbrauchsdaten der Energieverbraucher gefüllt wird + $allIdle = true; // Variable zur Überprüfung, ob alle Benutzer Idle = true sind + $totalAktuelle_Leistung = 0; // Variable zur Summierung der Aktuelle_Leistung Werte + + // Fülle das Array mit allen entsprechenden Werten der Verbraucher ab + foreach ($Verbraucher_Liste as $user) { + if (!IPS_InstanceExists($user["Verbraucher"])) { + IPS_LogMessage("Manager", "aufgerufen komisch"); + + continue; + } + + // Werte direkt von der Verbraucher-Instanz abrufen + $Aktuelle_Leistung = GetValue(IPS_GetObjectIDByIdent("Aktuelle_Leistung", $user["Verbraucher"])); + $Bezogene_Energie = GetValue(IPS_GetObjectIDByIdent("Bezogene_Energie", $user["Verbraucher"])); + $PV_Prio = GetValue(IPS_GetObjectIDByIdent("PV_Prio", $user["Verbraucher"])); + $Sperre_Prio = GetValue(IPS_GetObjectIDByIdent("Sperre_Prio", $user["Verbraucher"])); + $idle = GetValue(IPS_GetObjectIDByIdent("Idle", $user["Verbraucher"])); + $powerStepsJson = GetValue(IPS_GetObjectIDByIdent("PowerSteps", $user["Verbraucher"])); + $powerSteps = json_decode($powerStepsJson, true); + + // Verbraucher-Daten zum gefilterten Array hinzufügen + $filteredVerbraucher[] = [ + "Verbraucher" => $user["Verbraucher"], + "InstanceID" => $user["Verbraucher"], + "Aktuelle_Leistung" => $Aktuelle_Leistung, + "Bezogene_Energie" => $Bezogene_Energie, + "PV_Prio" => $PV_Prio, + "Sperre_Prio" => $Sperre_Prio, + "Idle" => $idle, + "PowerSteps" => $powerSteps, + ]; + + // Überprüfen, ob alle Benutzer Idle = true sind, wenn einer nicht ist, wird später verworfen... + if (!$idle) { + $allIdle = false; + IPS_LogMessage("Manager", "nciht idle"); + + } + + // Addiere die aktuell bereits verwendete Leistung auf, um sie bei der verteilung zu berücksichtigen + $totalAktuelle_Leistung += $Aktuelle_Leistung; + } + // Berücksichtigung der bereits verteilten Leistungen (nachher kann dafür wieder bei 0 begonnen werden zu verteilen) + $remainingPower += $totalAktuelle_Leistung; + + // Abbrechen wenn es keine gefilterten User gibt + if (empty($filteredVerbraucher)) { + return; + } + + // Wenn nicht alle Benutzer Idle = true sind, rufe SetAktuelle_Leistung mit Aktuelle_Leistung Werten auf, (alle Verbraucher behalten die aktuelle Leistung) + if (!$allIdle) { + foreach ($filteredVerbraucher as $user) { + IPS_RequestAction($user["InstanceID"],"SetAktuelle_Leistung",$user["Aktuelle_Leistung"]); + IPS_LogMessage("Manager", "aufgerufen nicht alle idle"); + + } + return; + } + + // Sortiere die Verbruacher nach Priorität entweder der PV_Prio oder der Peak Prio + usort($filteredVerbraucher, function ($a, $b) use ( + $Is_Peak_Shaving + ) { + $primaryKey = $Is_Peak_Shaving ? "Sperre_Prio" : "PV_Prio"; + // Wenn die Prio geleich ist, sortiere danach welcher verbraucher bisher am wenigsten Energie bekommen hat. + if ($a[$primaryKey] == $b[$primaryKey]) { + return $a["Bezogene_Energie"] <=> $b["Bezogene_Energie"]; + } + return $a[$primaryKey] <=> $b[$primaryKey]; + }); + + // Primärschlüssel für die Priorität basierend auf dem Parameter auswählen (für sortierung in gruppen anschliessend) + $priorityKey = $Is_Peak_Shaving ? "Sperre_Prio" : "PV_Prio"; + + // Schleife durch alle Prioritäten + $priorities = array_unique( + array_column($filteredVerbraucher, $priorityKey) + ); + $groupedUsers = []; + + foreach ($priorities as $priority) { + $groupedUsers[$priority] = array_filter( + $filteredVerbraucher, + function ($user) use ($priority, $priorityKey) { + return $user[$priorityKey] == $priority; + } + ); + } + // Jetzt werden die energien pro gruppe verteilt (Immer alle pro prio in einer gruppe miteinander) + foreach ($groupedUsers as $priority => $users) { + // Verbraucher mit gleicher Priorität sammeln + $samePriorityUsers = isset($groupedUsers[$priority]) + ? $groupedUsers[$priority] + : []; + + // Wenn keine Verbraucher mit gleicher Priorität vorhanden sind, überspringen + if (empty($samePriorityUsers)) { + continue; + } + $withZero = []; + $withoutZero = []; + // Verbraucher die nicht 0 Annhemen können, bekommen einfach den tiefsten wert + foreach ($samePriorityUsers as $entry) { + if (min($entry["PowerSteps"]) <= 0) { + $withZero[] = $entry; + } else { + $withoutZero[] = $entry; + } + } + // Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert + if (!empty($withoutZero)) { + foreach ($withoutZero as $entry) { + $instanceID = $entry["InstanceID"]; + $minPowerStep = min($entry["PowerSteps"]); + + IPS_RequestAction($instanceID,"SetAktuelle_Leistung",$minPowerStep); + $remainingPower -= $entry["Aktuelle_Leistung"]; + } + } + + // Nun die verteilen, die 0 erhalten können. + $samePriorityUsers = $withZero; + // Array für die verteilte Energie pro User erstellen + $userEnergyProv = []; + $userEnergyProv = array_fill_keys(array_column($samePriorityUsers, "InstanceID"), 0); // Initialisierung für jeden Benutzer auf 0 setzen + + // Alle Schritte der Benutzer in einem Array sammeln + $allSteps = []; + foreach ($samePriorityUsers as $user) { + foreach ($user["PowerSteps"] as $step) { + $allSteps[] = [ + "user" => $user["InstanceID"], + "step" => $step, + ]; + } + } + + // Sortiere die Schritte nach Größe + usort($allSteps, function ($a, $b) { + return $a["step"] <=> $b["step"]; + }); + + // Iteriere durch alle Schritte + foreach ($allSteps as $entry) { + $user = $entry["user"]; + $powerstep = $entry["step"]; + + // Überprüfe, ob noch genügend verbleibende Energie für den nächsten Schritt vorhanden ist + if ($remainingPower >= $powerstep - $userEnergyProv[$user]) { + // Aktualisiere die verbleibende Energie und die bereitgestellte Energie für den Benutzer + $remainingPower -= $powerstep - $userEnergyProv[$user]; + $userEnergyProv[$user] = $powerstep; + } + + } + + // Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt + foreach ($userEnergyProv as $userInstanceID => $leistung) { + $minimalleitsung = min( + array_column( + array_filter($allSteps, function ($entry) use ( + $userInstanceID + ) { + return $entry["user"] == $userInstanceID; + }), + "step" + ) + ); + + // Jedem user den höheren der beiden werte aus minimalwert oder vergebenem zuteilen + $leistung = max($leistung, $minimalleitsung); + + // Methode SetAktuelle_Leistung für jeden Verbraucher mit der entsprechenden Energie aufrufen + if (IPS_InstanceExists($userInstanceID)) { + IPS_RequestAction($userInstanceID,"SetAktuelle_Leistung",$leistung); + IPS_LogMessage("Manager", "aufgerufen setleistung normal"); + + } + } + } + } } ?> \ No newline at end of file diff --git a/Manager/form.json b/Manager/form.json index e3e25f0..bbebbc2 100644 --- a/Manager/form.json +++ b/Manager/form.json @@ -22,6 +22,26 @@ "name": "Netzbezug", "caption": "Variable mit dem zu regelnden Netzbezug" }, + { + "type": "CheckBox", + "name": "HauptmanagerAktiv", + "caption": "Hauptmanager Aktiv" + }, + { + "type": "NumberSpinner", + "name": "ManagerID", + "caption": "Manager ID" + }, + { + "type": "SelectVariable", + "name": "DatenHoch", + "caption": "Daten Hoch" + }, + { + "type": "SelectVariable", + "name": "DatenZuruck", + "caption": "Daten Zurück" + }, { "type": "List", "name": "Verbraucher_Liste", diff --git a/Manager/module.php b/Manager/module.php index 07c2c8b..6d77fbf 100644 --- a/Manager/module.php +++ b/Manager/module.php @@ -11,6 +11,10 @@ class Manager extends IPSModule $this->RegisterPropertyInteger("Ueberschussleistung", 0); $this->RegisterPropertyInteger("Netzbezug", 0); // Initialisierung mit 0 $this->RegisterPropertyString("Verbraucher_Liste", "[]"); + $this->RegisterPropertyBoolean("HauptmanagerAktiv", false); // Initialisierung mit 0 + $this->RegisterPropertyInteger("ManagerID", 0); // Initialisierung mit 0 + $this->RegisterPropertyInteger("DatenHoch", 0); // Initialisierung mit 0 + $this->RegisterPropertyInteger("DatenZuruck", 0); // Initialisierung mit 0 // Timer registrieren $this->RegisterTimer("Timer_DistributeEnergy",5000,"IPS_RequestAction(" .$this->InstanceID .', "DistributeEnergy", "");'); @@ -27,7 +31,24 @@ class Manager extends IPSModule { switch ($Ident) { case "DistributeEnergy": + if($this->ReadPropertyInteger("HauptmanagerAktiv")==true ){ + $data = json_decode(GetValue($this->ReadPropertyInteger("DatenZuruck")), true); + + if (isset($data["timestamp"])) { + $timestamp = $data["timestamp"]; + $currentTime = time(); + + if (($currentTime - $timestamp) < 30) { + $this->DistributeEnergy_Extern(); + } + } else { + $this->DistributeEnergy(); + } + + }else{ $this->DistributeEnergy(); + } + break; case "ApplyChanges": $this->ApplyChanges(); @@ -80,7 +101,6 @@ class Manager extends IPSModule foreach ($Verbraucher_Liste as $user) { if (!IPS_InstanceExists($user["Verbraucher"])) { IPS_LogMessage("Manager", "aufgerufen komisch"); - continue; } @@ -260,5 +280,193 @@ class Manager extends IPSModule } + + public function DistributeEnergy_Extern() + { + // Systemvariablen abrufen + $Netzbezug = GetValue($this->ReadPropertyInteger("Netzbezug")); + + // Alle Energieverbraucher auslesen und dekodieren + $Verbraucher_Liste = json_decode($this->ReadPropertyString("Verbraucher_Liste"), true); + + // Frage alle Energieverbraucher ab, was sie für Leistungen benötigen könnten + foreach ($Verbraucher_Liste as $user) { + if (IPS_InstanceExists($user["Verbraucher"])) { + IPS_RequestAction($user["Verbraucher"],"GetCurrentData", $Is_Peak_Shaving); + IPS_LogMessage("Manager", "aufgerufen getcurrentdata"); + + } + } + + $filteredVerbraucher = []; // Array das später mit allen Verbrauchsdaten der Energieverbraucher gefüllt wird + + // Fülle das Array mit allen entsprechenden Werten der Verbraucher ab + foreach ($Verbraucher_Liste as $user) { + + + // Werte direkt von der Verbraucher-Instanz abrufen + $Aktuelle_Leistung = GetValue(IPS_GetObjectIDByIdent("Aktuelle_Leistung", $user["Verbraucher"])); + $Bezogene_Energie = GetValue(IPS_GetObjectIDByIdent("Bezogene_Energie", $user["Verbraucher"])); + $PV_Prio = GetValue(IPS_GetObjectIDByIdent("PV_Prio", $user["Verbraucher"])); + $Sperre_Prio = GetValue(IPS_GetObjectIDByIdent("Sperre_Prio", $user["Verbraucher"])); + $idle = GetValue(IPS_GetObjectIDByIdent("Idle", $user["Verbraucher"])); + $powerStepsJson = GetValue(IPS_GetObjectIDByIdent("PowerSteps", $user["Verbraucher"])); + $powerSteps = json_decode($powerStepsJson, true); + + // Verbraucher-Daten zum gefilterten Array hinzufügen + $filteredVerbraucher[] = [ + "Verbraucher" => $user["Verbraucher"], + "InstanceID" => $user["Verbraucher"], + "Aktuelle_Leistung" => $Aktuelle_Leistung, + "Bezogene_Energie" => $Bezogene_Energie, + "PV_Prio" => $PV_Prio, + "Sperre_Prio" => $Sperre_Prio, + "Idle" => $idle, + "PowerSteps" => $powerSteps, + "ParentManager" => $this->ReadPropertyInteger("ManagerID") + + ]; + + } + + + $sendarray = []; + + $sendarray[] = [ + "Users" => $filteredVerbraucher, + "Netzbezug" => $Netzbezug, + "Timestamp" => time() + + ]; + SetValue($this->ReadPropertyInteger("DatenHoch"), json_encode($sendarray)); + + // Sortiere die Verbruacher nach Priorität entweder der PV_Prio oder der Peak Prio + usort($filteredVerbraucher, function ($a, $b) use ( + $Is_Peak_Shaving + ) { + $primaryKey = $Is_Peak_Shaving ? "Sperre_Prio" : "PV_Prio"; + // Wenn die Prio geleich ist, sortiere danach welcher verbraucher bisher am wenigsten Energie bekommen hat. + if ($a[$primaryKey] == $b[$primaryKey]) { + return $a["Bezogene_Energie"] <=> $b["Bezogene_Energie"]; + } + return $a[$primaryKey] <=> $b[$primaryKey]; + }); + + // Primärschlüssel für die Priorität basierend auf dem Parameter auswählen (für sortierung in gruppen anschliessend) + $priorityKey = $Is_Peak_Shaving ? "Sperre_Prio" : "PV_Prio"; + + // Schleife durch alle Prioritäten + $priorities = array_unique( + array_column($filteredVerbraucher, $priorityKey) + ); + $groupedUsers = []; + + foreach ($priorities as $priority) { + $groupedUsers[$priority] = array_filter( + $filteredVerbraucher, + function ($user) use ($priority, $priorityKey) { + return $user[$priorityKey] == $priority; + } + ); + } + // Jetzt werden die energien pro gruppe verteilt (Immer alle pro prio in einer gruppe miteinander) + foreach ($groupedUsers as $priority => $users) { + // Verbraucher mit gleicher Priorität sammeln + $samePriorityUsers = isset($groupedUsers[$priority]) + ? $groupedUsers[$priority] + : []; + + // Wenn keine Verbraucher mit gleicher Priorität vorhanden sind, überspringen + if (empty($samePriorityUsers)) { + continue; + } + $withZero = []; + $withoutZero = []; + // Verbraucher die nicht 0 Annhemen können, bekommen einfach den tiefsten wert + foreach ($samePriorityUsers as $entry) { + if (min($entry["PowerSteps"]) <= 0) { + $withZero[] = $entry; + } else { + $withoutZero[] = $entry; + } + } + // Verbraucher die nicht 0 annhemen können erhalten nun den minimalwert + if (!empty($withoutZero)) { + foreach ($withoutZero as $entry) { + $instanceID = $entry["InstanceID"]; + $minPowerStep = min($entry["PowerSteps"]); + + IPS_RequestAction($instanceID,"SetAktuelle_Leistung",$minPowerStep); + $remainingPower -= $entry["Aktuelle_Leistung"]; + } + } + + // Nun die verteilen, die 0 erhalten können. + $samePriorityUsers = $withZero; + // Array für die verteilte Energie pro User erstellen + $userEnergyProv = []; + $userEnergyProv = array_fill_keys(array_column($samePriorityUsers, "InstanceID"), 0); // Initialisierung für jeden Benutzer auf 0 setzen + + // Alle Schritte der Benutzer in einem Array sammeln + $allSteps = []; + foreach ($samePriorityUsers as $user) { + foreach ($user["PowerSteps"] as $step) { + $allSteps[] = [ + "user" => $user["InstanceID"], + "step" => $step, + ]; + } + } + + // Sortiere die Schritte nach Größe + usort($allSteps, function ($a, $b) { + return $a["step"] <=> $b["step"]; + }); + + // Iteriere durch alle Schritte + foreach ($allSteps as $entry) { + $user = $entry["user"]; + $powerstep = $entry["step"]; + + // Überprüfe, ob noch genügend verbleibende Energie für den nächsten Schritt vorhanden ist + if ($remainingPower >= $powerstep - $userEnergyProv[$user]) { + // Aktualisiere die verbleibende Energie und die bereitgestellte Energie für den Benutzer + $remainingPower -= $powerstep - $userEnergyProv[$user]; + $userEnergyProv[$user] = $powerstep; + } + + } + + // Prüfen, dass jeder User mindestens seinen minimalwert an Leistung bekommt + foreach ($userEnergyProv as $userInstanceID => $leistung) { + $minimalleitsung = min( + array_column( + array_filter($allSteps, function ($entry) use ( + $userInstanceID + ) { + return $entry["user"] == $userInstanceID; + }), + "step" + ) + ); + + // Jedem user den höheren der beiden werte aus minimalwert oder vergebenem zuteilen + $leistung = max($leistung, $minimalleitsung); + + // Methode SetAktuelle_Leistung für jeden Verbraucher mit der entsprechenden Energie aufrufen + if (IPS_InstanceExists($userInstanceID)) { + IPS_RequestAction($userInstanceID,"SetAktuelle_Leistung",$leistung); + IPS_LogMessage("Manager", "aufgerufen setleistung normal"); + + } + } + } + + + + + + } + } ?> diff --git a/library.json b/library.json index f3a34d7..15e7014 100644 --- a/library.json +++ b/library.json @@ -6,7 +6,7 @@ "compatibility": { "version": "7.1" }, - "version": "1.104", + "version": "1.105", "build": 0, "date": 0 } \ No newline at end of file