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
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".
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
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
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
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 (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 (http://forum.fhem.de/index.php/topic,49022.msg406915.html#msg406915)
lG
Wolfgang
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 (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 (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
Wichtig wäre, dass $1 nicht in Perl-Code-Fragmenten ausgewertet wird.
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.
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 ?
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?
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
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
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
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
Zitat von: Damian am 12 Februar 2016, 09:30:38
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.
Wenn man die DOIF-Threads liest, gewinnt man den Eindruck, dass viele Anwender bereits jetzt verwirrt sind, weil sie die unterschiedliche Syntax von Event, State, Reading und Timer (mit oder ohne ergänzende Ausdrücke) nicht so ohne weiteres auseinanderhalten können.
Hinzu kommt im Ausführungsteil die unterschiedliche Bedeutung von '(', '[' und '{' und deren Kombination sowie die Gültigkeit von $EVENT, $event usw.
Wenn dann noch weitere Sonderabfragen dazukommen, läuft DOIF ein bisschen Gefahr, zu einem Universal-Modul zu werden, dass aber weltweit nur noch von fünf Leuten verstanden wird.
Nur meine unmaßgebliche Meinung.
Gruß
crusader
Zitat von: crusader am 12 Februar 2016, 16:10:44
Wenn man die DOIF-Threads liest, gewinnt man den Eindruck, dass viele Anwender bereits jetzt verwirrt sind, weil sie die unterschiedliche Syntax von Event, State, Reading und Timer (mit oder ohne ergänzende Ausdrücke) nicht so ohne weiteres auseinanderhalten können.
Hinzu kommt im Ausführungsteil die unterschiedliche Bedeutung von '(', '[' und '{' und deren Kombination sowie die Gültigkeit von $EVENT, $event usw.
Wenn dann noch weitere Sonderabfragen dazukommen, läuft DOIF ein bisschen Gefahr, zu einem Universal-Modul zu werden, dass aber weltweit nur noch von fünf Leuten verstanden wird.
Nur meine unmaßgebliche Meinung.
Gruß
crusader
man kann es nutzen, muss es aber nicht
aber ich auch die Gefahr dass es ein Mischung von vi, emacs und edlin wird ...
aber hey, viele Dinge gehen damit deutlich einfacher als ein notify - und dafür ein großes
D a n k e
ja, ich denke auch wir sollten weiteren Features etwas Zeit lassen, zumindest für Dinge, die jetzt schon gut funktionieren.
Die Selbsttriggerung muss ich mir allerdings noch mal anschauen, denn das ist ja die Mindestvoraussetzung für einen EA.
Zitat von: Wuppi68 am 12 Februar 2016, 16:26:39
man kann es nutzen, muss es aber nicht
aber ich auch die Gefahr dass es ein Mischung von vi, emacs und edlin wird ...
aber hey, viele Dinge gehen damit deutlich einfacher als ein notify - und dafür ein großes
D a n k e
Schliesse mich vollumfänglich an.
Zitat von: Damian am 12 Februar 2016, 16:48:58
ja, ich denke auch wir sollten weiteren Features etwas Zeit lassen, zumindest für Dinge, die jetzt schon gut funktionieren.
Die Selbsttriggerung muss ich mir allerdings noch mal anschauen, denn das ist ja die Mindestvoraussetzung für einen EA.
Verstehe die Problematik nicht.
Selbsttriggerung über Timer-Substates geht doch jetzt schon.
Das selbsttriggernde Lauflicht aus Antwort#1 geht z.B. so:
define LL DOIF ([LL:?start] or [LL] eq "cmd_3")\
()\
()\
DOELSEIF ([LL] eq "cmd_1") \
()\
()\
DOELSEIF ([LL] eq "cmd_2")\
()\
()\
DOELSEIF ([LL:?stop])\
attr LL wait 0,10:0,10:0,10
Starten mit
trigger LL start
Stoppen mit:
trigger LL stop
Wenn man im Ausführungsteil einer ordentlich programmierten FSM auf den Status einwirken will, kann man das doch auch nur, indem man eine definierte Übergangsbedingung setzt, die beim nächsten Durchlauf abgefragt wird.
Bei event-gesteuerten Maschinen kann das zwangsläufig nur zeitverzögert geschehen.
Gruß
cruz
Zitat von: crusader am 12 Februar 2016, 22:58:45
Verstehe die Problematik nicht.
Selbsttriggerung über Timer-Substates geht doch jetzt schon.
Das selbsttriggernde Lauflicht aus Antwort#1 geht z.B. so:
define LL DOIF ([LL:?start] or [LL] eq "cmd_3")\
()\
()\
DOELSEIF ([LL] eq "cmd_1") \
()\
()\
DOELSEIF ([LL] eq "cmd_2")\
()\
()\
DOELSEIF ([LL:?stop])\
attr LL wait 0,10:0,10:0,10
Starten mit
trigger LL start
Stoppen mit:
trigger LL stop
Wenn man im Ausführungsteil einer ordentlich programmierten FSM auf den Status einwirken will, kann man das doch auch nur, indem man eine definierte Übergangsbedingung setzt, die beim nächsten Durchlauf abgefragt wird.
Bei event-gesteuerten Maschinen kann das zwangsläufig nur zeitverzögert geschehen.
Gruß
cruz
ja, ich habe zuvor extra mit viel Gehirnschmalz einen Mechanismus eingebaut, um Rekursionen im Modul zu verhindern, was nicht unwichtig ist, da sich user bereits indirekt Loop programmiert haben. Das Blockieren der Rekursion führt natürlich jetzt dazu, dass man beim Setzen des Status im Modul, das Modul nicht sofort angetriggert werden kann, bevor die vorherige Abarbeitung beendet ist - ich denke das muss auch so bleiben. Deine vorgeschlagene Lösung funktioniert zumindest, ansonsten werden wohl externe Events zu Zustandsübergängen führen und dann reichen einfache Abfragen des eigenen Zustands aus.
Gruß
Damian
Zitat von: Ellert am 12 Februar 2016, 11:18:03
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
Es ist ja auch logisch, dass es dort stehen bleibt, da beim zweiten Durchlauf [start] immer noch wahr ist. Du musst zum Starten am besten ein Event abfragen: [di:"start"], das wäre beim zweiten Durchlauf nicht mehr wahr und schon funktioniert die Sache, wie programmiert.
Gruß
Damian
Zitat von: Damian am 12 Februar 2016, 23:35:55
..., da sich user bereits indirekt Loop programmiert haben.
Ja, ich zum Beispiel.
Hab' mich zuerst gewundert, warum das DOIF nicht triggert. Als mir dann klar wurde, dass irgendein Mechanismus die Rückkopplung verhindert, war ich aber doch ziemlich froh drum.
Man glaubt ja gar nicht, wie fix so'n kleiner Prozessor heutzutage kilometerlange Logfiles 'rausschiebt :D .
Zitat von: Damian am 13 Februar 2016, 00:24:24
Es ist ja auch logisch, dass es dort stehen bleibt, da beim zweiten Durchlauf [start] immer noch wahr ist. Du musst zum Starten am besten ein Event abfragen: [di:"start"], das wäre beim zweiten Durchlauf nicht mehr wahr und schon funktioniert die Sache, wie programmiert.
Gruß
Damian
Ich habe immer gedacht alle Loops sind unterbunden und deshalb triggert [<doifname>] eq "cmd_<state>" nicht.
define di DOIF (["^start$"] or [di] eq "cmd_3") (({Log 1,"cmd_1"}))
DOELSEIF ([di] eq "cmd_1") (({Log 1,"cmd_2"}))
DOELSEIF ([di] eq "cmd_2") (({Log 1,"cmd_3"}))
wait 10:10:10
Hier habe ich ein Startereignis eingebaut, das DOIF läuft
trotzdem nicht weiter, es bleibt beim 1. "cmd_1" stehen.
2016.02.13 08:39:54 1: cmd_1
Das hätte ich auch erwartet, wenn Du nicht geschrieben hättest: "Die Selbsttriggerung in Verbindung mit wait ging immer schon."
Die Aussage habe ich so verstanden, dass nach Ablauf des Waittimers im 1. Bedingungszweig, dann die Bedingung im 2. Bedingungszweig [di] eq "cmd_1" triggert, usw.
Und wenn kein wait angegeben ist, dann würde [di] eq "cmd_1" nicht triggern.
Habe ich das richtig verstanden?Edit: Es funktioniert, so wie Du geschrieben hast, danke für die Geduld.
Wäre es eigentlich innerhalb einer solchen FSM mit DOIF auch möglich gleich die Fehlerüberwachung mit einzubauen? Konventionell muss ich dafür immer externe "at" starten.
DOIF hat das "at" ja schon drin und könnte vielleicht sowas: ?
- starte Schritt 1 + starte Überwachungszeit 1 + führe Befehl aus (z.B. Motor 1 start) -> warte auf Weiterschaltbedingung 1
- wenn Ablauf Überwachungszeit -> Fehler 1 erkannt -> führe Befehl aus (z.B. Motor stop + Signal + Nachricht 1)
- wenn Weiterschaltbedingung vor Ablauf Überwachungszeit -> stoppe Überwachungszeit 1 + schalte weiter zu Schritt 2 +
starte Überwachungszeit 2 + führe Befehl aus (z.B. Motor 2 start) -> warte auf Weiterschaltbedingung 2 usw.
... also das was man üblicherweise z.B. mit einer SPS macht.
Gruß
Frank
Zitat von: fiedel am 13 Februar 2016, 09:56:59
Wäre es eigentlich innerhalb einer solchen FSM mit DOIF auch möglich gleich die Fehlerüberwachung mit einzubauen? Konventionell muss ich dafür immer externe "at" starten.
DOIF hat das "at" ja schon drin und könnte vielleicht sowas: ?
- starte Schritt 1 + starte Überwachungszeit 1 + führe Befehl aus (z.B. Motor 1 start) -> warte auf Weiterschaltbedingung 1
- wenn Ablauf Überwachungszeit -> Fehler 1 erkannt -> führe Befehl aus (z.B. Motor stop + Signal + Nachricht 1)
- wenn Weiterschaltbedingung vor Ablauf Überwachungszeit -> stoppe Überwachungszeit 1 + schalte weiter zu Schritt 2 +
starte Überwachungszeit 2 + führe Befehl aus (z.B. Motor 2 start) -> warte auf Weiterschaltbedingung 2 usw.
... also das was man üblicherweise z.B. mit einer SPS macht.
Gruß
Frank
Vieles davon sieht man in den Readings des Moduls. Irgendwann baue ich noch ein ausführlicheres Logging dieser Ereignisse steuerbar über verbose.
Gruß
Damian
Zitat von: Ellert am 13 Februar 2016, 09:10:42
Ich habe immer gedacht alle Loops sind unterbunden und deshalb triggert [<doifname>] eq "cmd_<state>" nicht.
Man kann sagen: wenn eine Aktion im Modul indirekt (verzögert) über den internen Sleeptimer stattfindet (entspricht einer Verzögerung durch wait), so kann das Modul selbst drauf reagieren.
Gruß
Damian
Ah, so nennt man das.
Ggf. hat sich meine Nachfrage nach einer Variablen $SELF hier (http://forum.fhem.de/index.php/topic,49317.0.html) damit auch erledigt? Wir meinen wohl das gleiche. Allerdings kombiniere ich den Selbststatus zu 99% mit cmdState. $SELF sollte dann wohl tatsächlich den cmdState enthalten, während deine vereinfachte Schreibweise ala $1 o.ä. dann unabhängig von cmdState sein sollte. Also vielleicht muss man das beides als Ergänzung sehen?
Zitat von: Loredo am 14 Februar 2016, 14:45:32
Ah, so nennt man das.
Ggf. hat sich meine Nachfrage nach einer Variablen $SELF hier (http://forum.fhem.de/index.php/topic,49317.0.html) damit auch erledigt? Wir meinen wohl das gleiche. Allerdings kombiniere ich den Selbststatus zu 99% mit cmdState. $SELF sollte dann wohl tatsächlich den cmdState enthalten, während deine vereinfachte Schreibweise ala $1 o.ä. dann unabhängig von cmdState sein sollte. Also vielleicht muss man das beides als Ergänzung sehen?
Dann schauen wir mal, worauf man sich hier einigt.
Gruß
Damian
Zitat von: Loredo am 14 Februar 2016, 14:45:32
...
$SELF sollte dann wohl tatsächlich den cmdState enthalten,
...
Nach meinem Vorschlag würde $SELF (oder $THIS oder $NAME ) den devicename beinhalten. Damit liessen sich ganz unabhängig von der FSM-Anwendung Variablen im DOIF-Device speichern oder Attribute abrufen, ohne dass der aktuelle Name explizit angegeben (und bei rename umgeschrieben) werden muss.
Insofern wäre es ein Pendant zu '$name' in userReadings.
Der Ausdruck [$self] beinhaltet dann natürlich die state-Variable.
Im Übrigen bringt die Verwendung des cmdstate als FSM-State-Variable einige Nachteile mit sich:
1. Schwer lesbarer Code (durchnummerierte states statt selbstdokumentierende state-Namen)
2. Bei states mit mehreren Eingängen müssen alle transitions in eine DOELSEIF-Bedingung geschrieben werden. Dadurch entsteht ein unhandliches 'and/or' Konstrukt.
3. States können nicht gleichzeitig von timeout- und conditional transitions erreicht werden.
4. Unterscheidung von 'transition action' und 'state entry action' nicht möglich.
Eine übersichtliche FSM- Realisierung würde ich mir eher als Aneinanderreihung solcher Statements vorstellen:
...
DOELSEIF ([?$self:FS] eq "mystateA" and (<my transition-event X>))
(<my transition-action 1>)
({nextFS("$SELF","<my nextstateB>"})
...
in der externen Perl-Routine wird dann die entry-action ausgeführt und die FS-Variable gesetzt.
Damit liesse sich ein FSM-Entwurf (auf Papier oder per Grafik-Tool) 1:1 in DEFINE-code umsetzen (und die ein oder andere FSM mit state-Sackgasse würde sich vermeiden lassen ;) ).
Zitat von: crusader am 15 Februar 2016, 14:08:22
Nach meinem Vorschlag würde $SELF (oder $THIS oder $NAME ) den devicename beinhalten.
Ja so war es auch von mir gemeint (siehe verlinkter Post), hab mich unglücklich ausgedrückt was die Berücksichtigung von cmdState angeht.
Was ich meinte war eine Möglichkeit sowohl cmd_X als auch den dazu passenden cmdState Ersatzwert verwenden zu können. Der Grund dafür liegt außerhalb von DOIF. Innerhalb des selben DOIF ist es gewiss übersichtlicher auf cmdState zu verzichten, weil man es bei Änderungen sonst auch anpassen muss. Allerdings hilft cmdState beim Verschachteln mehrerer DOIF oder gar mit anderen Modulen. Dort weiß ich oft ohne nachzuschauen (zumindest nach langer Zeit) nicht mehr was nun hinter cmd_1, cmd_2 etc. von der Logik her steht und deshalb vergebe ich über cmdState entsprechend aussagekräftige Values. Die muss ich momentan dann natürlich auch im selben DOIF verwenden, da mir cmd_X dann nicht mehr zur Verfügung steht. Das ist aus den von dir beschriebenen Gründen dann für eine FSM eher doof.
Hoffe ich konnte mich nun etwas besser artikulieren ;-)
Zitat von: fiedel am 13 Februar 2016, 09:56:59
Wäre es eigentlich innerhalb einer solchen FSM mit DOIF auch möglich gleich die Fehlerüberwachung mit einzubauen?
Ist das identisch mit dieser Fragestellung (http://forum.fhem.de/index.php/topic,36323.msg286160.html#msg286160)?
Hi Per, Danke! Das geht schon in die Richtung. Mal abwarten was hier noch raus kommt, dann gibt es bestimmt noch ein gutes Beispiel in der Commandref dazu.
Gruß
Frank
Zitat von: crusader am 15 Februar 2016, 14:08:22
Eine übersichtliche FSM- Realisierung würde ich mir eher als Aneinanderreihung solcher Statements vorstellen:
...
DOELSEIF ([?$self:FS] eq "mystateA" and (<my transition-event X>))
(<my transition-action 1>)
({nextFS("$SELF","<my nextstateB>"})
...
in der externen Perl-Routine wird dann die entry-action ausgeführt und die FS-Variable gesetzt.
Alternativ könnte man auch ein zweites DOIF für die Entry-Actions verwenden.
Hab' das mal durchgespielt für die im Anhang dargestellte Maschine (Demo-Beispiel aus Yakindu).
Die externen Eingangssignale generiert ein Dummy:
define Event.TLW dummy
attr Event.TLW cmdIcon pedestrianRequest:remotecontrol/black_btn_STOP
attr Event.TLW room FSM
attr Event.TLW webCmd pedestrianRequest
Die Darstellung der externen Ausgangssignale übernehmen diese Dummies:
define TrafficLight.red dummy
attr TrafficLight.red devStateIcon true:rc_RED false:rc_BLANK
attr TrafficLight.red eventMap on:true off:false
attr TrafficLight.red room FSM
define TrafficLight.yellow dummy
attr TrafficLight.yellow devStateIcon true:rc_YELLOW false:rc_BLANK
attr TrafficLight.yellow eventMap on:true off:false
attr TrafficLight.yellow room FSM
define TrafficLight.green dummy
attr TrafficLight.green devStateIcon true:rc_GREEN false:rc_BLANK
attr TrafficLight.green eventMap on:true off:false
attr TrafficLight.green room FSM
define Pedestrian.green dummy
attr Pedestrian.green devStateIcon true:10px-kreis-gruen false:1px-spacer
attr Pedestrian.green eventMap on:true off:false
attr Pedestrian.green room FSM
define Pedestrian.red dummy
attr Pedestrian.red devStateIcon true:10px-kreis-rot false:1px-spacer
attr Pedestrian.red eventMap on:true off:false
attr Pedestrian.red room FSM
define Pedestrian.request dummy
attr Pedestrian.request devStateIcon true:remotecontrol/black_btn_YELLOW false:rc_BLANK
attr Pedestrian.request eventMap on:true off:false
attr Pedestrian.request room FSM
Für die eigentliche Implementation wird nun ein DOIF benötigt, in dem die Transitions definiert sind:
define Transition.TLW DOIF ([?State.TLW] =~ "cmd_1|initialized" and [Event.TLW] eq "pedestrianRequest")\
(setreading Transition.TLW nextState PedWaiting)\
\
DOELSEIF \
([State.TLW] eq "cmd_2") \
(setreading Transition.TLW nextState StreetAttention)\
####### PedWaiting Exit-Action ############## \
(trigger PedWaiting stop) ## Substate Deactivate\
\
DOELSEIF \
([State.TLW] eq "cmd_3") \
(setreading Transition.TLW nextState StreetRed) \
\
DOELSEIF \
([State.TLW] eq "cmd_4") \
(setreading Transition.TLW nextState PedestrianGreen) \
\
DOELSEIF \
([State.TLW] eq "cmd_5") \
(setreading Transition.TLW nextState PedestrianRed) \
\
DOELSEIF \
([State.TLW] eq "cmd_6") \
(setreading Transition.TLW nextState StreetPrepare) \
\
DOELSEIF \
([State.TLW] eq "cmd_7") \
(setreading Transition.TLW nextState StreetGreen)
attr Transition.TLW notexist init
attr Transition.TLW room FSM
attr Transition.TLW stateFormat nextState
...und eines, in dem die State-Actions beschreiben sind (incl. Timer-Starts):
define State.TLW DOIF ([Transition.TLW:nextState] eq "StreetGreen") ##cmd_1\
(set TrafficLight.red false,\
set TrafficLight.yellow false,\
set TrafficLight.green true)\
\
DOELSEIF ([Transition.TLW:nextState] eq "PedWaiting") ##cmd_2\
(set Pedestrian.request true)\
(trigger PedWaiting start) ## Substate Activate\
()\
\
DOELSEIF ([Transition.TLW:nextState] eq "StreetAttention") ##cmd_3\
(set TrafficLight.red false,\
set TrafficLight.yellow true)\
()\
\
DOELSEIF ([Transition.TLW:nextState] eq "StreetRed") ##cmd_4\
(set TrafficLight.red true, \
set TrafficLight.yellow false,\
set TrafficLight.green false)\
()\
\
DOELSEIF ([Transition.TLW:nextState] eq "PedestrianGreen") ##cmd_5\
(set Pedestrian.red false,\
set Pedestrian.green true)\
()\
\
DOELSEIF ([Transition.TLW:nextState] eq "PedestrianRed") ##cmd_6\
(set Pedestrian.red true,\
set Pedestrian.green false)\
()\
\
DOELSEIF ([Transition.TLW:nextState] eq "StreetPrepare") ##cmd_7\
(set TrafficLight.red true,\
set TrafficLight.yellow true,\
set TrafficLight.green false)\
()\
\
attr State.TLW room FSM
attr State.TLW wait 0:0,0,7:0,2:0,2:0,7:0,5:0,2
Für den Substate 'PedWaiting' benötigt man noch ein Extra-DOIF:
define PedWaiting DOIF ([PedWaiting:?start] or [PedWaiting] eq "cmd_2")\
(set Pedestrian.request true)\
()\
DOELSEIF ([PedWaiting] eq "cmd_1")\
(set Pedestrian.request false)\
()\
DOELSEIF ([PedWaiting:?stop])\
(set Pedestrian.request false)\
\
attr PedWaiting room FSM
attr PedWaiting wait 0,0.5:0,0.5
Sieht auf den ersten Blick etwas aufwendig aus, ist aber im Grunde nur Tipparbeit, da der eigentliche Entwurf und der Test der Maschine in Yakindu vorgenommen werden kann. (Die Exit-Action muss hier allerdings verschoben werden, da kein Exit-Mechanismus zur Verfügung steht).
Die Maschine ist in der oben vorgestellten Form lauffähig (und zeigt Dummy-Ampeln an).
Jedoch frage ich mich, ob mit dieser Mechanismus des Selbsttriggerns (eigentlich ist es ein Ping-Pong-Effekt zwischen den beiden DOIFs) tatsächlich stabil ist.
Wenn man nämlich in den Wait-Aufrufen(,die hier nur zum Triggern dienen und daher leer sind) Aktionen ausführt, bleibt die Maschine an dieser Stelle stehen.
Möglicherweise wäre da doch ein anderer Mechanismus notwendig !?
PS: Die Ampel ist übrigens eine US-Ampel. Nur falls sich jemand über die Gelbphasen wundert.
ZitatJedoch frage ich mich, ob mit dieser Mechanismus des Selbsttriggerns (eigentlich ist es ein Ping-Pong-Effekt zwischen den beiden DOIFs) tatsächlich stabil ist.
Aus meiner Erfahrung - ja.
Gruß
Damian
Wie ist es dann zu erklären, dass der Automat nicht weiterschaltet, wenn man statt einer leeren Anweisung im verzögerten Substate ein Kommando ausführt (meinetwegen '(set <Dummy> false)') ?
Die Funktion kommt dann erst wieder zustande, wenn man im anderen DOIF (welches auf den abgelaufenen Timer reagiert und postwendend ein Event generiert) ebenfalls eine Verzögerung einbaut.
Das habe ich für ein Indiz dafür gehalten, dass das Event-Timing für diese gegenseitige Triggerung grenzwertig ist
Ich habe einige Fehlerkorrekturen vorgenommen, insb. war das Schreibens der Readings wait_timer mit Event gewünscht, das habe ich wieder eingebaut.
Ich habe die Selbsttriggerung über Wait zunächst herausgenommen, weil mir die Sache nicht geheuer ist. Falls es doch irgendwo gewünscht ist, weil es schon im praktischen Einsatz ist, dann bitte melden. Diese Funktionalität könnte ich dann ggf. per Attribut einschaltbar machen.
Gruß
Damian
Zitat von: Damian am 28 Februar 2016, 19:44:32weil es schon im praktischen Einsatz ist, dann bitte melden.
Hiermit erfolgt.
Ich habe schon ein wenig an mir gezweifelt, weil bereits funktionierende Kontrukte nicht mehr wollen, ich aber eh noch in der Experimentierphase war.
i.V. mit
wait (geht ja auch bei leerer oder ohne Befehls-Klammer :D) wäre mir völlig ausreichend, ich brauche es ja eh nur dann, um den Timer auszuwerten.
Zitat von: Per am 13 März 2016, 01:52:07
Hiermit erfolgt.
Ich habe schon ein wenig an mir gezweifelt, weil bereits funktionierende Kontrukte nicht mehr wollen, ich aber eh noch in der Experimentierphase war.
i.V. mit wait (geht ja auch bei leerer oder ohne Befehls-Klammer :D) wäre mir völlig ausreichend, ich brauche es ja eh nur dann, um den Timer auszuwerten.
Ich habe eine Version erstellt, die das Attribut
selftrigger beinhaltet. Wenn es ungleich Null gesetzt wird, dann kann das Modul sich selbst triggern aber nur wenn der Auslöser und die daraufhin initiierte Ausführung über Wait stattfinden.
Desweiteren werden Events beim Setzen von Timern im DOIF generiert, wenn das Attribut
timerevent ungleich Null gesetzt wird, sonst nicht.
Bitte testen.
Gruß
Damian
Zitat von: Damian am 13 März 2016, 12:39:55Bitte testen.
Einen ersten Test hat
selftrigger schon bestanden.
Allerdings wird ist die Anzeige beim Reading
wait_timer mit und ohne
timerevent verwirrend (es wird nach kurzer Zeit z.B. "no timer" eingeblendet, nach aktuallisieren F5 zeigt es den richtigen Wert an). Aber es arbeitet korrekt.
Evtl. nochmal an das cmdState-Thema (https://forum.fhem.de/index.php/topic,49698.msg421716.html#msg421716) denken. Ich ändere die eine Zeile (in dieser "Beta" Zeile 722:
my $cmd=""; ->
my $cmd=ReadingsVal($hash->{NAME},"state","");) immer manuell, funst super, ob auch aktiv (!) i.V. mit
selftrigger muss ich noch testen.
Zitat von: Per am 13 März 2016, 16:20:22
Einen ersten Test hat selftrigger schon bestanden.
Allerdings wird ist die Anzeige beim Reading wait_timer mit und ohne timerevent verwirrend (es wird nach kurzer Zeit z.B. "no timer" eingeblendet, nach aktuallisieren F5 zeigt es den richtigen Wert an). Aber es arbeitet korrekt.
Evtl. nochmal an das cmdState-Thema (https://forum.fhem.de/index.php/topic,49698.msg421716.html#msg421716) denken. Ich ändere die eine Zeile (in dieser "Beta" Zeile 722: my $cmd=""; -> my $cmd=ReadingsVal($hash->{NAME},"state","");) immer manuell, funst super, ob auch aktiv (!) i.V. mit selftrigger muss ich noch testen.
wait_timer wird immer mit Event gesetzt - auch "no_timer".
Wenn es am Anfang das Reading nicht gibt, dann muss man F5 drücken damit es überhaupt sichtbar wird, das hat aber eher mit FHEMWEB etwas zu tun.
my $cmd=""; -> my $cmd=ReadingsVal($hash->{NAME},"state","")
funktioniert nicht richtig. Es bleibt bei mehreren Sequenzen der letzte Seqenzzustand z. B. cmd_2_1 stehen.
Ich habe erstmal bei nicht definiertem Zustand
z. B.
attr cmdstate erster|
für cmd_2 den entsprechenden Status genommen cmd_2 oder das was unter state definiert ist.
Später wird man jeden Zustand beliebig (auch für Sequenzen cmd_1_1) definieren können.
Nebenbei: In dieser Version können inzwischen auch wait-Timerangaben beliebig mit Komma und Doppelpunkt definiert werden:
z. B.
attr wait myfunc(10,20),[mydevice:myreading]:10,3:rand(myfunc(1,4))
Gruß
Damian
Ein Fünf-Minuten-Hack, vielleicht reicht es schon: siehe: https://forum.fhem.de/index.php/topic,51060.msg436110.html#msg436110
Gruß
Damian