GetFileFromURL <=> HttpUtils_NonblockingGet

Begonnen von Talkabout, 06 Juni 2015, 10:23:37

Vorheriges Thema - Nächstes Thema

Talkabout

Hallo zusammen,

Die Funktion "GetFileFromURL" blockiert ja FHEM während der Anfrage an einen Server. Ein Tipp, den ich bekommen habe ist, die Funktion "HttpUtils_NonblockingGet", die im Endeffekt einer asynchronen Funktion in JavaScript gleicht, zu verwenden. In meinem Modul ist es so, dass ich bei der "XXX_Set"-Funktion im Falle eines Fehlers (wenn der Request fehlgeschlagen ist oder der Service einen Fehler zurück gibt) diesen als String zurück gebe, und er dann auch im Frontend angezeigt wird. Diese Logik würde aber nicht mehr funktionieren, wenn ich über die asynchrone Funktion gehe. Wie habt Ihr dieses Problem gelöst?

Danke!

Gruss

herrmannj

Hi,

Verwendung der async function: top !

Du könntest ein passendes reading setzen, so etwas wie success/faiure. Darauf könnte der user in einem notify reagieren wenn er Aktionen ableiten möchte.

vg
joerf

Talkabout

Hallo herrmannj,

würde trotzdem das Problem nicht lösen, dass gleich nach dem Befehlsaufruf im Frontend ein Fehler angezeigt wird. Ich fand diese Ausgabe eigentlich immer ganz praktisch, da ich sofort sehen konnte, wenn was schief gelaufen ist.

Ich vermute mal, dass es dafür aber keine wirkliche Lösung gibt. Ein PERL-Sleep blockert ja auch, so wie ich es verstanden habe.

Es scheint in PERL auch das Konzept der "threads" zu geben. Wäre es nicht eine Überlegung wert, die Modul-internen Funktionen in separaten Threads auszuführen? Macht natürlich die Logik etwas komplizierter, aber vielleicht könnte man das per Modul-Funktion definieren, so was wie:

XXX_Should_Use_Treads

die im Fall von TRUE dazu führt, dass die anderen Methoden in separaten Threads aufgerufen werden. Damit würde auch ein "GetFileFromURL" keine Probleme mehr machen

Ich bin in PERL noch nicht so lange drin, von daher sorry, wenn der obige Vorschlag vielleicht "Mist" ist :)

Gruss


herrmannj

HI,

thread,fork,sleep:

set wird aufgerufen.
  setzt einen thread in Gang
  Warten auf das Ergebnis des thread (think twice !) ;)
set beenden mit "return $result".

Fehler erkannt ?

Woher kommt der Vorbehalt bzgl reading/event ?

Der User kann nicht mehr direkt auf die Rückgabe des set reagieren, stattdessen auf ein event.

Aber ist nicht das sowieso das Schema asynchroner Systeme ? ;)

vg
joerg

Markus Bloch

Hallo,

ich habe es bei mir so gelöst, dass ich mir nach einem HTTP Request immer merke ob das Gerät erreichbar ist, oder nicht. Wenn es erreichbar ist nehme ich Blocking-HTTP-Requests, wenn es nicht erreichbar ist nehme ich Non-Blocking-HTTP-Requests. Der Grund ist genau wie du schon beschrieben hast um eine Rückmeldung bei einem set-Befehl direkt geben zu können.

Wenn nun ein Blocking-HTTP-Request schief läuft wechselt das Modul auf non-Blocking.

Bei Daten die notwendig sind um einen Set-Befehl auszuführen, allerdings noch nicht geladen wurden, nehme ich auch Blocking-Requests um die Daten vor dem eigentlichen Ausführen des Set-Befehls nachzuladen in der Hoffnung, der User macht einen solchen Set-Befehl nur dann, wenn das Gerät auch an ist.

Viele Grüße

Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

justme1968

#5
ich mache es meistens so das alles was modul intern und automatisch arbeitet non blocking ist und nur die interaktiven set und get auf die rückgabe warten. aber auch nur dann wenn es sinnvoll ist. in allen fällen in denen es potentiell zu lange dauert ist das ergebniss in readings oder internals zu finden. manchmal gebe ich dem anwender noch die möglichkeit selber zu bestimmen ob die blocking oder non blocking variante verwendet wird. dann kann z.b. an anfang beim testen einfach interaktiv getestet werden auch wenn fhem dabei manchmal blockiert und im routine betrieb ist alles nonblocking. das geht mit den HttpUtils_ varianten sehr gut da die blocking und die non blocking variante syntaktisch identisch sind.

das es fehler gibt die zurück gemeldet werden sollte ja nicht die regel sein und es ist wichtiger das fhem läuft und reagiert. zumal normalerweise nicht die interaktive bedienung im vordergund steht sondern die automatismen.

weder threads noch prozesse sind übrigens eine lösung dafür eine meldung direkt zurück zu geben. selbst wenn das eigentliche processing in einem thread oder prozess im hintergrund läuft würde fhem im vordergrund warten bis das ergebniss da ist um es anzuzeigen.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Markus Bloch

Zitat von: Talkabout am 06 Juni 2015, 10:38:02

Es scheint in PERL auch das Konzept der "threads" zu geben. Wäre es nicht eine Überlegung wert, die Modul-internen Funktionen in separaten Threads auszuführen? Macht natürlich die Logik etwas komplizierter, aber vielleicht könnte man das per Modul-Funktion definieren, so was wie:

XXX_Should_Use_Treads

Das funktioniert zwar auf Raspberry's / PC's / usw. allerdings nicht auf NAS-Systemen und FritzBoxen, da dort die Perl-Umgebungen keinen Support für Threads besitzen, sonst wäre ich da schon ganz vorne dabei ;-)

Viele Grüße

Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

justme1968

Zitatweder threads noch prozesse sind übrigens eine lösung dafür eine meldung direkt zurück zu geben. selbst wenn das eigentliche processing in einem thread oder prozess im hintergrund läuft würde fhem im vordergrund warten bis das ergebniss da ist um es anzuzeigen.

beides kann verwendet werden um fhem nicht zu blockieren und etwas parallel zu verarbeiten. das direkte anzeigen einer meldung ist aber per definition blockierend und nicht parallel verarbeitung.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Talkabout

Hallo,

@herrmannj

Mein Vorschlag bezüglich der Threads war nicht direkt auf mein Problem bezogen, sondern eher darauf, dass man dann auch Blocking-Requests ausführen kann, ohne dass Fhem hängt. Da ich die internen Abläufe aber noch nicht kenne, weiss ich nicht, ob das so einfach umzusetzen wäre.

@Markus Bloch:

Für lokale Anfragen (an Geräte) ist das wahrscheinlich kein so grosses Problem, wenn die Anfragen nicht ewig dauern. Arbeitet man über das Internet, kann es schon mal passieren, dass ein Aufruf einige Zeit in Anspruch nimmt. Einen solchen Fall habe ich gerade.

@justme1968

Gibt es eine Möglichkeit rauszufinden, ob der aktuelle Aufruf des Moduls über das Frontend getätigt wurde? Dies könnte man dann als Weiche verwenden für Blocking/Non-Blocking.

Ansonsten danke ich Euch für die Tipps. Ich arbeite bereits mit Readings, das heisst die einzige Umstellung wäre die auf die HttpUtils_NonBlocking Funktion.

Gruss

Markus Bloch

Ja, meine Antwort bezog sich auf lokale Geräte im Netzwerk (AV-Receiver/BD-Player). Beim FB_CALLMONITOR frage ich auch Internetdienste an um einen Name zu einer Rufnummer zu finden. Diesen Request muss ich zwangsläufig Blocking machen, da ich den Namen direkt bei der Event-Bearbeitung sofort brauche. Da habe ich dann aber ein entsprechend straffes Timeout gesetzt.
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

herrmannj

ZitatGibt es eine Möglichkeit rauszufinden, ob der aktuelle Aufruf des Moduls über das Frontend getätigt wurde? Dies könnte man dann als Weiche verwenden für Blocking/Non-Blocking.

Oh Vorsicht. Der Call blockiert immer (!), völlig unabhängig davon ab er vom Frontend oder aus einem notify kommt. Selbst im optimalen Fall (Netzwerk da, Server erreichbar) wird ein call auf einen entfernten Service Verzögerungen im fhem Ablauf nach sich ziehen. Die bringen dann zB einen HMLAN (der in Millisekunden denkt) aus dem Takt.

Asynchron funktioniert IMMER (!). Für den dev bedeutet es unter Umständen eine andere Architektur.

Lass mich nochmal bohren: warum muss das "set" das Resultat zurückgeben ? Was ist damit machbar was nicht auch über ein event gehen würde ? Geht es um ein erneutes senden ? Kann genauso vom notify ausgelöst werden, oder optimalerweise ja auch direkt im Modul erledigt werden.

vg
joerg   

Markus Bloch

#11
Beispiel:

Wenn ich mein Licht ausschalte, soll der AV-Receiver via notify auch aus gehen, wenn das Reading playStatus = "stopped" ist. Nun wird der AV-Receiver aller 30 Sekunden abgefragt wie denn sein Status ist. Wenn ich nun auf den Lichtschalter drücke muss also zwangsläufig ein statusRequest gemacht werden bevor ich das Reading prüfen kann. Also muss ein set <name> statusRequest Blocking sein, damit nach dem Abschluss dieses Befehls auch wirklich die Readings aktualisiert sind und man diese prüfen kann, wenn das asynchron geschehen würde, hätte ein user nach erfolgtem statusRequest noch keine aktuellen Daten, da dazu mind. ein erneuter Main-Loop-Durchlauf von FHEM notwendig ist.
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

Talkabout

In meinem Fall geht es "lediglich" um die direkte Fehlerausgabe im Frontend, wenn man einen Befehl in die Command-Zeile eintippt. Aber ich sehe ein, dass das nicht der Haupt Use-Case ist. Von daher habe ich mein Modul nun komplett auf non-blocking umgebaut.

Danke Euch!

Gruss

Markus Bloch

Ich hatte das bei meinen YAMAHA Modulen zu Beginn alles auf Non-Blocking, bis dann mehrere User ankamen und solche Kommando Kontrukte wie

- Schalte ein
- gehe auf Eingang Y
- drücke Taste runter
- drücke OK

nicht mehr funktionierten, weil sie ja innerhalb von millisekunden abgefeuert worden sind obwohl das vorherige kommando noch garnicht durch war.

Daher hatte ich dass dann wieder zurückgezogen auf Blocking sofern das Gerät erreichbar ist.
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

justme1968

warum muss es im av-receiver beispiel blockierend sein?

das av-receiver modul sollte selber non-blocking und asynchron sein und den aktuellen status automatisch weiter melden.

das pollen an sich ist ja schon nur ein workaround dafür das das device seinen status nicht automatisch meldet.

aber selbst wenn tatsächlich gepollt werden muss: vor dem pollen aktivierst du ein av-aus notify das auf das status event wartet und sich danach wieder deaktiviert. da muss nichts blockieren.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968