Autor Thema: 72_UBUS.pm Code Review / Feedback  (Gelesen 3551 mal)

Offline xenos1984

  • Developer
  • Full Member
  • ****
  • Beiträge: 417
Antw:72_UBUS.pm Code Review / Feedback
« Antwort #45 am: 19 September 2021, 18:54:22 »
Danke, Rudi, für die Erläuterungen! Das entspricht so weit auch meinem Verständnis aus der Doku im Wiki.

Bei den WebSocket und HTTP Verbindungen, die jeweils asynchron kommunizieren, erscheint mir der Ablauf halbwegs klar. Da geschieht die Zuordnung zwischen Anfrage und Antwort über eine ID, die bei beiden Richtungen identisch ist. Wenn also ein logisches Gerät eine Anfrage mittels IOWrite schickt, ist dieser Anfrage eine ID zugeordnet. Wenn eine Antwort kommt, muss Parse nur schauen, welches Gerät die Anfrage dieser ID geschickt hat. Im Prinzip sehe ich dann zwei Möglichkeiten einer Implementierung:
  • Wenn ich das richtig sehe, gibt IOWrite auch den Rückgabewert der Write-Funktion des physikalischen Moduls an das logische Modul zurück. Man könnte also die Erzeugung und Verwaltung der IDs dem physikalischen Modul überlassen. Write erzeugt eine ID und führt den Aufruf aus, gibt dann die ID an das logische Modul zurück, und letzteres merkt sie sich. Wenn die Antwort kommt, ist die ID darin enthalten, und das logische Modul prüft in Parse, wer die Anfrage gestellt hat. "Bitte stellen Sie eine Frage und ziehen Sie eine Nummer. Sie werden aufgerufen." 8)
  • Andererseits übergibt IOWrite alle seine Argumente direkt an Write. Also könnte auch das logische Modul eine ID erstellen und zusammen mit der Anfrage übergeben. Die ID kann eine Zeichenkette sein, könnte also einfach den Namen des Geräts + eindeutiger Kennung enthalten. "Bitte stellen Sie eine Frage und geben Sie uns Ihre Telefonnummer und das Aktenzeichen, wir rufen zurück." 8)
Noch bin ich etwas unschlüssig, was besser ist, da es immer noch den Sonderfall gibt, der keine ID benutzt. Wenn man UBUS nicht über eine Schnittstelle, sondern über die Kommandozeile benutzt, erfolgt der Aufruf synchron, und es gibt keine ID. Letzteres ist vermutlich das kleinere Problem, da das physikalische Modul ja bei einem synchronen Aufruf die Zuordnung zwischen Anfrage und Antwort gegeben hat, und den ID-Mechanismus für das logische Modul simulieren kann.

Etwas unklar ist mir noch, wie man in dem Fall am besten die Antwort zurück schickt. Die kommt ja in dem Fall nicht über Read. Und in Write synchron Dispatch aufzurufen, ist vermutlich keine gute Idee, wenn das logische Modul als Reaktion gleich eine weitere Anfrage mittels IOWrite schicken könnte... Das gäbe eine Endlosschleife. Außerdem würde das nicht mit dem Mechanismus 1 von oben funktionieren, weil das logische Modul die Antwort bekäme, bevor das physikalische Modul über den Rückgabewert die ID mitgeteilt hat. D.h. die Nummer würde aufgerufen, bevor sie gezogen wurde :D Mechanismus 2 ginge, wenn sich das logische Modul die ID merkt, bevor es IOWrite aufruft.

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24707
Antw:72_UBUS.pm Code Review / Feedback
« Antwort #46 am: 20 September 2021, 11:28:21 »
Version 2 ist mir sympatischer, da die Abhaengigkeit zwischen den Modulen hier etwas kleiner ist.
Das Problem mit dem Antwort zurueckschicken habe ich leider nicht wirklich verstanden.

Offline xenos1984

  • Developer
  • Full Member
  • ****
  • Beiträge: 417
Antw:72_UBUS.pm Code Review / Feedback
« Antwort #47 am: 20 September 2021, 13:42:25 »
Version 2 ist mir sympatischer, da die Abhaengigkeit zwischen den Modulen hier etwas kleiner ist.
Inzwischen habe ich mir die Abfragen weiter angesehen und ich denke, dass beide Vor- und Nachteile haben, und man vielleicht die Vorteile von beiden nutzen kann. Letztlich soll die ID ja zwei Dinge erfüllen: A. Zuordnung einer Antwort zu dem logischen Device, das die Anfrage gestellt hat (mittels Dispatch / Parse), B. eindeutige Zuordnung der Antwort zu einer Anfrage innerhalb des logischen Device. Um A zu erreichen, macht es Sinn, den Namen des Device als Teil der ID zu benutzen, um diesen dann in Parse abzufragen. Dafür bräuchte es Methode 2 aus dem letzten Post, d.h. das logische Modul gibt den Device-Namen mit. Wenn man allerdings die Verwaltung von (innerhalb einer Session) eindeutigen IDs für Anfragen dem logischen Modul überlässt, muss man diese ggf. mehrfach implementieren, wenn man ein weiteres logisches Modul haben möchte, das auf das gleiche physikalische Gerät zugreift. Außerdem muss das physikalische Modul auch für seine eigenen Anfragen (Login, Session-Status, ggf. "list" als Information, welche Funktionen unterstützt werden) eine eindeutige ID erzeugen und verwalten. Das sowohl im physikalischen als auch im logischen Modul zu implementieren erscheint mir etwas nach doppeltem Aufwand.

Letzteres erscheint mir sinnvoll, weil "call" und "subscribe" zwei recht unterschiedlich strukturierte Anfrage-Antwort Modelle sind, die beide über die gleiche Schnittstelle laufen. Bei call kommt auf eine Anfrage eine Antwort mit der gleichen ID (Polling). Bei "subscribe" dagegen kommen Events ohne ID und man braucht wiederum eine andere Methode, um herauszufinden, wer die Anfrage gestellt hat...

Meine Idee wäre: Das logische Modul übergibt den Namen des aufrufenden Device mittels IOWrite, damit das physikalische Modul diesen in der ID unterbringen kann. Das physikalische Modul baut diesen in die ID ein und stellt sicher, dass IDs eindeutig sind. Write gibt die eindeutige ID an das logische Modul zurück.

Zitat
Das Problem mit dem Antwort zurueckschicken habe ich leider nicht wirklich verstanden.
Vielleicht hilft etwas Pseudo-Code:
sub Phys_Write
{
    my $id = createID();
    if(USE_ASYNC_METHOD)
    {
        sendRequest($id);
    }
    else
    {
        $ret = execProgram();
        Dispatch($id . ":" . $ret);
    }
    return $id;
}

sub Phys_Read
{
    Dispatch($ret_containing_id);
}

sub Log_Parse
{
    # Find device which asked for $id
}

sub Log_sendRequest
{
    $id = IOWrite(...)
    $some_table{id} = $hash->{NAME} # Remember who asked.
}
Wenn das physikalische Gerät Anfragen asynchron behandelt (z.B. über HTTP), gibt obiger Code kein Problem:

Anfrage stellen: Log_sendRequest - > IOWrite -> Phys_Write -> sendRequest; die $id wird an Log_sendRequest zurückgegeben und gespeichert. Wenn die Antwort kommt: Phys_Read -> Dispatch -> Log_Parse und letzteres vergleicht mit der gespeicherten ID.

Wenn die Anfrage synchron erfolgt (z.B. über ein Kommandozeilen-Programm), passiert aber folgendes:

Log_sendRequest - > IOWrite -> Phys_Write -> execProgram, Dispatch -> Parse; jetzt wird Parse aufgerufen und die ID übergeben, bevor Write die ID als Rückgabewert an das logische Modul zurückgegeben hat und letzteres sie sich merken konnte. Parse kann die ID also nicht finden.

Aber auch ohne dieses ID-Problem ist es wohl keine gute Idee, Dispatch direkt in Write aufzurufen, vermute ich mal. Wenn nun das logische Modul als Folge von Parse eine weitere Anfrage stellen möchte und erneut IOWrite aufruft, kommt es dann nicht zu einer Rekursions-Schleife, wenn sich beide ständig gegenseitig aufrufen? Ich habe den synchronen Fall jetzt so "entschärft", dass Write nicht direkt Dispatch aufruft, sondern einen InternalTimer von 1 Sekunde setzt, der dann Dispatch aufruft.

Könnte man alternativ auch direkt in Write die Antwort zurückgeben? Dann müsste aber das logische Modul unterscheiden können, ob es von IOWrite gerade die komplette Antwort bekommen hat, oder nur eine ID, mit der es später via Parse die Antwort bekommt.

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24707
Antw:72_UBUS.pm Code Review / Feedback
« Antwort #48 am: 20 September 2021, 14:42:52 »
Der IOWrite Aufrifer bekommt doch die Antwort von WriteFn, das hast Du doch selbst festgestellt, siehe oben.
Dispatch in WriteFn aufzurufen ist auch OK, nur ParseFn muss sich benehmen, und in diesem Fall nicht wieder IOWrite aufrufen.

Viel schlimmer ist "$ret = execProgram()", weil das potentiell blockiert.
Richtigerweise ruft man das Programm mit $fd = system("programm|") auf, und man packt $fd ins selectList, damit ReadFn damit aufgerufen wird. Alternativ verwendet man BlockingCall.

Offline xenos1984

  • Developer
  • Full Member
  • ****
  • Beiträge: 417
Antw:72_UBUS.pm Code Review / Feedback
« Antwort #49 am: 20 September 2021, 15:20:07 »
Der IOWrite Aufrifer bekommt doch die Antwort von WriteFn, das hast Du doch selbst festgestellt, siehe oben.
Dispatch in WriteFn aufzurufen ist auch OK, nur ParseFn muss sich benehmen, und in diesem Fall nicht wieder IOWrite aufrufen.
Ja, aber WriteFn gibt die ID an den IOWrite Aufrufer zurück, nachdem WriteFn die Antwort des Calls über Dispatch an ParseFn gegeben hat. In dem Moment wartet ParseFn aber noch gar nicht auf diese Antwort, die zu dieser ID gehört, weil es die ID noch nicht bekommen und in seine "meine IDs auf deren Antwort ich warte" Liste eingetragen hat.

Zitat
Viel schlimmer ist "$ret = execProgram()", weil das potentiell blockiert.
Richtigerweise ruft man das Programm mit $fd = system("programm|") auf, und man packt $fd ins selectList, damit ReadFn damit aufgerufen wird. Alternativ verwendet man BlockingCall.
Ah, da hast du natürlich Recht. Die Syntax $fd = system("programm|") ist mir in der Form noch nie begegnet... Hast du ein Beispiel, wo die in FHEM benutzt wird, oder einen Link, wo sie beschrieben wird? In der Perl-Doku konnte ich nur system() als blockierende Funktion finden, die den Exit-Status zurückliefert, aber keinen Dateideskriptor. Um die Programmausgabe zu lesen, verweist die Perl-Doku auf qx, was aber auch blockiert.

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24707
Antw:72_UBUS.pm Code Review / Feedback
« Antwort #50 am: 21 September 2021, 11:27:18 »
Zitat
Die Syntax $fd = system("programm|") ist mir in der Form noch nie begegnet...
Sorry, mein Fehler, ich habe es mit open verwechselt.
Siehe auch https://perldoc.perl.org/functions/open#Opening-a-filehandle-into-a-command

 

decade-submarginal