147 lines
7.1 KiB
Markdown
147 lines
7.1 KiB
Markdown
# Klassenhierarchie
|
|
|
|
Bestandsaufnahme aller Python-Klassen in `lib/` (Stand 2026-10-09). Kurzes
|
|
Ergebnis vorweg: es gibt praktisch **keine Vererbung** in diesem Code (eine
|
|
Ausnahme: `DerivedTro(TroBase)`, plus die Standard-Exception
|
|
`RegistrationError(RuntimeError)`). Alles andere sind flache, eigenständige
|
|
Klassen — entweder `@dataclass` (veränderlicher Laufzeit-/Analysezustand) oder
|
|
Pydantic `BaseModel` (validierte Daten, die auch als JSON ein-/ausgehen). Eine
|
|
klassische "ist-ein"-Hierarchie zu suchen wäre hier der falsche Blickwinkel;
|
|
interessanter ist, **wer wen enthält/referenziert** und in welchem Modul-Layer
|
|
eine Klasse sitzt (siehe Layer-Kommentar oben in `model.py`).
|
|
|
|
## Modul-Layer (wer darf wen importieren)
|
|
|
|
```
|
|
tro_catalog.py (Typ-Ebene: was ein TRO-Typ ist)
|
|
|
|
|
v
|
|
model.py (Instanz-Ebene: ein konkreter TRO, die TRO-JSON)
|
|
|
|
|
v
|
|
tro_flow.py (Ableitung: CSV-Graph -> Analysis -> TroDocument)
|
|
|
|
|
+----------------+
|
|
v v
|
|
scl_skeleton.py tro_annotate.py (Ausgabe: SCL-Skeleton / annotierte Zeichnung)
|
|
|
|
material_flow.py dxf_registration.py tro_overrides.py (eigenständig, ohne
|
|
Abhängigkeit auf obige)
|
|
```
|
|
|
|
`tro_flow.py`, `scl_skeleton.py`, `tro_annotate.py`, `material_flow.py` selbst
|
|
definieren keine eigenen Klassen mehr (nur Funktionen) — `material_flow.py`
|
|
ist die Ausnahme: seine Graph-Dataclasses sind CSV-Rohmodell, nicht TRO-Modell.
|
|
|
|
## Vererbung (ASCII)
|
|
|
|
```
|
|
RuntimeError (stdlib) BaseModel (pydantic, stdlib: pydantic)
|
|
| |
|
|
+-- RegistrationError +-- TroBase
|
|
(dxf_registration.py) |
|
|
+-- DerivedTro
|
|
(model.py)
|
|
```
|
|
|
|
Das ist die gesamte `class X(Y)`-Liste im Projekt — alle anderen Klassen unten
|
|
haben `object`/`BaseModel` direkt als einzige Basis und erscheinen hier nur
|
|
noch als Gruppen nach Datei.
|
|
|
|
## Klassen nach Datei, mit Komposition ("enthält")
|
|
|
|
```
|
|
lib/tro_catalog.py [Typ-Katalog, keine Instanzdaten]
|
|
├── TroItem (Konstanten-Klasse: Bauteilarten wie SEPARATOR, SWITCH)
|
|
├── TroSymbol (Konstanten-Klasse: Zeichensymbole wie CIRCLE, TRIANGLE)
|
|
├── TroStyle(BaseModel) Farbe/Darstellung einer Typgruppe
|
|
└── TroDefinition(BaseModel)
|
|
└─ style: TroStyle (1 TroDefinition hat genau 1 TroStyle)
|
|
|
|
lib/model.py [Instanz-Ebene / TRO-JSON]
|
|
├── TroBase(BaseModel)
|
|
│ └─ definition() --> TroDefinition (gelesen aus tro_catalog, nicht gespeichert)
|
|
│ └── DerivedTro(TroBase) [einzige echte Vererbung im Projekt]
|
|
├── Connection(BaseModel) (from/to/kind zwischen zwei TRO-IDs)
|
|
├── OpenPoint(BaseModel) (gemeldete, noch nicht modellierte Stelle)
|
|
├── Analysis(BaseModel)
|
|
│ ├─ tros: list[DerivedTro]
|
|
│ ├─ isolated: list[DerivedTro]
|
|
│ └─ boundaries: list[DerivedTro]
|
|
└── TroDocument(BaseModel) (= <csv>_tro.json)
|
|
├─ tros: list[DerivedTro]
|
|
├─ boundaries: list[DerivedTro]
|
|
├─ connections: list[Connection]
|
|
└─ open_points: list[OpenPoint]
|
|
|
|
lib/material_flow.py [CSV-Rohmodell -> Graph, @dataclass]
|
|
├── Element (1 CSV-Zeile)
|
|
├── Node
|
|
│ └─ element: Element
|
|
├── Edge
|
|
├── Graph
|
|
│ ├─ nodes: dict[str, Node]
|
|
│ ├─ edges: list[Edge]
|
|
│ └─ unassigned: list[Element]
|
|
├── TrackNode
|
|
│ └─ element: Element | None
|
|
├── TrackEdge
|
|
└── TrackGraph
|
|
├─ nodes: dict[str, TrackNode]
|
|
└─ edges: list[TrackEdge]
|
|
|
|
lib/tro_overrides.py [manuelle Korrekturen aus cfg/tro_overrides.ini, @dataclass]
|
|
├── TroOverride (Typkorrektur an einem abgeleiteten TRO)
|
|
├── SplitSpec (TRO aus einem bestehenden heraustrennen)
|
|
├── ConnectionSpec (bestätigte Verbindung zwischen zwei TROs)
|
|
├── MergeSpec (mehrere TROs zu einem zusammenfassen)
|
|
├── OpenPoint (eigene Kopie, NICHT dieselbe Klasse wie model.OpenPoint!)
|
|
└── Overrides (Sammelcontainer für eine CSV-Datei)
|
|
├─ tros: dict[str, TroOverride]
|
|
├─ splits: list[SplitSpec]
|
|
├─ merges: list[MergeSpec]
|
|
├─ connections: list[ConnectionSpec]
|
|
└─ open_points: list[OpenPoint] (tro_overrides.OpenPoint, s.o.)
|
|
|
|
lib/dxf_registration.py [CSV<->DXF Koordinatentransform]
|
|
├── RegistrationError(RuntimeError)
|
|
└── Transform(BaseModel)
|
|
```
|
|
|
|
## Auffälligkeiten für ein mögliches Refactoring
|
|
|
|
1. **`OpenPoint` existiert zweimal** mit fast identischen Feldern
|
|
(`name, x, y, z, expect, belongs_to/belongsTo, note` +
|
|
`describe_position()`): einmal in `model.py` (Pydantic, JSON-Alias
|
|
`belongsTo`), einmal in `tro_overrides.py` (Dataclass, kein Alias). Das ist
|
|
duplizierte Definition, kein Vererbungsfall — `tro_overrides.py` baut seine
|
|
`OpenPoint`-Liste und wandelt sie vermutlich später in `model.OpenPoint` um
|
|
(oder sollte das tun; laut Roadmap im Projekt-`CLAUDE.md` ist "`tro_overrides.py`
|
|
auf die gemeinsamen `model.Connection`/`model.OpenPoint`-Klassen umstellen"
|
|
ohnehin schon als offener Punkt vermerkt). Kandidat: `tro_overrides.py`
|
|
importiert künftig `model.OpenPoint` statt eine eigene Kopie zu pflegen.
|
|
2. **Zwei parallele Graph-Hierarchien** in `material_flow.py`
|
|
(`Node/Edge/Graph` vs. `TrackNode/TrackEdge/TrackGraph`) teilen sich keine
|
|
Basisklasse, obwohl Struktur und Feldnamen sich stark ähneln (`node_id,
|
|
kind, ...` / `src, dst, ...`). Eine gemeinsame leichte Basis (z. B. ein
|
|
generisches `GraphBase[NodeT, EdgeT]`) wäre möglich, aber angesichts der
|
|
wenigen Stellen, die beide Typen anfassen, vermutlich mehr Abstraktion als
|
|
Nutzen.
|
|
3. **`TroItem` und `TroSymbol` sind reine Konstanten-Namespaces** (keine
|
|
Instanzen, keine Methoden außer `TroItem.all()`), kein Pydantic-Modell wie
|
|
der Rest von `tro_catalog.py`. Das ist bewusst so (siehe Docstring: "damit
|
|
Tippfehler auffallen") und funktioniert wie ein `Enum`-Ersatz — ein `StrEnum`
|
|
wäre eine nahe liegende, idiomatischere Alternative, falls ein Refactoring
|
|
ansteht.
|
|
4. Die einzige echte Vererbung (`DerivedTro(TroBase)`) ist sauber: `TroBase`
|
|
ist bereits so geschnitten, dass es für Systemgrenzen (`boundaries`)
|
|
wiederverwendet wird, `DerivedTro` ergänzt nur das, was die CSV-Ableitung
|
|
zusätzlich weiß. Kein Handlungsbedarf.
|
|
|
|
**Fazit:** Die Code-Basis nutzt Vererbung bewusst kaum — die Architektur trennt
|
|
stattdessen über Module (Typ-Katalog vs. Instanz vs. Ableitung vs. Ausgabe,
|
|
siehe `model.py`-Kopfkommentar) und über Komposition. Ein Refactoring in
|
|
Richtung "mehr Vererbung" wäre hier eher kontraproduktiv; die einzig konkret
|
|
lohnenden Punkte sind die doppelte `OpenPoint`-Definition (1) und optional
|
|
`StrEnum` statt Konstanten-Klassen (3).
|