Beispiel für Threads gesucht

Begonnen von Tobias, 25 Dezember 2012, 14:50:48

Vorheriges Thema - Nächstes Thema

Tobias

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
Maintainer: Text2Speech, TrashCal, MediaList

Meine Projekte: https://github.com/tobiasfaust
* PumpControl v2: allround Bewässerungssteuerung mit ESP und FHEM
* Ein Modbus RS485 zu MQTT Gateway für SolarWechselrichter

Martin Fischer

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..
--
Admin, Developer, Gründungsmitglied des FHEM e.V.

Dr. Boris Neubert

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
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

rudolfkoenig

>  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;
}


Tobias

Halo Rudi, das sieht gut aus.... davon werde ich ausgehen...
Maintainer: Text2Speech, TrashCal, MediaList

Meine Projekte: https://github.com/tobiasfaust
* PumpControl v2: allround Bewässerungssteuerung mit ESP und FHEM
* Ein Modbus RS485 zu MQTT Gateway für SolarWechselrichter

Markus Bloch

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
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

rudolfkoenig

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.

Markus Bloch

mach ich.

Vielen Dank

Gruß
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)

sweetie-pie

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

Markus Bloch

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
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

sweetie-pie

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);

}

sweetie-pie

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?

sweetie-pie

Okay, ich habe die Mittagspause genutzt und probiert.
Ging einfacher als erwartet... das Ergebnis ist hier.

Danke
  Holger

sweetie-pie

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

sweetie-pie

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