neue Features: ereignisgesteuertes Perl - DOIF-Perl

Begonnen von Damian, 25 Februar 2018, 21:29:16

Vorheriges Thema - Nächstes Thema

Damian

#60
Neue Version eingecheck. set_Exec kann jetzt direkt Perlcode ausführen. Doku zum Perl-Modus wurde überarbeitet:

Auszug:

ZitatPerl Modus

Der Perl-Modus ist sowohl für einfache, als auch für komplexere Automatisierungsabläufe geeignet. Der Anwender hat mehr Einfluss auf den Ablauf der Steuerung als im FHEM-Modus. Die Abläufe lassen sich, wie in höheren Programmiersprachen üblich, strukturiert programmieren. Zum Zeitpunkt der Definition werden alle DOIF-spezifischen Angaben in Perl übersetzt, zum Zeitpunkt der Ausführung wird nur noch Perl ausgeführt, damit wird maximale Performance gewährleistet.

Syntax Perl-Modus:

define <name> DOIF <Blockname> {<Perlcode mit Ereignis-/Zeittriggern in eckigen Klammern>}


Ein Perlblock wird ausgeführt, wenn dieser bedingt durch Ereignis- und Zeittrigger in eckigen Klammern innerhalb des Blocks, getriggert wird. Es wird die vollständige Perl-Syntax unterstützt. Es können beliebig viele Perlblöcke innerhalb eines DOIF-Devices definiert werden. Sie werden unabhängig voneinander durch passende Trigger ausgeführt. Der Name eines Blocks ist optional.

Der Status des Moduls wird nicht vom Modul gesetzt, er kann vom Anwender mit Hilfe der Funktion set_Reading verändert werden, siehe spezifische Perl-Funktionen im Perl-Modus. FHEM-Befehle werden durch den Aufruf der Perlfunktion fhem"..." ausgeführt.

Der Benutzer kann mit der Funktion set_Timer/set_Exec beliebig viele eigene Timer definieren, die unabhängig voneinander gesetzt und ausgewertet werden können, siehe Spezifische Perl-Funktionen im Perl-Modus.

Definitionen im FHEM-Modus der Form:

DOIF (<Bedingung mit Trigger>) (<FHEM-Befehle>) DOELSE (<FHEM-Befehle>)

lassen sich wie folgt in Perl-Modus übertragen:

DOIF {if (<Bedingung mit Trigger>) {fhem"<FHEM-Befehle>"} else {fhem"<FHEM-Befehle>"}}

Die Bedingungen des FHEM-Modus können ohne Änderungen in Perl-Modus übernommen werden können.

Im Perl-Modus können beliebig viele Blöcke definiert werden, die unabhängig von einander durch einen Trigger ausgewertet und zur Ausführung führen können:

DOIF
{ if (<Bedingung mit Trigger>) ... }
{ if (<Bedingung mit Trigger>) ... }
...


Im Perlmodus sind beliebige Hierarchietiefen möglich:

DOIF
{ if (<Bedingung>) {
    if (<Bedingung>) {
      if (...
        ...
      }
    }
  }
}


Bemerkung: Innerhalb eines DOIF-Blocks muss mindestens ein Trigger in irgendeiner Bedingung definiert werden, damit der gesamte Block beim passenden Trigger ausgewertet wird.

Eigene Funktionen

Ein besonderer Perlblock ist der Block namens "subs". In diesem Block werden Perlfunktionen definiert werden, die innerhalb des DOIFs genutzt werden. Um eine möglichst hohe Kompatibilität zu Perl sicherzustellen, wird keine DOIF-Syntax in eckigen Klammern unterstützt, insb. gibt es keine Trigger, die den Block ausführen können.

Beispiel:

DOIF subs { ## Definition von Perlfunktionen lamp_on und lamp_off
  sub lamp_on {
     fhem"set lamp on";
     set_Reading("state","on",1);
  }
  sub lamp_off {
     fhem"set lamp off";
     set_Reading("state","off",1);
  }
}
{if ([06:00]) {lamp_on()  # Um 06:00 Uhr wird die Funktion lamp_on aufgerufen }
{if ([08:00]) {lamp_off() # Um 08:00 Uhr wird die Funktion lamp_off aufgerufen }

...
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

Welche Schreibweise ist für den nicht triggernden Zugriff auf Readings vorzuziehen? Gibt es nennenswerte Performanceunterschiede?

a) [?<name>:<Reading>,<Default>] oder
b) ReadingsVal(<name>, <Reading>,<Default>)

Ellert

In set_Exec gibt es noch eine Stolperfalle, die Definition
defmod di2400 DOIF test {\
  if (["^global$:^MODIFIED $SELF$"]){\
    set_Exec("test",10,'Log 1,"[?$SELF:state]"');;\
  }\
}

erzeugt eine Fehlermeldung, wegen der Hochkommata in ReadingValDoIf
Internals:
   DEF        test {
  if (["^global$:^MODIFIED $SELF$"]){
    set_Exec("test",10,'Log 1,"[?$SELF:state]"');
  }
}
   MODEL      Perl
   NAME       di2400
   NR         172
   NTFY_ORDER 50-di2400
   STATE      initialized
   TYPE       DOIF
   READINGS:
     2018-09-16 18:50:54   Device          global
     2018-09-16 18:50:54   block_test      condition c01: Bad name after di2400', line 3.

     2018-09-16 18:50:53   mode            enabled
     2018-09-16 18:50:53   state           initialized
   Regex:
     cond:
       :
         0:
           "^global$:^MODIFIED di2400$" ^global$:^MODIFIED di2400$
   condition:
     0         
  if (EventDoIf('^global$',$hash,'^MODIFIED di2400$',0)){
    DOIF_set_Exec($hash,"test",10,'Log 1,"ReadingValDoIf($hash,'di2400','state')"');
  }

   devices:
   helper:
     event      MODIFIED di2400
     globalinit 1
     last_timer 0
     sleeptimer -1
     triggerDev global
     triggerEvents:
       MODIFIED di2400
     triggerEventsState:
       MODIFIED di2400
   internals:
   itimer:
   perlblock:
     0          test
   readings:
   trigger:
   uiState:
   uiTable:
Attributes:

Damian

Wenn es sich um keine Trigger handelt, würde ich ReadingsVal nehmen. Dann weiß auch der Perl-Programmierer, was dahinter steckt, ansonsten wird es immer Probleme mit den Anführungszeichen geben, da man nie weiß, was genau dahinter steckt.

hier also:

set_Exec("test",10,'Log 1,ReadingsVal("$SELF","state","")')

Vielleicht definiere ich noch für Readings des Device das Gegenstück zu der Funktion set_Reading:

Reading (<Reading>,<default>);

Dann könnte man angeben:

set_Exec("test",10,'Log 1,Reading("state","")')


Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

#64
Ich habe vor do always Beispiele in der Doku in Perl-Modus zu überführen, weil sie meiner Meinung nach einfacher zu definieren sind, z. B.

aus
define di_rand_sunset DOIF ([{sunset()}])(set lamp on)
attr di_rand_sunset wait rand(1200)
attr di_rand_sunset timerWithWait 1
attr di_rand_sunset do always


wird
define di_rand_sunset DOIF {if ([{sunset()}]) {set_Exec("on",rand(1200),'fhem"set lamp on"')}}
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

Mit dem Attribut setList  kann man die Auswahlliste des Set-Befehls erweitern.
Durch die Erweiterung des Set-Befehls lassen sich bestimmte  Funktionen des DOIF im Perl-Modus direkt ausführen, z.B. um etwas zu Testen.

defmod directCommand DOIF {\
  if (["^$SELF$:^debug_1$"]){\
    Log 1,"$SELF: if-Block 1 getestet!";;\
  } elsif (["^$SELF$:^debug_2$"]) {\
    Log 1,"$SELF: if-Block 2 getestet!";;\
  } elsif (["^$SELF$:^debug_3$"]) {\
    Log 1,"$SELF: if-Block 3 getestet!";;\
  }\
}
attr directCommand setList debug_1 debug_2 debug_3
attr directCommand webCmd debug_1:debug_2:debug_3

Damian

#66
Zitat von: Ellert am 18 September 2018, 20:27:53
Mit dem Attribut setList  kann man die Auswahlliste des Set-Befehls erweitern.
Durch die Erweiterung des Set-Befehls lassen sich bestimmte  Funktionen des DOIF im Perl-Modus direkt ausführen, z.B. um etwas zu Testen.

defmod directCommand DOIF {\
  if (["^$SELF$:^debug_1$"]){\
    Log 1,"$SELF: if-Block 1 getestet!";;\
  } elsif (["^$SELF$:^debug_2$"]) {\
    Log 1,"$SELF: if-Block 2 getestet!";;\
  } elsif (["^$SELF$:^debug_3$"]) {\
    Log 1,"$SELF: if-Block 3 getestet!";;\
  }\
}
attr directCommand setList debug_1 debug_2 debug_3
attr directCommand webCmd debug_1:debug_2:debug_3


Im Perl-Modus ist es sinnvoller Zweige, die sich gegenseitig ausschließen in eigene Blöcke zu trennen, denn dann wird bei einem passenden Trigger jeweils nur der "eigene" Block ausgeführt und nicht immer der gesamte, hier also:

defmod directCommand DOIF \
  {if (["^$SELF$:^debug_1$"]){Log 1,"$SELF: if-Block 1 getestet!"}}\
  {if (["^$SELF$:^debug_2$"]){Log 1,"$SELF: if-Block 2 getestet!"}}\
  {if (["^$SELF$:^debug_3$"]){Log 1,"$SELF: if-Block 3 getestet!"}}

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Eine weitere Eigenschaft des Perl-Modus ist die Tatsache, dass bei einfachen Bedingung sogar die if-Abfrage weggelassen werden kann, hier z. b.

defmod directCommand DOIF \
  {["^$SELF$:^debug_1$"];Log 1,"$SELF: if-Block 1 getestet!"}\
  {["^$SELF$:^debug_2$"];Log 1,"$SELF: if-Block 2 getestet!"}\
  {["^$SELF$:^debug_3$"];Log 1,"$SELF: if-Block 3 getestet!"}


Es liegt am Perl, da die Angabe [...] eine Perl-Funktion darstellt, die wahr oder nicht wahr ist. Die braucht man eigentlich gar nicht auswerten, da das DOIF-Modul das bereits macht, um den passenden Block auszuführen.
if wird spätestens dann benötigt, wenn logische Abfragen erfolgen sollen.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

#68
Wenn man if weglassen kann, dann kann man z. B. Folgendes definieren

defmod directCommand DOIF {Log 1,"$SELF: ".["^$SELF$:^debug_":"(.*)",""]}

Hier führt die Triggerangabe [...] nicht nur dazu, dass der Block ausgeführt wird, sondern liefert gleichzeitig durch den angegebenen Regex-Filter das entsprechende Event.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

Interessanterweise triggert sogar der Kommentar.

defmod directCommand DOIF { # ["^$SELF$:^debug_1$"]\
  Log 1,"$SELF: Block 01 getestet!";;\
}
attr directCommand setList debug_1

Damian

Zitat von: Ellert am 18 September 2018, 23:31:08
Interessanterweise triggert sogar der Kommentar.

defmod directCommand DOIF { # ["^$SELF$:^debug_1$"]\
  Log 1,"$SELF: Block 01 getestet!";;\
}
attr directCommand setList debug_1


Ja, nur Kommentare mit ## werden vom DOIF-Modul ignoriert.

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

Die Verwendung von einer Raute zur Kommentierung in den Beispielen der Commandref zum Perl-Modus hat mich verführt einen Trigger mit einer einfachen Raute auszukommentieren. Es hat ein bisschen gedauert bis ich gemerkt habe, dass es nicht funktioniert.

Damian

#72
Zitat von: Ellert am 19 September 2018, 00:05:02
Die Verwendung von einer Raute zur Kommentierung in den Beispielen der Commandref zum Perl-Modus hat mich verführt einen Trigger mit einer einfachen Raute auszukommentieren. Es hat ein bisschen gedauert bis ich gemerkt habe, dass es nicht funktioniert.

Zuerst werden Kommentare mit doppelter Raute von DOIF gefiltert, der Rest wird nach Triggern durchsucht und geht anschließend an Perl. Dort gelten die Perl-Regeln mit Kommentaren mit einfacher Raute.

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

#73
Mit der Version 98_DOIF.pm 17358 2018-09-16 19:54:57Z Damian führt die alte Syntax von set_Exec zu einer Fehlermeldung.
Das Ausführen von Block 0 und 1 erzeugt den Fehler.

defmod directCommand DOIF {["^$SELF$:^debug_1$"];;set_Exec("debug_1",5, "fhem", "setreading $SELF test1 Block 1 getestet!");;}\
{["^$SELF$:^debug_2$"];;set_Exec("debug_2",5, "fhem", "setreading directCommand test2 Block 2 getestet!");;}\
{["^$SELF$:^debug_3$"];;set_Exec("debug_3",5, 'fhem "setreading $SELF test3 Block 3 getestet!"');;}\
{["^$SELF$:^debug_4$"];;set_Exec("debug_4",5, 'set_Reading("test4", "Block 4 getestet!",1)');;}
attr directCommand room 0_Test
attr directCommand setList debug_1 debug_2 debug_3 debug_4
attr directCommand webCmd debug_1:debug_2:debug_3:debug_4

Damian

Zitat von: Ellert am 19 September 2018, 11:04:11
Mit der Version 98_DOIF.pm 17358 2018-09-16 19:54:57Z Damian führt die alte Syntax von set_Exec zu einer Fehlermeldung.
Das Ausführen von Block 0 und 1 erzeugt den Fehler.

defmod directCommand DOIF {["^$SELF$:^debug_1$"];;set_Exec("debug_1",5, "fhem", "setreading $SELF test1 Block 1 getestet!");;}\
{["^$SELF$:^debug_2$"];;set_Exec("debug_2",5, "fhem", "setreading directCommand test2 Block 2 getestet!");;}\
{["^$SELF$:^debug_3$"];;set_Exec("debug_3",5, 'fhem "setreading $SELF test3 Block 3 getestet!"');;}\
{["^$SELF$:^debug_4$"];;set_Exec("debug_4",5, 'set_Reading("test4", "Block 4 getestet!",1)');;}
attr directCommand room 0_Test
attr directCommand setList debug_1 debug_2 debug_3 debug_4
attr directCommand webCmd debug_1:debug_2:debug_3:debug_4


Er hat es doch bemerkt :)

Den Fehler habe ich bereits gestern korrigiert, aber noch nicht hochgeladen, dachte - benutzt noch keiner. :)

Ich werde es heute noch einchecken.

Alternative Syntax:

["^$SELF$:^debug_1$"];;set_Exec("debug_1",5, "fhem\"setreading $SELF test1 Block 1 getestet!\"")

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF