VDV454 Fahrten-Analyse — Technische Dokumentation

Solution: VBK_VDV454_Import.sln | Framework: .NET 8/9, WPF, DevExpress | Stand: März 2026
Inhalt

1. Architektur & Projektstruktur

VBK_VDV454_Import.sln ├── VBK_VDV454_Import_UI (.NET 8.0-windows, WPF) ← Manuelle Analyse-Oberfläche │ ├── MainWindow.xaml/cs ← Hauptfenster (DockLayoutManager) │ ├── MainWindowViewModel.cs ← MVVM ViewModel (DevExpress.Mvvm) │ ├── Models/ │ │ ├── FahrtModel.cs ← Fahrten-Grid + Detail-Grid Modell │ │ ├── FahrtModelFormation.cs ← Formations-Grid Modell │ │ └── FahrtModelMitFahrzeug.cs ← Fahrt mit Fzg-Informationen │ ├── HstColumnTemlateSelector.cs ← Template-Auswahl für Haltestellenspalten │ └── HstColumnFormationTemlateSelector.cs │ ├── VBK_VDV454_ImportCore (.NET 8.0, Klassenbibliothek) ← Gemeinsame Kern-Logik │ ├── ImportService.cs ← Import-Logik (Init, Execute, Query) │ ├── FolderWatcherService.cs ← FileSystemWatcher-basiert │ ├── AutoImportService.cs ← Zeitgesteuerter Batch-Import │ ├── Interfaces/ ← IImportService, IFolderWatcherService, ... │ └── Models/ ← XML-Modelle, Output-Modelle, Session-Modelle │ ├── VBK_VDV454_WindowsService (.NET 9.0, Worker Service) ← Automatisierter Betrieb ├── VBK_VDV454_Betriebstag_Importer (.NET 9.0, Console) ← CLI-Import └── VBK_VDV454_Prognose_CSV_Exporter (.NET 9.0, Console) ← CSV-Export

Abhängigkeiten

Alle Projekte referenzieren VBK_VDV454_ImportCore. Die UI nutzt zusätzlich DevExpress 24.1.14 (Wpf.Grid, Mvvm, Themes).

2. UI-Aufbau (Fahrten-Ansicht)

Das Hauptfenster verwendet einen DockLayoutManager mit folgender Aufteilung:

Fahrten-Ansicht: Steuerung links, Fahrten-Grid oben rechts, Detail-Grid unten rechts

Abb. 1: Fahrten-Ansicht — Steuerung-Panel (links), Fahrten-Master-Grid (oben), Fahrt-Detail-Grid (unten)

BereichKomponenteBeschreibung
LinksLayoutPanel (500px, fixiert)Steuerung mit Tabs: Betriebstage, Import, Einstellungen
Oben rechtsGridControl + TableViewFahrten-Master-Grid (ItemsSource: FahrtenCollection)
Unten rechtsGridControl + dynamische SpaltenFahrt-Detail-Grid mit Haltestellen als Spalten, Telegramme als Zeilen
Tab-LeisteDocumentGroupUmschaltung zwischen [Fahrten], [Formation] und [Fahrt-Detail]

Quelle: MainWindow.xaml

3. Steuerung-Panel (links)

Tab „Betriebstage“

SpalteBinding (FieldName)TypBeschreibung
BetriebstagBetriebstagDateTimeDatum des Betriebstages (Format: d = kurzes Datum)
Anzahl FahrtenAnzahlFahrtenintGesamtanzahl importierter Fahrten
Importiert amImportTimeDateTimeZeitpunkt des Imports (Format: g = Datum + Uhrzeit)
Import fehlerhaftHasErrorsboolCheckbox — true wenn Import-Fehler auftraten
FehlertextErrorMessagestringFehlermeldung (leer bei fehlerfreiem Import)

Datenquelle: ImportedBetriebstage (List<ImportInfoModel>), geladen via IImportService.FindImportedBetriebstage(rootFolder).

Selektion eines Betriebstages triggert GetFahrtenAsync() im ViewModel.

Tab „Import“

Manueller Import-Workflow:

  1. Import-Ordner wählen (ButtonEdit mit FolderBrowserDialog)
  2. „Import vorbereiten“PrepareImportCommandIImportService.InitImport()
    Erkennt Betriebstage aus XML/LOG-Dateien, füllt Combobox
  3. Betriebstag auswählen
  4. „Import ausführen“ExecuteImportCommandIImportService.ExecuteImport()
    Schreibt JSON-Dateien in Data/{YYYY}/{MM}/{DD}/

Tab „Einstellungen“

EinstellungBindingPersistenz
Daten-OrdnerSettings_DataFolderWPF Settings.Default
Temporärer Import-OrdnerSettings_TempImportFolderWPF Settings.Default
Standard Import-OrdnerSettings_DefaultImportFolderWPF Settings.Default
Global-ID anzeigenShowGlobalIdSession (nicht persistiert)

4. Fahrten-Grid (Master-Tabelle, oberer Bereich)

Zeigt alle Fahrten eines ausgewählten Betriebstages. Selektion einer Zeile lädt das Fahrt-Detail-Grid.

SpalteFieldNameTypSichtbarkeit
StartzeitStartzeitDateTime?Immer
EndzeitEndzeitDateTime?Immer
Fahrt BezeichnerFahrtBezeichnerstringImmer
Fahrt NrFahrtNrintImmer
Linien TextLinienTextstringImmer
Richtungs IDRichtungsIDstringImmer
Umlauf IDUmlaufIDstringImmer
BetreiberBetreiberstringImmer
Start Halt TextStartHaltTextstringImmer
Linien-IDLinienIDstringNur bei ShowGlobalId = true
Start Halt IDStartHaltIDstringNur bei ShowGlobalId = true
End Halt IDEndHaltIDstringNur bei ShowGlobalId = true

Die fokussierte Zeile wird fett und mit grauem Hintergrund (#eeeeee) hervorgehoben (FocusedRowStyle).

Modell: FahrtModel (VBK_VDV454_Import_UI/Models/FahrtModel.cs)

5. Fahrt-Detail-Grid (unterer Bereich)

Zeigt die zeitliche Entwicklung einer einzelnen Fahrt. Zeilen = VDV454-Datenabrufe (zeitlich aufsteigend), Spalten = Haltestellen (in Reihenfolge der Fahrt).

Feste Spalten (links, fixiert)

SpalteFieldNameTemplateBeschreibung
ZeitstempelZeitstempelZeitstempelColumnTemplateZeitpunkt des Datenabrufs. Format: HH:mm:ss:ffff. Hintergrund: Khaki, fett.
K. (Komplettfahrt)KomplettfahrtKomplettfahrtColumnTemplateCheckbox (bool). Gibt an, ob der Datensatz eine vollständige Fahrt enthält.
P. (Prognose fortlaufend)PrognoseFortlaufendPrognoseFortlaufendColumnTemplateCheckbox (bool). Fortlaufender Prognose-Status. Wird aus PrognoseMoeglich fortgeschrieben — bleibt true solange PrognoseMoeglich true ist, wird false wenn PrognoseMoeglich auf false wechselt.
P. (Prognose möglich)PrognoseMoeglichPrognoseMoeglichColumnTemplateCheckbox (bool?, dreistufig: true/false/null). Aktueller Prognose-Status aus VDV454-Datensatz.

Dynamische Haltestellen-Spalten

Für jede Haltestelle der Fahrt wird dynamisch eine Spalte erzeugt. Die Spalten verwenden DefaultColumnTemplate und zeigen in jeder Zelle ein HstCell-Objekt.

Aufbau einer Haltestellen-Zelle (HstCell)

ElementPropertyPositionDarstellung
Abfahrtszeit ZeitAbfahrt Rechts, vertikal zentriert Format HH:mm. Wenn PrognoseFortlaufend aktiv: zeigt IstAbfahrtPrognose, sonst Abfahrtszeit.
Prognose-Statuslinie IstAbfahrtPrognoseStatus Rechter Rand, vertikale Linie Farbige Linie (2px breit, 22px hoch):
prognose = Blau
real = Grün
geschaetzt = Orange
unbekannt = Rot
Besetztgrad-Quadrat Besetztgrad Rechts oben (5×5px Rechteck) Farbiges Quadrat:
schwach besetzt
normal besetzt
stark besetzt
unbekannt
Einsteigeverbot Einsteigeverbot Links oben Buchstabe E (rot, 10pt) wenn true
Aussteigeverbot Aussteigeverbot Links unten Buchstabe A (rot, 10pt) wenn true
Durchfahrt Durchfahrt Links mitte Buchstabe D (rot, 10pt) wenn true
Hinweis: Die Spalten werden über ExpandoObject-Instanzen dynamisch erzeugt. Jede Zeile ist ein ExpandoObject, dessen Keys die Haltestellen-IDs sind und dessen Values jeweils ein HstCell-Objekt. Der HstColumnTemlateSelector wählt das passende Template je nach FieldName.

Template-Logik: MainWindow.xaml (Zeilen 169–300) | Datenaufbau: FahrtModel.FromOutputFahrtModel()

6. Fahrt-Detail Tab (Mehrfach-Telegramme)

Der Tab „Fahrt-Detail“ erscheint nur, wenn eine Fahrt mehrere Telegramm-Nummern (Komplettfahrten) besitzt (HasMultipleFahrten = true). Er zeigt jede Telegramm-Nummer als separates Grid untereinander.

Fahrt-Detail Tab mit mehreren Telegramm-Blöcken

Abb. 2: Fahrt-Detail Tab — Mehrere Telegramm-Blöcke mit Checkbox-Steuerung (oben) und Detail-Grids (unten)

Sichtbarkeitssteuerung

Das Panel ist an HasMultipleFahrten gebunden:

Visibility="{Binding HasMultipleFahrten, Converter={BooleanToVisibilityConverter}}"

HasMultipleFahrten wird im ViewModel auf true gesetzt, wenn für den gewählten FahrtBezeichner mehr als eine Fahrt (Telegramm) existiert.

Layout-Aufbau

┌────────────────────────────────────────────────────────────┐
│ [☑] 1  [☑] 2  [ ] 3  [ ] 4  [☑] 5    S11 * de:kvv:00S11 * 56010-5048 │
│                                          (LinienText * LinienID * Bezeichner)  │
├────────────────────────────────────────────────────────────┤
│ GroupBox "1"                                                             │
│   GridControl (HstColumnTemplateSelector, MaxHeight=600)                │
│   → ColumnsSource: FahrtenItem.Fahrten[0].HstColumns                   │
│   → ItemsSource:   FahrtenItem.Fahrten[0].FahrtAbrufenItems             │
├────────────────────────────────────────────────────────────┤
│ GroupBox "2"                                                             │
│   GridControl (gleicher Aufbau)                                          │
├────────────────────────────────────────────────────────────┤
│ GroupBox "5" (3, 4 ausgeblendet via IsVisible)                           │
│   GridControl (gleicher Aufbau)                                          │
└────────────────────────────────────────────────────────────┘

Komponenten-Struktur

ElementTyp / BindingBeschreibung
Telegramm-Checkboxen FlowLayoutControl
ItemsSource: FahrtenItem.Fahrten
Horizontale Reihe von CheckEdit-Elementen. Content: FahrtNr, IsChecked: IsVisible. Steuert die Sichtbarkeit der einzelnen GroupBoxen.
Fahrt-Info (blau) TextBlock (Foreground: Blue, Bold) Zeigt LinienText * LinienID * FahrtBezeichner der aktuell gewählten Fahrt.
Telegramm-Blöcke FlowLayoutControl (Vertical)
ItemsSource: FahrtenItem.Fahrten
Je FahrtModel eine GroupBox mit Header = FahrtNr. Visibility gebunden an IsVisible.
Detail-Grids GridControl
MaxHeight: 600
Identischer Aufbau wie das Fahrt-Detail-Grid im Fahrten-Tab. Nutzt denselben HstColumnTemplateSelector mit dynamischen Haltestellen-Spalten.

Datenfluss

Beim Klick auf eine Fahrt im Fahrten-Grid:

  1. GetSelectedFahrtAllesAsync() lädt alle Fahrten mit gleichem FahrtBezeichner
  2. Sortierung nach FahrtNr aufsteigend
  3. Jede Fahrt wird als FahrtModel in FahrtenItem.Fahrten gespeichert
  4. Erste Fahrt erhält IsVisible = true, restliche ebenfalls (alle Checkboxen initial aktiviert)
  5. HasMultipleFahrten wird true wenn Fahrten.Count > 1 → Tab wird sichtbar
  6. SingleFahrt wird auf die Fahrt mit der selektierten FahrtNr gesetzt (für das Detail-Grid im Fahrten-Tab)
Unterschied zum Fahrten-Tab: Im Fahrten-Tab zeigt das Detail-Grid immer nur eine Telegramm-Nummer (FahrtenItem.SingleFahrt). Der Fahrt-Detail-Tab zeigt alle Telegramme einer Fahrt als separate, ein-/ausblendbare Blöcke. Die Grid-Konfiguration (Templates, Spalten, Formatierung) ist identisch.

Quelle: MainWindow.xaml (Zeilen 870–960) | ViewModel: MainWindowViewModel.GetSelectedFahrtAllesAsync()

7. Formation-Ansicht (Tab „Formation“)

Zeigt Fahrten aufgeschlüsselt nach Fahrzeugen in der Zugformation. Die Ansicht hat zwei umschaltbare Detail-Modi: Belegungsgrad und Barrierefreiheit.

Formation-Ansicht im Barrierefreiheit-Modus

Abb. 3: Formation-Ansicht — Modus Barrierefreiheit (Checkbox „Belegungs-Grad“ deaktiviert)

Formation-Ansicht im Belegungsgrad-Modus

Abb. 4: Formation-Ansicht — Modus Belegungsgrad (Checkbox „Belegungs-Grad“ aktiviert)

Master-Grid (oben) — Modell: FahrtModelMitFahrzeug

Jede Zeile = eine Kombination aus Fahrt + Fahrzeug. Erbt von FahrtModel und ergänzt fahrzeugspezifische Felder.

Spalte (Header)FieldNameTypFixedBeschreibung
StartzeitStartzeitDateTime?LeftFahrtbeginn (Format: g)
EndzeitEndzeitDateTime?LeftFahrtende (Format: g)
Fahrt BezeichnerFahrtBezeichnerstringLeftEindeutige Fahrt-Kennung
Telegramm NrFahrtNrintHeader weicht ab: „Telegramm Nr“ statt „Fahrt Nr“
Richtungs IDRichtungsIDstringFahrtrichtung
Umlauf IDUmlaufIDstringUmlauf-Zuordnung
BetreiberBetreiberstringVerkehrsunternehmen
Fahrzeug IdFahrzeugIdstringEindeutige Fahrzeug-Kennung (z.B. V661-1556)
Fahrzeug TypFahrzeugTypstringFahrzeugtyp-Bezeichnung (z.B. 111, 114)
SitzplätzeSitzplätzestringAnzahl Sitzplätze (z.B. 42, 49)
StehplätzeStehplätzestringAnzahl Stehplätze (z.B. 70, 99)
Gruppen-IDGruppenIdstringFahrzeuggruppen-Bezeichner (Zugverband)
Gruppen-ZielGruppenZielstringZielhalt der Fahrzeuggruppe
Gruppen-StartGruppenStartstringStarthalt der Fahrzeuggruppe
(versteckt)FahrtIdstringInternes Mapping-Feld ({FahrtBezeichner}_{FahrtNr}_{Startzeit})

Optionale Spalten (nur bei ShowGlobalId = true): LinienID, StartHaltID, EndHaltID

Detail-Grid — Zwei Modi via ShowBelegungsGrad

Der Detail-Bereich hat einen umschaltbaren Header: „Belegungsgrad“ oder „Barrierefreiheit“, gesteuert durch die Checkbox „Belegungs-Grad“.

Band-Struktur (ColumnBands)

Das Detail-Grid nutzt GridControlBand zur Gruppierung der Spalten:

Band "Info" (Fixed=Left)
└── Zeitstempel (HH:mm:ss:ffff)
└── Fzg Position (int)

Band "{StartHst} - {EndHst}"          ← Je Fahrtabschnitt ein Band
└── Haltestelle 1 (FormationHstCell)      (z.B. "Durlach Turm")
└── Haltestelle 2 (FormationHstCell)      (z.B. "Stupferich Ri")
└── ...
└── Haltestelle N_E (FormationHstCell)     (Endhalte: FieldName += "_E")

Zellenaufbau: FormationHstCell — Modus „Belegungsgrad“ (ShowBelegungsGrad = true)

ElementPropertyPositionDarstellung
Fahrzeug-IDFahrzeugIdLinksText (z.B. „V661-1556“). Rot wenn FzgChanged = true
Belegung (%)BelegungRechts obenZahl in Blau, fett, 10pt. Nur sichtbar wenn HasFzg = true

Zellenaufbau: FormationHstCell — Modus „Barrierefreiheit“ (ShowBelegungsGrad = false)

ElementPropertyPositionDarstellung
Fahrzeug-IDFahrzeugIdLinksText. Rot wenn FzgChanged = true
Barrierefreiheit „B“ Barrierefreiheit Rechts oben (10pt) B = barrierefrei (true)
B = nicht barrierefrei (false, fett)
B = unbekannt (null)
Ausgeblendet wenn HasFzg = false
Ankunft=Abfahrt „A“ AnkunftGleichAbfahrt Rechts unten (10pt) A = Ankunft gleich Abfahrt (true)
A = Ankunft ungleich Abfahrt (false, fett)
Ausgeblendet wenn HasFzg = false

Gemeinsame Eigenschaften beider Modi

ElementPropertyDarstellung
Zeilen-AlternierungAlternatedHintergrund #22C3B091 (leichtes Beige) bei jeder zweiten Datenabruf-Gruppe
Zeitstempel-SpalteHintergrund Khaki, fett (FormatCondition)
Fzg PositionFzgPositionInteger (1-basiert). Gibt die Position des Fahrzeugs innerhalb der Formation an.

Beispieldaten im Belegungsgrad-Modus

Zeitstempel      Fzg Pos  Karlsruhe Haup  Söllingen (b.K.  Berghausen Am  Grötzingen Kru  ...
02:59:59:8600      1      V86084_4562     V86084_4562 [1]  V86084_4562 [0] V86084_4562 [1]  ...
03:04:29:4510      1      V86084_4562     V86084_4562 [1]  V86084_4562 [0] V86084_4562 [1]  ...
                                           ^                ^               ^
                                           Belegung %       Belegung %      Belegung %
                                           (blau, fett)     (blau, fett)    (blau, fett)

Beispieldaten im Barrierefreiheit-Modus

Zeitstempel      Fzg Pos  Durlach Turm     Stupferich Ri    Stupferich Ki   ...
12:27:58:3250      1      V661-1556 [B,A]  V661-1556 [B,A]  V661-1556 [B,A] ...
                                    ^  ^
                                    |  Ankunft=Abfahrt (grün/rot)
                                    Barrierefreiheit (grün/rot/blau)
Bekanntes Problem: Umlaute in Haltestellen-Namen (z.B. „Söllingen“, „Grötzingen“) werden im Grid teilweise als „S◊llingen“ angezeigt. Dies deutet auf ein Encoding-Problem bei der XML-Verarbeitung hin (Quell-Encoding vs. Anzeige-Encoding).
Datenaufbau-Logik (FahrtModelFormation.FromOutputFahrtModel):
Pro Datenabruf (FahrtRow) werden N Zeilen erzeugt (N = Anzahl Fahrzeuge in der Formation). Jede Zeile wird als ExpandoObject mit Zeitstempel, FzgPosition und dynamischen Haltestellen-Keys (jeweils FormationHstCell) aufgebaut. Bei der Endhalte eines Abschnitts wird "_E" an den FieldName angehängt, um Duplikate zu vermeiden. Die FzgChanged-Erkennung vergleicht die FahrzeugId mit der vorherigen Haltestelle.

Quellen: FahrtModelFormation.cs | FahrtModelMitFahrzeug.cs | MainWindow.xaml (Zeilen 29–145, 733–860)

8. Datenmodell-Übersicht

ImportInfoModel              ← Betriebstag-Übersicht (JSON: _vdv454_import_*.json)
│
├── OutputFahrtModel            ← Eine Fahrt mit allen Datenabrufen
│   ├── IstFahrtRowModel[]       ← Einzelne Datenabruf-Zeilen (FahrtRows)
│   │   ├── IstHaltRowModel[]    ← Haltestellen pro Abruf (IstHalts)
│   │   └── IstFormationXmlModel ← Formations-Daten (Fahrzeuge, Gruppen, Haltestellen)
│   └── OutputFzgModel[]         ← Fahrzeug-Kopfdaten (FormationModels)
│
└── UI-Modelle (VBK_VDV454_Import_UI/Models/)
    ├── FahrtModel               ← Flaches Modell für Fahrten-Grid
    │   ├── HstColumn             ← Dynamische Spalten-Definition
    │   └── HstCell               ← Zellen-Daten (Abfahrt, Prognose, Besetztgrad, ...)
    └── FahrtModelFormation      ← Formation-spezifisches Modell
        ├── FormationHstColumn    ← Spalte mit Abschnitt-Zuordnung
        └── FormationHstCell      ← Zelle mit Fzg-ID, Belegung, Barrierefreiheit

9. Vollständige Symbol- & Farbcodierungs-Referenz

Prognose-Statuslinie (rechter Zellrand, Fahrt-Detail)

Wert (IstAbfahrtPrognoseStatus)FarbeBedeutung
"prognose"BlauBerechnete Prognose
"real"GrünEchtzeit-Messwert
"geschaetzt"OrangeGeschätzter Wert
"unbekannt"RotStatus unbekannt

Besetztgrad-Indikator (rechts oben in Zelle, 5×5px)

Wert (Besetztgrad)FarbeBedeutung
"schwach besetzt"GrünWenig Fahrgäste
"normal besetzt"OrangeNormale Auslastung
"stark besetzt"RotHohe Auslastung
"unbekannt"GrauKeine Daten

Buchstaben-Indikatoren (Fahrt-Detail)

BuchstabePositionFarbePropertyBedeutung
ELinks obenRotEinsteigeverbotAn dieser Haltestelle darf nicht eingestiegen werden
ALinks untenRotAussteigeverbotAn dieser Haltestelle darf nicht ausgestiegen werden
DLinks mitteRotDurchfahrtHaltestelle wird ohne Halt durchfahren

Checkbox-Spalten (Fahrt-Detail, fixiert)

SpalteTypBeschreibung
K. (Komplettfahrt)bool (2-stufig)Datensatz enthält vollständige Fahrt-Information
P. (Prognose fortlaufend)bool (2-stufig)Fortlaufender Status: bleibt aktiv bis Prognose nicht mehr möglich
P. (Prognose möglich)bool? (3-stufig)Aktueller Status des Datensatzes: true, false oder unbestimmt (null)

Formations-spezifische Farben (Detail-Grid)

ElementBedingung / ModusDarstellung
Fahrzeug-ID TextFzgChanged = trueRot (Fahrzeug hat Position gewechselt)
Modus: Belegungsgrad (ShowBelegungsGrad = true)
Belegung (%)Zahl rechts oben in ZelleBlau, fett, 10pt (z.B. 0, 1, 2, 4)
Modus: Barrierefreiheit (ShowBelegungsGrad = false)
Barrierefreiheit „B“Barrierefreiheit = trueGrün (rechts oben)
Barrierefreiheit „B“Barrierefreiheit = falseRot, fett (rechts oben)
Barrierefreiheit „B“Barrierefreiheit = nullBlau (rechts oben)
Ankunft=Abfahrt „A“AnkunftGleichAbfahrt = trueGrün (rechts unten)
Ankunft=Abfahrt „A“AnkunftGleichAbfahrt = falseRot, fett (rechts unten)
Beide Modi
Zeilen-HintergrundAlternated = trueLeichtes Beige (#22C3B091)

Allgemeine UI-Styles

ElementDarstellung
Fokussierte Zeile (Master-Grids)Fett + Hintergrund #eeeeee
Fokussierte Zeile (Detail-Grids)Hintergrund #eeeeee (ohne Fett)
Zeitstempel-SpalteHintergrund Khaki + Fett