Was ist DESPG?

DESPG (Discrete Event Simulation Playground) ist eine Java Implementierung zum Erstellen von ereignisbasierten Simulationen. Sie dient dazu Studierenden und Interessierten die Möglichkeit zu geben eine Simulationsplattform über ihre Implementierung kennenzulernen. Herzstück von DESPG ist kompakter Simulationskern, der alle notwendigen Komponenten hat, um komplexe reale Probleme zu modellieren und zu simulieren. Dieser wird in Erweiterungen (z.B. zur Visualisierung von Simulationsläufen) sukzessive erweitert.

Diskrete ereignisbasierte Simulation (engl. “Discrete Event Simulation” – DES)

Mit Hilfe von diskreten, ereignisbasierten Simulationen werden dynamische Systeme simuliert. Zeit wird in diesen System diskret betrachtet in Form der Zeitpunkte von Ereignissen in denen sich der Zustand des Systems ändert. Alle Simulationsobjekte können Ereignisse emittieren und konsumieren. Dies dient der Kommunikation zwischen (verschiedenen) Simulationsobjekten (oder auch wenn ein Simulationsobjekt mit sich selbst kommunizieren möchte, z.B. um sich nach einer gewissen Zeit an eine Aktion zu “erinnern”). Jedes Ereignis verfügt über einen Zeitschritt in dem es konsumiert werden kann und z.B. über eine Menge von Adressaten an die es gerichtet ist. Darüber hinaus lassen sich weitere Daten an das Ereignis binden. Zeit vergeht in einer diskreten Simulation immer dann, wenn keine Ereignisse emittiert oder konsumiert werden (= eigentlich nichts passiert) bis zum nächsten Ereignis auf das reagiert werden soll vom System.

Der Simulationskern

Der Simulationskern besteht aus den Klassen und Interfaces des Packetes de.despg.core. Die Klassen SimulationObject und Simulation, sowie das Interface UniqueEventDescription müssen zum Entwickeln eines Simulationsmodells nach speziellen Regeln implementiert werden. In der Methode simulate() der Klasse Simulation befindet sich der zweite Teil des Kerns. Sie implementiert die Simulationsschleife (siehe Abbildung 1) und wird beim Ableiten der Klasse nicht überschrieben.

Abbildung 1: Simulationsschleife
Abbildung 2: dev.despg.core

Implementierung von Simulationsmodellen

SimulationObject:

Implementierungen dieser Klasse sind die zentralen Entitäten einer Simulation (zb. Lkw, Verladestation, Mitarbeiter). Sie müssen die Methode simulate(timeStep: int): boolean implementieren. Sie wird von der Simulationsschleife in jedem Simulationsschritt aufgerufen und enthät das verhalten der Entität. Wenn der Zustand des Systems während des Aufrufs geändert wird (Event konsumieren/emittieren, oder Elemente aus einer Queue entnehmen oder ihr hinzufügen) muss true zurückgegeben werden, andernfalls false.
Event werden konsumiert, indem zunächst auf der EventQueue (Singleton) die Methode getNextEvent(…) aufgerufen wird. Die Paramter geben an für welche Art von Event sich das SimulationsObject interssiert. Wenn das Event konsumiert werden soll muss es über die Methode remove(event: Event) aus der EventQueue entfernt werden.
Zum emittiern eines Events wird es instaziiert und der EventQueue mit der Methode add(…) hinzugefügt. Elemente einer Queue werden ebenfalls über get- und remove-Methoden konsumiert. Dabei kann mit einem Filter gearbeitet werden, siehe Abschnitt Queue.
Zusätzlich enthalten SimulationObjects ein System zum Erfassen ihrer jeweiligen Auslastung (utilization). Beispielsweise wird bei einem Aufruf von simulate(…) this.utilStart() aufgerufen und bei einem späteren Zeitpunkt this.utilStop().

Event:

Events sind die zentralen Objekte um die Kommunikation zwischen SimulationOjekten zu steuern. Sie werden in den simulate() Methoden aus der EventQueue(Singleton) entnommen(konsumieren) und ihr hinzugefügt(emittieren). Der timeStep gibt an ab welchem Simulationszeitpunk das Event konsumiert werden kann. Auch ein späterer Zeitpunkt ist möglich, wenn beim Aufruf von getNextEvent der Parameter past true ist. Events haben immer einen Typen (UniqueEventDescription), und eines der beiden Attribute receivingObject und receivingClass muss gesetzt sein. ReceivingClass bedeutet, dass es von jeder Instanz der angegebenen Klasse konsumiert werden kann. Wenn receivingObject gesetzt ist kann es nur von der angegebenen konsumiert werden. Das Attribut objectAttached ist optional.
Bei der Erzeugung von Events kann die Auslastung des angehängten Objekts mit der Methode addUtilization(…) um den zeitlichen Abschnitt zwischen emittieren und potenziellem konsumieren des Events erhöht werden.

Queue:

Queues sind keine für den DES-Kern notwendigen Elemente. Die Klasse ist selber eine Implementierung eines SimulationObjekts und arbeitet intern mit Events(vom Typ CoreEventTypes.Delay). Es ist aber ein hilfreiches und häufig verwendetes Konstrukt und daher im Kern enthalten.
Queues können SimulationOjekte aufnehmen und werden ähnlich verwendet wie die EventQueue. Sie dienen der Modellierung von Verbindungen zwischen Komponenten(Kundenwarteschlangen, Straßen, …). Bei der Entnahme eines Objekts muss, wie bei der EventQueue, zunächst get() und danach remove() aufgerufen werden. Beim Hinzufügen eines Objekts mit der add(…) Methode kann diese im gleichen Zeitschritt bereits wieder entnommen werden. Wenn es mit addWithDelay(…) hinzugefügt wird bleibt es mindestens die angegebene Zeit in der Queue bevor es wieder entnommen werden kann. Bei der Verwendung mit get() wird das erste Element zurückgegeben. Mit get(filter: Map<String, Object>) wird zusätzlich ein Filter übergeben, der dafür sorgt, dass das erste passende Element genommen wird. Der Key jedes Entries der Map passt dabei zu einer Getter-Methode der enthaltenen Objekte (dabei wird der Name des Attributes angegeben, zb.: “load” → getLoad()). Der Value kann entweder ein konkreter Wert/Objekt sein, oder aber eine Instanz der Klasse Filter. In diesem Fall wird auf dem Rückgabewert des Getters die Filtermethode angewendet und muss true zurückgeben.
Nach der Benutzung einer Queue muss true zurückgeben werden, um dem System die Möglichkeit zu geben auf den geänderten Zustand zu reagieren.

Simulation:

Die Klasse Simulation enthält in der Methode simulate() die Simulationsschleife, diese wird bei der Implementierung nicht überschrieben. Ihr wird beim erstellen von Modellen die main-Methode zur Initialisierung des Modells hinzugefügt. Hier werden alle sonstigen Implementierungen instanziiert und es können Objekte den Queues hinzugefügt, sowie initiale Events erzeugt werden. Zum Start der Simulation wird die Methode simulate() aufgerufen. Bevor die Zeit erhöht wird, ruft die Simulationsschleife die Methode printEveryStep() auf. Hier können Meldungen zur Ausgabe auf der Konsole hinzugefügt werden, siehe Logging.

Randomizer:

Um reale Systeme besser nachzubilden zu können wird mit der Hilfe der Klasse Randomizer die Zeitpunkte des möglichen Konsumierens von Events und der Entnahme von Objekten aus den Queues durch ein Zufallsverfahren in festgelegtem Rahmen variiert.
Durch vielfaches ausführen der Modelle, und Bildung von Mittelwerten der Ergebnisse kann eine bessere Aussage über reale Systeme getroffen werden.

Beispielmodel GravelShippingWithQueue