FHEM Forum

FHEM - Entwicklung => FHEM Development => Thema gestartet von: freetz am 10 März 2018, 07:33:54

Titel: Rekursives Aufrufen einer Modulfunktion
Beitrag von: freetz am 10 März 2018, 07:33:54
Hallo zusammen,

vermutlich ist das keine Anfängerfrage, aber da es für die Programmierung von Modulen kein eigenes Forum gibt (ansonsten bitte gerne verschieben!), probiere ich es einmal hier:
Für die Anbindung meiner Rasenmähroboter-Steuerung "Robotan" (https://github.com/fredlcore/robotan) an FHEM schreibe ich gerade ein dazu passendes Modul. Für einzelne Befehle, die an den Roboter geschickt werden, klappt das auch schon sehr gut. Nun ist es aber so, das es einige Befehle gibt, für die zuerst eine Rückmeldung eines ersten Befehls abgewartet werden müssen, um dann einen entsprechenden zweiten oder dritten Befehl hinterher zu senden.

Bisher habe ich eine _Set Funktion, die über httpNonBlockingGet eine Anfrage schickt, deren Antwort dann in der Funktion _Eval_Response ausgewertet wird. Aus dieser Funktion müsste ich nun unter bestimmten Bedingungen die _Set Funktion nochmals mit anderen Parametern aufrufen.

Meine Frage ist nun: Geht das? Und wenn ja, welche Parameter erwartet die Set-Funktion normalerweise?

Falls es zur Modulprogrammierung speziellere Dokus gibt (in der DeveloperAPI-Artikel im Wiki habe ich zu solchen Details nichts gefunden), dann freue ich mich auch über einen Hinweis.

Vielen Dank schon mal!

F.
Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: CoolTux am 10 März 2018, 07:37:49
Es gibt das Developer Forum, darin kannst DU SELBER den Thread verschieben. Ganz unten ganz links THEMA VERSCHIEBEN

Dieser erste Befehl gibt es diesen auch in der SetFn? Vielleicht kannst Du etwas mehr erzählen was das genau ist.
Titel: Rekursives Aufrufen einer Modulfunktion
Beitrag von: justme1968 am 10 März 2018, 08:01:59
natürlich geht das. am einfachsten mit fhem("set $name ...");

aber ich glaube du denkst zu kompliziert. die setFn in deinem modul gehört doch dir. die kannst du es fach direkt aufrufen. so wie jede andere routine auch.

welche parameter erwartet werden siehst du auch direkt in seinem code.

gruss
  andre

ps: den hash bekommst du z.b. in dem du ihn mit in die parameter beim nonBlocking aufruf steckst. siehe z.b. BSB modul [emoji4]

pps: das ganze ist nicht rekursiv da du durch den nonBlocking aufruf alles entkoppelst.

ppps: falls du eine feste reihenfolge von kommandos hast kannst du auch alles auf einen stack pushen und dann nach ankunft einer antwort das nächste kommando vom stack holen und los schicken. auch dafür gibt es z.b. im BSB modul ein beispiel wenn eine reihe kathegorien nacheinander geholt wird.
Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: freetz am 10 März 2018, 08:06:05
Danke, bis eben hatte ich da noch keinen Zugriff, dachte nicht, dass das so schnell geht :)!

Meine Set-Funktion ist (momentan) nicht viel mehr, als dass auf einen set-Aufruf eine URL aufgerufen wird, deren Rückmeldung in den meisten Fällen nicht relevant ist. Wenn ich z.B. "set Robotan Drive_Left" aufrufe, wird nur nachgeschlagen, welche Befehlsnummer an den Roboter gesendet werden muss und daraus die URL gebildet. Wenn ich "set Robotan Set_Mow_Days 1,5,7" aufrufe, wird neben der Befehlsnummer noch eine Umrechnung der Tage in ein Bitmuster vorgenommen und das dann an die URL übermittelt. httpNonBlockingGet übergibt die Antwort des Roboters dann an die _Parse Funktion, die bisher nur bei Fehlermeldungen den Status entsprechend aktualisiert.

Einige Befehle erwarten aber einen bestimmten Status des Roboters, z.B. dass er sich nicht bewegen darf. Das würde mir über entsprechende Werte in der Rückantwort mitgeteilt, so dass ich dann einen weiteren Befehl zum Pausieren schicken könnte. Ich müsste also aus der Parse-Funktion heraus wieder die _Set-Funktion mit einem anderen Parameter aufrufen und nach erfolgreicher Ausführung den ursprünglichen Befehl erneut senden. Den "Pause"-Befehl gibt es auch in der SetFn, falls das helfen sollte.
Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: freetz am 10 März 2018, 08:11:40
@Andre: Ja, am BSB-Modul orientiere ich mich auch gerade :) - ich habe den direkten Aufruf über die Set-Funktion auch schon probiert, auch das hash übermittle ich bereits in der httpNonBlockingGet Funktion. Aber ich müsste zum einen noch weitere Parameter übermitteln, zum anderen hatte ich beim direkten Aufruf der Set-Funktion aus der Parse-Funktion bisher das Problem, dass FHEM meckert, dass die Anzahl der übermittelten Parameter nicht mit der erwarteten Anzahl übereinstimmen.  Normalerweise steht ja ein "$" für einen übermittelten Parameter. Aber bei der BSB-Set-Funktion steht z.B. "$$@" und es werden drei Skalare und ein Array erwartet. Oder bei BSB_Attr steht "$$$" und es werden vier Skalare verarbeitet.

Letzteres sind sicherlich ziemliche FHEM-Programmier-Anfängerfragen (obwohl ich schon lange in Perl programmiere), aber ich habe wie gesagt nirgendwo eine genauere Beschreibung dazu gefunden. Falls ich doch etwas übersehen haben sollte, tut mir das natürlich leid und ich freue mich über jeden Hinweis!
Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: CoolTux am 10 März 2018, 09:08:17
Ich habe mal kurz über Dein Modul geschaut. Wenn Du uns mal Deinen Aufruf zeigst der nicht geht dann kann ich mir das mal anschauen.

Darüber hinaus hast Du etwas zu viel übernommen  :)


$param->{cl} = $hash->{CL} if( ref($hash->{CL}) eq 'HASH' );

Das denke ich mal brauchst Du nicht, zu mindest wertest Du es nirgens weiter aus.

Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: freetz am 10 März 2018, 09:48:39
Danke für's Anschauen! Ja, ich habe Andres Modul Stück für Stück abgespeckt - irgendwie ging etwas ohne diese Zeile nicht mehr, aber da ich sie auch in 98_BSB.pm nicht richtig einordnen konnte, habe ich sie erst mal drin gelassen ;).

Mit dem direkten Aufruf der _Set-Funktion aus der _Eval-Funktion klappt es jetzt seltsamer- bzw. erfreulicherweise auch. Ich hätte dann nur noch zwei Fragen:
Werden bei httpNonBlockingGet zwei direkt aufeinander folgende Aufrufe so gesendet, dass der zweite Befehl erst dann gesendet wird, wenn vom ersten die Antwort gekommen ist? Oder wie kann ich am besten darauf warten?
Und zweitens: Wenn aus der _Eval-Funktion die _Set-Funktion aufgerufen wird, und dann wiederum die _Eval-Funktion: Bleiben Variablen, die ich innerhalb der _Eval-Funktion setze, bei nachfolgenden Aufrufen erhalten? Mir geht es darum, einen Retry-Counter umzusetzen. Dazu müsste ich mir merken, dass es a) einen Fehler gegeben hat und b) wie oft dieser versucht wurde zu beheben. Sollte ich dafür eine paketweite Variable verwenden oder macht man das anders?

Vielen Dank auf jeden Fall noch mal!
Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: freetz am 10 März 2018, 10:03:54
...und irgendwie werden die Parameter, die ich über die Erweiterung des hashes an _Eval übergebe, dort nicht übernommen:

  my $param = {
    url => $url,
    timeout => 20,
    hash => $hash,
    method => "GET",
    callback => \&Robotan_Eval_HTTP
  };
  $param->{origCmd} = $cmd;
  $param->{origParam} = $params[0];

  HttpUtils_NonblockingGet($param);


Wenn ich diese dann mit
my $origCmd = $hash->{origCmd};
my $origParam = $hash->{origParam};

zuweisen möchte, meldet FHEM:
2018.03.10 10:00:48 1: PERL WARNING: Use of uninitialized value $origCmd in print at ./FHEM/98_Robotan.pm line 298.
2018.03.10 10:00:48 1: PERL WARNING: Use of uninitialized value $origParam in print at ./FHEM/98_Robotan.pm line 298.


EDIT: Ok, ich Hirsch muss natürlich aus $params und nicht aus $hash übernehmen  ;)
Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: CoolTux am 10 März 2018, 10:24:37
Kann leider nur kurz antworten, die Käselauchsuppe verlangt Aufmerksamkeit  ;D

Die httpNonBlockingGet beachtet nichts. Schickst du damit was los wird das ausgeführt und die Antwort an die Callback Funktion übergeben.
Du musst selber in deiner Funktion eine Logik einbauen. Entweder schickst du die Befehle in eine Queue mittels Array oder du machst es irgendwie anders.
Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: freetz am 10 März 2018, 12:29:36
Ok, danke! Ein "fertiges" Queue-Handling gibt es noch nicht, oder? Frage nur, weil die Problemstellungen vielleicht schon öfter aufgetreten ist.
Dann aber erst mal guten Appetit :)!
Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: CoolTux am 10 März 2018, 12:34:26
Schau Dir mal die Funktion
TeslaPowerwall2AC_Timer_GetData($)
im Modul 46_TeslaPowerwall2AC.pm (https://github.com/LeonGaultier/fhem-TeslaPowerwall2AC/blob/master/46_TeslaPowerwall2AC.pm) an.
In dieser Funktion wird die Queue gefüllt und ich denke man kann sehr gut verfolgen wie es weiter geht.
Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: CoolTux am 10 März 2018, 12:44:21
Noch eine persönliche Anmerkung.
Mir ist bewusst das jeder Entwickler seinen eignen Stil hat, dennoch würde ich gerne ein zwei Worte zu Deinem Modul verlieren.

Du solltest vielleicht drüber nachdenken Deine Funktionen kleiner zu machen. Auf jeden Fall die SetFn. Das bringt Übersichtlichkeit und hilft auch mal anderen Entwicklern. Das zusammensetzten der Parameter und der Aufruf der HttpUtils_NonblockingGet Funktion kann man sehr gut in einer weiteren Funktion auslagern. So was muss nicht unbedingt mit in die SetFn. Das sollte Dir auch mehr Flexibilität geben.



Grüße
Titel: Antw:Rekursives Aufrufen einer Modulfunktion
Beitrag von: freetz am 10 März 2018, 13:05:51
Danke! Ja, ich wollte das auch noch alles etwas besser strukturieren, wenn erst mal alles läuft - ist als erstes Modul noch ziemlich mit der heißen Nadel gestrickt ;)...