![]() |
||
| Zustandsbasierte Modellierung |
Material
Beispiele
Ampel
Getränkeautomat
Pawlowscher Hund
|
|
|
Ein endlicher Automat (EA) besteht aus
Im klassischen Automatenmodell wird ein Zustandsübergang durch die Eingabe eines Eingabezeichens bewirkt. Allgemeiner kann man sich vorstellen, dass Zustandsübergänge durch Ereignisse ausgelöst werden. Solche Ereignisse könnten z.B. sein:
Auch das Ausgeben von Zeichen kann zu dem Durchführen irgendeiner Aktion verallgemeinert werden.
Die Menge E kann also als eine Menge von Ereignissen, die Menge A als eine Menge von Aktionen aufgefasst werden.

| Z ↓ / E → | e50 | e100 | eKorr | eWare |
| z0 | z50 / aNichts | z100 / aNichts | z0 / aNichts | z0 / aNichts |
| z50 | z100 / aNichts | z150 / aNichts | z0 / a50 | z50 / aNichts |
| z100 | z150 / aNichts | z100 / a100 | z0 / a100 | z100 / aNichts |
| z150 | z150 / a50 | z150 / a100 | z0 / a150 | z0 / aGetr |
Aus der Auflistung von Zuständen, Eingaben und Ausgaben
ergibt sich eine 'int-Darstellung' der Tabelle. Diese Tabelle lässt sich übersichtlich in die Definition einer Funktion übertragen.
| Z ↓ / E → | 0 | 1 | 2 | 3 |
| 0 | 1 / 0 | 2 / 0 | 0 / 0 | 0 / 0 |
| 1 | 2 / 0 | 3 / 0 | 0 / 1 | 1 / 0 |
| 2 | 3 / 0 | 2 / 2 | 0 / 2 | 2 / 0 |
| 3 | 3 / 1 | 3 / 2 | 0 / 3 | 0 / 4 |
Basis-Realisierung
class Automat(object):
def __init__(self):
self.zustaende = ('z0','z50','z100','z150') # Zustände
self.eingaben = ('e50','e100','eKorr','eWare') # Eingaben
self.ausgaben = ('aNichts','a50','a100','a150','aGetr') # Ausgaben
self.f = (((1,0),(2,0),(0,0),(0,0)), # Automatentabelle
((2,0),(3,0),(0,1),(1,0)),
((3,0),(2,2),(0,2),(2,0)),
((3,1),(3,2),(0,3),(0,4)))
self.zustand = 0 # Anfangszustand
self.ausgabe = None # anfangs keine Ausgabe gespeichert
self.eingabe = None # anfangs keine Eingabe gespeichert
def ein(self,eingabe):
self.eingabe = eingabe
(self.zustand,self.ausgabe) = self.f[self.zustand][self.eingabe] # Arbeitsschritt
mit callback OnChange und Zusicherungen
class Automat(object):
def __init__(self,OnChange):
self.zustaende = ('z0','z50','z100','z150')
self.eingaben = ('e50','e100','eKorr','eWare')
self.ausgaben = ('aNichts','a50','a100','a150','aGetr')
self.f = (((1,0),(2,0),(0,0),(0,0)),
((2,0),(3,0),(0,1),(1,0)),
((3,0),(2,2),(0,2),(2,0)),
((3,1),(3,2),(0,3),(0,4)))
self.zustand = 0
self.ausgabe = None
self.eingabe = None
self.OnChange = OnChange
def ein(self,eingabe):
if type(eingabe) == str:
eingabe = list(self.eingaben).index(eingabe)
assert (type(eingabe) == int) and (0 <= eingabe < len(self.eingaben))
self.eingabe = eingabe
(self.zustand,self.ausgabe) = self.f[self.zustand][self.eingabe]
assert 0 <= self.zustand < len(self.zustaende)
assert 0 <= self.ausgabe < len(self.ausgaben)
if self.OnChange != None:
self.OnChange()
Obige Version verfügt über den Komfort, dass eine Eingabe auch über die String-Entsprechung gemacht werden kann. In diesem Fall wird über die Methode index der richtige Index ermittelt. Tupel verfügen nicht über diese Methode, sodass zunächst mit einem Typecast aus dem Tupel self.eingaben eine Liste gemacht wird.
Mit Hilfe von MVC ist View und Controller völlig unabhängig vom speziellen Automaten. Vorausgesetzt ist
dabei, dass sich der Automat (Model) an die Schnittstellen hält und dass der Controller View mit dem Automaten
bekannt macht. Alternativ könnte man View zu Beginn die Tupel zustaende, eingaben und ausgaben
übergeben.
Was ist die bessere Modellierung? Bitte beachten, wie mit einem eventuell fehlenden Automatennamen
umgegangen wird.
# model
class Automat(object):
def __init__(self,OnChange):
self.name = 'Getränkeautomat 1'
self.zustaende = ('z0','z50','z100','z150')
self.eingaben = ('e50','e100','eKorr','eWare')
self.ausgaben = ('aNichts','a50','a100','a150','aGetr')
self.f = (((1,0),(2,0),(0,0),(0,0)),
((2,0),(3,0),(0,1),(1,0)),
((3,0),(2,2),(0,2),(2,0)),
((3,1),(3,2),(0,3),(0,4)))
self.zustand = 0
self.ausgabe = None
self.eingabe = None
self.OnChange = OnChange
def ein(self,eingabe):
if type(eingabe) == str:
eingabe = list(self.eingaben).index(eingabe)
assert (type(eingabe) == int) and (0 <= eingabe < len(self.eingaben))
self.eingabe = eingabe
(self.zustand,self.ausgabe) = self.f[self.zustand][self.eingabe]
assert 0 <= self.zustand < len(self.zustaende)
assert 0 <= self.ausgabe < len(self.ausgaben)
if self.OnChange != None:
self.OnChange()
# view
import tkinter
class View(tkinter.Tk):
def __init__(self,cbTaste,automat):
tkinter.Tk.__init__(self)
# Fenster
try:
self.title(automat.name)
except:
self.title('Automat')
self.geometry('400x360')
# Labels
self.lZ = tkinter.Label(self,text='Zustand')
self.lZ.place(x=10,y=35)
self.lE = tkinter.Label(self,text='Eingabe')
self.lE.place(x=10,y=75)
self.lA = tkinter.Label(self,text='Ausgabe')
self.lA.place(x=10,y=115)
self.lEin = tkinter.Label(self,text='Eingaben')
self.lEin.place(x=160,y=10)
self.lAus = tkinter.Label(self,text='Ausgaben')
self.lAus.place(x=280,y=10)
# Optionmenus
self.vZ = tkinter.StringVar(master=self)
self.vZ.set(automat.zustaende[0])
self.oZ = tkinter.OptionMenu(self,self.vZ,*automat.zustaende)
self.oZ.place(x=60,y=30, width=80)
self.vE = tkinter.StringVar(master=self)
self.vE.set(automat.eingaben[0])
self.oE = tkinter.OptionMenu(self,self.vE,*automat.eingaben)
self.oE.place(x=60,y=70, width=80)
self.vA = tkinter.StringVar(master=self)
self.vA.set(automat.ausgaben[0])
self.oA = tkinter.OptionMenu(self,self.vA,*automat.ausgaben)
self.oA.place(x=60,y=110, width=80)
# Button
self.b = tkinter.Button(self,text='bearbeite')
self.b.bind('<1>',cbTaste)
self.b.place(x=60,y=170,width=78)
# Listboxes
self.lbE = tkinter.Listbox(self)
self.lbE.place(x=160,y=30,width=100,height=300)
self.lbA = tkinter.Listbox(self)
self.lbA.place(x=280,y=30,width=100,height=300)
# controller
class Controller(object):
def __init__(self):
self.model = Automat(self.update)
self.view = View(self.taste,self.model)
self.view.mainloop()
def update(self):
# Eingabe in Listbox Eingaben
self.view.lbE.insert(tkinter.END,self.model.eingaben[self.model.eingabe])
# Zustand setzen
self.view.vZ.set(self.model.zustaende[self.model.zustand])
# Ausgabe setzen
self.view.vA.set(self.model.ausgaben[self.model.ausgabe])
# Ausgabe in Listbox Ausgaben
self.view.lbA.insert(tkinter.END,self.model.ausgaben[self.model.ausgabe])
def taste(self,event):
# Zustandsnummer ermitteln
z = list(self.model.zustaende).index(self.view.vZ.get())
# Eingabenummer ermitteln
e = list(self.model.eingaben).index(self.view.vE.get())
# zu DEBUGGING-Gründen Zustand setzen
self.model.zustand = z
# Automat arbeiten lassen
self.model.ein(e)
# Hauptprogramm
c = Controller()
Das Python-Programm getraenke1_.py enthält mindestens einen logischen Fehler. Finde ihn heraus!
selbstdefinierte Datentypen
Tzustand = (z0,z50,z100,z150); Teingabe = (e50,e100,eKorr,eWare); Tausgabe = (aNichts,a50,a100,a150,aGetr);
Realisierung der Automatentafel
//-------- verarbeiteEingabe (public) ----------------------------------
procedure TAutomat.verarbeiteEingabe(eingabe : Teingabe);
begin
case zustand of
z0 : case eingabe of
e50 : begin SetZustand(z50); SetAusgabe(aNichts); end;
e100 : begin SetZustand(z100); SetAusgabe(aNichts); end;
eKorr : begin SetZustand(z0); SetAusgabe(aNichts); end;
eWare : begin SetZustand(z0); SetAusgabe(aNichts); end;
end;
z50 : case eingabe of
e50 : begin SetZustand(z100); SetAusgabe(aNichts); end;
e100 : begin SetZustand(z150); SetAusgabe(aNichts); end;
eKorr : begin SetZustand(z0); SetAusgabe(a50); end;
eWare : begin SetZustand(z50); SetAusgabe(aNichts); end;
end;
z100 : case eingabe of
e50 : begin SetZustand(z150); SetAusgabe(aNichts); end;
e100 : begin SetZustand(z100); SetAusgabe(a100); end;
eKorr : begin SetZustand(z0); SetAusgabe(a100); end;
eWare : begin SetZustand(z100); SetAusgabe(aNichts); end;
end;
z150 : case eingabe of
e50 : begin SetZustand(z150); SetAusgabe(a50); end;
e100 : begin SetZustand(z150); SetAusgabe(a100); end;
eKorr : begin SetZustand(z0); SetAusgabe(a150); end;
eWare : begin SetZustand(z0); SetAusgabe(aGetr); end;
end;
end;
end;