DevIO und udp

Begonnen von daniel2311, 26 Mai 2017, 17:17:00

Vorheriges Thema - Nächstes Thema

daniel2311

Hallo zusammen,

aufgrund folgenden Postes https://forum.fhem.de/index.php/topic,38231.msg639998.html#msg639998 muss ich doch noch mal nachfragen, wie es korrekt gemacht wird, denn ich bin nicht wirklich zum Erfolg gekommen und vllt. habe ich das Konzept auch noch nicht ganz verstanden.

Die Kommunikation mit dem Device, welches ich ansprechen möchte, erfolgt über UDP. Die ursprünglichen Varianten des Moduls haben tatsächlich teils sogar ziemlich lange FHEM geblockt, wenn das Gerät nicht erreichbar ist. Ich nutze Windows und da scheint es zumindest bei Windows keine vernünftig funktionierende Timeout-Option zu geben.

Jetzt habe ich versucht mit Devio durchzuführen, aber mit keinem Erfolg. Wenn ich es richtig sehe, unterstützt DevIO aktuell auch keine UDP-Kommunikation oder? Ich habe versucht, hier eine Anpassung zu machen, aber ohne Erfolg.

Wie macht man es richtig? Habt ihr Tips, wo ich mich einlesen kann - letztlich bin ich mit meiner Variante zwar eigentlich recht zufrieden und sie funktioniert gut, aber verbessern und "richtig" machen, ist ja mit Sicherheit sinnvoll.

Vielen Dank

Daniel

rudolfkoenig

DevIo unterstuetzt kein UDP. Benoetigst du UDP als Server oder als Client?

justme1968

was genau hat denn blockiert?

das senden einer udp nachricht kann eigentlich garnicht blockieren und wenn man zum lesen nicht aktiv wartet sondern die fhem select loop nutzt blockiert hier auch nichts.

schau dir mal die fakeRoku und plex module an. die verwenden zwar nicht devio aber udp broadcast und multicast ohne das irgendetwas blockiert.

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

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

daniel2311

#3
Hi zusammen,

Danke für eure Antworten!

Ich benötige eine UDP-Client-Verbindung und leider benötige ich allerdings auch Antwort vom Server-Port, also vom Device.

Mein ursprüngliches Problem war, dass beim Öffnen des Sockets über IO::Socket::INET und senden von Daten zu einem sehr langem Timeout kam, denn ein angegebener Timeout wird faktisch ignoriert. Heißt war das Gerät ausgeschaltet und habe ich Daten an das Gerät gesendet, war so ca. 30 sek FHEM nicht erreichbar, denn er hing im Verbindungsaufbau.

Nach Internet-Recherchen habe ich dann folgendes, doch recht gut funktionierendes Konstrukt gefunden, welches allerdings trotzdem zum eingestellten Timeout bzw. 3 Sekunden FHEM quasi blockiert, bis ich eine Antwort enthalten habe, weil ich ja in der FHEM Hauptschleife bin. Natürlich nur, wenn jemand eine set-Funktion triggert und das Gerät nicht erreichbar ist.


my $timeout = AttrVal($hash->{NAME}, 'socket_timeout', 3.0);
eval {
  local $SIG{ALRM} = sub { die 'Timed Out'; };
  alarm $timeout;
 
  #send udp packet
  my $socket = IO::Socket::INET->new(
        Proto       => 'udp',
        PeerAddr    => $hash->{ip},
        PeerPort    => '80',
ReuseAddr  => 1,
Timeout => $timeout,
        #Blocking => 0
  )  or Log3 $hash->{NAME}, 5, $hash->{NAME} . ": " . "Problem with socket";
 
  my $select = IO::Select->new($socket) if $socket;
 
 
  #$socket->autoflush;   
  $socket->send(pack('C*',@packet));
  #IO::Select->select($select, undef, undef, 3);
  if ($select->can_read($timeout)) {
$socket->recv($data, 1024);
  } else {
Log3 $hash->{NAME}, 5, $hash->{NAME} . ": " . "can't read";
  }
  $socket->close();
  alarm 0;
};
alarm 0; # race condition protection
Log3 $hash->{NAME}, 3, $hash->{NAME} . ": " . 'Error Timout' if ( $@ && $@ =~ /Timed Out/ );
Log3 $hash->{NAME}, 3, $hash->{NAME} . ": " . "Error: Eval corrupted: $@" if $@;
Log3 $hash->{NAME}, 5, $hash->{NAME} . ": " . length($data) . " bytes received from socket";


Das hier war mein recht naiver Ansatz mit meiner modifizierten DevIo.pm, die einen Socket auch für UDP öffnete, was aber nicht funktionierte und ich ein "Bad file descriptor" bekam.
my $ret = DevIo_OpenDev($hash, 0, undef);
return DevIo_Expect($hash, pack('C*',@packet), $timeout);


Ich bin mir aber auch nicht ganz sicher, ob ich das ganze Konzept mit den FD und usw. verstanden habe, wenn du von der "select loop" sprichst. Die DevelopmentModuleIntro redet von einer X_Read- und Write-Methoden. Aber dann bekäme ich ja eine Antwort Asynchron und weiß ja nicht mehr zwingend wozu das eine Antwort ist oder sehe ich das falsch?

Bei den genannten Beispielen in fakeRoku und Plex verstehe ich den Nutzen, denn beide machen ein UDP-Broadcast, um herauszufinden, welche Geräte vorhanden sind und dann ist die Antwort ja nicht spezifisch. Aber bin mir auch noch nicht ganz sicher, ob ich das mal beim Groben drüber gucken überhaupt geschnallt habe. Ich schaue mir mal die Funktionsweise genauer an und gucke, ob ich es verstehe, bis jetzt hat es noch nicht "Klick" gemacht.

PS: und vllt. wechsel ich mal doch das OS: Windows mag doch so einiges nicht. Ich wollte mal Plex ausprobieren, um mir ein genaures Bild zu machen, aber es will nicht. "nanosleep(): unimplemented in this platform"

CoolTux

Schau Dir mal an wie das LG_WebOS eine TCP Verbindung auf baut und die Socketinfo in die selectlist steckt. Gleiches gilt für HEOSMaster.
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

justme1968

ja. die beiden module verwenden broadcast und es gibt in diesem speziellen fall keine direkte zuordnung von gesendeter und empfangener nachricht. aber ich glaube das macht eigentlich keinen unterschied.

diese kannst du aber auch mit direktem warten auf die antwort nicht sicher stellen. abgesehen davon das es aus performance gründen keine gute idee ist: udp ist verbindungslos, nicht gesichert und nicht zuverlässig. es kann also sein das du auf eine antwort für einen nachricht wartest die dein gegenüber nie erhalten hat. auch ist die reihenfolge der nachrichten nicht garantiert. netzwerk kommunikation ist in aller regel asynchron.

wenn die zuordnung von nachricht und antwort wichtig ist gehört das ins protokoll. bei udp gibt es keine andere zuverlässige methode das sonst sicherzustellen. bei tcp kannst du einfach einen fifo verwenden und kommst damit schon ziemlich weit.

das blockieren beim öffnen des sockets bekommt man eigentlich mit dem Timeout paramter in den griff. öffnen musst du nicht bei jedem senden sondern nur ein mal.  du sendest do dann immer wieder auf dem gleichen socket.

was ist das für ein gerät/protokoll um das es geht?

ps: sig alarm (oder andere signale) dauerhaft umzubiegen (d.h. ohne später den alten wert wieder herzustellen) ist keine gute idee und kann fhem ziemlich durcheinander bringen.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

rudolfkoenig

Folgendes ist als Ergaenzung zu justme1968 und CoolTux zu verstehen:

FHEM ist single threaded, d.h. jede Art von sleep (oder select mit timeout) ist unerwuenscht.
Einzige Ausnahme ist das globale select in fhem.pl, was alle nutzen sollten, wenn sie von irgendwoher Daten erwarten.

Dafuer traegt  man den Device-Hash, der ein FD enthalten muss, ins globale %selectlist ein. Falls auf diesem FD Daten anlegen, wird die zum Device gehoerende ReadFn aufgerufen.

DevIo, HttpUtils_NonBlockingGet, TcpServer_Open, Blocking, etc kapseln diese Funktionalitaet fuer unterschiedliche Zwecke, leder gibt es noch kein "UdpServer_Open" (obwohl vermutlich sehr einfach).

Windows macht in _diesem_ Zusammenhang uebrigens gar keine Probleme, da alles mit Netzwerk (dank BSD-Code-Klau^H^H^H^HUebernahme) hervorragend mit select funktioniert. Nur die serielle (d.h USB) Kommunikation ist unter FHEM@Windows suboptimal (d.h. via polling) geloest.


daniel2311

Vielen Dank schon einmal für's Erklären!

Vom Grundsatz her, habe ich grob verstanden, was gemacht werden soll und was ich falsch mache. Da habe ich jetzt schon ordentlich mehr vom Rahmenwerk zusätzlich verstanden. Das Wie werde ich dann bei der Umsetzung mir ein wenig erarbeiten. Aber nicht mehr heute, dafür ist zu schönes Wetter!

Dann werde ich auch noch genau mal anschauen, was tatsächlich blockiert hat bzw. welche Timeout nicht gegriffen haben. Wobei die ja möglicherweise auch irrelevant werden, wenn ich nicht mehr auf eine Antwort warten muss und damit alles andere blockiere...

Ich werde mich melden, wenn ich Schwierigkeiten habe ;)

Prof. Dr. Peter Henning

Ich klinke mich hier mal ein, weil ich auch Probleme mit einem über WLAN angebundenen 1-Wire Adapter habe.

Das TCP-Device (sagen wir 192.168.0.97:23) wird ordnungsgemäß mit DevIo_OpenDev geöffnet.

Synchrone Schreib-Leseoperationen mit


DevIo_SimpleWrite($hash, $cmd, 0)
...
eval {
    local $SIG{ALRM} = sub { die "alarm\n" }; 
    alarm $timeout;
    $buffer = main::DevIo_DoSimpleRead($hash);
    alarm 0;
  };
  if ($@) {
    main::Log3 $name, 1, "OWX_TCP::Query timed out in first attempt";
    $timedout = 1;
  }


funktionieren prima. Sobald ich allerdings nur die Schreiboperation durchführe, wartet sich die Kiste tot. fhem.pl ruft niemals die Read-Operation auf, obwohl in der %selectlist der Eintrag 'OWX_WIFI.192.168.0.97:23' ordnungsgemäß drin steht und der Filedescriptor existiert.

Verstehe ich da irgendetwas nicht, oder braucht es dazu einen Trick ?

LG

pah

rudolfkoenig

Das Eintragen in %selectlist bewirkt, dass ReadFn aufgerufen wird, falls das globale select in fhem.pl der Ansicht ist, dass was zu lesen gibt. D.h. read (sysread/DevIo_SimpleRead/etc) sollte genau _einmal_ aus ReadFn aufgerufen werden.

Wegen der ... kann  ich nicht beurteilen, ob das hier der Fall ist.

Was ist der Grund, dass du DevIo_DoSimpleRead statt DevIo_SimpeRead verwendest?

Fuer Fortgeschrittene: Wenn man vorhat, Daten in Bereich von mehreren 100kB zu schreiben, dann sollte man lieber addToWritebuffer verwenden. Damit blockiert das Schreiben/FHEM nicht, falls die andere Seite langsam beim Abnehmen der Daten ist, weil dann das Schreiben auch erst nach einer Pruefung via select erfolgt.

P.S.: das ist eine Thread-Entfuehrung, da nichts mit UDP zu tun hat.

Prof. Dr. Peter Henning

Ich bin eben so ein Entführer...  ;)

Ich bin immer noch auf der Suche nach dem Problem. Werde Dich informieren, wenn ich es gefunden habe.

LG

pah

CoolTux

Andere Leute suchen nach Lösungen, pah sucht nach dem Problem   ;D



Grüße
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

Prof. Dr. Peter Henning

Stimmt auffallend. Ist mein Erfolgsrezept: Aggressionen richte ich gegen Probleme, nicht gegen Lösungen.

LG

pah