FHEM Forum

FHEM - Entwicklung => FHEM Development => Thema gestartet von: Tobias am 25 Dezember 2012, 14:50:48

Titel: Beispiel für Threads gesucht
Beitrag von: Tobias am 25 Dezember 2012, 14:50:48
Hi,
kennt jemand Beipspielcode um länger laufende Prozesse/Funktionen als Threads zu starten? Bzw. arbeitet eine Funktion bei mit mehrere sekunden bis minuten (Datenbankjobs) und ich möchte diese vom fhem-Hauptprozess abkoppeln
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: Martin Fischer am 25 Dezember 2012, 17:59:06
mein owtemp müsste das drin haben.. wenn nicht im trunk, dann auf meiner platte (//images/smiley_icons/icon_smile.gif)

schau mal rein und wenn es nicht drin ist, dann liefere ich nach..
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: Dr. Boris Neubert am 29 Dezember 2012, 11:34:29
Zitat von: Tobias schrieb am Di, 25 Dezember 2012 14:50kennt jemand Beipspielcode um länger laufende Prozesse/Funktionen als Threads zu starten? Bzw. arbeitet eine Funktion bei mit mehrere sekunden bis minuten (Datenbankjobs) und ich möchte diese vom fhem-Hauptprozess abkoppeln

Echte Threads wollen wir nicht einführen, weil viele kleine Systeme das nicht unterstützen und außerdem wesentliche Teile von FHEM nicht thread-safe sind.

Der Weg von Martin ist der richtige (contrib/21_OWTemp.pm.fork). Er ist sogar ansatzweise in http://www.fhemwiki.de/wiki/DevelopmentGuidelines#Mechanismus_fuer_langsame_Readings dokumentiert.

Falls Du das so umsetzt, wäre es super, wenn Du den Mechanismus soweit kapseln kannst, daß er später in ein separates Modul ausgelagert und von verschiedenen Geräte genutzt werden kann.

Viele Grüße
Boris
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: rudolfkoenig am 29 Dezember 2012, 16:20:06
>  Falls Du das so umsetzt, wäre es super, wenn Du den Mechanismus soweit kapseln kannst, daß er später in ein separates Modul ausgelagert und von verschiedenen Geräte genutzt werden kann.

Ich habe FHEM/Blocking.pm eingecheckt, hier wird die Funktion BlockingCall mit bis zu vier Parameter definiert. Details:

- der erste Parameter in BlockingCall ist der Name einer Funktion. Dieser wird nach einem fork in dem Kind-Prozess aufgerufen. Diese Funktion kann gerne blockieren, das stoert fhem nicht.

- der zweite Parameter in BlockingCall ist ein Argument fuer die vorherige Funktion.

- der dritte Parameter in BlockingCall (falls vorhanden) ist der Name einer sog. Auswerte-Funktion. Die erste (blockierende) Funktion hat zwar Zugriff auf alle Daten, aber eine Aenderung von Variablen (z.Bsp. Readings) wird im Vaterprozess nicht sichtbar, und geht damit verloren. Deswegen wird im Vater-Prozess mit dem Rueckgabewert der ersten, blockierenden Funktion diese zweite Funktion aufgerufen, um z.Bsp. die Readings aendern, oder trigger auszuloesen.

- der vierte Parameter in BlockingCall (optional) spezifiziert eine Zeit in Sekunden, nachdem das Kindprozess terminiert wird.

Beispielaufruf:

sub TestBlocking()
{
  require "Blocking.pm";
  BlockingCall("DoSleep", 5, "SleepDone", 8);
}

sub
DoSleep($)
{
  sleep(shift);
  return "I'm done";
}

sub
SleepDone($)
{
  Log 1, "SleepDone: " . shift;
}

Titel: Aw: Beispiel für Threads gesucht
Beitrag von: Tobias am 29 Dezember 2012, 18:21:46
Halo Rudi, das sieht gut aus.... davon werde ich ausgehen...
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: Markus Bloch am 07 Januar 2013, 19:27:18
Hallo Rudi,

darf ich die Funktion für meine offiziellen Module nutzen? Da ich auch HTTP-Aufrufe dabei habe, die im Falle eines nicht erreichbaren Devices natürlich auch FHEM für $timeout Sekunden blocken.

Ich würde die Funktion verwenden wollen, sofern sie final ist und auch auf Dauer so bleiben würde.

Viele Grüße

Markus
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: rudolfkoenig am 07 Januar 2013, 20:34:40
Das kannst Du gerne, allerdings bin ich nicht sicher, ob die Funktion mit ganzen HTML Seiten als Antwort umgehen kann.
Wenn Du Probleme findest, dann melde Dich.
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: Markus Bloch am 07 Januar 2013, 20:57:42
mach ich.

Vielen Dank

Gruß
Markus
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: sweetie-pie am 10 Februar 2013, 23:28:41
Zitat von: rudolfkoenig schrieb am Sa, 29 Dezember 2012 16:20- der dritte Parameter in BlockingCall (falls vorhanden) ist der Name einer sog. Auswerte-Funktion. Die erste (blockierende) Funktion hat zwar Zugriff auf alle Daten, aber eine Aenderung von Variablen (z.Bsp. Readings) wird im Vaterprozess nicht sichtbar, und geht damit verloren. Deswegen wird im Vater-Prozess mit dem Rueckgabewert der ersten, blockierenden Funktion diese zweite Funktion aufgerufen, um z.Bsp. die Readings aendern, oder trigger auszuloesen.

sub
SleepDone($)
{
  Log 1, "SleepDone: " . shift;
}


Ich habe gerade überlegt, wie das bei mir einzubauen ist. Ich habe da aber noch ein Verständnisproblem. Hier mal der aufs wesentliche reduzierte Code:

sub VBUS_GetStatus($)
{
my ($hash) = @_;
my $name  = $hash->{NAME};
my $delay = $attr{$name}{delay}||120;

        InternalTimer(gettimeofday()+$delay, "VBUS_GetStatus", $hash, 0);

require "blocking.pm";
BlockingCall("VBUS_FetchData", $hash, "VBUS_DecodeData", );

}

sub VBUS_FetchData($)
{
my ($hash) = @_;
my $name  = $hash->{NAME};
my $host  = $hash->{Host};
my $port  = $hash->{Port};


my $socket = IO::Socket::INET->new(PeerAddr=>$host, PeerPort=>$port, timeout=>2, blocking=>1);
if (!$socket) {
$hash->{STATE} = "disconnected";
Log 1,"$name: Error opening Connection to $host";
return;
}

#Diverse Auswertung, Ergebnis im String $payload

return $payload;

}


sub VBUS_DecodeData($$)
{

my ($hash, $payload) = @_;

#Diverse Auswertung von $payload

readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"temp01",$temp_s1);
#....
readingsEndUpdate($hash, 1);

}


In der zweiten sog. Auswerte-Funktion, brauche ich ja noch den Hashwert für die ReadingUpdates, oder? Woher bekomme ich die?

Gruß
 Holger
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: Markus Bloch am 10 Februar 2013, 23:38:48
Hi,

schauh dir mal das PRESENCE-Modul an, dort gibt es eine Funktion StartLocalScan und eine ProcessLocalScan. Generell musst du einen Verweis auf das zugrundeliegende Device mitgeben. Ich hab es so gemacht, dass ich den Namen des Devices ($hash->{NAME}) mit gegeben habe und in der Auswertefunktion mit $hash = $defs{$name}; wieder darauf zugegriffen habe.

Das ist zumindest meine Variante, wüsste auch keinen anderen Weg, da ja das Ergebniss ein String sein muss, da dieser via Telnet zurückgegeben wird.

Viele Grüße

Markus
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: sweetie-pie am 11 Februar 2013, 10:45:34
Okay, hier dann nochmal in Trockenübung... so müsste es gehen, oder?

sub VBUS_GetStatus($)
{
my ($hash) = @_;
my $name  = $hash->{NAME};
my $host  = $hash->{Host};
my $port  = $hash->{Port};
my $delay = $attr{$name}{delay}||120;

        InternalTimer(gettimeofday()+$delay, "VBUS_GetStatus", $hash, 0);

BlockingCall("VBUS_FetchData", $name."|".$host."|".$port , "VBUS_DecodeData", );

}

sub VBUS_FetchData($$)
{

my ($string) = @_;
my ($name, $host, $port) = split("\\|", $string);

my $socket = IO::Socket::INET->new(PeerAddr=>$host, PeerPort=>$port, timeout=>2, blocking=>1);
if (!$socket) {
Log 1,"$name: Error opening Connection to $host";
return $name."|Error opening Connection";
}

#Diverse Auswertung, Ergebnis im String $payload

return $name."|".$payload;

}


sub VBUS_DecodeData($)
{

my ($string) = @_;
my ($hash, $payload) = split("\\|", $string, 2);

#Diverse Auswertung von $payload

readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"temp01",$temp_s1);
#....
readingsEndUpdate($hash, 1);

}
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: sweetie-pie am 11 Februar 2013, 10:51:40
Wobei ich immer dachte das Kind erbt alles, deswegen müsste doch auch für die Fetch-Funktion vorherige Ansatz mit dem $hash gehen, also:


BlockingCall("VBUS_FetchData", $hash , "VBUS_DecodeData", );

und

sub VBUS_FetchData($)
{
my ($hash) = @_;
my $name  = $hash->{NAME};
my $host  = $hash->{Host};
my $port  = $hash->{Port};


Das eigentliche Problem liegt in der Rückgabe, oder?
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: sweetie-pie am 11 Februar 2013, 13:07:48
Okay, ich habe die Mittagspause genutzt und probiert.
Ging einfacher als erwartet... das Ergebnis ist hier. (http://forum.fhem.de/index.php?topic=10303.msg63410#msg63410)

Danke
  Holger
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: sweetie-pie am 12 Februar 2013, 22:46:07
Hallo,

nachdem mein Modul jetzt ca. 30h lief, gibt es jetzt Probleme.
Die Rückgabe vom Kind an die Auswertung schient nicht zu funktionieren,
soweit konnte ich es mal einschränken.

Mein Kind-Prozess endet mit folgendem Code inkl. einem Debug-Helper $dummyhelper
der den Bytestring den ich übergeben möchte ins Log ausgibt.
       my $dummyhelper = unpack('H*',$payload);

        Log 3,"$name Beende das Kind erfolreich. Rueckgabe: $dummyhelper";

        return $name."|".$payload;


Hier dazu die Log-Ausgabe....

2013.02.12 22:24:04 3: MyVBUSDevice Beende das Kind erfolreich. Rueckgabe: fbffc6014a02b822b822b8224b02ac013b02410200000000010000003c000000640000000000000001000000000000000000000000000000000000000103e10add07020c


Ändere ich jetzt den Inhalt von $payload z.B. in "Teststring"
wird die Auswertefunktion wieder wie erwartet aufgerufen.

Ich vermute mal, dass im Rückgabestring irgendwas ungültiges drin ist, was die Telentverbindung abbricht,oder?
Muss ich da was escapen? Was könnte es sein? Wie könnte ich das am besten Debuggen?

Gruß
 Holger
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: sweetie-pie am 13 Februar 2013, 10:58:25
Okay, habe das nochmal weiter verfolgt.

Hier ein Rückgabewert vom Kind
edff71010d02b822b822b82299019a019b01e901000000000100000000000000000000000000000001000000000000000000000000000000000000000103870cdd07020d
Und hier wie es in der Auswertung landet:
edff710102b822b822b82299019a019b01e901000000000100000000000000000000000000000001000000000000000000000000000000000000000103870cdd0702

Man sieht, dass das 5. Sendebyte verschluckt wird, ASCII 0x0d = 13 = CR.

Wie kann/sollte man das lösen?
Ich kann natürlich im Kindprozess dafür sorgen, das kein 0x0d mehr in der Rückgabe ist, aber das ist ja mehr ein generisches Problem der Blocking.pm, um welches ich dann herum programmiere...!?

Gruß
 Holger



Titel: Aw: Beispiel für Threads gesucht
Beitrag von: rudolfkoenig am 13 Februar 2013, 11:40:40
Die Antwort wird als
  {$finishFn('$ret')}
im FHEM/telnet aufgerufen, deswegen darf hier weder ein NL noch ein ; vorkommen.
Immerhin wird ' mit \' ersetzt, das sollte also kein Problem sein.
Generell empfehle ich keine Binaerdaten zurueckzuliefern, also z.Bsp. (wie culfw) die Daten nach HEX wandeln.
Titel: Aw: Beispiel für Threads gesucht
Beitrag von: sweetie-pie am 13 Februar 2013, 11:46:48
okay, dann habe ich jetzt bei mir in Blocking-Funktion die Rückgabewerte in einen reinen Hexstring gewandelt, dann sind NL und CR egal.
       my $dummyhelper = unpack('H*',$payload);

        Log 3,"$name Beende das Kind erfolreich. Rueckgabe: $dummyhelper";

        return $name."|".$dummyhelper;
 


In der Auswertefunktion mach ich das Gegenteil:
       my ($name, $payload_exp) = split("\\|", $string, 2);

        my $payload = pack('H*',$payload_exp);



So funktioniert es offensichtlich bei mir ohne viel Änderungen...

Gruß
 Holger