• Keine Ergebnisse gefunden

Simulation kontinuierlicher Materialflüsse

Ebene 0 - Simulationskern

4.3 Modifikationen von DESMO-J

Entwicklung eines Minimalsystems

Das Paket DESMO-J ermöglicht es, innerhalb einer Simulation gleichzei-tig ereignisorientierte und prozeßorientierte Elemente zu verarbeiten.

Die Modellierung von Elementen als Prozeß setzt eine Darstellung der Zustände und Transitionen in Form von Zustandsdiagrammen (vgl.

Pa-ge u. Kreutzer 2005, S. 98, Object ManaPa-gement Group 2009, Kap. 15) voraus. Diese Form der Modellierung ist für alle Entitäten innerhalb ei-ner Materialflußsimulation möglich. Folgende Gründe sprechen jedoch gegen diese Darstellung:

• Jedes prozeßorientierte Element wird in Java durch einen eige-nen Systemprozeß (Thread) abgebildet. Die Performance der von Java gebildeten Threads ist im Vergleich zur reinen ereignisorien-tierten Simulation, die innerhalb eines einzigen Threads abläuft, sehr schlecht. Threads werden in Java als native System-Threads erzeugt und unterliegen somit auch der Prozeßverwaltung des Betriebssystems. Innerhalb eines Multi-Thread Programms müs-sen alle Ressourcen, auf die gemeinsam schreibend (und teilweise auch lesend) zugegriffen wird, aufwendig synchronisiert werden, was erheblich Zeit kostet. Ein weiterer Nachteil ist die erhöhte Fehleranfälligkeit in der Programmierung durch die zusätzlichen Synchronisationsmechanismen.

• Nach der Einführung von Java 5 wurde der Mechanismus zur Steuerung von Threads und zur Synchronisation vollstängig über-arbeitet (und verbessert), so daß auch eine Anpassung des Quell-codes von DESMO-J nötig ist.

• Die Darstellung als Zustandsmaschinen ist insbesondere für die Lager bei Berücksichtigung aller Fälle (Backlog, Lostsales) sehr komplex. Eine Vereinfachung der Zustandsmaschine auf wenige Zustände und Transitionen führt hingegen zu einer Logik, wie sie auch bei der ereignisorientierten Sichtweise implementiert wer-den muß, so daß keine Verbesserung durch die andere Sichtweise erreicht werden kann.

Daher wurden sämtliche Bestandteile der prozeßorientierten Si-mulation aus DESMO-J entfernt, um ein rein ereignisorientiertes Mi-nimalsystem zu erhalten. Dieses System wird im Folgenden auch als Kernsystem bezeichnet. Sämtliche Erweiterungen bauen auf diesem System auf. Gleichzeitig werden im Kernsystem auch einige

Neuerun-gen eingeführt, die entweder die Modellierung erleichtern, oder eine Vereinfachung der internen Datenstrukturen darstellen.

Einsatz von Value-Objekten

„Value Objects are instantiated to represent elements of the design that we care about only for what they are, not who or which they are.“ (Evans 2004, S. 98). Value-Objekte (oder auch Value-Typen genannt) sind Klassen, die ihren Zustand bei der Initialisierung erhalten und ihn nicht mehr ändern können. Im Gegensatz zu Entitäten (Evans 2004, S. 89) ist Ihre Identität irrelevant, da sie nur genutzt werden, um eine Menge an Attributen auszudrücken. Liegen zwei inhaltlich identische Instanzen vor, so ist der Programmablauf identisch, unabhängig davon, welche der beiden Instanzen verwendet wird.

Da sie nur einen Zustand besitzen und ihre Identität für den Einsatz nicht verwendet wird, reicht eine Instanz innerhalb eines Programms aus. Auf diese Instanz kann von anderen Objekten beliebig oft verwie-sen werden, ohne das Verhalten der anderen Objekte zu beeinflusverwie-sen.

Dieses „Object-Sharing“ ist auch einer der Gründe, für das Anlegen von Value-Objekten (Fowler 1999, S. 179). Aufgrund einer aufwendigen Initialisierung oder einer hohen Anzahl an Referenzen kann durch den Einsatz eines „Flyweight“ Entwurfsmusters die Erzeugung der Instan-zen konsolidiert werden (vgl. Evans 2004, S. 100, Gamma u. a. 1995, S. 195).

Eine der wichtigsten Anforderungen an ein Value-Objekt ist die Un-veränderlichkeit (im engl. immutable, Evans 2004, S. 99, Ausnahmen hiervon werden in Evans 2004, S. 101 erläutert). Auf Java bezogene Anforderungen zur Umsetzung der Designaspekte werden von Bloch (2008, S. 73) vorgestellt. Zur Erzeugung wird eine Builder-Klasse ein-gesetzt, wenn viele Konfigurationsoptionen zur Verfügung stehen (vgl.

Gamma u. a. 1995, S. 97 und Bloch 2008, S. 11). Ist die Anzahl an Kon-figurationsmöglichkeiten gering, so werden statische Factory-Methoden verwendet. In beiden Fällen wird der Konstruktor verborgen, um ei-ne direkte Initialisierung durch den Benutzer zu vermeiden. Nach der Erzeugung ist der Zustand nicht mehr veränderlich. Eine äquivalente

Darstellung von Value-Objekten ist das Konzept derimmutable classvon Bloch (2008, S. 73).

Der Aspekt der inhaltlichen Identität wird in Java durch die Me-thodenequalsundhashCodeausgedrückt. Ihre Implementierung wird grundsätzlich für alle Klassen empfohlen, die zum Informationsaus-tausch zwischen zwei Klassen oder zur Charakterisierung einer Klasse eingesetzt werden (Bloch 2008, S. 33,45). Eine Implementierung ist daher für Value-Objekte zwingend erforderlich.

Da ein Value-Objekt bereits bei der Instanziierung seinen Zustand erhält, kann der Hashwert berechnet und zwischengespeichert wer-den (Bloch 2008, S. 49) - spätere Berechnungen werwer-den so überflüssig.

Innerhalb vonequalswird dieser Wert eingesetzt, um verkürzend fest-zustellen, daß der Inhalt zweier Instanzen nicht identisch ist.

Value-Objekte werden sowohl auf dieser Ebene, als auch auf der Materialflußebene eingesetzt. Ihr Hauptanwendungsbereich liegt im Informationsaustausch (z. B. die KlasseAbstractObjectID) sowie in der Darstellung komplexer Datenstrukturen und den damit verbundenen Berechnungen (z. B. inAbstractRecipe). Die sich hieraus ergebenden Vorteile werden detailliert in Evans (2004, S. 100) und Bloch (2008, S. 73) aufgeführt. In den Abschnitten zur Implementierung der Schnitt-stelleRecipedurchAbstractRecipeauf Seite 149 undAbstractObjectID auf Seite 64 werden diese anhand der konkreten Implementierung detailliert beschrieben.

Serialisierung

Ein wesentliches Designziel der ersten Ebene ist die Implementierung der Vervielfältigung von Simulationsmodellen. Diese kann eingesetzt werden, um ein Modell mit unterschiedlichen Zufallszahlen zu simu-lieren und so eine breitere statistische Basis zu erhalten. DESMO-J bietet hierfür keinen Mechanismus, so daß ein entsprechender Mecha-nismus entwickelt und implementiert werden mußte. Eine wesentliche Anforderung war eine einfache Implementierung, welche möglichst wenige Anpassungen am Code benötigt und die auch für alle darauf aufbauenden Ebenen einsetzbar ist.

Java stellt zwei elementare Methoden zur Vervielfältigung zur Ver-fügung: Serialisierung und Klonen. Die Serialisierung ist ein Vorgang, der den Inhalt eines Objekts in einen Datenstrom überführt, um ihn z. B. über ein Netzwerk zu übertragen oder in einer Datei abzuspei-chern (Sun 2006, Interface Serializable, Bloch 2008, S. 289). Durch die Deserialisierung wird der Vorgang umgekehrt und neue Instanzen, die unabhängig von den serialisierten Originalen sind, werden erzeugt.

Das Klonen von Objekten hingegen führt direkt zu einer Kopie des Originals im Speicher und nicht zu einer anderen Darstellung, die da-von getrennt gespeichert werden kann. Der Aufwand zur Herstellung eines Klons ist wesentlich höher als bei der Serialisierung, da für jedes Objekt individuell die Methode zum Klonen überschrieben werden muß (Bloch 2008, S. 54). Gleichzeitig muß darauf geachtet werden, daß alle Referenzen zu anderen Objekten ebenfalls geklont werden, um vollständig unabhängige Kopien zu erhalten. Vergleichbare Probleme gibt es bei der Serialisierung nicht. Die für die Serialisierung spezifi-schen Probleme und Designaspekte werden in Bloch (2008, S. 289) beschrieben. Diese treten innerhalb der Simulation jedoch nicht auf, so daß der Serialisierung der Vorzug gegenüber dem Klonen gegeben wurde.

Voraussetzungen für den Einsatz der Serialisierung ist die Verwen-dung der SchnittstelleSerializablein jedem Objekt, welches serialisiert wird, inklusive aller in dem Objekt verwendeten Felder. Sind diese Felder von einem elementaren Datentyp, so ist dies unproblematisch.

Felder, die ein anderes Objekt repräsentieren, müssen hingegen eben-falls serialisierbar sein, sofern sie nicht als transient deklariert sind (Sun 2005, Kap. 8.3.1.3). Der gesamte Simulationskern wurde vollständig umgestellt, so daß jede Klasse serialisierbar ist. Die Serialisierung wur-de so implementiert, daß jewur-de Klasse wur-den Wert von serialVersionUID auf 1 setzt. Dieser Standardwert signalisiert dem Programmierer, daß die Klasse nicht für eine permanente Speicherung auf einem Daten-träger geeignet ist. Dadurch wird vermieden, daß nach Änderungen der internen Datenrepräsentation einer Klasse auch die Methoden zur Serialisierung-/Deserialisierung angepaßt werden müssen (Bloch 2008, S. 289). Die Serialisierung wird nur für die Vervielfältigung im

Spei-cher während eines Programmlaufs eingesetzt, so daß die von Bloch beschriebenen Einschränkungen hier nicht zutreffen.

Außer der Vervielfältigung von Testläufen gibt es ein weiteres An-wendungsgebiet, die verteilte Simulation. Zukünftige Anwendungen können die in Abschnitt 4.4.1 auf Seite 98 vorgestellte KlasseTestrunner so erweitern, daß die serielle Ausführung von mehreren Testläufen parallel erfolgt. Da die Serialisierung eigenständige Objekte erzeugt, kann dieser Mechanismus problemlos zur Vervielfältigung und anschlie-ßender paralleler Ausführung der Instanzen genutzt werden. Für die Behandlung von gemeinsamen Ressourcen muß der Code noch so ange-paßt werden, daß diese nicht dupliziert werden.

Umstieg auf Java 5.0

Mit der Veröffentlichung Java 5.0 im Jahr 2005 hat Sun mehrere neue Sprachelemente eingeführt. Die Einführung von Generics (Sun 2005, Abs. 8.1.2, 8.4.4., 8.8.4, 9.1.2) ermöglicht die Überprüfung der Typsi-cherheit bereits während der Compilierung. Dieses Sprachelement wird auf allen Ebenen der Simulation eingesetzt.

Innerhalb des Kerns wurden sämtliche Vorkommen von Klassen aus dem Java-Collections-Framework typisiert. Durch den Einsatz von Me-thoden aus diesem Framework konnten viele Klassen stark vereinfacht werden. Ein Beispiel sind die Operationen zum Sortieren der Ereignisli-ste in der KlasseEventPriorityQueue, die vomSchedulergenutzt wird (siehe Abschnitt 4.4.1 auf Seite 105).

Zufallszahlengenerator

Das Paket DESMO-J enthält zwei verschiedene Generatoren - zum einen eine Adapter-Klasse für den Zufallszahlengenerator, der von Sun im Paket java.utilsbereitgestellt wird, sowie eine Implementierung des Mersenne Twister von Matsumoto u. Nishimura (1998). Die Implemen-tierung von Sun entspricht einem einfachen linearen Kongruenzauto-maten, wie sie von Knuth (1997, Kap. 3.2.1) beschrieben wird. Die Periodenlänge wird durch den Modulo begrenzt (Knuth 1997, Kap.

3.2.1.2), welcher den Wert248≈2, 8·1014annimmt. Diese Perioden-länge ist für Simulationen mittlerer Größe ausreichend hoch. Der Gene-rator erfüllt jedoch nicht das Kriterium der Reproduzierbarkeit, da Sun keine Garantie für die Beibehaltung der Implementierung abgibt, son-dern nur allgemeine Spezifikationen vorgibt und implementiert. Auch wenn diese Veränderungen in der Implementierung von grundlegenden Klassen selten sind, wurden sie hier bereits durchgeführt (Sun 2006, java.utils.Random.nextFloat).

Die zweite Implementierung, die in DESMO-J zur Verfügung steht - der Mersenne Twister von Matsumoto u. Nishimura (1998) - besitzt eine Periodenlänge von219937−1≈4, 31·106001, welche für eine Simu-lation vollkommend ausreichend ist. Wie bereits in Abschnitt 2.3 dar-gestellt hat dieser Generator einen sehr hohen Speicherbedarf, so daß er (vorerst) nicht verwendet wird. Zur Anwendung kommt daher der Generator MRG32k3a von L’Ecuyer u. a. (2002b), dessen Schnittstellen so modifiziert wurden, daß sie eine einfache Behandlung der Teilströme ermöglichen. Diese Teilströme werden verwendet, um Testläufe mit ver-änderten Anfangsbedingungen zu replizieren. Details hierzu erläutert Abschnitt 4.4.1 auf Seite 98.

Etablierung eines Ebenenmodells

Zur Unterstützung des Refactoring hin zu einem Simulationskern und zur besseren Systematisierung wurden alle Elemente der Simulationsbi-bliothek in ein Ebenenmodell eingeordnet. Das Ziel dieser Klassifikation ist die Zuordnung von Funktionsbereichen zu Ebenen. Gleichzeitig soll die Sichtbarkeit der Klassen so verringert werden, daß jede Ebene Kon-takt zu anderen Ebenen nur über öffentliche Schnittstellen aufnimmt.

Alle anderen Elemente, die nur zur Bereitstellung von Diensten in-nerhalb der Ebene dienen, werden somit verdeckt. Die Konstruktion hat den Vorteil, daß die interne Datenorganisation verändert werden kann, ohne die Schnittstellen anzupassen. Das Designprinzip lautet:

Jede Ebene soll nur Elemente aus der unmittelbar darunterliegenden Ebene benutzen. Nur die Elemente einer Ebene, die eine grundlegende Infrastruktur bereitstellen, dürfen von allen darüberliegenden Ebenen

verwendet werden. Der Aufbau der Ebenen entspricht dem in Tabel-le 4.1 auf Seite 75, so daß hier nur kurz die DesignzieTabel-le vorgestellt werden:

Ebene 0 enthält die Kernelemente einer Simulation. Die Ebene stellt die gesamte Infrastruktur für eine diskrete ereignisorientierte Simulation bereit.

Ebene 1 enthält alle Elemente zur Simulation von diskreten und konti-nuierlichen Materialflüssen.

Ebene 2 ist eine Zwischenschicht, die Elemente von Ebene 1 zu Einhei-ten zusammenfaßt für Planungsaufgaben der darüberliegenden Ebene. Typische Beispiele hierfür sind Fertigungsleitstände, die für eine Maschinengruppe verantwortlich sind. Elemente der Ebene 2 haben direkten Zugriff auf einzelne Ressourcen oder Lager und stellen Informationen hierüber der dritten Ebene zur Verfügung.

Ebene 3 stellt den Kontakt zur Planung her oder enthält Planungsalgo-rithmen, um den Materialfluß zu steuern. Die Pläne werden von dieser Ebene aus an die Fertigungsleitstände weitergegeben, so daß die dritte Ebene keinen direkten Kontakt zu den einzelnen Maschinen erhält.

In den folgenden Kapiteln werden die wichtigsten Elemente der er-sten beiden Ebenen vorgestellt, sowie ein Überblick der Funktionsweise und Koordination der Elemente innerhalb einer Ebene und zwischen den Ebenen gegeben. Die Funktionen der Ebenen zwei und drei werden in Kapitel 6 vorgestellt, ohne näher auf die Implementierung einzuge-hen, da diese im Wesentlichen auf den dort vorgestellten Algorithmen aufbaut.