[gelöst] Shell Befehl qx() non-blocking ausführen?

Begonnen von FHEMAN, 09 November 2016, 22:54:11

Vorheriges Thema - Nächstes Thema

Prof. Dr. Peter Henning

Ein mehrzeiliges Reading macht ungefähr soviel Sinn wie ein Klotz am Bein. Das ist nämlich nur extrem umständlich weiter zu verarbeiten.

Also schlage ich doch vor, sich einmal, statt über Linux herumzumeckern und Windows hoch leben zu lassen, die Frage zu stellen: Warum ist diese Vorgehensweise so ungewöhnlich ? Könnte es vielleicht daran liegen, dass diejenigen, die mehr von Linux und Shells oder Programmierung im Allgemeinen verstehen, das für schlecht halten ?

LG

pah

FHEMAN

Zitat von: CoolTux am 12 November 2016, 07:21:33
Bittel schön

https://forum.fhem.de/index.php/topic,28753.msg501336.html#msg501336

Bei Fragen einfach fragen.

Hallo CoolTux, das sieht gut aus, hab vielen Dank. Bevor ich weitere Fragen stelle, teste ich damit mal ein bißchen rum.
Ich habe inzwischen zwar herausgefunden, dass mein SMSInbox Aufruf doch nicht bei perfmon anschlägt. Aber auch wenn es knapp unter 1 Sek. dauert, wäre es verbesserungswürdig. Außerdem lungern noch diverse andere qx() Aufrufe in meinen Funktionen.

Sollte ich am Ende eine halbwegs generische Lösung haben, stelle ich die natürlich gern allen zur Verfügung.

Gruß
Ronny
NUC7i5 | PROXMOX | FHEM 6.2 | 1 HMLAND | 2 UART | HM | LMS | HIFIBERRY | DOORBIRD | BLINK | BUDERUS | HUE | ALEXA | MILIGHT | LUFTDATENINFO | MQTT| ZIGBEE2MQTT | INDEGO | ROBOROCK | SMA | APC | OPENWB

FHEMAN

#17
Ich habe Cooltux' Funktion nun ein wenig umgestrickt, so dass ein System Aufruf nun folgendermaßen möglich ist:

systemCommand(COMMAND, [{DEVICE READING}])

COMMAND: Systemkommando, bspw. "sendEmail -f 'smarthome\@mailbox.org' -t ..."
DEVICE READING: Optionales Devicereading, in welches ein eventueller Rückgabewert geschrieben wird, bspw. "Lampe state" oder "SMS inbox"

Ein vollständiges Beispiel für den non-blocking E-Mail Versand:

systemCommand("sendEmail -f 'smarthome\@gmx.net' -t 'fhemuser\@mailbox.org' -u 'Subject' -m 'Subject Text ' -s 'mail.gmx.net:25' -xu 'smarthome\@gmx.net' -xp 'p@ssw0rd' -o tls=yes -o message-charset=utf-8", "Mail sent");

Danke nochmal an Cooltux für den wertvollen Input!
Ich frage mich, ob man bei fehlendem Rückgabewert SystemCommand_Done nicht überspringen könnte?

Es wäre schön, wenn es noch jemand anders testen könnte.

Gruß
Ronny

Code

package main;

use strict;
use warnings;
use POSIX;
use Blocking;



sub myUtils_SystemCommands_Initialize($;$) {

  my ($hash) = @_;
}

sub systemCommand($;$) {

    my ($command, $reading) = @_;
$command = encode_base64($command,"");
$reading //= 0;
$reading = encode_base64($reading,"");
$command .= "|".$reading;
    my $hash = $defs{$command};
   
    Log3 "SystemCommand", 4, "Sub systemCommand - START: $command";   
BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID}));
    $hash->{helper}{RUNNING_PID} = BlockingCall("systemCommand_Run", $command, "systemCommand_Done", 60, "systemCommand_Aborted", $hash) unless(exists($hash->{helper}{RUNNING_PID}));
    Log3 "SystemCommand", 4, "Sub systemCommand - START BLOCKING CALL - $command";
}

sub systemCommand_Run($) {

    my ($string) = @_;
    my ( $command, $reading ) = split("\\|", $string);
    my $result;
   
    Log3 "SystemCommand", 4, "Sub systemCommand - BEGIN SystemCommand: $command";
my $cmd = decode_base64($command);
$result = qx($cmd);
Log3 "SystemCommand", 4, "Sub systemCommand - END SystemCommand: $command, Result: $result";   
    if (!$result) {
$result = 0;
}
    $result = encode_base64($result,"");
    return "$command|$reading|$result";
}

sub systemCommand_Done($) {

    my ($string) = @_;
    my @a = split("\\|",$string);
    my $hash = $defs{$a[0]};
    my $name = $hash->{NAME};
    my $reading = $a[1];
my $result = $a[2];   
   
    delete($hash->{helper}{RUNNING_PID});
   
    Log3 "SystemCommand", 3, "Sub systemCommand_Done - $name) - Der Helper ist disabled. Daher wird hier abgebrochen" if($hash->{helper}{DISABLED});
    return if($hash->{helper}{DISABLED});
   
    $reading = decode_base64($reading);
if ($reading) {
if ((split(" ",$reading)) == 2 && $defs{(split(" ",$reading))[0]}) {
$result = decode_base64($result);
# add slashes at the end of lines for valid multiline readings
$result =~ s/\n/\\/g;
# $result =~ /(Sender:\s)(\+\d*)\s/;
fhem "setreading " . (split("\\|",$reading))[0] . " " . (split("\\|",$reading))[1] . " " . $result;
Log3 "SystemCommand", 4, "Sub systemCommand_Done - Result: $result, Reading: $reading";
} else {
Log3 "SystemCommand", 3, "Sub systemCommand_Done ERROR - device reading '$reading' not valid or device does not exist. Use setreading syntax like 'Device MyReading'";
}
} elsif ($result) {
Log3 "SystemCommand", 4, "Sub systemCommand_Done INFO - existing system command result has been ignored because you did not specify a device reading.";
}
       
}

sub systemCommand_Aborted($) {

    my ($hash) = @_;
    my $name = $hash->{NAME};

    delete($hash->{helper}{RUNNING_PID});
    Log3 "SystemCommand", 3, "Sub systemCommand_Aborted - $name - The BlockingCall Process terminated unexpectedly. Timedout.";
}

1;


//edit: Code robuster gemacht bei den Device Readings
NUC7i5 | PROXMOX | FHEM 6.2 | 1 HMLAND | 2 UART | HM | LMS | HIFIBERRY | DOORBIRD | BLINK | BUDERUS | HUE | ALEXA | MILIGHT | LUFTDATENINFO | MQTT| ZIGBEE2MQTT | INDEGO | ROBOROCK | SMA | APC | OPENWB

CoolTux

Hallo Ronny,

Eine Rückgabefunktion muss es immer geben und in diese muss auch bei erfolgreichen Ausführen gesprungen werden. Schließlich muss ja der Fork auch sauber beendet werden  ;)


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

FHEMAN

Alles klar, es sah mir ja auch danach aus, als würde da noch ein bisschen Magic passieren..
Ich habe die Funktion jetzt problemlos beim E-Mail Versand und dem aus den Eingangsthreat beschriebenen SMS Inbox Szenario eingesetzt.

Das Thema mit den Backslashes am Zeilenende habe ich zwar eingebaut, verstehe ich aber doch noch nicht ganz. Denn das Reading frisst auch mehrzeiligen Input ohne "\" !?

Gruß
Ronny
NUC7i5 | PROXMOX | FHEM 6.2 | 1 HMLAND | 2 UART | HM | LMS | HIFIBERRY | DOORBIRD | BLINK | BUDERUS | HUE | ALEXA | MILIGHT | LUFTDATENINFO | MQTT| ZIGBEE2MQTT | INDEGO | ROBOROCK | SMA | APC | OPENWB

FHEMAN

#20
Noch etwas zum Thema:
Führt ein grep auf Perl Ebene innerhalb von FHEM auch zu Freezes?
Ich hole mir diverse Gerätschaften via

my @devs = grep { $defs{$_}{NAME} =~ m/^Licht.Aussen.*$/ } keys %defs;

Dort, wo perfmon wegen Freezes anschlägt, folgt dann noch ein Logging der Status der Geräte mittels "trigger $dev $status" (addLog). Aber das Logging wird es doch wohl erst recht nicht sein?
NUC7i5 | PROXMOX | FHEM 6.2 | 1 HMLAND | 2 UART | HM | LMS | HIFIBERRY | DOORBIRD | BLINK | BUDERUS | HUE | ALEXA | MILIGHT | LUFTDATENINFO | MQTT| ZIGBEE2MQTT | INDEGO | ROBOROCK | SMA | APC | OPENWB

dev0

FHEM arbeitet generell single threaded. Vereinfacht gesagt ist FHEM für x Sekunden blockiert, wenn eine Routine x Sekunden läuft. Das Durchsuchen eines Hash dauert nicht lange, das Schreiben (auch von logs) kann, abhängig vom I/O System und der Datenmenge, schon etwas länger dauern. Apptime hilft beim Suchen des "Übeltäters".

FHEMAN

Ist es denn geplant, zumindest solche arbeitsintensiven Prozesse in FHEM auf non-blocking umzustellen? Immerhin leben wir ja gerade in einer Zeit voll billiger Multi Core CPUs.
Das Einfachste wäre dann wohl die erwähnte zweite FHEM Instanz samt FHEM2FHEM? Oder frisst der Verwaltungsoverhead (bei fhem und beim Anwender) die Vorteile wieder auf?
NUC7i5 | PROXMOX | FHEM 6.2 | 1 HMLAND | 2 UART | HM | LMS | HIFIBERRY | DOORBIRD | BLINK | BUDERUS | HUE | ALEXA | MILIGHT | LUFTDATENINFO | MQTT| ZIGBEE2MQTT | INDEGO | ROBOROCK | SMA | APC | OPENWB

FHEMAN

Da sich das bei 15 TFKs sich auf 1,3 Sek. Freezes summiert und das Logging absolut zeitunkritisch ist, habe ich mir nun mit einem vorangestellten sleep beholfen:
fhem("sleep 0.1 ; trigger $dev $logentry   << addLog");
Ich frage mich nur, ob die 0,1 Sekunden Ausführungszeit zwar perfmon genügen, aber ein Tastendruck zu diesem Zeitpunkt dann doch wieder verzögert ausgeführt wird.
NUC7i5 | PROXMOX | FHEM 6.2 | 1 HMLAND | 2 UART | HM | LMS | HIFIBERRY | DOORBIRD | BLINK | BUDERUS | HUE | ALEXA | MILIGHT | LUFTDATENINFO | MQTT| ZIGBEE2MQTT | INDEGO | ROBOROCK | SMA | APC | OPENWB

rudolfkoenig

ZitatIst es denn geplant, zumindest solche arbeitsintensiven Prozesse in FHEM auf non-blocking umzustellen?
Welche arbeitsintensive Prozesse meinst du?

Ich plane keine Umstellung auf Multithreaded, da vieles dagegen spricht:
- perl is mAn nicht dafuer gebaut
- viel "legacy" FHEM-Code, was auch nicht dafuer gebaut wurde, und angepasst werden muesste
- Race-Conditions zu debuggen ist nicht lustig, und in FHEM koennte so einiges Nebenlaeufig sein.
- Locking wird nicht von jedem verstanden bzw. wenn irgendwo fehlt, sieht man das nicht auf Anhieb. Vmtl. haben die meisten FHEM-Modul-Autoren keine Erfahrung mit Multithreading.

ZitatDa sich das bei 15 TFKs sich auf 1,3 Sek. Freezes summiert und das Logging absolut zeitunkritisch ist, habe ich mir nun mit einem vorangestellten sleep beholfen:
Und was hat das gebracht?

FHEMAN

#25
Zitat von: rudolfkoenig am 19 November 2016, 15:30:33
Welche arbeitsintensive Prozesse meinst du?
Die erwähnten IO Zugriffe beispielsweise. Vielleicht könnte man das für sinnvolle Funktionalitäten ohne Rückgabewert additiv bereitstellen mittels des an anderer Stelle schon verwendeten extra Attributs "nonblocking"?


Ich plane keine Umstellung auf Multithreaded, da vieles dagegen spricht:
- perl is mAn nicht dafuer gebaut
- viel "legacy" FHEM-Code, was auch nicht dafuer gebaut wurde, und angepasst werden muesste
- Race-Conditions zu debuggen ist nicht lustig, und in FHEM koennte so einiges Nebenlaeufig sein.
- Locking wird nicht von jedem verstanden bzw. wenn irgendwo fehlt, sieht man das nicht auf Anhieb. Vmtl. haben die meisten FHEM-Modul-Autoren keine Erfahrung mit Multithreading.

Ich verstehe, der Schmerz ist scheinbar nicht groß genug für den Aufwand. Ist aus meiner Sicht absolut legitim. Ich kann mit gelegentlichen Aussetzern bestimmt auch leben. Aber perspektivisch - selbst unabhängig von den anderen Smarthome Systemen - wäre dies mMn eine sinnvolle Eigenschaft.

Zitat
Und was hat das gebracht?
Tatsächlich leider nur einmalig kein Freeze. Ich hoffte, es wäre so eine Art Application.ProcessMessages.

Gruß
Ronny
NUC7i5 | PROXMOX | FHEM 6.2 | 1 HMLAND | 2 UART | HM | LMS | HIFIBERRY | DOORBIRD | BLINK | BUDERUS | HUE | ALEXA | MILIGHT | LUFTDATENINFO | MQTT| ZIGBEE2MQTT | INDEGO | ROBOROCK | SMA | APC | OPENWB

rudolfkoenig

#26
ZitatVielleicht könnte man das für sinnvolle Funktionalitäten ohne Rückgabewert additiv bereitstellen

Das gibt es seit 10 Jahren (d.h. von Anfang an), indem man ein Befehl mit "" ausfuehrt (Siehe auch http://fhem.de/commandref.html#command). Falls man Rueckgabewerte auswerten will, dann packt man das Parsen/Auswerten in einem Shellskript, der die Aktionen in fhem per "perl fhem.pl localhost:7072 'set myDummy Value'" bzw. Vergleichbares ausloest. Scheinbar haben aber die meisten Angst von Shellskripten, oder meinen, das waere unordentlich, jedenfalls wird diese Methode sehr selten eingesetzt.

Falls man sowas in der Schleife braucht, dann gehoert das externe Programm als Daemon (Windows Benutzer sagen Service dazu), den man z.Bsp. per HTTP abfragen kann, und fuer die HTTP-Abfrage gibt es HTTPMOD.

FHEMAN

#27
Genau das will ich möglichst vermeiden. Ich will den FHEM Kontext für normale FHEM Aufgaben nicht verlassen. Natürlich auch, da es für mich Windows Fanboy (;)) umständlicher ist zu handhaben.

Ich frage mich gerade, ob ich nicht die SystemCommand Funktion genau dafür aufbohren kann.

PS:
Zitat von: rudolfkoenig am 20 November 2016, 08:23:10
(Siehe auch http://fhem.de/commandref.html#command)
Ist das merkwürdige Favicon normal oder wurde die Seite gehackt?
NUC7i5 | PROXMOX | FHEM 6.2 | 1 HMLAND | 2 UART | HM | LMS | HIFIBERRY | DOORBIRD | BLINK | BUDERUS | HUE | ALEXA | MILIGHT | LUFTDATENINFO | MQTT| ZIGBEE2MQTT | INDEGO | ROBOROCK | SMA | APC | OPENWB

FHEMAN

#28
Ich habe die Funktion nochmal erweitert, um manch langwierige, aber zeitunkritische FHEM Aktionen nicht-blockierend auszuführen.

//edit: entfernt, da es so nicht funktioniert.
NUC7i5 | PROXMOX | FHEM 6.2 | 1 HMLAND | 2 UART | HM | LMS | HIFIBERRY | DOORBIRD | BLINK | BUDERUS | HUE | ALEXA | MILIGHT | LUFTDATENINFO | MQTT| ZIGBEE2MQTT | INDEGO | ROBOROCK | SMA | APC | OPENWB

CoolTux

Sehe ich das richtig das du einen fhem Befehl innerhalb einer NonBlocking Routine ausführen willst?
Wüsste nicht das das geht. Da du dich in einem FHEM Fork bewegst der am Ende ohne Einfluss auf das Eltern FHEM beendet. Du schaltest eine Holographie.
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