Doku: README auf Hauptweg CSV -> TRO-JSON -> SCL, Klassenhierarchie ausgebaut
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
29077caa81
commit
5764df1ac8
@@ -9,61 +9,110 @@
|
|||||||
|
|
||||||
## Stand des Projekts
|
## Stand des Projekts
|
||||||
|
|
||||||
Aus einem CSV-Export der mechanischen Planung (ILS 2.0) werden automatisch
|
Die Kette **CSV → TRO-JSON → SCL** läuft in einer ersten Version durch. Aus
|
||||||
|
einem CSV-Export der mechanischen Planung (ILS 2.0) entstehen automatisch:
|
||||||
|
|
||||||
- der **Materialfluss-Graph** der mechanischen Objekte (Strecken, Gefällestrecken,
|
- der **Materialfluss-Graph** der mechanischen Objekte (Strecken,
|
||||||
Kreisel),
|
Gefällestrecken, Kreisel),
|
||||||
- die daraus abgeleitete **TRO-Liste** samt eigenem Flussdiagramm,
|
- die daraus abgeleitete **TRO-Liste** samt eigenem Flussdiagramm,
|
||||||
- eine **annotierte Kopie der BricsCAD-Zeichnung** mit einem Marker-Symbol pro
|
- die **TRO-JSON** `<name>_tro.json` (Schema `sps_skel-tro-v3`, Modell
|
||||||
TRO (danach in BricsCAD von Hand nachbearbeitbar — TROs verschieben,
|
`lib/model.py`) — die Schnittstelle zwischen Ableitung und SCL-Erzeugung,
|
||||||
ergänzen, Typ ändern),
|
|
||||||
- daraus zurückgelesen ein **JSON-Modell der TROs** (liest nur die Zeichnung,
|
|
||||||
erfasst also auch von Hand vorgenommene Korrekturen), und
|
|
||||||
- daraus ein **SCL-Gerüst für `FB_Main`** — ein `REGION` pro TRO mit
|
- daraus ein **SCL-Gerüst für `FB_Main`** — ein `REGION` pro TRO mit
|
||||||
Instanzaufruf und allen Parametern in der richtigen Reihenfolge, aber
|
Instanzaufruf und allen Parametern in der richtigen Reihenfolge, aber
|
||||||
bewusst nicht lauffähig: jeder Wert, der aus der E-Planung stammen muss
|
bewusst nicht lauffähig: jeder Wert, der aus der E-Planung stammen muss
|
||||||
(Sensor-/Aktoradressen, Separator-/Weichennummern, Staubereiche, ungeprüfte
|
(Sensor-/Aktoradressen, Separator-/Weichennummern, Staubereiche, ungeprüfte
|
||||||
Zeiten), ist als `TODO(E-Planung)` markiert statt geraten.
|
Zeiten), ist als `TODO(E-Planung)` markiert statt geraten. Optional
|
||||||
|
(`--templates`) kommen die REGIONs aus Jinja2-Templates
|
||||||
|
(`lib/scl_templates/<fbType>.scl.j2`, Pilot: `FB_ILS_MTRO_1Sep1Swi`),
|
||||||
|
- zur Sichtkontrolle eine **annotierte Kopie der BricsCAD-Zeichnung** mit einem
|
||||||
|
Marker-Symbol pro TRO. Die Zeichnung ist nur Ansicht, keine Datenquelle für
|
||||||
|
die SCL.
|
||||||
|
|
||||||
Diese fünf Werkzeuge liegen als CLI-Tools in `lib/` und sind über
|
Details stehen in `doc/`, allen voran
|
||||||
`bin/`-Wrapper aufrufbar (siehe unten). Details, Domänenmodell und die
|
[`doc/Python_Scripts.md`](doc/Python_Scripts.md) (alle Tools und Schalter) und
|
||||||
verbleibende Roadmap stehen in `doc/`, allen voran `doc/Python_Scripts.md`.
|
[`doc/Klassenhierarchie.md`](doc/Klassenhierarchie.md) (Klassen und wozu sie da
|
||||||
|
sind).
|
||||||
|
|
||||||
## Roadmap
|
## Ablauf
|
||||||
|
|
||||||
Offen ist vor allem die JSON-Layout-Datei nach dem in
|
```text
|
||||||
`doc/HundM/Json_Layout-Konzept.md` beschriebenen Schema (`plc`,
|
data/<name>.csv (ILS-2.0-Export)
|
||||||
`controlUnits`, `sensors[]`, `conveyors[]`, `tros[]`, `loadingBooms[]`,
|
│
|
||||||
`emptyCarrBuffers[]`, `routing`, `jamAreas[]`, `scanners[]`, `connections[]`,
|
▼
|
||||||
`destinations[]`) als von Hand editierbares Zwischenformat vor der
|
material_flow ──► <name>_material_flow.dot/.svg/.md Materialfluss der Mechanik
|
||||||
SCL-Erzeugung, sowie die Generierung der übrigen Bausteine je Steuerung
|
│
|
||||||
(`FB_CallSensors`, `FC_Direction`, `FC_Call_Jams`) — `scl_skeleton.py` deckt
|
▼
|
||||||
bisher nur `FB_Main` ab. Siehe `CLAUDE.md` (Abschnitt "Roadmap") für den
|
tro_flow --json ──► <name>_tro.json (+ .dot/.svg/.md) TROs ableiten
|
||||||
aktuellen Stand der Planung dazu.
|
│ │
|
||||||
|
│ ▼
|
||||||
|
│ scl_skeleton ──► <name>_FB_Main.scl SCL-Gerüst
|
||||||
|
│
|
||||||
|
└── gleiche Ableitung ──► tro_annotate ──► <name>_annotated.dxf (nur Ansicht)
|
||||||
|
(braucht zusätzlich die DXF-Zeichnung)
|
||||||
|
```
|
||||||
|
|
||||||
|
Alle Ausgaben landen in `results/` (`%SKEL_RESULTS%`).
|
||||||
|
|
||||||
|
## Welche Skripte gibt es und wofür?
|
||||||
|
|
||||||
|
### Umgebung (`bin/`, je `.bat` für Windows und `.sh` für Linux/macOS)
|
||||||
|
|
||||||
|
Diese vier Skripte verwalten nur Umgebung und venv, sie rufen kein
|
||||||
|
Python-Programm auf.
|
||||||
|
|
||||||
|
| Skript | Wofür |
|
||||||
|
| --- | --- |
|
||||||
|
| `setenv` | Setzt `SPS_SKEL` und die `SKEL_*`-Variablen, hängt `SKEL_LIB` an `PYTHONPATH`, legt fehlende Ordner an und gibt alles zur Kontrolle aus. `.sh` muss gesourct werden. |
|
||||||
|
| `install_py` | Einmalig: legt `.venv` an und installiert `requirements.txt`. Bricht ab, wenn `.venv` schon existiert. |
|
||||||
|
| `activate_venv` | Je Sitzung: aktiviert `.venv` und zeigt Python-Version und Pakete. `.sh` muss gesourct werden. |
|
||||||
|
| `get_cmd` | Öffnet eine neue Shell mit gesetzten Umgebungsvariablen. |
|
||||||
|
|
||||||
|
### CLI-Tools (`lib/<name>.py`, aufgerufen über `bin/<name>.bat`/`.sh`)
|
||||||
|
|
||||||
|
Jeder Wrapper ruft `setenv`, aktiviert `.venv` (falls vorhanden) und startet
|
||||||
|
das Modul mit allen übergebenen Argumenten.
|
||||||
|
|
||||||
|
| Tool | Wofür | Wichtige Schalter |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `material_flow` | Liest den CSV-Export und baut den gerichteten **Materialfluss-Graphen** der mechanischen Objekte (Strecke, Gefällestrecke, Kreisel); Separatoren/Scanner hängen an ihrem Objekt. Grundlage für `tro_flow`. | `--file`, `--tosvg`, `--use-cords`, `--doc`, `--connect`, `--track`, `--show-bbs`, `--chain-graph` |
|
||||||
|
| `tro_flow` | Leitet aus demselben Graphen die **TROs** ab (Typ aus dem Trägerobjekt des Separators, Weichen aus den Abgängen), verknüpft sie und zeichnet ihren Fluss. Mit `--json` entsteht die TRO-JSON — der Hauptweg zur SCL. | `--file`, `--json`, `--tosvg`, `--use-cords`, `--doc`, `--connect` |
|
||||||
|
| `scl_skeleton` | Liest die TRO-JSON (geprüft gegen `lib/model.py`) und schreibt das **`FB_Main`-Gerüst** als SCL. Mit `--templates` aus den Jinja2-Templates, sonst generisch. | `--json`, `--out`, `--start`, `--templates [DIR]`, `--strict` |
|
||||||
|
| `tro_annotate` | Zeichnet dieselben TROs als Marker (Farbe/Form je Typ, Attribute `ID`/`TYPE`/`FB_BLOCK`) in eine **Kopie** der BricsCAD-Zeichnung — zur Sichtkontrolle. Bestimmt dafür den CSV↔DXF-Lagebezug und legt ihn in `cfg/dxf_registration.json` ab. Erzeugt mit `--emit-lisp` die Typliste `cad/tro_types.lsp` für den `TROEDIT`-Dialog. | `--file`, `--dxf`, `--flow`, `--fb`, `--legend`, `--offset`, `--re-register`, `--check`, `--emit-lisp` |
|
||||||
|
| `tro_extract` | **Geparkt.** Liest TROs aus der annotierten Zeichnung zurück in das alte JSON-Format `sps_skel-tro-extract-v1`, das `scl_skeleton` nicht mehr annimmt. Nicht gelöscht, nicht weiterentwickelt. | `--dxf` |
|
||||||
|
|
||||||
|
### Bibliotheken (`lib/`, ohne eigenen Aufruf)
|
||||||
|
|
||||||
|
| Modul | Wofür |
|
||||||
|
| --- | --- |
|
||||||
|
| `tro_catalog.py` | Katalog der TRO-**Typen**: Kurzname, Siemens-FB, Bauteile, Farbgruppe, CAD-Symbol. Einzige Quelle für FB-Typ und Bauteile. |
|
||||||
|
| `model.py` | Gemeinsames Modell der TRO-**Instanzen** und der TRO-JSON (`TroBase`, `DerivedTro`, `Connection`, `TroCandidate`, `Analysis`, `TroDocument`). |
|
||||||
|
| `scl_context.py` | Wandelt die TRO-JSON in den Render-Kontext für die SCL (Reihenfolge, Bauteilblöcke, Staubereiche, Weichenziele, offene Namen). |
|
||||||
|
| `scl_templates/` | Jinja2-Templates je FB-Typ (`<fbType>.scl.j2`) plus gemeinsame Makros; nur mit `scl_skeleton --templates`. |
|
||||||
|
| `dxf_registration.py` | Ermittelt, prüft und speichert die Transformation zwischen CSV- und DXF-Koordinaten. |
|
||||||
|
| `tro_overrides.py` | Liest die Handkorrekturen aus `cfg/tro_overrides.ini`; heute nur von `material_flow --connect` genutzt. |
|
||||||
|
|
||||||
|
### Sonstige Skripte
|
||||||
|
|
||||||
|
| Skript | Wofür |
|
||||||
|
| --- | --- |
|
||||||
|
| `doc/TRO_Katalog/tro_graphs/gen_tro_graphs.py` | Doku-Werkzeug, nicht Teil der Pipeline: erzeugt die Typ-Schaubilder und Topologie-Graphen für `doc/TRO_Katalog/TRO_Katalog.md` aus `connect.ini`. Aufruf: `py doc/TRO_Katalog/tro_graphs/gen_tro_graphs.py` |
|
||||||
|
| `cad/tro_types.lsp` | Generiertes BricsCAD-LISP (Typliste für den `TROEDIT`-Dialog), erzeugt von `tro_annotate --emit-lisp`. |
|
||||||
|
| `tests/` | pytest-Tests: TRO-Modell, Render-Kontext, Golden-Files der SCL-Ausgabe (`tests/fixtures/scl/`), Templates gegen die Referenz-Aufrufstellen. |
|
||||||
|
|
||||||
## Projektstruktur
|
## Projektstruktur
|
||||||
|
|
||||||
```text
|
```text
|
||||||
sps_skel/
|
sps_skel/
|
||||||
bin/ Umgebungsskripte + ein .bat/.sh-Wrapper-Paar je CLI-Tool
|
bin/ Umgebungsskripte + ein .bat/.sh-Wrapper-Paar je CLI-Tool
|
||||||
setenv.bat/.sh Umgebungsvariablen setzen
|
cad/ generiertes BricsCAD-LISP (tro_types.lsp) für den TROEDIT-Dialog
|
||||||
install_py.bat/.sh venv erstellen + pip install
|
cfg/ Konfiguration: dxf_registration.json, tro_overrides.ini
|
||||||
activate_venv.bat/.sh venv aktivieren
|
data/ Eingabedaten - CSV-Exporte, DXF (nicht im Git)
|
||||||
get_cmd.bat/.sh Shell mit Umgebung oeffnen
|
doc/ Dokumentation, siehe doc/Python_Scripts.md
|
||||||
material_flow.bat/.sh Wrapper fuer lib/material_flow.py
|
examples/ Beispieldateien (noch leer)
|
||||||
tro_flow.bat/.sh Wrapper fuer lib/tro_flow.py
|
lib/ Python-Quellcode (CLI-Tools, Bibliotheken, scl_templates/)
|
||||||
tro_annotate.bat/.sh Wrapper fuer lib/tro_annotate.py
|
log/ Log-Dateien (nicht im Git)
|
||||||
tro_extract.bat/.sh Wrapper fuer lib/tro_extract.py
|
results/ Ergebnisse der CLI-Tools (.dot/.svg/.md/.json/.scl/.dxf, nicht im Git)
|
||||||
scl_skeleton.bat/.sh Wrapper fuer lib/scl_skeleton.py
|
tests/ Unit Tests (pytest)
|
||||||
cad/ generiertes BricsCAD-LISP (tro_types.lsp) fuer den TROEDIT-Dialog
|
|
||||||
cfg/ Konfigurationsdateien (INI/JSON); dxf_registration.json
|
|
||||||
data/ Eingabedaten - CSV-Exporte, DXF (nicht im Git)
|
|
||||||
doc/ Dokumentation, siehe doc/Python_Scripts.md
|
|
||||||
examples/ Beispieldateien
|
|
||||||
lib/ Python-Quellcode (CLI-Tools + Bibliotheken)
|
|
||||||
log/ Log-Dateien (nicht im Git)
|
|
||||||
results/ Ergebnisse der CLI-Tools (.dot/.svg/.md/.dxf, nicht im Git)
|
|
||||||
tests/ Unit Tests
|
|
||||||
.gitignore
|
.gitignore
|
||||||
LICENSE
|
LICENSE
|
||||||
README.md
|
README.md
|
||||||
@@ -72,94 +121,104 @@ sps_skel/
|
|||||||
|
|
||||||
## Umgebungsvariablen
|
## Umgebungsvariablen
|
||||||
|
|
||||||
| Variable | Beschreibung |
|
| Variable | Beschreibung |
|
||||||
|----------------|---------------------------|
|
|-----------------|-------------------------|
|
||||||
| `SPS_SKEL` | Wurzelverzeichnis |
|
| `SPS_SKEL` | Wurzelverzeichnis |
|
||||||
| `SKEL_BIN` | Skriptverzeichnis |
|
| `SKEL_BIN` | Skriptverzeichnis |
|
||||||
| `SKEL_LIB` | Python-Quellcode |
|
| `SKEL_LIB` | Python-Quellcode |
|
||||||
| `SKEL_CFG` | Konfigurationsdateien |
|
| `SKEL_CFG` | Konfigurationsdateien |
|
||||||
| `SKEL_DATA` | Eingabedaten |
|
| `SKEL_DATA` | Eingabedaten |
|
||||||
| `SKEL_DOC` | Dokumentation |
|
| `SKEL_DOC` | Dokumentation |
|
||||||
| `SKEL_LOG` | Log-Dateien |
|
| `SKEL_LOG` | Log-Dateien |
|
||||||
| `SKEL_RESULTS` | Ergebnisse |
|
| `SKEL_RESULTS` | Ergebnisse |
|
||||||
| `SKEL_EXAMPLES` | Beispieldateien |
|
| `SKEL_EXAMPLES` | Beispieldateien |
|
||||||
| `SKEL_TESTS` | Unit Tests |
|
| `SKEL_TESTS` | Unit Tests |
|
||||||
| `PYTHONPATH` | Erweitert um `SKEL_LIB` |
|
| `PYTHONPATH` | Erweitert um `SKEL_LIB` |
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Voraussetzungen
|
### Voraussetzungen
|
||||||
|
|
||||||
- Python 3.10 oder hoeher
|
- Python 3.10 oder höher
|
||||||
- Graphviz (`dot`) auf `PATH` fuer `--tosvg`, optional aber empfohlen
|
- Pakete aus `requirements.txt`: `pydantic`, `jinja2` (SCL-Templates), `ezdxf`
|
||||||
- fuer `tro_annotate.py`: `ezdxf` (wird per `requirements.txt` installiert)
|
(nur `tro_annotate`), `pytest`
|
||||||
|
- Graphviz (`dot`, für `--use-cords` bei `tro_flow` auch `neato`) auf `PATH`
|
||||||
|
für `--tosvg` — optional, aber empfohlen
|
||||||
|
|
||||||
### Setup (Windows)
|
### Setup
|
||||||
|
|
||||||
```bat
|
```bat
|
||||||
bin\install_py.bat
|
bin\install_py.bat # Windows
|
||||||
```
|
```
|
||||||
|
|
||||||
### Setup (Linux / macOS)
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bash bin/install_py.sh
|
bash bin/install_py.sh # Linux / macOS
|
||||||
```
|
```
|
||||||
|
|
||||||
## Nutzung
|
## Nutzung
|
||||||
|
|
||||||
### Umgebung setzen
|
### Umgebung
|
||||||
|
|
||||||
```bat
|
```bat
|
||||||
bin\setenv.bat # Windows
|
bin\setenv.bat # Windows: nur Variablen setzen
|
||||||
source bin/setenv.sh # Linux / macOS
|
bin\activate_venv.bat # Windows: venv aktivieren
|
||||||
|
bin\get_cmd.bat # Windows: neue Shell mit Umgebung
|
||||||
```
|
```
|
||||||
|
|
||||||
### Shell mit gesetzten Variablen oeffnen
|
```sh
|
||||||
|
source bin/setenv.sh
|
||||||
```bat
|
source bin/activate_venv.sh
|
||||||
bin\get_cmd.bat # Windows
|
bash bin/get_cmd.sh
|
||||||
source bin/get_cmd.sh # Linux / macOS
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### venv aktivieren
|
### Typischer Durchlauf
|
||||||
|
|
||||||
|
`--file`/`--dxf` nehmen einen Dateinamen (aufgelöst gegen `%SKEL_DATA%`) oder
|
||||||
|
einen vollen Pfad; `--json` sucht in `%SKEL_RESULTS%`.
|
||||||
|
|
||||||
```bat
|
```bat
|
||||||
bin\activate_venv.bat # Windows
|
REM 1) Materialfluss der mechanischen Objekte (optional, zur Kontrolle)
|
||||||
source bin/activate_venv.sh # Linux / macOS
|
|
||||||
```
|
|
||||||
|
|
||||||
### CLI-Tools ausführen
|
|
||||||
|
|
||||||
Jedes Tool wird über seinen `bin/<name>.bat`/`.sh`-Wrapper aufgerufen; dieser
|
|
||||||
setzt die Umgebung, aktiviert `.venv` und ruft dann das Python-Modul auf.
|
|
||||||
`--file`/`--dxf` akzeptieren entweder einen Dateinamen (wird gegen
|
|
||||||
`%SKEL_DATA%` aufgelöst) oder einen vollen Pfad; Ausgaben landen in
|
|
||||||
`%SKEL_RESULTS%`.
|
|
||||||
|
|
||||||
```bat
|
|
||||||
REM 1) Materialfluss der mechanischen Objekte
|
|
||||||
bin\material_flow.bat --file mubea.csv --tosvg --doc
|
bin\material_flow.bat --file mubea.csv --tosvg --doc
|
||||||
|
|
||||||
REM 2) TRO-Liste + eigenes Flussdiagramm ableiten
|
REM 2) TROs ableiten und als TRO-JSON schreiben
|
||||||
bin\tro_flow.bat --file mubea.csv --tosvg --doc
|
bin\tro_flow.bat --file mubea.csv --tosvg --doc --json
|
||||||
|
|
||||||
REM 3) TROs als Marker-Symbole in eine Kopie der CAD-Zeichnung einbrennen
|
REM 3) FB_Main-SCL-Geruest erzeugen (generisch bzw. aus Templates)
|
||||||
|
bin\scl_skeleton.bat --json mubea_tro.json
|
||||||
|
bin\scl_skeleton.bat --json mubea_tro.json --templates
|
||||||
|
|
||||||
|
REM 4) Optional: TROs zur Sichtkontrolle in eine Kopie der Zeichnung
|
||||||
bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend
|
bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend
|
||||||
|
|
||||||
REM (Zeichnung optional in BricsCAD von Hand nachbearbeiten)
|
|
||||||
|
|
||||||
REM 4) TROs aus der (ggf. bearbeiteten) Zeichnung zurueck in JSON lesen
|
|
||||||
bin\tro_extract.bat --dxf export_annotated.dxf
|
|
||||||
|
|
||||||
REM 5) FB_Main-SCL-Geruest aus dem JSON erzeugen
|
|
||||||
bin\scl_skeleton.bat --json export_tro.json --start TRO07
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Alle Switches, Ausgabedateien und Exit-Codes der drei Tools sind in
|
`--start TRO07` bei `scl_skeleton` ist nur nötig, solange die Anlage ein
|
||||||
[`doc/Python_Scripts.md`](doc/Python_Scripts.md) dokumentiert; das
|
geschlossener Kreis ohne Be-/Entladestation ist. Danach im erzeugten
|
||||||
Domänenmodell (TRO-Typen, geplantes JSON-Layout, I/O-Listen-Analyse) findet
|
`*_FB_Main.scl` nach `TODO(E-Planung)` suchen — das sind die Stellen, die aus
|
||||||
sich in den übrigen Dokumenten unter `doc/`.
|
der E-Planung gefüllt werden müssen.
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
```bat
|
||||||
|
python -m pytest tests/
|
||||||
|
```
|
||||||
|
|
||||||
|
mit aktivem `.venv`. Die Golden-Files in `tests/fixtures/scl/` legen die
|
||||||
|
SCL-Ausgabe byteweise fest; neu schreiben (`SKEL_UPDATE_GOLDEN=1`) nur bei einer
|
||||||
|
geprüften, gewollten Änderung.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
Noch offen:
|
||||||
|
|
||||||
|
- das volle **JSON-Layout-Schema** aus `doc/HundM/Json_Layout-Konzept.md`
|
||||||
|
(`plc`, `controlUnits`, `sensors[]`, `conveyors[]`, `tros[]`, `jamAreas[]`,
|
||||||
|
`routing`, …) als Zwischenformat — die TRO-JSON ist bisher TRO-zentriert;
|
||||||
|
- die übrigen Bausteine je Steuerung (`FB_CallSensors`, `FC_Direction`,
|
||||||
|
`FC_Call_Jams` als eigene Dateien) — erzeugt wird bisher nur `FB_Main`;
|
||||||
|
- Templates für die weiteren FB-Typen (siehe `doc/SCL_Templates.md`);
|
||||||
|
- typabhängige Zeit-Defaults statt `TODO(E-Planung): pruefen`.
|
||||||
|
|
||||||
|
Details: Abschnitt "Roadmap" in `CLAUDE.md`.
|
||||||
|
|
||||||
## Lizenz
|
## Lizenz
|
||||||
|
|
||||||
|
|||||||
+302
-32
@@ -1,6 +1,6 @@
|
|||||||
# Klassenhierarchie
|
# Klassenhierarchie
|
||||||
|
|
||||||
Bestandsaufnahme aller Python-Klassen in `lib/` (Stand 2026-10-09). Kurzes
|
Bestandsaufnahme aller Python-Klassen in `lib/` (Stand 2026-10-10). Kurzes
|
||||||
Ergebnis vorweg: es gibt praktisch **keine Vererbung** in diesem Code (eine
|
Ergebnis vorweg: es gibt praktisch **keine Vererbung** in diesem Code (eine
|
||||||
Ausnahme: `DerivedTro(TroBase)`, plus die Standard-Exception
|
Ausnahme: `DerivedTro(TroBase)`, plus die Standard-Exception
|
||||||
`RegistrationError(RuntimeError)`). Alles andere sind flache, eigenständige
|
`RegistrationError(RuntimeError)`). Alles andere sind flache, eigenständige
|
||||||
@@ -21,27 +21,33 @@ tro_catalog.py (Typ-Ebene: was ein TRO-Typ ist)
|
|||||||
v
|
v
|
||||||
tro_flow.py (Ableitung: CSV-Graph -> Analysis -> TroDocument)
|
tro_flow.py (Ableitung: CSV-Graph -> Analysis -> TroDocument)
|
||||||
|
|
|
|
||||||
+----------------+
|
+----------------------+
|
||||||
v v
|
v v
|
||||||
scl_skeleton.py tro_annotate.py (Ausgabe: SCL-Skeleton / annotierte Zeichnung)
|
scl_context.py tro_annotate.py (Ausgabe: annotierte Zeichnung)
|
||||||
|
(TroDocument -> SclFile, Render-Kontext)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
scl_skeleton.py (Ausgabe: SclFile -> FB_Main.scl, generisch oder per Jinja-Template)
|
||||||
|
|
||||||
material_flow.py dxf_registration.py tro_overrides.py (eigenständig, ohne
|
material_flow.py dxf_registration.py (eigenständig, ohne Abhängigkeit auf obige)
|
||||||
Abhängigkeit auf obige)
|
tro_overrides.py (importiert model.Connection / model.TroCandidate)
|
||||||
```
|
```
|
||||||
|
|
||||||
`tro_flow.py`, `scl_skeleton.py`, `tro_annotate.py`, `material_flow.py` selbst
|
`tro_flow.py` und `tro_annotate.py` definieren keine eigenen Klassen (nur
|
||||||
definieren keine eigenen Klassen mehr (nur Funktionen) — `material_flow.py`
|
Funktionen). `scl_skeleton.py` hat genau eine (`SclTemplates`, Jinja-Anbindung).
|
||||||
ist die Ausnahme: seine Graph-Dataclasses sind CSV-Rohmodell, nicht TRO-Modell.
|
`material_flow.py` hat Graph-Dataclasses — das ist CSV-Rohmodell, nicht
|
||||||
|
TRO-Modell.
|
||||||
|
|
||||||
## Vererbung (ASCII)
|
## Vererbung (ASCII)
|
||||||
|
|
||||||
```
|
```
|
||||||
RuntimeError (stdlib) BaseModel (pydantic, stdlib: pydantic)
|
RuntimeError (stdlib) BaseModel (pydantic, stdlib: pydantic)
|
||||||
| |
|
| |
|
||||||
+-- RegistrationError +-- TroBase
|
+-- RegistrationError +-- _Positioned (model.py, Mixin)
|
||||||
(dxf_registration.py) |
|
(dxf_registration.py) |
|
||||||
+-- DerivedTro
|
+-- TroBase
|
||||||
(model.py)
|
| +-- DerivedTro
|
||||||
|
+-- TroCandidate
|
||||||
```
|
```
|
||||||
|
|
||||||
Das ist die gesamte `class X(Y)`-Liste im Projekt — alle anderen Klassen unten
|
Das ist die gesamte `class X(Y)`-Liste im Projekt — alle anderen Klassen unten
|
||||||
@@ -62,8 +68,10 @@ lib/model.py [Instanz-Ebene / TRO-JSON]
|
|||||||
├── TroBase(BaseModel)
|
├── TroBase(BaseModel)
|
||||||
│ └─ definition() --> TroDefinition (gelesen aus tro_catalog, nicht gespeichert)
|
│ └─ definition() --> TroDefinition (gelesen aus tro_catalog, nicht gespeichert)
|
||||||
│ └── DerivedTro(TroBase) [einzige echte Vererbung im Projekt]
|
│ └── DerivedTro(TroBase) [einzige echte Vererbung im Projekt]
|
||||||
|
├── _Positioned(BaseModel) (Mixin: position {x,y,z}, describe_position)
|
||||||
|
│ ├── TroBase, TroCandidate
|
||||||
├── Connection(BaseModel) (from/to/kind zwischen zwei TRO-IDs)
|
├── Connection(BaseModel) (from/to/kind zwischen zwei TRO-IDs)
|
||||||
├── OpenPoint(BaseModel) (gemeldete, noch nicht modellierte Stelle)
|
├── TroCandidate(_Positioned) (gemeldete Stelle, an der ein TRO erwartet wird)
|
||||||
├── Analysis(BaseModel)
|
├── Analysis(BaseModel)
|
||||||
│ ├─ tros: list[DerivedTro]
|
│ ├─ tros: list[DerivedTro]
|
||||||
│ ├─ isolated: list[DerivedTro]
|
│ ├─ isolated: list[DerivedTro]
|
||||||
@@ -72,7 +80,7 @@ lib/model.py [Instanz-Ebene / TRO-JSON]
|
|||||||
├─ tros: list[DerivedTro]
|
├─ tros: list[DerivedTro]
|
||||||
├─ boundaries: list[DerivedTro]
|
├─ boundaries: list[DerivedTro]
|
||||||
├─ connections: list[Connection]
|
├─ connections: list[Connection]
|
||||||
└─ open_points: list[OpenPoint]
|
└─ candidates: list[TroCandidate]
|
||||||
|
|
||||||
lib/material_flow.py [CSV-Rohmodell -> Graph, @dataclass]
|
lib/material_flow.py [CSV-Rohmodell -> Graph, @dataclass]
|
||||||
├── Element (1 CSV-Zeile)
|
├── Element (1 CSV-Zeile)
|
||||||
@@ -91,35 +99,47 @@ lib/material_flow.py [CSV-Rohmodell -> Graph, @dataclass
|
|||||||
└─ edges: list[TrackEdge]
|
└─ edges: list[TrackEdge]
|
||||||
|
|
||||||
lib/tro_overrides.py [manuelle Korrekturen aus cfg/tro_overrides.ini, @dataclass]
|
lib/tro_overrides.py [manuelle Korrekturen aus cfg/tro_overrides.ini, @dataclass]
|
||||||
├── TroOverride (Typkorrektur an einem abgeleiteten TRO)
|
├── TroOverride (Typkorrektur an einem abgeleiteten TRO: tro_id ODER separator)
|
||||||
├── SplitSpec (TRO aus einem bestehenden heraustrennen)
|
├── SplitSpec (TRO aus einem bestehenden heraustrennen)
|
||||||
├── ConnectionSpec (bestätigte Verbindung zwischen zwei TROs)
|
|
||||||
├── MergeSpec (mehrere TROs zu einem zusammenfassen)
|
├── MergeSpec (mehrere TROs zu einem zusammenfassen)
|
||||||
├── OpenPoint (eigene Kopie, NICHT dieselbe Klasse wie model.OpenPoint!)
|
|
||||||
└── Overrides (Sammelcontainer für eine CSV-Datei)
|
└── Overrides (Sammelcontainer für eine CSV-Datei)
|
||||||
├─ tros: dict[str, TroOverride]
|
├─ tros: dict[str, TroOverride]
|
||||||
├─ splits: list[SplitSpec]
|
├─ splits: list[SplitSpec]
|
||||||
├─ merges: list[MergeSpec]
|
├─ merges: list[MergeSpec]
|
||||||
├─ connections: list[ConnectionSpec]
|
├─ connections: list[model.Connection]
|
||||||
└─ open_points: list[OpenPoint] (tro_overrides.OpenPoint, s.o.)
|
└─ candidates: list[model.TroCandidate]
|
||||||
|
|
||||||
lib/dxf_registration.py [CSV<->DXF Koordinatentransform]
|
lib/dxf_registration.py [CSV<->DXF Koordinatentransform]
|
||||||
├── RegistrationError(RuntimeError)
|
├── RegistrationError(RuntimeError)
|
||||||
└── Transform(BaseModel)
|
└── Transform(BaseModel)
|
||||||
|
|
||||||
|
lib/scl_context.py [Render-Kontext für die SCL, BaseModel]
|
||||||
|
├── SclName (Wert + Merker todo: noch aus der E-Planung offen?)
|
||||||
|
├── SclSeparator (ein stInSeparator<n>-Block)
|
||||||
|
├── SclSwitch (ein stInSwitch<n>-Block + FC_Direction-Aufruf)
|
||||||
|
├── SclSwitchExit (Weichenabgang + darüber erreichbare TROs)
|
||||||
|
├── SclScanner (ein stInBarcodeReader-Block)
|
||||||
|
├── SclJam (Staubereich an Ein-/Ausgang)
|
||||||
|
├── SclTro (alles für eine REGION)
|
||||||
|
│ ├─ separators: list[SclSeparator]
|
||||||
|
│ ├─ switches: list[SclSwitch]
|
||||||
|
│ ├─ switch_exits: list[SclSwitchExit]
|
||||||
|
│ ├─ scanners: list[SclScanner]
|
||||||
|
│ ├─ jam_entries / jam_exits: list[SclJam]
|
||||||
|
│ └─ main_tro, safety, ... : SclName
|
||||||
|
└── SclFile (die ganze Datei)
|
||||||
|
└─ tros: list[SclTro] (in Fliessrichtung)
|
||||||
|
|
||||||
|
lib/scl_skeleton.py [SCL-Ausgabe]
|
||||||
|
└── SclTemplates (Ordner mit <fbType>.scl.j2, hält ein jinja2.Environment)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Auffälligkeiten für ein mögliches Refactoring
|
## Auffälligkeiten für ein mögliches Refactoring
|
||||||
|
|
||||||
1. **`OpenPoint` existiert zweimal** mit fast identischen Feldern
|
1. ~~`OpenPoint` existiert zweimal~~ — erledigt 2026-10-10: zu
|
||||||
(`name, x, y, z, expect, belongs_to/belongsTo, note` +
|
`model.TroCandidate` zusammengefasst (`expect` → `type_name`, `x/y/z` →
|
||||||
`describe_position()`): einmal in `model.py` (Pydantic, JSON-Alias
|
`position`); `ConnectionSpec` durch `model.Connection` ersetzt;
|
||||||
`belongsTo`), einmal in `tro_overrides.py` (Dataclass, kein Alias). Das ist
|
`tro_overrides.py` importiert beide aus `model.py`.
|
||||||
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`
|
2. **Zwei parallele Graph-Hierarchien** in `material_flow.py`
|
||||||
(`Node/Edge/Graph` vs. `TrackNode/TrackEdge/TrackGraph`) teilen sich keine
|
(`Node/Edge/Graph` vs. `TrackNode/TrackEdge/TrackGraph`) teilen sich keine
|
||||||
Basisklasse, obwohl Struktur und Feldnamen sich stark ähneln (`node_id,
|
Basisklasse, obwohl Struktur und Feldnamen sich stark ähneln (`node_id,
|
||||||
@@ -141,6 +161,256 @@ lib/dxf_registration.py [CSV<->DXF Koordinatentransform]
|
|||||||
**Fazit:** Die Code-Basis nutzt Vererbung bewusst kaum — die Architektur trennt
|
**Fazit:** Die Code-Basis nutzt Vererbung bewusst kaum — die Architektur trennt
|
||||||
stattdessen über Module (Typ-Katalog vs. Instanz vs. Ableitung vs. Ausgabe,
|
stattdessen über Module (Typ-Katalog vs. Instanz vs. Ableitung vs. Ausgabe,
|
||||||
siehe `model.py`-Kopfkommentar) und über Komposition. Ein Refactoring in
|
siehe `model.py`-Kopfkommentar) und über Komposition. Ein Refactoring in
|
||||||
Richtung "mehr Vererbung" wäre hier eher kontraproduktiv; die einzig konkret
|
Richtung "mehr Vererbung" wäre hier eher kontraproduktiv. Die doppelte
|
||||||
lohnenden Punkte sind die doppelte `OpenPoint`-Definition (1) und optional
|
`OpenPoint`-Definition (1) ist behoben; offen bleibt nur optional `StrEnum`
|
||||||
`StrEnum` statt Konstanten-Klassen (3).
|
statt Konstanten-Klassen (3).
|
||||||
|
|
||||||
|
## Die komplexen Klassen im Einzelnen
|
||||||
|
|
||||||
|
Die folgenden Abschnitte beschreiben je Klasse, **wozu** sie da ist, **was
|
||||||
|
darin passiert** und **wer sie erzeugt bzw. liest**. Einfache Datenhalter
|
||||||
|
ohne eigene Logik — `Connection`, `TroCandidate`, `Edge`, `TrackEdge`,
|
||||||
|
`TroItem`/`TroSymbol` und die kleinen Blöcke `SclSeparator`, `SclSwitch`,
|
||||||
|
`SclSwitchExit`, `SclScanner`, `SclJam` — sind im Baum oben ausreichend
|
||||||
|
beschrieben und bekommen keinen eigenen Abschnitt.
|
||||||
|
|
||||||
|
### `TroDefinition` (+ `TroStyle`) — `lib/tro_catalog.py`
|
||||||
|
|
||||||
|
**Wozu:** Beschreibt einen TRO-**Typ**, nicht eine Stelle in der Anlage: Kurzname
|
||||||
|
(`1Sep1Swi`), Siemens-FB (`FB_ILS_MTRO_1Sep1Swi`), Bauteile
|
||||||
|
(`{"Separator": 1, "Switch": 1}`), Farbgruppe und CAD-Symbol. Der Katalog
|
||||||
|
aller Definitionen ist die ausführbare Form von `doc/TRO_Typen.md`.
|
||||||
|
|
||||||
|
**Was passiert darin:** Pydantic mit `validate_assignment` und `extra="forbid"` —
|
||||||
|
eine Definition kann nie ungültig werden, auch nicht über die `set_*`-Methoden.
|
||||||
|
`_clean_items` wirft Bauteile mit Anzahl ≤ 0 heraus. Ein eigener `__init__`
|
||||||
|
erlaubt die alte Positionsform `TroDefinition("1Sep", "FB_...", {...})`. Dazu
|
||||||
|
kommen Abfragen (`get_items`, `has_item`, `get_item_fb`, `get_total_items`) und
|
||||||
|
Darstellungshelfer (`get_dot_attrs`, `mermaid_class/_classdef`,
|
||||||
|
`describe_items`). `TroStyle` liefert nur die Farben einer Gruppe für Graphviz,
|
||||||
|
Mermaid und CAD.
|
||||||
|
|
||||||
|
**Wer nutzt sie:** Über `get_tro(name)`/`catalog()` jeder, der vom Typnamen auf
|
||||||
|
FB oder Bauteile kommen muss — vor allem `TroBase.definition()`; außerdem
|
||||||
|
`tro_flow` (Typprüfung), `tro_annotate` (Farbe/Symbol, `--emit-lisp`) und die
|
||||||
|
Doku-Grafiken.
|
||||||
|
|
||||||
|
### `TroBase` / `DerivedTro` — `lib/model.py`
|
||||||
|
|
||||||
|
**Wozu:** Ein konkreter TRO in einer Anlage (Instanz). `TroBase` ist der Kern, den
|
||||||
|
jeder TRO hat, egal woher er stammt: `tro_id`, `type_name`, `separators`,
|
||||||
|
`predecessors`/`successors`, `position`. `DerivedTro` ergänzt, was `tro_flow`
|
||||||
|
beim Ableiten aus dem CSV zusätzlich weiß: `hosts`, `scanners`, `reason`,
|
||||||
|
`colocated`, `position_from`/`position_points`. Systemgrenzen (`SYSn`) sind
|
||||||
|
ebenfalls `DerivedTro`.
|
||||||
|
|
||||||
|
**Was passiert darin:**
|
||||||
|
|
||||||
|
- `fb_block` und `items` sind **berechnete** Felder: bei jedem Zugriff aus dem
|
||||||
|
Katalog gelesen (`definition()` → `get_tro(type_name)`), nie gespeichert. In der
|
||||||
|
JSON stehen sie trotzdem (`fbType`, `items`), damit sie lesbar ist.
|
||||||
|
- `_check_catalog_fields` (Validator vor dem Einlesen): steht in einer JSON ein
|
||||||
|
`items`/`fbType`, das nicht zum Typ passt, ist das ein Fehler — "den Typ ändern,
|
||||||
|
nicht die Bauteile". Danach werden die Felder verworfen.
|
||||||
|
- Aliase: in der JSON `id`/`type`, im Code `tro_id`/`type_name`
|
||||||
|
(`populate_by_name`).
|
||||||
|
- `_Positioned` (Mixin) wandelt `position` zwischen Tupel und
|
||||||
|
`{"x","y","z"}` und liefert `x`/`y`/`z` und `describe_position()`.
|
||||||
|
- `DerivedTro.connected`: hat der TRO überhaupt Nachbarn?
|
||||||
|
|
||||||
|
**Wer nutzt sie:** `tro_flow.derive_tros`/`derive_boundaries` erzeugen sie,
|
||||||
|
`scl_context` liest sie (als `TroBase`), `tro_annotate` zeichnet sie.
|
||||||
|
|
||||||
|
### `Analysis` — `lib/model.py`
|
||||||
|
|
||||||
|
**Wozu:** Das Arbeitsergebnis eines Ableitungslaufs, bevor daraus eine Datei wird.
|
||||||
|
Enthält mehr als die JSON: Kanten als Tupel, Kantenarten, isolierte TROs,
|
||||||
|
unbekannte Typen, Befunde, die verwendete Methode.
|
||||||
|
|
||||||
|
**Was passiert darin:** Fast nur Daten; `by_id()` sucht über TROs und
|
||||||
|
Systemgrenzen. Befüllt wird sie in `tro_flow.analyse_tros` in fester
|
||||||
|
Reihenfolge: `derive_tros` → `derive_boundaries` → `locate_tros` (Position,
|
||||||
|
nötig für die Reihenfolge auf einer Kreiselbahn) → `link_tros` (Kanten und
|
||||||
|
Kantenarten) → Prüfung gegen den Katalog.
|
||||||
|
|
||||||
|
**Wer nutzt sie:** `tro_flow` (Bericht, Diagramm, `--json`) und `tro_annotate`
|
||||||
|
(zeichnet direkt aus der `Analysis`, ohne Umweg über die JSON).
|
||||||
|
|
||||||
|
### `TroDocument` — `lib/model.py`
|
||||||
|
|
||||||
|
**Wozu:** Die TRO-JSON `<csv>_tro.json` (Schema `sps_skel-tro-v3`) — die
|
||||||
|
Schnittstelle zwischen Ableitung (`tro_flow --json`) und SCL-Ausgabe
|
||||||
|
(`scl_skeleton`). Weil beide Seiten dieselbe Klasse benutzen, kann das Format
|
||||||
|
nicht unbemerkt auseinanderlaufen.
|
||||||
|
|
||||||
|
**Was passiert darin:** `from_analysis()` macht aus einer `Analysis` ein Dokument
|
||||||
|
(Kanten + Kantenarten → `Connection`-Liste). `to_json()` schreibt mit den
|
||||||
|
JSON-Aliasen, `from_json()` liest und prüft. `extra="forbid"` sorgt dafür, dass ein
|
||||||
|
umbenannter oder falscher Schlüssel als Fehler auffällt statt still einen
|
||||||
|
Standardwert zu bekommen; `scl_skeleton.main` meldet bei falschem `schema`
|
||||||
|
gezielt "mit tro_flow neu erzeugen".
|
||||||
|
|
||||||
|
**Wer nutzt sie:** geschrieben von `tro_flow --json`, gelesen von
|
||||||
|
`scl_skeleton.load_document` → `scl_context.build_context`.
|
||||||
|
|
||||||
|
### `Element`, `Graph`, `TrackGraph` — `lib/material_flow.py`
|
||||||
|
|
||||||
|
**Wozu:** Das Rohmodell aus dem CSV-Export (ILS 2.0), noch ohne TRO-Begriff.
|
||||||
|
|
||||||
|
- `Element` = eine CSV-Zeile: Art, ID, Name, Nachbarn, `track_ids`,
|
||||||
|
Merkmale, Position, Bounding-Box, Anschlusspunkte K1..K4.
|
||||||
|
- `Graph` (`Node`/`Edge`) = grober **Materialfluss-Graph**: Transportobjekte
|
||||||
|
(Strecke, Gefällestrecke, Kreisel-Bahn) als Knoten, Geräte (Separatoren,
|
||||||
|
Weichen, Scanner …) an die Knoten gehängt (`Node.devices`), Stationen und
|
||||||
|
Übergaben als Kanten.
|
||||||
|
- `TrackGraph` (`TrackNode`/`TrackEdge`) = feiner **Bahnzusammenhang**: jedes
|
||||||
|
Bahnelement (AS, SEP, ES, Weiche, BTMT …) ist ein Knoten, verkettet in
|
||||||
|
Bau- bzw. Drehrichtung (siehe `doc/track.md`).
|
||||||
|
|
||||||
|
**Was passiert darin:** `Element` hat viele abgeleitete Properties: Klassifizierung
|
||||||
|
(`is_transport`, `is_circle`, `is_device`, `is_station`, `is_transfer`,
|
||||||
|
`is_entry`/`is_exit`), Geometrie (`x/y/z`, `bbox_x/y`, `height`,
|
||||||
|
`height_from/to`, `rise`), Antrieb (`drive_dir`, `rotation`). Die Graphklassen
|
||||||
|
selbst sind reine Container; die Logik steckt in den Funktionen
|
||||||
|
`build_graph()` (Knoten bilden, Geräte zuordnen, Kanten sammeln, Fliessrichtung
|
||||||
|
aus Gefälle/Antrieb, Stationen und Übergaben einhängen) und
|
||||||
|
`build_track_graph()` (Ketten aus der Spalte `TrackIds`; ein Element in zwei
|
||||||
|
Containern verbindet diese automatisch). `TrackNode` kann ohne `element`
|
||||||
|
existieren (Platzhalter für eine nicht auflösbare Bahn, generisches
|
||||||
|
Ein-/Ausgang-Symbol).
|
||||||
|
|
||||||
|
**Wer nutzt sie:** `material_flow` selbst (DOT/SVG/connect.ini/Bericht) und
|
||||||
|
`tro_flow.analyse_tros(graph, elements)` als Eingang der TRO-Ableitung;
|
||||||
|
`tro_annotate` für Ankerpunkte des Lagebezugs.
|
||||||
|
|
||||||
|
### `Overrides` (+ `TroOverride`, `SplitSpec`, `MergeSpec`) — `lib/tro_overrides.py`
|
||||||
|
|
||||||
|
**Wozu:** Von Hand gepflegte, vor Ort bestätigte Korrekturen aus
|
||||||
|
`cfg/tro_overrides.ini` für das, was die Ableitung nicht erkennen kann.
|
||||||
|
|
||||||
|
- `TroOverride`: Typ eines TRO korrigieren. Adressiert über `tro_id` **oder**
|
||||||
|
— stabiler — über einen `separator`, weil TRO-Nummern sich zwischen Läufen
|
||||||
|
verschieben, Separatornummern aus dem Export nicht (`matches()`).
|
||||||
|
- `SplitSpec`: Separatoren/Scanner aus einem TRO herauslösen und einen neuen TRO
|
||||||
|
bilden (Übergabestelle, deren Zuordnung auf die Strecke statt auf die
|
||||||
|
Kreiselbahn zeigt).
|
||||||
|
- `MergeSpec`: mehrere TROs zu einem Steuerobjekt zusammenfassen (z. B. zwei
|
||||||
|
Sperren beiderseits eines Kreisel-Berührpunkts).
|
||||||
|
- `Overrides`: Sammelcontainer je CSV-Datei, dazu gesetzte Topologie
|
||||||
|
(`connections`, ersetzt die abgeleiteten Kanten vollständig), erwartete
|
||||||
|
TROs (`candidates`), Protokoll (`applied`) und `warnings`. `bool(overrides)`
|
||||||
|
ist True, sobald irgendeine Korrektur vorhanden ist.
|
||||||
|
|
||||||
|
**Was passiert darin:** `load_overrides(csv_name)` liest den INI-Abschnitt der
|
||||||
|
CSV-Datei und baut daraus die Objekte; Lesefehler landen in `warnings` statt
|
||||||
|
den Lauf abzubrechen.
|
||||||
|
|
||||||
|
**Wer nutzt sie:** Stand heute **nur** `material_flow --connect`
|
||||||
|
(`render_connect_ini` schreibt sie als `[<Anlage>.manual]`-Abschnitt). `tro_flow`
|
||||||
|
wendet sie (noch) nicht an — sie fliessen also nicht in die TRO-JSON und nicht
|
||||||
|
in die SCL.
|
||||||
|
|
||||||
|
### `Transform` (+ `RegistrationError`) — `lib/dxf_registration.py`
|
||||||
|
|
||||||
|
**Wozu:** Der Lagebezug zwischen CSV-Export und BricsCAD-Zeichnung: eine starre
|
||||||
|
Transformation `csv = R(rotation) · dxf + (dx, dy)`. Ohne sie landen die
|
||||||
|
TRO-Marker in `tro_annotate` an der falschen Stelle.
|
||||||
|
|
||||||
|
**Was passiert darin:** `to_csv()`/`to_dxf()` rechnen Punkte hin und zurück,
|
||||||
|
`describe()` fasst Verschiebung, Drehung, Punktzahl und Restfehler zusammen.
|
||||||
|
Die Ermittlung steckt in Funktionen: `derive_transform()` ordnet punktartige
|
||||||
|
Objekte gleicher Rolle (Separator, Scanner) aus CSV und DXF zu, stimmt über
|
||||||
|
Verschiebungen ab, gleicht aus und wählt den besten Kandidaten;
|
||||||
|
`verify_transform()` rechnet die Restfehler nach und schreibt sie ins Objekt;
|
||||||
|
`load_transform()`/`save_transform()` lesen/schreiben
|
||||||
|
`cfg/dxf_registration.json`. `RegistrationError` heisst: der Bezug liess sich
|
||||||
|
nicht **nachweisbar** bestimmen — dann lieber abbrechen als falsch zeichnen.
|
||||||
|
|
||||||
|
**Wer nutzt sie:** `tro_annotate.resolve_transform` (Reihenfolge: `--offset` →
|
||||||
|
abgelegter Bezug → neu ermitteln, jeweils mit Prüfung), dazu das geparkte
|
||||||
|
`tro_extract`.
|
||||||
|
|
||||||
|
### `SclFile`, `SclTro`, `SclName` — `lib/scl_context.py`
|
||||||
|
|
||||||
|
**Wozu:** Der **Render-Kontext** für das FB_Main-Gerüst. Alles, was aus der
|
||||||
|
TRO-JSON *abgeleitet* wird, steht hier fertig drin; die Ausgabe
|
||||||
|
(`emit_*`-Funktionen oder Jinja-Templates) rechnet nichts mehr selbst, sie
|
||||||
|
liest nur noch. So können generischer Aufbau und Templates nicht
|
||||||
|
unterschiedlich ableiten.
|
||||||
|
|
||||||
|
**Was passiert darin:**
|
||||||
|
|
||||||
|
- `build_context(document, source, start_id)` baut aus einem `TroDocument` ein
|
||||||
|
`SclFile`: Kopfdaten plus die TROs in Fliessrichtung.
|
||||||
|
- `flow_order()` bestimmt die Reihenfolge der REGIONs: Start bei `--start`, sonst
|
||||||
|
beim TRO ohne Vorgänger, sonst beim TRO, den eine Station (`SYSn`) speist,
|
||||||
|
sonst wird der Kreislauf willkürlich aufgetrennt — der Grund steht im
|
||||||
|
SCL-Kopf (`start_reason`). Danach Breitensuche; nicht erreichte TROs werden
|
||||||
|
hinten angehängt, damit keiner verloren geht.
|
||||||
|
- `_tro_context()` baut je TRO ein `SclTro`: so viele Separator-, Weichen- und
|
||||||
|
Scannerblöcke, wie der Katalogtyp Bauteile hat (`items`); je Vorgänger ein
|
||||||
|
Staubereich am Eingang, je Nachfolger einer am Ausgang; `to1_destinations`
|
||||||
|
nur bei genau einer Weiche (bei mehreren ist die Aufteilung eine
|
||||||
|
Planungsentscheidung); die Zielliste je Weichenabgang über
|
||||||
|
`reachable_via()` (Erreichbarkeit im TRO-Graphen, ohne über die Weiche
|
||||||
|
zurückzulaufen).
|
||||||
|
- `SclName` = Wert + Merker `todo`. Alles, was aus der E-Planung kommen muss
|
||||||
|
(Separator-/Weichennummern, Sensor-/Aktoradressen, MainTro, Staubereichs-DB …),
|
||||||
|
ist heute ein Platzhalter mit `todo=True`; die Ausgabe hängt daran den
|
||||||
|
Kommentar `TODO(E-Planung)`. Spätere Namensregeln ersetzen nur den Wert und
|
||||||
|
löschen den Merker — die Ausgabe ändert sich dafür nicht.
|
||||||
|
|
||||||
|
**Wer nutzt sie:** erzeugt in `scl_skeleton.render()`/`main()`, gelesen von
|
||||||
|
`render_context()`, den `emit_*`-Funktionen und den Templates (als `t`).
|
||||||
|
|
||||||
|
### `SclTemplates` — `lib/scl_skeleton.py`
|
||||||
|
|
||||||
|
**Wozu:** Die Anbindung der parametrisierten SCL-Aufrufstellen
|
||||||
|
(`lib/scl_templates/*.scl.j2`) an die Ausgabe. Ein Objekt steht für einen
|
||||||
|
Template-Ordner.
|
||||||
|
|
||||||
|
**Was passiert darin:** Der Konstruktor baut ein `jinja2.Environment` mit
|
||||||
|
`StrictUndefined` (ein Tippfehler im Template wird zum Fehler statt zu einer
|
||||||
|
leeren SCL-Stelle), `trim_blocks`/`lstrip_blocks` und der globalen Variable
|
||||||
|
`TODO`. `has(fb)` prüft, ob `<fb>.scl.j2` existiert; `render(tro)` rendert das
|
||||||
|
Template mit `t = SclTro` und fasst danach mehrfache Leerzeilen zusammen (statt
|
||||||
|
das mit `{%- -%}` im Template zu tun, was die Einrückung bricht).
|
||||||
|
|
||||||
|
#### Template ↔ TRO: Zuordnung und Einstieg in die Produktion
|
||||||
|
|
||||||
|
**Zuordnung — es gibt keine Tabelle, nur eine Namenskonvention:
|
||||||
|
Dateiname = FB-Typ.** Die Kette:
|
||||||
|
|
||||||
|
```text
|
||||||
|
DerivedTro.type_name "1Sep1Swi" (TRO-JSON, Feld "type")
|
||||||
|
-> tro_catalog.get_tro() TroDefinition
|
||||||
|
-> TroDefinition.fb_block "FB_ILS_MTRO_1Sep1Swi"
|
||||||
|
-> TroBase.fb_block (berechnet, nicht gespeichert)
|
||||||
|
-> SclTro.fb (scl_context._tro_context)
|
||||||
|
-> SclTemplates.has/render lib/scl_templates/FB_ILS_MTRO_1Sep1Swi.scl.j2
|
||||||
|
```
|
||||||
|
|
||||||
|
Ein neues Template für einen weiteren Typ heisst also einfach
|
||||||
|
`<fbType>.scl.j2` und wird automatisch für alle TROs dieses Typs verwendet.
|
||||||
|
Heute gibt es nur den Piloten `FB_ILS_MTRO_1Sep1Swi.scl.j2` (plus
|
||||||
|
`_macros.scl.j2` mit gemeinsamen Makros). Alle anderen Typen laufen über den
|
||||||
|
generischen Aufbau (`emit_region()`).
|
||||||
|
|
||||||
|
**Einstieg — nur per Schalter (opt-in):**
|
||||||
|
|
||||||
|
- `scl_skeleton --templates [DIR]` (ohne DIR: `lib/scl_templates`). `main()`
|
||||||
|
legt dann ein `SclTemplates` an; `render_context()` nimmt pro TRO
|
||||||
|
`templates.render(tro)`, wenn `templates.has(tro.fb)`, sonst `emit_region()`.
|
||||||
|
Der SCL-Kopf listet, welche FB-Typen aus Template bzw. generisch kamen.
|
||||||
|
- `--strict` bricht ab (Exit 1, keine SCL), wenn ein FB-Typ der Anlage kein
|
||||||
|
Template hat. Ein `TemplateError` (fehlende Variable, Syntaxfehler) bricht
|
||||||
|
ebenfalls ab — lieber keine SCL als eine mit stillen Lücken.
|
||||||
|
- Ohne `--templates` werden Templates gar nicht geladen; die Ausgabe ist die
|
||||||
|
bisherige (Golden-Files `tests/fixtures/scl/*_FB_Main.scl`). Mit Templates
|
||||||
|
pinnen `*_FB_Main_templates.scl` das Ergebnis.
|
||||||
|
- `bin/scl_skeleton.bat`/`.sh` reichen die Schalter nur durch; der Standardaufruf
|
||||||
|
ohne `--templates` nutzt also **keine** Templates.
|
||||||
|
|
||||||
|
**Nicht Produktion:** `doc/TRO_Katalog/scl_templates/*.scl` sind die realen
|
||||||
|
Referenz-Aufrufstellen. Gegen sie prüft `tests/test_scl_templates.py` jedes
|
||||||
|
Template im Round-Trip; in die erzeugte SCL fliessen sie nicht ein.
|
||||||
|
|||||||
Reference in New Issue
Block a user