Allgemeines
Interaktion wird in SalesPoint-Anwendungen mit Hilfe von Prozessen realisiert. Prozesse benötigen eine Umgebung, in der sie laufen können, den sogenannten ProcessContext
. Im Framework gibt es zwei dieser Kontexte, nämlich SalesPoint
s und Shop.ProcessHandle
, eine innere Klasse im Shop, welche für Hintergrundarbeiten gedacht ist.
Ein Prozess ist wie ein Automat aufgebaut, er besteht aus Zuständen, Gate
s genannt, und Übergängen, den Transition
s. Gates sind für länger andauernde Aktionen gedacht, zum Beispiel Nutzerinteraktion. Deshalb gibt es auch eine spezielle Klasse UIGate
, welche FormSheets,
also die Nutzungsoberflächen, anzeigen kann. Ein Prozess kann an einem Gate unterbrochen werden.
Transitionen dienen dem Wechsel der Gates. Sie sind Zustandsübergänge. Zusätzlich können selbst definierte Aktionen ausgeführt werden, zum Beispiel das Verarbeiten der eingegebenen Daten in einem FormSheet. Prozesse können während einer Transition nicht unterbrochen werden.
Details
Start und Ablauf eines Prozesses
Vorbereitungen zum Prozessstart
Ein Prozess auf einem SalesPoint wird gestartet, indem die Methode runProcess()
dieses SalesPoints mit dem zu startenden Prozess p
als Parameter aufgerufen wird. Falls gerade ein Prozess läuft, wird er gesichert. Der SalesPoint bekommt eine Referenz auf den neuen Prozess, der Prozess eine Referenz auf den SalesPoint und dessen DataBasket. Danach schließt sich der eigentliche Prozessstart mit p.start()
an.
Falls der zu startende Prozess ein Hintergrundprozess sein soll, wird dieser mittels der runProcess()
Methode des Shops gestartet. Es wird zunächst ein neuer ProcessHandle erzeugt. Im Konstruktor des ProcessHandle werden wie in der runProcess()
Methode des SalesPoints Referenzen zwischen Prozess und Handle gesetzt. Anschließend wird der Handle in einer Liste aller ProcessHandles, die vom Shop verwaltet wird, abgespeichert. Falls der Shop nicht gerade angehalten ist, wird der Prozess mit start()
gestartet, andernfalls gestoppt mit suspend()
.
Prozessstart
Falls nicht noch ein Prozess läuft, werden zunächst einige Initialisierungen vorgenommen. Das Flag fSuspended
, welches eine Prozessunterbrechung anzeigt, wird auf false
gesetzt, da der Prozess anlaufen soll und nicht mehr unterbrochen sein kann. Falls der Prozess neu ist, also gar nicht unterbrochen war, wird zusätzlich einer Variablen gCurGate
das Startgate mit Hilfe von getInitialGate()
des Prozesses zugewiesen. Ein Prozess wird gerade fortgesetzt, wenn das Flag fResumed
true
ist, ansonsten handelt es sich um einen frisch gestarteten.
Es wird anschließend ein Thread trdMain
erzeugt und gestartet, welcher die Methode main()
ausführt.
Auch hier finden zu Beginn kleinere Initialisierungen statt. Falls der Prozess gerade neu startet, also nicht nur fortgesetzt wird, wird die Methode processStarted()
des Prozesskontextes ausgeführt. Somit kann jeder Prozesskontext angemessen auf einen Prozessstart reagieren. Falls der Kontext ein SalesPoint ist, werden dessen Form- und Menusheet entfernt, außerdem meldet sich der SalesPoint, der bislang als Listener am Display registriert war, ab. Ist der Kontext ein Shop.ProcessHandle, also ein Kontext für Hintergrundprozesse, werden beim Aufruf von processStarted()
keinerlei Aktionen ausgeführt. Als nächstes wird onResumeOrStart()
aufgerufen. Dabei handelt es sich um eine Hook-Methode, die vom Anwendungsprogrammierer zu überschreiben ist, falls spezieller Initialisierungscode beim Start eines Prozesses benötigt wird. Tritt in dieser Methode ein Fehler auf, wird gCurGate
ein Fehlergate zugewiesen.
Die Prozessschleife
Es folgt der Eintritt in die eigentliche Kontrollschleife. Diese wird so lange ausgeführt, wie curGate
nicht null
und fSuspended
false ist, der Prozess also nicht unterbrochen oder beendet wurde.
Es wird ein SaleProcess$SubProcess trdGate
, im Folgenden GateThread genannt, erzeugt und ausgeführt. Ein SaleProcess$SubProcess ist eine Unterklasse von Thread, die sich von diesem nur dadurch unterscheidet, dass sie beim Ausführen spezielle Throwables vom Typ ProcessErrorError
, welche für die prozessinterne Fehlerbehandlung benutzt werden, abfängt. Der GateThread hat die Aufgabe, eine Transition zurückzuliefern und der Variablen tCurTransition
zuzuweisen. Das geschieht durch den Aufruf der Methode getNextTransition()
von gCurGate
. Falls der Prozess unterbrochen wird, bevor eine Transition zurückgeliefert wurde (fSuspended
ist true
), wird tCurTransition
zu null
und die Hauptkontrollschleife verlassen.
Sobald der GateThread beendet ist, wird ein SaleProcess$SubProcess trdTransition
, der TransitionThread, erzeugt und ausgeführt. Dieser liefert das nächste Gate zurück, zu dem gesprungen werden soll, indem tCurTransition.perform()
aufgerufen wird. Dieses Gate wird in gCurGate
gespeichert. Falls versucht wird, den Prozess zu unterbrechen, solange die Transition noch im Gange ist, wird dies ignoriert. Nachdem der TransitionThread seine Aufgabe beendet hat und ein Zielgate gefunden wurde, wird onSuspended()
aufgerufen, eine leere Hook-Methode wie die bereits erwähnte Methode onResumeOrStart()
.
Falls der Prozess nicht unterbrochen oder beendet wurde, wird die Hauptkontrollschleife nun erneut abgearbeitet.
Prozessende
Nach verlassen der Prozessschleife wird die Hook-Methode onFinished()
aufgerufen. Falls der Prozess richtig beendet wurde, also nicht nur unterbrochen ist, wird zusätzlich pcContext.processFinished()
ausgeführt. Ist der Prozesskontext ein SalesPoint, wird bei Ausführung von processFinished()
nachgesehen, ob es noch inaktive Prozesse gibt, die vom eben abgearbeiteten verdrängt worden sind. Wenn dem so ist, wird der aktuellste von ihnen zum aktiven Prozess des SalesPoints gemacht und fortgesetzt. Andernfalls wird null
als aktueller Prozess gespeichert und es werden die Änderungen, die beim Prozessstart von processStarted()
gemacht wurden, rückgängig gemacht. Der SalesPoint meldet sich wieder als FormSheetListener am Display an, außerdem werden Form- und MenuSheet des SalesPoints gesetzt.
Wird processFinished()
eines ProcessHandles aufgerufen, wird die Referenz des beendeten Prozesses auf den Handle entfernt sowie der Handle aus der Liste der ProcessHandles im Shop gelöscht.
Gates am Beispiel UIGate
Wie bei der Beschreibung der Prozessschleife erwähnt, ist ein Prozess hauptsächlich ein Wechsel zwischen Gates und Transitionen. Die meistgenutzten Gates sind die UIGates. Diese haben die besondere Eigenschaft, dass an ihnen Form- und MenuSheets angezeigt werden können. Damit ist es möglich, dass der Nutzer durch seine Eingaben aktiv den Verlauf des Prozesses bestimmt.
Jedes Gate besitzt die Methode getNextTransition()
, mit der der steuernde Prozess in seiner Kontrollschleife die nächste auszuführende Transition erfragt. Beim UIGate kann es beliebig lange dauern, bis diese Transition feststeht, da sie durch die Nutzereingaben bestimmt und ausgelöst wird. Die getNextTransition()
Methode besteht deshalb hauptsächlich aus einer Schleife, die erst verlassen wird, wenn tatsächlich die auszuführende Transition feststeht.
Das UIGate besitzt ein Integerflag nChanged
, welches je nach Zustand anzeigt, ob ein FormSheet oder MenuSheet gesetzt wurde, eine Transition bestimmt wurde oder ob sich nichts geändert hat. Beim Start von getNextTransition()
wird zunächst die auszuführende Transition mit null
initialisiert. Außerdem wird in nChanged
festgehalten, dass sich FormSheet und MenuSheet geändert haben. Der Grund ist, dass im Konstruktor von UIGate ein FormSheet und ein MenuSheet übergeben werden, welche zum Zeitpunkt des Methodenaufrufs getNextTransition()
jedoch noch nicht angezeigt wurden.
Anschließend wird eine Schleife betreten, die solange ausgeführt wird, wie nChanged
anzeigt, dass noch keine Transition festgelegt wurde. Falls nChanged
anzeigt, dass sich das Form- und/oder MenuSheet geändert hat, wird setFormSheet()
bzw. setMenuSheet()
des aktuellen Prozesskontextes aufgerufen und die Sheets werden gesetzt. Anschließdend wird nChanged
auf NOTHING
gesetzt. Damit ist der Schleifenkörper abgearbeitet und ein neuer Durchlauf könnte beginnen. Allerdings würde die Schleife sehr oft durchlaufen, ohne dass sich überhaupt etwas verändert hat. Deshalb wird der aktuelle Thread schlafengelegt und erst wieder aufgeweckt, wenn irgend eine Änderung stattfindet, welche einen erneuten Schleifendurchlauf rechtfertigt.
Setzen von FormSheets, MenuSheets und Transitionen
Die Klasse UIGate besitzt die Methoden setFormSheet()
, setMenuSheet()
und setNextTransition()
. Diese Methoden arbeiten nach dem gleichen Prinzip. Zunächst wird das zu ändernde Attribut, FormSheet, MenuSheet oder Transition, in einer lokalen Variablen gespeichert. Danach wird in nChanged
eingetragen, dass sich das entsprechende Attribut geändert hat. Anschließend wird der Thread, der am Ende der Schleife von getNextTransition()
schlafengelegt wurde, wieder aufgeweckt. Falls keine Transition gesetzt wurde, ist das Resultat ein erneuter Schleifendurchlauf, in dem Form- und MenuSheet nach Bedarf aktualisiert werden. Falls aber eine Transition gesetzt wurde, wird die Schleife verlassen und die Transition zurückgeliefert. Damit geht die Kontrolle zurück an die Prozessschleife des SaleProcess.
Transitionen
Was für Gates die Methode getNextTransition()
ist, ist für Transitionen perform()
. Damit wird der Prozessschleife das Gate zurückgeliefert, zu dem gesprungen werden soll. Innerhalb der perform()
Methode kann bei Bedarf beliebiger Code ausgeführt werden. Allerdings sollte sich der Entwickler an das Prozesskonzept halten und keine Nutzerinteraktion in der Transition implementieren.
Catalogs |