Grundsätzliche Fragen zur Modulerstellung

Hallo Paresy und Michael,

was das Thema angeht bin ich ja noch sehr unsicher. Daher noch mal mein Gedankengang:

  • Wird ein Child gelöscht soll der Parent (=Splitter) darüber benachrichtigt werden
  • dazu habe ich in allen Childs etwas ähnliches wie dieses hier eingefügt:
        // Überschreibt die interne IPS_Destroy($id) Funktion
        public function Destroy() {
            // Diese Zeile nicht löschen.
            parent::Destroy();
            IPS_LogMessage("IPS2GPIO GPIO Destroy: ","Genutzte Pin löschen");
            $this->SendDataToParent(json_encode(Array("DataID"=> "{A0DAAF26-4A2D-4350-963E-CC02E74BD414}", "Function" => "gpio_destroy", "Pin" => $this->ReadPropertyInteger("Pin_I"))));
            $this->SendDataToParent(json_encode(Array("DataID"=> "{A0DAAF26-4A2D-4350-963E-CC02E74BD414}", "Function" => "gpio_destroy", "Pin" => $this->ReadPropertyInteger("Pin_O"))));

        }

Die IPS_LogMessages aus diesem Code sehe ich noch im Log

  • im Parent (=Splitter) soll dieses angesprochen werden:
		    // GPIO Kommunikation
		    case "gpio_destroy":
		    	// Löschen einer GPIO-Belegung
		    	// aus der Liste der genutzten GPIO
		    	$PinUsed = unserialize(GetValueString($this->GetIDForIdent("PinUsed")));
		    	IPS_LogMessage("IPS2GPIO GPIO Destroy: ",$data->Pin);
		    	if (in_array($data->Pin, $PinUsed)) {
		    		IPS_LogMessage("IPS2GPIO GPIO Destroy: ","Pin in PinUsed");
		    		array_splice($PinUsed, $data->Pin, 1);	
		    	}
		    	SetValueString($this->GetIDForIdent("PinUsed"), serialize($PinUsed));
		        // aus der Liste der Notify-GPIO
		        $PinNotify = unserialize(GetValueString($this->GetIDForIdent("PinNotify")));
		        if (in_array($data->Pin, $PinNotify)) {
		    		IPS_LogMessage("IPS2GPIO GPIO Destroy: ","Pin in PinNotify");
		    		array_splice($PinNotify, $data->Pin, 1);
		    		SetValueString($this->GetIDForIdent("PinNotify"), serialize($PinNotify));
		    		If (GetValueInteger($this->GetIDForIdent("Handle")) >= 0) {
			           	// Notify neu setzen
			           	$this->CommandClientSocket(pack("LLLL", 19, GetValueInteger($this->GetIDForIdent("Handle")), $this->CalcBitmask(), 0), 16);
				}
		    	}
		        break;

Im Log erscheint aber keine - der dort extra zur Kontrolle eingefügten - IPS_LogMessages, stattdessen kommt eine Fehlermeldung das Instanz xxxxx nicht existiert. (wahrscheinlich die gelöschte Instanz).

Der Code selbst spielt jetzt m.E. keine Rolle, sondern das zumindest der Verdacht entsteht, dass der Sprung zum Parent gar nicht mehr durchgeführt wird.

Daher noch mal die Frage: Ist das parent::Destroy(); im Child an der richtigen Stelle? Oder sollte es ans Ende der Destroy-Funktion?

Joachim

An der Stelle (Destroy) ist die Instanz bereits vom Parent getrennt und im System so gut wie aufgeräumt. Du hast also kaum noch Möglichkeiten zu agieren. Das klappt somit nicht. Dein Ansatz auf die FM_* Nachrichten im Splitter zu warten müsste aber korrekt laufen.

Jedoch: FM_DISCONNECT wird mit der SenderID des Childs aufgerufen. Und somit hast du ein Problem, dass der Parent nichts von seinem Kind weiß und somit nicht auf den Disconnect reagieren kann.

Bevor wir jetzt eine Workaround suchen (auf die schnelle habe ich keine gute Idee) frage ich noch mal, warum der Splitter das „pin_destroy“ wissen muss. Findest du nicht eine Lösung die „Stateless“ funktioniert? Also ohne irgendwelche Registrierung?

Ich könnte ja problemlos FM_* Befehle für den Parent hinzufügen, aber bisher habe ich in keinem der IPS Module solch ein komplexes Verfahren genutzt.

paresy

Hallo Paresy,

im Splitter werden zentral die benutzten GPIO, Handle und I²C-DeviceAdressen verwaltet. Wird eine Child-Instanz hinzugefügt, werden diese Daten im Splitter gespeichert.

Wird IPS neu gestartet, „sammelt“ der Splitter diese Daten neu ein, wird eine Child-Instanz gelöscht, so wäre es sicherlich am „saubersten“ wenn diese sich auch „abmeldet“, damit die oben beschriebenen Daten aus der Splitter-Instanz entfernt werden können.

Daher war für mich eine der Möglichkeiten dieses für die Einzelinstanz über die Destroy-Funktion zu lösen.
Alternativ - wenn eine allgemeine Benachrichtigung erfolgt - über die „Daten-Sammelfunktion“ des Splitters das neue Einlesen aller wichtigen Daten der Child-Instanzen zu veranlassen (->viel JSON-Datenverkehr).

Eine - in meinen Augen unprofessionelle - Möglichkeit wäre sicherlich, das Vorhanden sein von Child-Instanzen regelmäßig über einen Timer zu überprüfen…

Joachim

Hallo Paresy,

vielleicht sollte man die Dokumentation auch um den Hinweis erweitern, das der Code im „Destroy-Event“ nur bedingt bis zum Ende ausgeführt wird (was aus alten VB-Zeiten meine Erwartungshaltung gewesen wäre)?

Für meinen Fall und unter der Voraussetzung kann ich die Funktion also gänzlich aus meinen Child-Instanzen löschen?

Habe ich die Chance, das es eine Nachricht generiert wird, wenn die Child-Instanz abgemeldet wird?

Joachim

Du kannst es ja anders lösen (alles im Splitter):

Beim starten von IPS ließt du ja jetzt schon alle Childs ein.
Das alles als serialisiertes Array in einen Buffer schreiben.
Sich auf jede ChildID mit FM_Disconnect registrieren.
Dann im MessageSink den SenderID (ist dann ja der alte Child) aus den Array löschen und UnregisterMessage auf die alte ID.
Anschließend kannst du anhand des Array deine GPIOs bereinigen.

Für das neu erstellen eines Child im laufenden Betrieb, könntest verschiedene zwei Wege nutzen.

Im Splitter sich auf Sender 0 und FM_CONNECT registrieren, dann musst du prüfen ob in array $Daten vom MessageSink dein Splitter als Parent identisch ist (Nachteil: Jedes FM_Connect jeder Instanz führt erstmal deinen Code aus => Performance?)

Oder du musst das im Child lösen und dort, wie gehabt, dich auf FM_CONNECT der eigenen Instanz registrieren.
Und dann dort im MessageSink eine Nachricht an den Parent senden.

@paresy: Wenn davon etwas ‚Mist‘ ist, bitte korrigieren :wink:

Michael

…ja, könnte man so machen…:wink:

Vielleicht mag Paresy sich aber doch noch mal zur Destroy-Funktion (was soll da „eigentlich“ passieren, wenn Codeabarbeitung nur eingeschränkt möglich) und zu der Möglichkeit der Erweiterung der Nachrichten äußert…:wink:

Joachim

Verstehe ich nicht… Da ist doch nix eingeschränkt.
IPS muss bestimmte Funktionen beim löschen von Instanzen in einer festen Reihenfolge ausführen.
Macht IPS das nicht knall es.
Ist wie bei Objektorientierte Programmieren. Du kannst nicht erst das Objekt zerstören und dann die Verweise aufheben.
Hier werden jetzt halt erst die Instanzen vom Parent getrennt, Dann vielleicht noch Unterobjekte gelöscht und ganz am Ende kommt dann endlich mal dein Destroy.

Mich würde da eher diese Reihenfolge interessieren (teilweise ist die wohl anders als im Delphi-SDK).
Wobei man die ja auch einfach mal mit einem Test auf diverse Messages selbst rausfinden könnte.

Michael

Ich hätte meine eigenen Destroy Aktionen auch in der Destroy Funktion vor dem parent::destroy ausgeführt. Schliesslich habe ich meine Objekte in der create Funktion auch erst nach dem parent::create angepasst. Meine Erwartung wäre es, das man vor dem parent::destroy Aufruf auch noch alles Intakt hat, um aufzuräumen. So wäre es eigentlich Programmierstandard.

Habe die Destroy Methode allerdings bisher noch nicht benötigt…

My2Cents
Tommi

Stimmt schon, aber in dem Create (auch parent::create) hast du auch noch keine IPS-Interne Datenverbindung zum Parent-ID.
Dies passiert ja auch nicht automatisch beim erstellen sondern erst wenn der User eine herstellt bzw. du es selbst im Code mit ConnectParent / RequireParent veranlasst.

Und somit ist es im Destroy dann ja wohl auch… erst löscht IPS die Verbidnung inkl. Nachricht in die MessageSink und dann wird die Instanz gelöscht, also auch destroy aufgerufen.

Mit den parent::create und ::destroy sehe ich das auch so wie du, nur haben die ja eh gar keine Funktion.
Auch muss man unterscheiden was gehört zur Instanz bzw. zu dem Objekt einer IPS-Instanz innerhalb von IPS und was nicht…
Die Datenverbindung ist nicht Bestandteil einer Instanz. Die Buffer dafür schon. Somit müssen die Buffer vor parent::Destroy noch intakt sein und anschließend ‚könnten‘ sie schon weg sein. Keine Ahnung ob das aktuell wirklich zutrifft, ich brauchte noch keinen Buffer im Destroy und das aufräumen überlasse ich Paresy :wink:

Michael

…Paresy sollte ja wissen wir das eigentlich gedacht ist.

Im VB gab es ein „Unload“ (wenn mich meine Erinnerung nicht trügt) - da konnte man noch mal „aufräumen“ und erst dann wurde die instanz „zerstört“.

Mir erschließt sich der Zweck eine „Destroy-Funktion“ aber nicht vollständig, wenn der dort erstellt Code nicht uneingeschränkt ausgeführt wird…:stuck_out_tongue:

Joachim

Dateien von dem Datenträger löschen, Webhooks de-registrieren etc… also mit fällt da eine Menge ein und bei mir funktioniert das auch :stuck_out_tongue:

Und warum willst du da überhaupt an ausgerechnet der Stelle machen… das FM_Disconnect brauchst du so oder so und kommt immer vor dem Destroy.
Wäre ja eh doppelt gemoppelt :smiley:

Michael

…in diesem Fall würde eine der beiden helfen. In einer anderen Situation bzw. einen anderen Aufgabenstellung vielleicht nicht.
Ich würde das dann - wenn hier von Paresy keine Aussage kommt - dann so ähnlich umsetzen wie Du es oben beschrieben hast…

Joachim

…gibt es dazu „Empfehlungen“, wann Einträge über „IPS_LogMessage“ an die allgemeinen Meldungen gesendet werden sollen und wann ein „SendDebug“ geeigneter wäre?

Ich würde mal sagen:

  • bei Fehlern im Modul diese an „IPS_LogMessage“
  • zusätzliche Informationen, die insbesondere für die Fehlersuche interessant sind an „SendDebug“

Joachim

Meine Interpretation:

IPS_LogMessage: das ist alles das, was Du Deinen Usern ständig ins Logfile schreiben möchtest. IPS_LogMessage kann von jedem beliebigen Script ausgeführt werden.

SendDebug wie der Name schon sagt: detaillierte Informationen zu einem Modul (nur dort gibt es die Funktion), mit denen der normale User nicht unbedingt belästigt werden muss, aber im Debug Fenster auf Anforderung zur Analyse hervorgezaubert werden kann.

Tommi

Wobei man Fehler nicht zwangsläufig loggen muss.
Da ja auch, je nach Aufrufer, Echos, Exceptions und errors im Log landen. Wobei man Diese in den Funktionen welche am Datenaustausch beteiligt sind besser nicht nutzen sollte…gibt sonst schöne Folgefehler.
Michael

Hallo Leute,

hier noch mal eine Verständnisfrage. Im der form.json kann ich im „Action“ Bereich einige Funktionen anbieten. Wirklich verstanden habe ich es nicht, aber eine Funktion habe ich dennoch hinbekommen.
Wenn ich

{ "type": "Button", "label": "Toggle", "onClick": "I2GDMR_Toggle_Status($id);"},

nutze, woher soll man wissen das „$id“ für die Instanz-ID zu verwenden ist? Oder kann ich auch jeden anderen beliebigen Ausdruck verwenden?
Wenn ich einen Slider verwenden möchte,

{ "type": "HorizontalSlider", "name": "Slider", "minimum": 0,  "maximum": 255, "onChange": "I2GDMR_Set_Intensity($id, $Slider);" }

Woher weiß ich welche Variable den Wert enthält? Es funktioniert mit „$Slider“, aber warum?

Und jetzt?
Ich wollte eigentlich drei Slider (Rot, Bau, Grün) anordnen und jeweils aus der Wertekombination dieser eine Funktion ansprechen…
Geht das?

Joachim

Der Name ist auch der Variablennamen.
Probier mal das aus:
IPSMS35/form.json at master · Nall-chan/IPSMS35 · GitHub
Michael

…ich habe da mal wieder so eine Herausforderung…

Eigentlich steht hier in dem Modul noch gar nicht viel drin. Trotzdem ist da irgend ein- für mich unsichtbarer - Fehler.
Will ich im IPS die Instanz erstellen, dann wird eine Fehlermeldung - leider ohne Text - ausgegeben, das Modul wird dann an anderer Stelle als ewünscht und ohne Namen erstellt. Ich kann es öffnen, aber irgendetwas ist doch da nicht ganz in Ordnung…:confused:

Habe schon nach Leerzeilen hinter den PHP-Tags etc. gesucht - ich finde den Fehler einfach nicht…

Wer mag mir den entscheidenden Tipp geben?:wink:

Joachim

Nicht Leerzeile, aber Leerzeichen sind hinter den PHP-Tag.
Diesen kannst du aber, ebenso wie die ganzen Returns weglassen.

Hallo Michael,

vielen Dank! Auf die Idee bin ich nicht gekommen…:frowning:

Aber noch mal eine Frage zu den „return“'s:
Ich habe das vielfach ergänzt, da für mich eine Funktion immer mit einem Return beendet wird…
Ist das in PHP nicht notwendig?

Joachim