FHEM Forum

FHEM => Automatisierung => Thema gestartet von: ftsinuglarity am 04 Juni 2018, 17:08:59

Titel: sleep in Funktionen
Beitrag von: ftsinuglarity am 04 Juni 2018, 17:08:59
Hallo liebe Gemeinde,

ich komme nicht so recht klar mit Pausen in Funktionen. Mir ist klar, daß das ein vieldiskutiertes Thema ist, ich schnalls trotzdem nicht. Ich entschuldige mich im Vorraus für die Frage und machs so kurz wie es geht.

Scriptbeispiel:
sub irgendwas {
    my $oldstate = ReadingsVal("xyDummy","xyReading",unset);
    my $state = do_anything_that_needs_time();
   
    if($state eq $oldstate) { print("ist gleich"); }
    else { print("ist NICHT gleich"); }
}
Hier kann es passieren, das $state noch nicht gesetzt ist.


Funktionieren würde:
    ..
    my $oldstate = ReadingsVal("xyDummy","xyReading",unset);
    my $state = do_anything_that_needs_time();
    fhem("sleep 10; set other_Dummy $state");
    ..

   
Nur wie bringe ich eine Pause non-blocking in das erste Scriptbeispiel ?
Also etwa :
    ..
    my $oldstate = ReadingsVal("xyDummy","xyReading",unset);
    my $state = do_anything_that_needs_time();
    sleep 10;
    if($state eq $oldstate) { print("ist gleich");
    ..

   
Wenn ich das so mache, würde ganz FHEM blockiert.
Titel: Antw:sleep in Funktionen
Beitrag von: CoolTux am 04 Juni 2018, 17:55:13
Schau dir im Developer Wiki mal InternalTimer ab
Titel: Antw:sleep in Funktionen
Beitrag von: ftsinuglarity am 04 Juni 2018, 18:17:42
Zitat von: CoolTux am 04 Juni 2018, 17:55:13
Schau dir im Developer Wiki mal InternalTimer ab
Danke CoolTux!
Damit müsste ich eine weitere Funktion definieren, die ich dann aufrufe. (Oder die gleiche Funktion aufrufen und mit dafür reserviertem Parameter, gesetzter Variable o.ä.)
Das wird bei mehreren "sleep's" in einer Funktion schon sehr umständlich oder gesplittet.

Eine "einfachere" Form im Sinne von
sleep 10 &&  if($state eq $oldstate) .. die ich direkt einfügen kann, .. gibt es eine ähnliche Möglichkeit ?

(ich habe etliches ausprobiert, nichts davon hatte die gleichen Vorstellungen von sleep wie ich :D )
Titel: Antw:sleep in Funktionen
Beitrag von: CoolTux am 04 Juni 2018, 18:21:14
Wer oder was führt denn die Funktion aus, und wieso ist state dann Deiner Meinung nach noch nicht aktuell?
Das ist viel interessanter.
Titel: Antw:sleep in Funktionen
Beitrag von: ftsinuglarity am 04 Juni 2018, 18:51:06
Zitat von: CoolTux am 04 Juni 2018, 18:21:14Wer oder was führt denn die Funktion aus
Aktuell habe ich eine Funktion, in der ein Linux-Script im Hintergrund einen DNS-Check durchführt.

Zitat von: CoolTux am 04 Juni 2018, 18:21:14
.. und wieso ist state dann Deiner Meinung nach noch nicht aktuell?
Die Auführung des Scripts dauert ein paar Sekunden, erst dann sind die Variablen in der aufrufenden 99_*.pm Funktion gefüllt. (die momentan per DNS aufgelöste IP zb)
Die aufrufende Funktion wartet jedoch nicht, bis die Scripte zu Ende ausgeführt wurden, sondern startet gleich den darauffolgenden Vergleich
"aktuelle_IP" eq "letzte_gespeicherte_IP"
Dazwischen bräuchte ich eine Pause.

Sleep brauche ich hin und wieder in Funktionen, um auf ähnliche Dinge wie in obigem DNS Beispiel warten zu können. Bisher habe ich immer Wege gefunden, das letztlich auch zu erreichen, aber sauber ist das alles nicht so wirklich.  Ich suche noch nach einem knackigen sleep 10 && mach irgendwas ... oder habe damit einen falschen Ansatz gewählt. Brauchst du sowas nie in Funktionen Tux?


Edit: Achso, die DNS Funktion wartet deshalb nicht auf das Ende des Scripts um dann erst die IP's zu vergleichen, weil ich sie im Hintergrund ausführen lasse um non-blocking zu arbeiten.
Beispiel: my $cur_ip = `$script cur_ip &`;
Titel: Antw:sleep in Funktionen
Beitrag von: CoolTux am 04 Juni 2018, 19:07:07
mir persönlich würde da noch Blocking.pm und die Funktion BlockingCall einfallen.
Aber das ist wieder Mehraufwand, ähnlich InternalTimer. Mir persönlich fällt da nichts schnelles ein.
Titel: Antw:sleep in Funktionen
Beitrag von: ftsinuglarity am 04 Juni 2018, 19:20:03
Zitat von: CoolTux am 04 Juni 2018, 19:07:07mir persönlich würde da noch Blocking.pm und die Funktion BlockingCall einfallen.Aber das ist wieder Mehraufwand, ähnlich InternalTimer.
Ja :D . Blocking.pm hatte ich schonmal bei einer anderen Frage. Da kam ich auf die nicht-wirklich-geile Idee, immer wiederkehrende Checks komplett in den Hintergrund zu verlagern.
Letztlich stellte sich heraus, das es Unsinnig war, überhaupt derartig große Checks im Hintergrund als quasi-cronjob auszuführen, sondern die Checks einzeln über entprechende Events anzustoßen. Was dazu führte, das es zum erstenmal richtig klick gemacht hat, das FHEM eben ein Event-basiertes System ist, und es deshalb kontrproduktiv ist, mit Gewalt was anderes rüberstülpen zu wollen.  Manchmal brauchts das .. uff  :o


Zitat von: CoolTux am 04 Juni 2018, 19:07:07Mir persönlich fällt da nichts schnelles ein.
Ich dank dir dennoch für deine Zeit.

Edit: Wenn nichtmal dir auf-die-schnelle was einfällt, brauchst du sleep in der Form anscheinend auch nicht. Dann hab ich hier einen falschen Ansatz anscheinend.
Titel: Antw:sleep in Funktionen
Beitrag von: rudolfkoenig am 04 Juni 2018, 22:57:09
Ich wundere mich, warum man so selten das Feature nutzt, dass man mit "..." einen nicht blockierenden externen Prozess starten kann. Das kann gerne ein Shell-, Python-, Perl-Script, oder sonstwas Ausfuehrbares sein. Mit FHEM kann man z.Bsp. via
perl fhem.pl 7072 'set x y'
kommunizieren, oder per wget, Beispiele zu wget gibts sicher irgendwo.
Titel: Antw:sleep in Funktionen
Beitrag von: ftsinuglarity am 05 Juni 2018, 08:45:33
Zitat von: rudolfkoenig am 04 Juni 2018, 22:57:09
Ich wundere mich, warum man so selten das Feature nutzt, dass man mit "..." einen nicht blockierenden externen Prozess starten kann. Das kann gerne ein Shell-, Python-, Perl-Script, oder sonstwas Ausfuehrbares sein. Mit FHEM kann man z.Bsp. via
perl fhem.pl 7072 'set x y'
kommunizieren, oder per wget, Beispiele zu wget gibts sicher irgendwo.

Hallo Rudolf. Könntest du "..." näher erläutern ?
Ich bin bisher am besten mit: my $var = `/pfad/script parameter &` gefahren.

my $var = "/pfad/script parameter" ist nur ein String, deswegen bin ich etwas verwirrt was du genau meinst.

perl fhem.pl 7072 '{functionName("test")}'
ist ein wenig Kanone auf Spatz  oder ?

Kannst du bitte ein Beispiel geben, wie du ein x-beliebiges Script mit Parameter innerhalb einer 99_*.pm Funktion so aufrufst, wie du das meinst ?
Titel: Antw:sleep in Funktionen
Beitrag von: CoolTux am 05 Juni 2018, 09:06:22
Im Grunde geht es darum das Du ein externes Script auf rufst, das machst Du aber innerhalb von FHEM also nicht in Perl wechseln

fhem('"meinscript.pl"');


Glaube so muss es gehen. Ist am Ende das selbe als wenn Du in FHEMWEB oben in der Kommandozeile "meinscript.pl" auf rufst. Damit startest Du ein externes Script nonBlocking. Problem ist und bleibt aber der Erhalt des Ergebnis. Das musst Du dann im externen Script so machen das es die erhaltenen Daten wieder an FHEM liefert mit

perl fhem.pl 7072 'set dummy reading ergebnis'
Titel: Antw:sleep in Funktionen
Beitrag von: rudolfkoenig am 05 Juni 2018, 09:32:15
Ein Beispiel dafuer sieht man in contrib/garden.pl.
Diesen Skript (bzw. den Nachfolger davon) rufe ich aus einem at auf.
Das muss aber (wie geschrieben) kein Perl sein, falls jemand sich mit anderen Sprachen wohler fuehlt.
Titel: Antw:sleep in Funktionen
Beitrag von: ftsinuglarity am 05 Juni 2018, 14:04:06
Zitat von: rudolfkoenig am 05 Juni 2018, 09:32:15
Ein Beispiel dafuer sieht man in contrib/garden.pl.
Diesen Skript (bzw. den Nachfolger davon) rufe ich aus einem at auf.
Das muss aber (wie geschrieben) kein Perl sein, falls jemand sich mit anderen Sprachen wohler fuehlt.

Okaaay .. das geht dann an die Grenzen meiner Kompetenz. Du rufst sicher nicht das ziemlich spezielle komplette Script auf, sondern nutzt dessen Funktionen, richtig?

Insbesondere den Teil hier:
..  fhzcommand("set $dev $how");..

###########################
sub
fhzcommand($)
{
  my $cmd = shift;

  my ($ret, $buf) = ("", "");
  $server = IO::Socket::INET->new(PeerAddr => "localhost:$fhzport");
  die "Can't connect to the server at port $fhzport\n" if(!$server);
  syswrite($server, "$cmd;quit\n");
  while(sysread($server, $buf, 256) > 0) {
    $ret .= $buf;
  }
  close($server);
  return $ret;
}


Tut mir leid wenn ich nicht ganz mitkomme und deswegen frage, was für dich der springende Punkt an der garden.pl ist und wie du das dann nutzt.
fhzcommand() löst das "sleep-Problem" durch das auslesen des Buffers bis nichts mehr kommt, wenn ich das richtig deute (hab die Funktion noch nicht vollständig verstanden), und gibt erst dann das Ergebnis ($ret) zurück. War es das worauf du hinaus wolltest ?
Titel: Antw:sleep in Funktionen
Beitrag von: rudolfkoenig am 05 Juni 2018, 14:10:53
ZitatDu rufst sicher nicht das ziemlich spezielle komplette Script auf, sondern nutzt dessen Funktionen, richtig?
Falsch. Ich rufe per at den "ganzen" Skript auf, was einen Bewaesserungsvorgang (dauert zwischen 5 und ca 120 Minuten) durchfuehrt.Das von Dir zitierte Funktion ist nur eine veraltete Kopie der gleichen Funktionalitaet aus fhem.pl, was man mit
Zitatperl fhem.pl 7072 'set x y'
erreichen kann.
Titel: Antw:sleep in Funktionen
Beitrag von: ftsinuglarity am 05 Juni 2018, 14:31:41
Zitat von: rudolfkoenig am 05 Juni 2018, 14:10:53
Falsch. Ich rufe per at den "ganzen" Skript auf, was einen Bewaesserungsvorgang (dauert zwischen 5 und ca 120 Minuten) durchfuehrt.
Ahso. Du hast wirklich ne Bewässerungsanlage :D ..
Titel: Antw:sleep in Funktionen
Beitrag von: CoolTux am 05 Juni 2018, 14:36:14
Zitat von: ftsinuglarity am 05 Juni 2018, 14:31:41
Ahso. Du hast wirklich ne Bewässerungsanlage :D ..

Wie gesagt es ist lediglich ein externes Script welches Deine Aufgabe erfüllt (unabhängig von FHEM) und die Rückgabewerte welches es bekommt über einen FHEM Kanal an FHEM übermittelt.
Im Script von Rudi ist es telnet, Du kannst aber auch FHEM direkt aufrufen und die Daten übermitteln.

perl fhem.pl 7072 'set dummy reading ergebnis'


Ich hätte es in Erwägung gezogen hättest Du nicht von EINFACH und SIMPEL gesprochen  ;D
Titel: Antw:sleep in Funktionen
Beitrag von: ftsinuglarity am 05 Juni 2018, 14:59:56
Zitat von: CoolTux am 05 Juni 2018, 14:36:14
Wie gesagt es ist lediglich ein externes Script welches Deine Aufgabe erfüllt (unabhängig von FHEM) und die Rückgabewerte welches es bekommt über einen FHEM Kanal an FHEM übermittelt.
Im Script von Rudi ist es telnet, Du kannst aber auch FHEM direkt aufrufen und die Daten übermitteln.

perl fhem.pl 7072 'set dummy reading ergebnis'


Ich hätte es in Erwägung gezogen hättest Du nicht von EINFACH und SIMPEL gesprochen


Ich glaube so langsam verstehe ich es.

sub aktuelle_funktion{ / oder direkt aufrufen
     fhem('"$script cur_ip"');
}


Im Script werden die Werte dann auf die eine oder andere Weise an FHEM übergeben. zB:

#!/bin/bash
..
ip=`wget ...ip_adresse..`
perl /path/fhem.pl 7072 "set Dummy $ip"
...



Um auf die Auswertung zu reagieren, könnte dann ein Notify auf den Dummy gesetzt werden
Habt ihr das so in etwa gemeint ?


Zitat von: CoolTux am 05 Juni 2018, 14:36:14
Ich hätte es in Erwägung gezogen hättest Du nicht von EINFACH und SIMPEL gesprochen
Joa, ist jetzt ein wenig mehr als "sleep 10" :D

Der InternalTimer ist für andere Szenarien auch sehr interessant.


Titel: Antw:sleep in Funktionen
Beitrag von: rudolfkoenig am 05 Juni 2018, 15:48:18
ZitatHabt ihr das so in etwa gemeint ?
Noe :), eher so:
define bewaessern at *19:00 "bin/garden.pl"
An FHEM-Daten kommt man aus dem Skript in etwa so ran:
my $regenmenge=`perl fhem.pl 7072 "{ReadingsNum('wetterstation','rain',0)}"`;
Und so kann man einen Schalter aus dem Skript setzen:
`perl fhem.pl 7073 "set Pumpe on-for-timer $dauer"`;oder ein Event auslosen:`perl fhem.pl 7073 "trigger bewaessern giessen_ist_fertig"`;

Ich will niemanden zwingen, so zu arbeiten, ich wollte nur zeigen, dass man so relativ einfach was bauen kann, was selbst schlafen/blockieren/lange rechnen kann, ohne FHEM zu blockieren, und man ist dabei auch nicht auf perl festgelegt. Und ich fand es merkwuerdig, dass kaum jemand sowas macht, obwohl das von Anfang an moeglich war.
Titel: Antw:sleep in Funktionen
Beitrag von: ftsinuglarity am 05 Juni 2018, 16:59:34
Zitat von: rudolfkoenig am 05 Juni 2018, 15:48:18
Noe :) , eher so:
Mist :)
Zitat von: rudolfkoenig am 05 Juni 2018, 15:48:18define bewaessern at *19:00 "bin/garden.pl"
ah .. da ist das "..." :) .. schön direkt

Zitat von: rudolfkoenig am 05 Juni 2018, 15:48:18An FHEM-Daten kommt man aus dem Skript in etwa so ran:
my $regenmenge=`perl fhem.pl 7072 "{ReadingsNum('wetterstation','rain',0)}"`;
Und so kann man einen Schalter aus dem Skript setzen:
`perl fhem.pl 7073 "set Pumpe on-for-timer $dauer"`;oder ein Event auslosen:`perl fhem.pl 7073 "trigger bewaessern giessen_ist_fertig"`;
Ja vielen Dank für die Details! Das ist mal echt schick. (so ganz daneben lag ich doch aber grad nicht ??)
Und so schwer wars eigentlich gar nicht zu verstehen .. man man, der Schlauch ist manchmal ziemlich lang...

Zitat von: rudolfkoenig am 05 Juni 2018, 15:48:18Ich will niemanden zwingen, so zu arbeiten, ich wollte nur zeigen, dass man so relativ einfach was bauen kann, was selbst schlafen/blockieren/lange rechnen kann, ohne FHEM zu blockieren, und man ist dabei auch nicht auf perl festgelegt. Und ich fand es merkwuerdig, dass kaum jemand sowas macht, obwohl das von Anfang an moeglich war.
Wüßte nicht wer jetzt behaupten könnte, du wolltest ihn zu irgendwas zwingen ;)
Vielen Dank für eure Zeit und diesen sehr informativen Ausflug an euch beide!!

Ist jetzt zwar keine einfaches "sleep 10 && machirgendwas()" geworden, das hilft mir aber enorm weiter, mit "sicheren" Werten zu arbeiten. Ein reines "sleep 10" ist ja immer nur ne Schätzung. Wenn das Script länger als 10 Sekunden bräuchte, wären die Variablen trotz sleep nicht gefüllt. (ok, dann könnte ich in eine sleep-Schleife gehen bis die Variablen voll sind) So ists aber wesentlich sauberer und direkter und vor allem non-blocking.

Titel: Antw:sleep in Funktionen
Beitrag von: ftsinuglarity am 19 Juni 2018, 12:25:07
Ich habe mit InternalTimer experimentiert, mir ist einiges noch nicht ganz klar.


Beispiel:
Meine Waschmaschine hängt an einer schaltbaren Strommessdose.
Schalte ich sie ein, wird die Funktion switch_device_on(DEVICE,Wattgrenze) aufgerufen.
Diese schaltet das DEVICE ein, und schreibt in den Hash des DEVICE den Gerätenamen und die Wattgrenze (limit) und übergibt diese per InternalTimer an die Funktkion: auto_power_depend_OFF
Dort ziehe ich mir aus dem Hash dann den DEVICE-Namen und die Wattgrenze.


Parameterübergabe mit 2 Parametern über InternalTimer:

sub switch_device_on($$) {
  my ($device,$limit) = @_;
  my $hash = $defs{$device};

  fhem("set $1 on");
  $hash = {'device' => $device, 'limit' => $limit};
  InternalTimer(gettimeofday()+5, "auto_power_depend_OFF", $hash);
}

sub auto_power_depend_OFF {
  my ($hash) = @_;
  my $device = $hash->{device};
  my $limit = $hash->{limit};
   ...
}




Frage: Ist die Parameterübergabe so im Sinne des Erfinders, oder mache ich es unnötig kompliziert ?

---------------------
Frage 2: auto_power_depend_OFF prüft ob der Stromverbrauch mehrere Schleifen lang unter der $limit (Wattgrenze) liegt, und schaltet dann aus, bzw. nicht wenn die Wattgrenze einmal überschritten wurde.
Die Schleife erzeuge ich dadurch, das ich einen at_Timer setze, der dann wiederum die Funktion erneut aufruft und die Stromwerte checkt.
Das geht sicher besser. Mir ist hier nicht klar, wie ich den hash per at_Timer übergebe.


sub auto_power_depend_OFF {
  my ($hash) = @_;
  my $device = $hash->{device};
  my $limit = $hash->{limit};
   ...
  fhem("defmod at_Timer at +00:00:30 {auto_power_depend_OFF(".$hash.")}");
  ...
}


Dieser Aufruf führt zu:
ERROR evaluating {auto_power_depend_OFF("HASH(0x58cd430)")}: Can't use string ("HASH(0x58cd430)") as a HASH ref while ...
oder wahlweise:
Undefined subroutine &main::HASH called at ..


--> gelöst
Selbst wenn der Hash erfolgreich übergeben würde, wären dann:
  my $device = $hash->{device};
  my $limit = $hash->{limit};
überhaupt noch gesetzt ?
--> Ja, weil direkt ins Device geschrieben wird
Titel: Antw:sleep in Funktionen
Beitrag von: ftsinuglarity am 20 Juni 2018, 11:08:09
Ich habe es jetzt selbst anders gelöst.


switch_device_on()  prüft nur noch das Device auf Existenz und ruft dann per InternalTimer die eigentliche Funktion "auto_power_depend_OFF ()" auf
Einen Hash für InternalTimer brauche ich nicht mehr, da der vorher auch nicht mehr machte, als in das Device zu schreiben. Das mache ich jetzt direkt in "auto_power_depend_OFF()"


# eigentlich benötige ich diese Funktion nur noch zum vereinfachten Aufruf im Notify

sub switch_device_on($) {
my $device = $_[0];
        if(!$defs{$device}) {return 0;}
InternalTimer(gettimeofday()+5, "auto_power_depend_OFF", $device);
}



in auto_power_depend_OFF() kann ich jetzt direkt das Device übergeben

sub auto_power_depend_OFF($) {
  my $device = $_[0];
   ...



und damit auch dem Timer von:

  fhem("defmod at_Timer at +00:00:30 {auto_power_depend_OFF(".$hash.")}");
  ...
}



auf

  fhem("defmod at_Timer at +00:00:30 { InternalTimer(gettimeofday()+1, 'auto_power_depend_OFF', \"".$device."\")}");
  ...
}


setzen

Außerdem läuft auf diese Weise die Funktion bei jedem Aufruf non-blocking, nicht nur beim ersten Aufruf durch switch_device_on(), wie in meiner 1. Version.



Warum die Hashübergabe im at_Timer nicht funktioniert, ist mir noch nicht klargeworden.

sub myTest {
my $dev = "Testdevice";
my $hash = $defs{$dev};

InternalTimer(gettimeofday()+2, "myTest2", $hash) ;
}

sub myTest2(@}) {
my ($hash) = @_;

fhem("defmod at_Timer at +00:00:05 {myTest2(".$hash.")}");
}