Endlicher Automat (finite state maschine)

Begonnen von Damian, 10 Februar 2016, 18:14:56

Vorheriges Thema - Nächstes Thema

Damian

Ich überlege eine vereinfachte Syntax in DOIF einzubauen, mit der sich ein endlicher Automat (EA) einfach realisieren lässt.

Man kann jetzt schon einen EA mit DOIF wie folgt realisieren:

DOIF (<Bedingung1> and [?<mein_doif>] eq "cmd_1") (<tue das>)
DOELSEIF (<Bedingung2> and [?<mein_doif>] eq "cmd_2") (<tue jenes>)
DOELSEIF ...


Die Abfrage des eigenen Status ist recht aufwändig. Meine Überlegung ist den eigenen Status statt mit:

... and [?<mein_doif>] eq "cmd_1")


z. B. mit

... and [?1])


abzufragen.

Dann dürfte es allerdings keine Definition in FHEM mit dem Namen "1" geben, was vermutlich unproblematisch ist.


Man könnte auch statt [?1] auch [$1] definieren.

Vielleicht hat jemand noch bessere Vorschläge für die abkürzende Schreibweise einer solchen Abfrage.

Edit: Für Abfragen nach Zwischenzuständen z. B. cmd_1_1 würde man dann logischerweise [?1_1] angeben können.

Gruß

Damian

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

Ellert

Für $ würde sprechen, dass es als $elf gelesen werden könnte. Kann es einen Konflikt mit den speziellen Regexp-Variablen  $1 usw. geben?

In diesem Zusammenhang könnte es auch praktisch sein ein Attribut "modifyState" zu haben, dass nach einem modify_<doifname> einen Status vorgibt, statt "initialized".

Es könnte auch interessant sein Eigentriggerung zu zulassen. Für solche Konstrukte:
([$3] and ![Stop]) (...)
DOELSEIF ([$1]) (...)
DOELSEIF ([$2]) (...)

wait 10:10:10
selftrigger $3:$1:$2
modifyState cmd_3

Das DOIF läuft im Kreis bis [Stop] wahr wird, zB. für ein Lauflicht. Dann hätte man eine Art senkrechtes "repeatcmd".

justme1968

#2
wäre es nicht besser dafür ein eigenes modul zu bauen statt es auch noch in die DOIF syntax zu pressen?

ich hatte mit einem solchen modul angefangen bin aber noch nicht dazu gekommen es hier vorzustellen. die syntax bei mir schaut etwa so aus:#define <sm> stateMachine CUL_HM_HM_PB_2_WM55_2B63C7_Btn_01,CUL_HM_HM_PB_2_WM55_2B63C7_Btn_02 Sonos_Esszimmer
attr <sm> transitions \
{ OFF => [ { event => '$1:Short', action => 'set $TARGET Volume 15; set $TARGET Play', newState => 'ON'  }, ], \
  ON  => [ { event => '$2:Short', action => 'set $TARGET Stop',                        newState => 'OFF' },    \
           { event => '$1:Long',  action => 'set $TARGET VolumeU',                     newState => 'ON'  },    \
           { event => '$2:Long',  action => 'set $TARGET VolumeD',                     newState => 'ON'  }, ], \
}
attr <sm> start OFF

define <sm> stateMachine CUL_HM_HM_PB_2_WM55_2B63C7_Btn_01,CUL_HM_HM_PB_2_WM55_2B63C7_Btn_02 Sonos_Esszimmer
attr <sm> stateFn {return 'OFF' if(ReadingsVal($TARGET,'transportState','STOPPED') eq 'STOPPED'); return 'ON'}
attr <sm> transitions \
{ OFF => [ { event =>   ':Short', action => 'set $TARGET Volume 15;; set $TARGET on', newState => 'ON'  }, ], \
  ON  => [ { event => '$1:Short', action => 'set $TARGET VolumeU', timeout => 0.5,    newState => 'ON'  },    \
           { event => '$1:Short', action => 'set $TARGET Volume 15',                  newState => 'ON'  },    \
           { event => '$2:Short', action => 'set $TARGET Stop',                       newState => 'OFF' },    \
           { event => '$1:Long',  action => 'set $TARGET VolumeU',                    newState => 'ON'  },    \
           { event => '$2:Long',  action => 'set $TARGET VolumeD',                    newState => 'ON'  }, ], \
}
attr <sm> start OFF

define <sm> stateMachine CUL_HM_HM_PB_2_WM55_2_25EBC7_Btn_01,CUL_HM_HM_PB_2_WM55_2_25EBC7_Btn_02 LED2_3
attr <sm> transitions \
{ OFF     => [ { event => '$1:Short|Long', action => 'set $TARGET on',      newState => 'ON'      },                  \
               { event => '$2:Short',      action => 'set $TARGET 50%',     newState => 'ON'      }, ],               \
  ON      => [ { event => '$1:Short',      action => 'set $TARGET 100%',    newState => 'ON', timeout => 0.5,      }, \
               { event => '$2:Short',      action => 'set $TARGET off',     newState => 'OFF'     },                  \
               { event => '$1:Long',       action => 'set $TARGET dimUp',   newState => 'DIMUP'   },                  \
               { event => '$2:Long',       action => 'set $TARGET dimDown', newState => 'DIMDOWN' }, ],               \
  DIMUP   => [ { event =>   ':Long',       action => 'set $TARGET dimUp',   newState => 'DIMUP'   },                  \
               { event =>   ':Release',    action => '{Log 1, $EVENT}',     newState => 'ON'      }, ],               \
  DIMDOWN => [ { event =>   ':Long',       action => 'set $TARGET dimDown', newState => 'DIMDOWN' },                  \
               { event =>   ':Release',    action => '{Log 1, $EVENT}',     newState => 'ON'      }, ],               \
}
attr <sm> start OFF


die idee ist im define die devices anzugeben auf deren events reagiert werden soll und dann den start zustand, die übergänge und die aktionen in attribute zu packen.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Damian

#3
Zitat von: Ellert am 10 Februar 2016, 19:30:26
Für $ würde sprechen, dass es als $elf gelesen werden könnte. Kann es einen Konflikt mit den speziellen Regexp-Variablen  $1 usw. geben?

In diesem Zusammenhang könnte es auch praktisch sein ein Attribut "modifyState" zu haben, dass nach einem modify_<doifname> einen Status vorgibt, statt "initialized".

Es könnte auch interessant sein Eigentriggerung zu zulassen. Für solche Konstrukte:
([$3] and ![Stop]) (...)
DOELSEIF ([$1]) (...)
DOELSEIF ([$2]) (...)

wait 10:10:10
selftrigger $3:$1:$2
modifyState cmd_3

Das DOIF läuft im Kreis bis [Stop] wahr wird, zB. für ein Lauflicht. Dann hätte man eine Art senkrechtes "repeatcmd".

Die Selbsttriggerung in Verbindung mit wait ging immer schon. Ohne wait ist gefährlich, weil sich die Leute schnell mit einem Loop das System lahm legen würden.

[$1] wäre unproblematisch da es keine Devices mit $ gibt und Regex für Devices werden ja in Anführungszeichen angegeben.

Man könnte im Konzept des Moduls bleiben mit  [?$1] ohne Trigger und [$1] mit Trigger.

Gruß

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

Damian

#4
Zitat von: justme1968 am 10 Februar 2016, 19:51:47
wäre es nicht besser dafür ein eigenes modul zu bauen statt es auch noch in die DOIF syntax zu pressen?

ich hatte mit einem solchen modul angefangen bin aber noch nicht dazu gekommen es hier vorzustellen. die syntax bei mir schaut etwa so aus:#define <sm> stateMachine CUL_HM_HM_PB_2_WM55_2B63C7_Btn_01,CUL_HM_HM_PB_2_WM55_2B63C7_Btn_02 Sonos_Esszimmer
attr <sm> transitions \
{ OFF => [ { event => '$1:Short', action => 'set $TARGET Volume 15; set $TARGET Play', newState => 'ON'  }, ], \
  ON  => [ { event => '$2:Short', action => 'set $TARGET Stop',                        newState => 'OFF' },    \
           { event => '$1:Long',  action => 'set $TARGET VolumeU',                     newState => 'ON'  },    \
           { event => '$2:Long',  action => 'set $TARGET VolumeD',                     newState => 'ON'  }, ], \
}
attr <sm> start OFF

define <sm> stateMachine CUL_HM_HM_PB_2_WM55_2B63C7_Btn_01,CUL_HM_HM_PB_2_WM55_2B63C7_Btn_02 Sonos_Esszimmer
attr <sm> stateFn {return 'OFF' if(ReadingsVal($TARGET,'transportState','STOPPED') eq 'STOPPED'); return 'ON'}
attr <sm> transitions \
{ OFF => [ { event =>   ':Short', action => 'set $TARGET Volume 15;; set $TARGET on', newState => 'ON'  }, ], \
  ON  => [ { event => '$1:Short', action => 'set $TARGET VolumeU', timeout => 0.5,    newState => 'ON'  },    \
           { event => '$1:Short', action => 'set $TARGET Volume 15',                  newState => 'ON'  },    \
           { event => '$2:Short', action => 'set $TARGET Stop',                       newState => 'OFF' },    \
           { event => '$1:Long',  action => 'set $TARGET VolumeU',                    newState => 'ON'  },    \
           { event => '$2:Long',  action => 'set $TARGET VolumeD',                    newState => 'ON'  }, ], \
}
attr <sm> start OFF

define <sm> stateMachine CUL_HM_HM_PB_2_WM55_2_25EBC7_Btn_01,CUL_HM_HM_PB_2_WM55_2_25EBC7_Btn_02 LED2_3
attr <sm> transitions \
{ OFF     => [ { event => '$1:Short|Long', action => 'set $TARGET on',      newState => 'ON'      },                  \
               { event => '$2:Short',      action => 'set $TARGET 50%',     newState => 'ON'      }, ],               \
  ON      => [ { event => '$1:Short',      action => 'set $TARGET 100%',    newState => 'ON', timeout => 0.5,      }, \
               { event => '$2:Short',      action => 'set $TARGET off',     newState => 'OFF'     },                  \
               { event => '$1:Long',       action => 'set $TARGET dimUp',   newState => 'DIMUP'   },                  \
               { event => '$2:Long',       action => 'set $TARGET dimDown', newState => 'DIMDOWN' }, ],               \
  DIMUP   => [ { event =>   ':Long',       action => 'set $TARGET dimUp',   newState => 'DIMUP'   },                  \
               { event =>   ':Release',    action => '{Log 1, $EVENT}',     newState => 'ON'      }, ],               \
  DIMDOWN => [ { event =>   ':Long',       action => 'set $TARGET dimDown', newState => 'DIMDOWN' },                  \
               { event =>   ':Release',    action => '{Log 1, $EVENT}',     newState => 'ON'      }, ],               \
}
attr <sm> start OFF


die idee ist im define die devices anzugeben auf deren events reagiert werden soll und dann den start zustand, die übergänge und die aktionen in attribute zu packen.

gruss
  andre

ja, kann man sicherlich programmieren, wenn man die Zeit dazu hat. Die Idee das in DOIF einzubauen kam bei mir auf, als ich diverse Vorschläge insb. von Ellert dazu mit DOIF gesehen habe. So etwas in das Modul DOIF einzubauen wäre für mich relativ einfach - das würde ich in meiner knappen Freizeit noch hinbekommen.

Gruß

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

ZeitlerW

Hallo Damian,

wenn ich Dich richtig verstanden habe wäre dies ja auch eine generische Lösung für mein Problem hier: http://forum.fhem.de/index.php/topic,49022.0.html.

Und auch die Lösung von Ellert: http://forum.fhem.de/index.php/topic,49022.msg406915.html#msg406915

lG
Wolfgang

Damian

Zitat von: ZeitlerW am 11 Februar 2016, 10:47:11
Hallo Damian,

wenn ich Dich richtig verstanden habe wäre dies ja auch eine generische Lösung für mein Problem hier: http://forum.fhem.de/index.php/topic,49022.0.html.

Und auch die Lösung von Ellert: http://forum.fhem.de/index.php/topic,49022.msg406915.html#msg406915

lG
Wolfgang

ja, es ist natürlich keine neue Funktionalität, sondern lediglich eine Vereinfachung in der Definition des Moduls.

Gruß

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

Ralli

Wichtig wäre, dass $1 nicht in Perl-Code-Fragmenten ausgewertet wird.
Gruß,
Ralli

Proxmox 8.4 Cluster mit HP ED800G2i7, Intel NUC11TNHi7+NUC7i5BNH, virtualisiertes fhem 6.4 dev, virtualisierte RaspberryMatic (3.81.5.20250527) mit HB-RF-ETH 1.3.0 / RPI-RF-MOD, HM-LAN-GW (1.1.5) und HMW-GW, FRITZBOX 7490 (07.59), FBDECT, Siri und Alexa

Damian

Zitat von: Ralli am 11 Februar 2016, 14:16:03
Wichtig wäre, dass $1 nicht in Perl-Code-Fragmenten ausgewertet wird.

Das sollte kein Problem sein: Alles innerhalb von eckigen Klammern ist DOIF-Welt, alles außerhalb dieser Klammern in der Bedingung ist Perl-Welt.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

crusader

Hallo Damian,

State-Machines mit DOIF sind eine interessante Anwendung.

Wichtiger als die Vereinfachung der Syntax wäre aus meiner Sicht aber die einfache Ermittlung des Timer-states, denn das ist eigentlich ja ein separater Status.

Momentan müsste man dazu wohl den Wert von wait_timer parsen oder hab' ich da was übersehen ?

Damian

Zitat von: crusader am 11 Februar 2016, 21:27:07
Hallo Damian,

State-Machines mit DOIF sind eine interessante Anwendung.

Wichtiger als die Vereinfachung der Syntax wäre aus meiner Sicht aber die einfache Ermittlung des Timer-states, denn das ist eigentlich ja ein separater Status.

Momentan müsste man dazu wohl den Wert von wait_timer parsen oder hab' ich da was übersehen ?

Kannst du mir ein konkretes Beispiel nennen?
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

crusader

#11
Ich gehe einfach mal vom Entwurfsmechanismus einer state-machine aus:

State_1 -> Event_x -> neuer State_2
            -> Event_y -> neuer State_3
State_2 -> Event_x -> neuer State_4

Diesen Mechanismus kann man über die vorgeschlagenen state-Abfrage einfach verwirklichen.
Man möchte aber auch oft Timeouts als auslösenden state-Übergang definieren.
Also z.B.:

State_2 -> Timeout_t1 -> neuer State_5

über das wait-Attribut kann man zwar eine Aktion bei Timeout auslösen, jedoch ändert sich ja der state nach Ablauf des Timers nicht.
Beispiel Waschmaschine:

State_off               -> (power > 5) -> State_debounce
State_debounce    -> (power < 4) -> State_off
State_debounce    -> Timeout_t1  -> State_on

Edit:
Unvollständiges Beispiel. Es fehlt:

State_debounce   -> (power > 1000) -> State_power

crusader

#12
Das Beispiel ist etwas wenig praxisnah.

Hier was konkretes:
Waschmaschine soll Fertigzustand mitteilen, falls mindestens 5 min power > 5W erkannt und danach 5 min power < 3W erkannt.

Lösung mit Zusatz-reading 'status':
([test.power:P.Waschmaschine] > 5)
    (setreading ctrl.washer status wait)
    (setreading ctrl.washer status on)
DOELSEIF ([test.power:P.Waschmaschine] < 3 and [?ctrl.washer:status] eq "wait")
    (setreading ctrl.washer status off)
DOELSEIF ([test.power:P.Waschmaschine] < 3 and [?ctrl.washer:status] eq "on")
    (setreading ctrl.washer status ready)
    (say Waschmaschine ist fertig)


mit attr wait 0,300:0:300

Das Beispiel zeigt auch gleich die Lösung mit Verwendung von state. Der state wechselt nämlich nach Ablauf des Timers von cmd_1_1 auf cmd_1.
Die Lösung mit der vorgeschlagenen DOIF-Änderung könnte also so aussehen:
([test.power:P.Waschmaschine] > 5)
    ()   #on   = 1
    ()   #wait = 1_1
DOELSEIF ([test.power:P.Waschmaschine] < 3 and [?1_1])
    ()  #off = 2
DOELSEIF ([test.power:P.Waschmaschine] < 3 and [?1] )
    (say Waschmaschine ist fertig)   #ready = 3


Die Waschmaschinen-Fraktion wird sich freuen.

Edit:
Ich finde die augenblickliche Syntax [?<mein_doif>] eq "cmd_1" aber nicht als derart monströs, dass da unbedingt die Einführung einer neuen Option erforderlich wäre.
Lästig daran ist nur, dass diese Konstruktion kein rename oder copy überlebt. Das gleiche gilt auch für Readings oder Attribute, die man in <mein_doif> schreiben will (s.o.).
Wäre es nicht praktischer, eine variable für <mein_doif> einzuführen ?

Vielleicht: $self oder $name

Damian

Zitat von: crusader am 11 Februar 2016, 23:23:41

Edit:
Ich finde die augenblickliche Syntax [?<mein_doif>] eq "cmd_1" aber nicht als derart monströs, dass da unbedingt die Einführung einer neuen Option erforderlich wäre.
Lästig daran ist nur, dass diese Konstruktion kein rename oder copy überlebt. Das gleiche gilt auch für Readings oder Attribute, die man in <mein_doif> schreiben will (s.o.).
Wäre es nicht praktischer, eine variable für <mein_doif> einzuführen ?

Vielleicht: $self oder $name

ja, [$1] stünde für einen ganzen Vergleich und ist natürlich etwas neues, was jeder lernen muss. Dagegen würde [$this] eq "cmd_1" wohl jeder verstehen. Der Status kann allerdings auch durch state manipuliert sein. Daher wäre vielleicht ein weiteres Reading z. B. "mystate", im Modul, welches statt "cmd_1" nur "1", bei "cmd_1_1" nur "1_1" enthält, interessant. Dann könnte man z. B. realisieren

[$mystate] eq "1"  $mystate wäre dann [<mein_device>:mystate]

Daher ist es wichtig sich vorher Gedanken zu machen, bevor man etwas unwiderruflich einführt.

Gruß

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

Ellert

ZitatDaher ist es wichtig sich vorher Gedanken zu machen, bevor man etwas unwiderruflich einführt.

Manchmal hängen DOIFs auch zusammen, daher könnte eine DOIF übergreifende vereinfachte Syntax hilfreich sein.

Beispiel:
5 DOIF "di1 ... di5" steuern je ein Heizventil.
1 DOIF "diz" sammelt die Stati der 5 DOIF und steuert eine Gastherme.

diz DOIF ([$di1:$1_2] or [$di2:$1_2] ...) (set Gastherme on)

ZitatDie Selbsttriggerung in Verbindung mit wait ging immer schon.
Das DOIF bleibt nach dem [start] trigger bei cmd_1 stehen. Hier solllte der Status triggern.
di ([start] or [di] eq "cmd_3") ()
DOELSEIF ([di] eq "cmd_1") ()
DOELSEIF ([di] eq "cmd_2") ()

wait 10:10:10