Zustandsbasierte Modellierung Material Beispiele Ampel Getränkeautomat Pawlowscher Hund
Pfad: Startseite / Fächer / Informatik / Zustandsbasierte Modellierung
Autor: mk
15.03.2010 18:49
3165

Definition:

Ein endlicher Automat (EA) besteht aus

Zustandsautomaten - objektorientiert betrachtet

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.

Getränkeautomat

Automatengraph

Automatengraph

Automatentafel
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
Python-Realisierung

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.

Python-Überprüfungsumgebung

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.

GUI

# 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()

getraenke31.py

Aufgabe

Das Python-Programm getraenke1_.py enthält mindestens einen logischen Fehler. Finde ihn heraus!

Delphi-Realisierung

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;

getraenkeautomat.zip