neue Features: ereignisgesteuertes Perl - DOIF-Perl

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

Vorheriges Thema - Nächstes Thema

Damian

Zitat von: Ellert am 23 März 2018, 18:12:03
commandref_join liefert für # $Id: 98_DOIF.pm 16471 2018-03-23 11:42:27Z Damian $
In Zeile 3827 wird der code-tag zu früh geschlossen er müsste gelöscht werden.

Danke für den Hinweis. Der code-tag war da immer schon falsch drin. Warum liefert commandref_join bei mir keine Fehlermeldung?
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

Ich vermute Du hast eine ältere commandref_join. Seit 26.2.18 werden negative code-tags gezählt, s. https://forum.fhem.de/index.php/topic,84953.0.html

Damian

Zitat von: Ellert am 23 März 2018, 22:53:49
Ich vermute Du hast eine ältere commandref_join. Seit 26.2.18 werden negative code-tags gezählt, s. https://forum.fhem.de/index.php/topic,84953.0.html
ok, muss ich dann bei mir aktualisieren.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

Einen init-Block gibt es ja schon, wäre auch ein beforeShutdown-Block denkbar?

Ein Vorteil wäre, dass init-Variablen bei häufigen Aktualisierungen im Betrieb nicht in Readings oder als KeyValues gespeichert werden müssten, sondern nur vor einem Shutdown.

Als nächsten Schritt könnte das Lesen und Schreiben von Variablen, die einen Neustart überleben sollen automatisiert werden.

Es könnten alle Variablennamen, die in einem Array @_INIT stehen, automatisch geladen und gespeichert werden.

Siehe auch setKeyValue/getKeyValue https://wiki.fhem.de/wiki/DevelopmentModuleAPI#setKeyValue

Damian

Zitat von: Ellert am 26 März 2018, 10:36:30
Einen init-Block gibt es ja schon, wäre auch ein beforeShutdown-Block denkbar?

Ein Vorteil wäre, dass init-Variablen bei häufigen Aktualisierungen im Betrieb nicht in Readings oder als KeyValues gespeichert werden müssten, sondern nur vor einem Shutdown.

Als nächsten Schritt könnte das Lesen und Schreiben von Variablen, die einen Neustart überleben sollen automatisiert werden.

Es könnten alle Variablennamen, die in einem Array @_INIT stehen, automatisch geladen und gespeichert werden.

Siehe auch setKeyValue/getKeyValue https://wiki.fhem.de/wiki/DevelopmentModuleAPI#setKeyValue

ja, das wäre eine Möglichkeit, aber man kann jetzt schon Variablen sichern, z. B.:

{ if (["^global$:^SHUTDOWN$"]) {setKeyValue("$SELF_$_myvariable", $_myvariable)}}
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Alle Variablen sichern könnte so aussehen:

{ if (["^global$:^SHUTDOWN$"]) {
    foreach my $key (keys %{$hash->{var}}) {
        setKeyValue("$SELF_$hash->{var}{$key}", $hash->{var}{$key});
    }
  }
}


Man könnte Funktionen zur Verfügung stellen: saveVar() und getVar().

Automatisches Speichern würde ich eher vermeiden. Es soll möglichst wenig interne Abhängigkeiten geben. Der Programmierer will auch schon mal sauber initialisierte Variablen haben, zumal es sich hier um hash-Variablen handelt, die man nicht deklarieren muss.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

#21
saveVar() und getVar() sollten mit Parameter funktionieren, damit nur die benötigten Keys gespeichert werden

saveVar(<Regex für Keys>) und keine Angabe (oder .*) für Alles.

Bei einem getKeyValue muss Key bekannt sein.
Ich denke der Geräte-Hash ändert sich beim Neustart.
Daher dürfte $SELF_$hash nicht funktionieren, sondern $SELF_$key, also
setKeyValue("$SELF_$hash->{var}{$key}", $hash->{var}{$key})

($error, $hash->{var}{$key}) = getKeyValue("$SELF_$hash->{var}{$key}", $hash->{var}{$key})


Edit:
Ich bin nicht sicher ob es reicht das globale Event SHUTDOWN auszuwerten.
Ich denke ein beforeShutdown Block müsste in der Modul-Funktion X_Shutdown untergebracht werden.
https://wiki.fhem.de/wiki/DevelopmentModuleIntro#X_Shutdown
und $key muss aus einer, nach dem Neustart bekannten, Liste kommen.

Damian

Zitat von: Ellert am 26 März 2018, 13:09:33
saveVar() und getVar() sollten mit Parameter funktionieren, damit nur die benötigten Keys gespeichert werden

saveVar(<Regex für Keys>) und keine Angabe (oder .*) für Alles.

Bei einem getKeyValue muss Key bekannt sein.
Ich denke der Geräte-Hash ändert sich beim Neustart.
Daher dürfte $SELF_$hash nicht funktionieren, sondern $SELF_$key, also
setKeyValue("$SELF_$hash->{var}{$key}", $hash->{var}{$key})

($error, $hash->{var}{$key}) = getKeyValue("$SELF_$hash->{var}{$key}", $hash->{var}{$key})


und $key muss aus einer, nach dem Neustart bekannten, Liste kommen.

Die key-Liste müsste mitgespeichert werden.

Was mir noch grundsätzlich im Perl-Modus nicht gefällt, ist das ständige Patchen des Codes vor der Ausführung, mit $DEVICE, $EVENTS .... Das kostet unnötig Performance.

Man kann jetzt schon Variablen $device, $events usw. verwenden. Allerdings müsste ich dann die Erkennung in der DOIF-Syntax einbauen, z. B.

[$device] soll [$DEVICE] entsprechen.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

Zitat von: Damian am 26 März 2018, 13:23:27
Die key-Liste müsste mitgespeichert werden.

Was mir noch grundsätzlich im Perl-Modus nicht gefällt, ist das ständige Patchen des Codes vor der Ausführung, mit $DEVICE, $EVENTS .... Das kostet unnötig Performance.

Man kann jetzt schon Variablen $device, $events usw. verwenden. Allerdings müsste ich dann die Erkennung in der DOIF-Syntax einbauen, z. B.

[$device] soll [$DEVICE] entsprechen.

Ja, wenn es performanter ist.

Bevor es in der vorhergehenden Antwort untergeht:

Ich bin nicht sicher ob es reicht das globale Event SHUTDOWN auszuwerten.
Ich denke ein beforeShutdown Block müsste in der Modul-Funktion X_Shutdown untergebracht werden.
https://wiki.fhem.de/wiki/DevelopmentModuleIntro#X_Shutdown
und $key muss aus einer, nach dem Neustart bekannten, Liste kommen.

Damian

Zitat von: Ellert am 26 März 2018, 13:32:35
Ja, wenn es performanter ist.

Bevor es in der vorhergehenden Antwort untergeht:

Ich bin nicht sicher ob es reicht das globale Event SHUTDOWN auszuwerten.
Ich denke ein beforeShutdown Block müsste in der Modul-Funktion X_Shutdown untergebracht werden.
https://wiki.fhem.de/wiki/DevelopmentModuleIntro#X_Shutdown
und $key muss aus einer, nach dem Neustart bekannten, Liste kommen.

ja, müsste ich mal umsetzen und testen, der Aufwand ist überschaubar.

Ich denke das Patchen werde ich wohl drin lassen müssen, allein wegen der Kompatibilität, der Performance-Verlust dürfte nicht messbar sein. Bei mir 0,0003 mSek für ca. 80 Zeichen-Code. Raspi ist vermutlich 10 mal langsamer - immer noch nicht "messbar".
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Zitat von: Damian am 26 März 2018, 13:53:22
ja, müsste ich mal umsetzen und testen, der Aufwand ist überschaubar.

Ich habe mir die Sache mit dem Speichern von Instanzvariablen genauer angeschaut und bin zu der Erkenntnis gekommen, da nichts zu unternehmen, denn

-es wird keine Eindeutigkeit der Schlüssel gewährleistet, der gleiche Schlüssel wird mehrfach mit verschieden Inhalten gespeichert.
-erforderliche Verwaltung der Schlüssel, wenn z. B. ein DOIF-Modul gelöscht wird, müssten die Einträge ebenfalls gelöscht werden.
-wenn ein User etwas bewusst sichern will, dann kann er das in Readings des Moduls tun, diese sind eindeutig und verschwinden mit dem Löschen des Devices



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

Damian

#26
Neue Features.

1. Man kann jetzt ohne Umwege über myutils Perlfunktionen im Block subs im DOIF definieren
2. Timer können jetzt nicht nur Events auslösen, sondern auch Perlfunktionen aufrufen (neue Funtionen set_Exec, get_Exec, del_Exec)
3. Angaben in eckigen Klammern können jetzt im Perl-Modus verwendet werden, wenn sie mit DOIF-Syntax nicht kollidieren, insb. Arrays z. B. a[1] oder regex /^[\d].*/

Das Beispiel aus der Commandref

Treppenhauslicht mit Bewegungsmelder

define di_light DOIF bewegung {   #Perlblock namens "bewegung" reagiert auf Bewegung von FS
  if (["FS:motion"]) {
    if ([?lamp:state] ne "on") {   #wenn Lampe aus ist
      fhem"set lamp on";   #Lampe einschalten
      set_Reading ("state","on",1);   #setze Status des DOIF-Moduls auf "on"
    }
    set_Timer("lamp_off",30);   #Timer wird gesetzt bzw. verlängert
  }
}
ausschalten {   #Perlblock namens "ausschalten" reagiert auf Trigger vom des Timers "lamp_off"
  if ([$SELF:"lamp_off"]) {   #Wenn Timer lamp_off abläuft
    fhem"set lamp off";   #schalte Lampe aus
    set_Reading ("state","off",1);   #setze Status des DOIF-Modus auf "off"
  }
}


lässt sich zukünftig auch wie folgt definieren:

define di_light DOIF
##block subs für Definition eigener Perl-Funktionen, hier ist nur Perl erlaubt ohne DOIF-Syntax
subs {
   sub ein {if (ReadingsVal ("lamp","state","") ne "on") { fhem"set lamp on"; set_Reading ("state","on",1)}} # Perlfunktion ein zum Einschalten wird definiert
   sub aus {fhem"set lamp off";set_Reading ("state","off",1)}                                                # Perlfunkton aus zum Ausschalten wird definiert
}
bewegung {                          #Perlblock namens "bewegung" reagiert auf Bewegung von FS
  if (["FS:motion"]) {
    ein();                          #Perlfunktion "ein" wird ausgeführt
    set_Exec("Timer_aus",30,"aus");             #Timer für das Ausschalten über Perlfunktion "aus" wird gesetzt bzw. verlängert
  }
}


Doku wird noch erstellt.

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

Damian

#27
Ich habe nun die Version v0.3 mit Doku fertiggestellt.

Auszüge aus der Doku:

ZitatEin besonderer Perlblock ist der Block namens "subs". Dieser Block wird nur zum Definitionszeitpunkt ausgeführt. In diesem Block sollten vornehmlich 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.

und

Zitat
Funktionstimer

Timer setzen: set_Exec(<timerName>, <seconds>, <function>, <parameter>), mit <timerName>: beliebige Angabe, sie spezifiziert eindeutig einen Timer, welcher nach Ablauf die angegebene Perl-Funktion <function> mit optionalen Parameter <parameter> aufruft. Die Perlfunkion muss eindeutig sein und in FHEM zuvor deklariert worden sein. Wird set_Exec mit dem gleichen <timerName> vor seinem Ablauf erneut aufgerufen, so wird der laufender Timer gelöscht und neugesetzt.

Timer holen: get_Exec(<timerName>), Returnwert: 0, wenn Timer abgelaufen oder nicht gesetzt ist, sonst Anzahl der Sekunden bis zum Ablauf des Timers

Laufenden Timer löschen: del_Exec(<timerName>)


Zur Demonstration der neuen Funktionalität wird hier ein größeres Projekt (ähnlich diesem https://forum.fhem.de/index.php/topic,48847.0.html) vorgestellt, welches eine Einknopf Garagentor-/Rollladen-Steuerung realisiert. Dabei werden zwei Relais (rauf/runter), die sich gegenseitig sperren und automatische Endabschaltung des Tores/Rollos vorausgesetzt.

Zur Funktionsweise: Ein geschlossenes Tor wird beim Tastendruck geöffnet, ein geöffnetes wird geschlossen, wenn während der Laufzeit Taste gedrückt wird, bleibt das Tor stehen, ein erneuter Tastendruck bewegt das Tor in umgekehrte Richtung. Dabei werden unterschiedliche Laufzeiten des Tores (rauf/runter) berücksichtigt, ebenso wird die aktuelle Position des Tores aufgrund der Laufzeit berechnet und im Reading abgelegt.

Der Sourcecode ist weitgehend dokumentiert und kann mit Dummys (switch_up, switch_down) ausprobiert werden.


DOIF subs {                                       # im Block subs werden Perlfunktionen definiert, die innerhalb des DOIFs benutzt werden
sub drive { my ($downup)=@_;                      # Funktion zum Herunterfahren oder Hochfahren des Garagentors
  my $seconds; 
  if ($downup eq "up") {
    fhem"set $_up on";                            # Schalter zum Hochfahren einschalten
    $seconds=$_drive_up_time*(1-$_pos/100);       # Laufzeit in Sekunden berechnen
    $_pos=100;                                    # Endposition vorbelegen
  } else {
    fhem"set $_up on";                            # Schalter zum Herunterfahren einschalten
    $seconds=$_drive_down_time*($_pos/100);       # Laufzeit in Sekunden berechnen
    $_pos=0;                                      # Endposition vorbelegen
  }
  set_Reading("state","drive_$downup",1);         # Status des Modus auf drive_down oder drive_up setzen
  set_Exec("Timer_off",$seconds,"off",$downup);   # Setze Timer zum Abschalten des Switches switch_down oder switch_up
}                                             
 
sub off {                                         # Funktion zum Abschalten des hoch-/runter-Schalters
  my ($downup)=@_;
  if ($downup eq "up") {
    fhem"set $_up off";                           # Schalter zum Hochfahren abschalten
  } else {
    fhem"set $_down off";                         # Schalter zum Herunterfahren  abschalten
  }
  set_Reading("state",$downup,1);                 # setze Status des Moduls auf up oder down
  set_Reading("position",int($_pos),1);           # aktuelle Position im Reading festhalten
}

}

init {                                      # Vorbelegung von Instanzvariablen
   $_up="switch_up";                        # Name des Schalters zum Hochfahren
   $_down="switch_down";                    # Name des Schalters zum Herunterfahren
   $_drive_up_time=15;                      # Fahrzeit des Garagentors nach oben
   $_drive_down_time=10;                    # Fahrzeit des Garagentors nach unten
   $_pos=0;                                 # aktuelle Position des Garagentors (0-down, 100-up)
   set_Reading("position",0,0);             # Reading mit der Position auf 0 setzen
}

main {                                                         # Hauptblock reagiert auf Fernbedienung FS und steuert switch up/down abhängig vom Zustand
if ([FS]) {
  if ([?$SELF] eq "up") {                                      # wenn Garagentor oben dann herunterfahren
    drive ("down");                                            # Garagentor herunterfahren
  } elsif ([?$SELF] eq "drive_up") {                           # Garagentor fährt nach oben
    $_pos = (1 - get_Exec("Timer_off")/$_drive_up_time)*100;   # Position bestimmen
    off ("up");                                                # Garagentor stoppen
    del_Exec("Timer_off");                                     # Timer löschen
    set_Reading("state","stop_drive_up",1);                    # Zustand des Moduls setzen
  } elsif ([?$SELF] eq "drive_down") {                         # Garagentor fährt nach unten
    $_pos = (get_Exec("Timer_off")/$_drive_down_time)*100;     # Position bestimmen
    off ("down");                                              # Garagentor stoppen
    del_Exec("Timer_off");                                     # Timer löschen
    set_Reading("state","stop_drive_down",1);                  # Zustand des Moduls setzen
  } elsif ([?$SELF] eq "stop_drive_up") {                      # Garagentor wurde beim Hochfahren gestoppt
    drive ("down");                                            # Herunterfahren
  } elsif ([?$SELF] eq "stop_drive_down") {                    # Garagentor wurde beim Herunterfahren gestoppt
    drive ("up");                                              # Garagentor Hochfahren
  } else {                                                     # "down-Zustand oder ein sonstiger Anfangszustand
    drive ("up");                                              # Garagentor hochfahren
  }
}
}


Edit: aktuelle Version eingecheckt
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

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

Ellert

Ich habe die Commandref angepasst, in der Version # $Id: 98_DOIF.pm 17155 2018-08-17 11:45:19Z Damian $, commandref_join.pl zeigt keine Fehler.

- Label eingefügt, damit werden bei Auswahl von Attribut, Set oder Get auch für DOIF die Hilfetexte angezeigt, wenn das globale Attribut language auf DE gestellt ist, s. Bild.

- Kurzreferenz aktualisiert.