Endlicher Automat (finite state maschine)

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

Vorheriges Thema - Nächstes Thema

Loredo

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 ;-)
Hat meine Arbeit dir geholfen? ⟹ https://paypal.me/pools/c/8gDLrIWrG9

Maintainer:
FHEM-Docker Image, https://github.com/fhem, Astro(Co-Maintainer), ENIGMA2, GEOFANCY, GUEST, HP1000, Installer, LaMetric2, MSG, msgConfig, npmjs, PET, PHTV, Pushover, RESIDENTS, ROOMMATE, search, THINKINGCLEANER

Per

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?

fiedel

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
FeatureLevel: 6.1 auf Wyse N03D ; Deb. 11 ; Perl: v5.14.2 ; IO: HM-MOD-RPI-PCB + VCCU|CUL 868 V 1.66|LinkUSBi |TEK603
HM: SEC-SCO|SCI-3-FM|LC-SW4-PCB|ES-PMSW1-PL|RC-4-2|SEN-MDIR-O|SEC-WDS-2
CUL: HMS100TF|FS20 S4A-2 ; OWDevice: DS18S20|DS2401|DS2406|DS2423

crusader

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.

Damian

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
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

crusader

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

Damian

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
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Per

#37
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.

Damian

#38
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
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Per

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 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.

Damian

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 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.

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

Damian

#41
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
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