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
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..
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
> 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;
}
Halo Rudi, das sieht gut aus.... davon werde ich ausgehen...
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
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.
mach ich.
Vielen Dank
Gruß
Markus
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
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
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);
}
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?
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
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
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
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.
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