-
Notifications
You must be signed in to change notification settings - Fork 0
02 Design Dokumentation
Im Folgenden wird das Design und der Aufbau der einzelnen Komponenten aus dem Komponenten-Diagramm beschrieben. (siehe Architektur Dokumentation)
TODO: Kurze Beschreibung wie Datenübertragung über DataBridge, Serialisierung, Persistierung funktioniert.

Das Graphical User Interface (GUI) der DynamicRuleEngine ist mittels Windows Presentation Foundation (WPF) aufgebaut und verfolgt das MainViewViewModel (MVVM) Design Pattern. Durch dieses Binding können dynamische Elemente einfach zur Oberfläche hinzgefügt oder entfernt werden.
Die GUI Komponente besteht aus folgenden Kernbereichen:
- View: Grafische Oberflächen
- Model: Datenhaltung der Oberfläche bzw. des Programms
- ViewModel: Verindung zwischen View und Model
- Technics: Technische Klassen für die View bzw. die Models
Beim Start der Oberfläche werden die Crossway Konstellationen und alle dynamischen Regeln geladen und in die lokale Datenhaltung kopiert. Die Oberfläche aktualisiert sich nach dem Laden der Elemente selbst und alle Kreuzungen werden mit ihren Grünphasen dargestellt. Für jede Kreuzung wird anschließend ein Timer gestartet und bei Ablauf des Timers werden die Regeln aktualisiert und ans Backend gesendet. Die Änderung einer Grünphase ist jederzeit durch den Benutzer möglich, ist jedoch zwischen 4 und 59 sec limitiert. Die Limitierung der Werte ist durch eine ValidationRule gesichert. Das bedeutet, die Zeit wird nur aktualisiert, wenn die Eingabe im erlaubten Bereich ist.

- TrafficSimulation: Hauptklasse mit Main-Methode. Ist dafür verantwortlich alle anderen Komponenten zu initialisieren und zu starten.
- AgentSpawner: Generiert neue Agenten mit randomisierten Eigenschaften (MaxVelocity, MaxAcceleration, etc.) und platziert diese auf der Karte.
- AgentSim: Simuliert das Verhalten der Agenten: vorausschauendes Fahren, Einhaltung der Verkehrsregeln, etc.
- PhysicsEngine: Bewegt die Agenten über die Map, je nach festgelegtem Verhalten durch die AgentSim.
- Allgemeines: Wie man im Diagram sieht, haben verwenden die Klassen alle das property dataManager. Über dieses wird mit der zentralen DataBridge kommuniziert, welche die Daten zwischen den Komponenten überträgt (siehe Architektur).
Mitte des Semesters wurde der Change Request eingekippt, dass Fahrzeuge welche defekt sind, überholt werden können sollen.

Unser Datenmodell sah von Anfang an vor, dass man für eine Edge (auf denen sich Fahrzeuge bewegen) Nachbar-Edges definieren kann. Außerdem existert ein Typ DynamicEdge der es erlaubt, im nachhinein ins bestehende Modell neue Edges einzufügen. Merkt ein Fahrzeug also, dass vor ihm ein defektes Fahrzeug steht, so kann dieses zur Laufzeit eine DynamicEdge zur Nachbar-Edge einfügen, das defekte Fahrzeug auf der Gegenfahrbahn überholen und danach wieder über eine weitere DynamicEdge auf die eigentliche Fahrbahn zurückkehren.
- Main wird aufgerufen
- Data Access Dependencies werden durch Factory Aufruf instanziert
- DataManager wird initialisiert, alle simulationsrelevanten Daten werden vom Data Access selektiert
- DataManager baut im Speicher den Objektbaum der ganzen Knoten und Kanten des Kartengraphes
- DataManager startet Synchronisationsthread für die DynamicRules (restliche Daten werden nur von der Simulation manipuliert, d.h. diese müssen nicht ständig gelesen werden)
- PhysicEngine Main Thread wird gestartet
- AgentSim Main Thread wird gestartet
- AgentSpawner Main Thread wird gestartet
- Über Agents iterieren oder eigener Thread je Agent
- Geschwindigkeit aufgrund der Beschleunigung ändern
- Zu fahrende Laufmeter berechnen aus der aktuellen Geschwindigkeit
- Laufmeter auf der vom Agent gewählten Route fahren
- Wenn Agent einen Endknoten erreicht -> Agent aus dem Speicher löschen oder wenn es ein Remote Endpoint ist, dann Agent an andere Simulation übertragen
- Die Klasse AgentSim ist für die Bestimmung des Verhaltens der Agenten zuständig.
- Bei jeder Zeiteinheit (TimerTick) wird über alle von der DataBridge erhaltenen Agenten iteriert und folgende Aktionen durchgeführt:
- Prüfung, ob für den Agenten bereits eine Route durch die Karte existiert. Ist dies nicht der Fall, so wird zufällig eine Route berechnet. (Methode calculateRouteIfNeccessary)
- Berechnung des eigenen Bremsweges basierend auf der aktuellen Geschwindigkeit und der MaxDeceleration.
- Prüfung aller Regeln, die im Bereich des eigenen Bremsweges liegen. So kann beispielsweise rechtzeitig vor Kreuzungen gebremst werden.
- Prüfung aller Agenten, die im Bereich des eigenen Bremsweges liegen. Fährt ein Agent vor mir langsamer, so wird gebremst (Hinweis: Die AgentSim kann nicht selbständig die eigene Geschwindigkeit direkt modifizieren. Sie kann nur bremsen und Gas geben - die Geschwindigkeitsanpassung erfolgt in der PhysicsEngine!)
- Prüfung, ob ein Agent vor mir defekt/deaktiviert ist. Ist dies der Fall, so wird zur Neighbor-Edge (falls vorhanden) eine DynamicEdge eingefügt und überholt.
- Mögliche Startpunkte selektieren
- Über AgentSimConfigurations iterieren und auswürfeln ob diese Configuration als Agent instanziert werden soll
- Startpunkt auswürfeln (wenn besetzt -> Spawn abbrechen)
- max. Geschwindigkeit, max. Beschleunigung und max. Verzögerung auswürfeln
- Agent instanzieren und auf dem Startpunkt spawnen
Das Graphical User Interface (GUI) ist mittels Windows Presentation Foundation (WPF) aufgebaut und verfolgt das MainViewViewModel (MVVM) Design Pattern. Durch dieses Design ist es möglich die Oberfläche zu ändern, ohne das Backend anpassen zu müssen. Dadurch kann schnell auf Designänderungen eingegangen werden.
Die GUI Komponente besteht aus folgenden Kernbereichen:
- View: Grafische Oberflächen
- Model: Datenhaltung der Oberfläche bzw. des Programms
- ViewModel: Verindung zwischen View und Model
- Technics: Technische Klassen für die View bzw. die Models
Beim Starten der GUI werden die Interfaces der DataAccess geladen und die Hintergrundkarte angefordert. Danach werden zwei Dispatcher Timer erstellt, die für die Aktualisierung der Oberfläche zuständig sind. Zwei Timer werden deshalb verwendet, da die Rules eine kleinere Aktualisierungsfrequenz benötigen und somit Leistung gespart werden kann.
- Im Event des ersten DispatcherTimers werden die Positions (Ecken), Edges (Linien) und Agents (Fahrzeuge) aus dem DataAccess geladen. Anschließend werden sie auf der Karte angezeigt, indem sie in eigene Objekte geladen werden, die dynamisch auf die Oberfläche gebunden sind.
- Im Event des zweiten DispatcherTimers werden die Rules (Verkehrszeichen) aus dem DataAccess geladen. Anschließend werden sie ebenfalls mittels Binding auf der Karte angezeigt.
Die Knoten und Kanten werden auf der Oberfläche nicht angezeigt, können aber mit F2 eingeblendet werden (was sich aber negativ auf die maximale Performance auswirkt). Diese Funktion sollte nur für Debugzwecke verwendet werden.
Die neue Anforderung ein Auto "kaputt" gehen zu lassen und es somit auf der Karte stehen zu bleiben wurde in der GUI so implementiert, dass jeder Agent mit einem Event ausgestattet ist (ohne das MVVM Pattern zu brechen) dass über eine Message Queue die Simulation triggert, die anschließend den Typ des Agents ändert.