From 12079ef9e8d5d9d56b10c974571b3814b44af14f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fliger?= Date: Fri, 28 Feb 2025 07:00:52 +0100 Subject: [PATCH] =?UTF-8?q?websocket=20eingef=C3=BChgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HauptManager/form.json | 22 +++- HauptManager/module.php | 286 ++++++++++------------------------------ library.json | 2 +- 3 files changed, 86 insertions(+), 224 deletions(-) diff --git a/HauptManager/form.json b/HauptManager/form.json index e3e25f0..c0722dc 100644 --- a/HauptManager/form.json +++ b/HauptManager/form.json @@ -4,7 +4,6 @@ "type": "Label", "caption": "Einstellungen Energiemanager" }, - { "type": "NumberSpinner", "name": "Peakleistung", @@ -16,12 +15,12 @@ "name": "Ueberschussleistung", "caption": "Sollwertvorgabe für Solarladen", "suffix": "Watt" - }, + }, { "type": "SelectVariable", "name": "Netzbezug", "caption": "Variable mit dem zu regelnden Netzbezug" - }, + }, { "type": "List", "name": "Verbraucher_Liste", @@ -41,6 +40,21 @@ } } ] + }, + { + "type": "List", + "name": "ClientList", + "caption": "Eingehende Verbindungen", + "add": false, + "delete": false, + "sortable": false, + "columns": [ + { + "caption": "Client", + "name": "Client", + "width": "auto" + } + ] } ] -} +} \ No newline at end of file diff --git a/HauptManager/module.php b/HauptManager/module.php index 29c1064..ce6eaa5 100644 --- a/HauptManager/module.php +++ b/HauptManager/module.php @@ -2,6 +2,8 @@ class HauptManager extends IPSModule { + private $clients = []; + public function Create() { parent::Create(); @@ -13,14 +15,17 @@ class HauptManager extends IPSModule $this->RegisterPropertyString("Verbraucher_Liste", "[]"); // 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 + // Liste aller Verbraucher einlesen $Verbraucher_Liste = $this->ReadPropertyString("Verbraucher_Liste"); + + // WebSocket-Server starten + $this->StartWebSocketServer(); } public function RequestAction($Ident, $Value) @@ -39,226 +44,69 @@ class HauptManager extends IPSModule public function DistributeEnergy() { - // Systemvariablen abrufen - $Netzbezug = GetValue($this->ReadPropertyInteger("Netzbezug")); - $Peakleistung = $this->ReadPropertyInteger("Peakleistung"); - $Ueberschussleistung = $this->ReadPropertyInteger("Ueberschussleistung"); + // Energieverteilung implementieren + } - // 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; + private function StartWebSocketServer() + { + $server = new \Ratchet\App('localhost', 8080); + $server->route('/ws', new WebSocketHandler($this), ['*']); + $server->run(); + } + + public function AddClient($conn) + { + $this->clients[] = $conn; + $this->UpdateClientList(); + } + + public function RemoveClient($conn) + { + $index = array_search($conn, $this->clients); + if ($index !== false) { + unset($this->clients[$index]); + $this->clients = array_values($this->clients); // Reindizieren des Arrays + $this->UpdateClientList(); } + } - // Alle Energieverbraucher auslesen und dekodieren - $Verbraucher_Liste = json_decode($this->ReadPropertyString("Verbraucher_Liste"), true); - - if (empty($Verbraucher_Liste)) { - // Liste ist leer, daher nichts zu tun - IPS_LogMessage("Manager", "aufgerufen leere liste"); - - return; + private function UpdateClientList() + { + $clientList = []; + foreach ($this->clients as $client) { + $clientList[] = ['Client' => (string)$client->resourceId]; } - - // 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"); - - } - } - } - - - - - + $this->UpdateFormField('ClientList', 'values', json_encode($clientList)); } } -?> + +class WebSocketHandler implements \Ratchet\MessageComponentInterface +{ + private $module; + + public function __construct($module) + { + $this->module = $module; + } + + public function onOpen(\Ratchet\ConnectionInterface $conn) + { + $this->module->AddClient($conn); + } + + public function onClose(\Ratchet\ConnectionInterface $conn) + { + $this->module->RemoveClient($conn); + } + + public function onError(\Ratchet\ConnectionInterface $conn, \Exception $e) + { + $conn->close(); + } + + public function onMessage(\Ratchet\ConnectionInterface $from, $msg) + { + // Nachrichtenverarbeitung implementieren + } +} +?> \ No newline at end of file diff --git a/library.json b/library.json index b2fdf1f..d9fd79c 100644 --- a/library.json +++ b/library.json @@ -6,7 +6,7 @@ "compatibility": { "version": "7.1" }, - "version": "1.098", + "version": "1.099", "build": 0, "date": 0 } \ No newline at end of file