# Konzept: Layout und Hardwareplanung → Anlagen-JSON → SCL Stand 2026-10-11. Zielbild für den Ausbau der Pipeline, damit die beiden Fachabteilungen unabhängig voneinander und zeitversetzt liefern können. Welche Angaben aus welcher Quelle kommen, steht in `doc/Datenquellen_Layout_Hardwareplanung.md`; die Rohangaben beider Quellen in `doc/Klassenhierarchie.md`, Kapitel "Daten". ## Ausgangslage Die Produktion läuft über zwei Abteilungen: 1. **Anlagenlayout** (aus dem Angebotswesen, detaillierter aus der Projektierung). Liefert alles für die TROs: Bauteile, Abfolge, Weichen, Staubereiche, Systemgrenzen. 2. **Hardwareplanung** (Karten, Kabel, Sensoren, Aktoren). Liefert die globalen Variablen und die tatsächlichen Sensoren und Aktoren mit Namen und Adressen. Die Hardwareplanung kommt **Tage bis Wochen später** als das Layout. Das System muss deshalb mit dem Layout allein ein brauchbares SCL-Skelett erzeugen und die Hardware später nachtragen können, ohne Layout-Arbeit zu wiederholen. ## Zielbild ```text Abteilung 1: Anlagenlayout Abteilung 2: Hardwareplanung (später) CSV ──tro_flow──> _tro.json EA/TIA/WSCAD.xlsx, positionsdraw.json (TROs, Abfolge, ──hw_import──> _hw.json Staubereiche, Slots) (globale Variablen, Geräte) │ │ └──────────── hw_assign ─────────────────┘ │ _anlage.json (Layout + Zuordnungen) │ scl_skeleton ──> FB_Main.scl, FB_CallSensors.scl, … ``` | Stufe | Skript | Eingabe | Ausgabe | Status | |---|---|---|---|---| | 1 Layout | `tro_flow.py --json` | CSV, `cfg/tro_overrides.ini` | `_tro.json` | vorhanden, wird erweitert | | 2 Hardware | `hw_import.py` | `*_TIA.xlsx`, `*_EA.xlsx`, `*_WSCAD.xlsx`, `*_positionsdraw.json` | `_hw.json` | neu | | 3 Zuordnung | `hw_assign.py` | `_tro.json`, `_hw.json`, Regeln, `cfg/hw_overrides.ini` | `_anlage.json`, Bericht `.md` | neu (Logik aus `e_planning.py`) | | 4 SCL | `scl_skeleton.py` | `_anlage.json` (oder nur `_tro.json`) | `.scl` | vorhanden, wird umgestellt | Grundsätze: - **Stufe 1 allein reicht für ein Skelett.** Ohne Hardware wird jeder offene Wert wie heute `TODO(E-Planung)`. - **Die Stufen verändern sich nicht gegenseitig.** Die Zuordnung legt ihre Ergebnisse neben das Layout, statt es zu überschreiben. Ein neuer Layoutstand oder ein neuer Hardwarestand heißt: die betroffene Stufe und alles danach neu ausführen. - **Lieber eine Lücke als ein falscher Name** (wie heute in `cfg/e_planning.ini`): was keine Regel sicher trifft, bleibt offen und steht im Bericht. - **Drei Quellen, jede sichtbar:** Layout, Hardwareplanung und Defaults (`cfg/defaults.ini`, Typ-Katalog). Jede SCL-Zeile lässt sich einer davon zuordnen. ## Umbauten ### 1. TRO-JSON zur Layout-JSON ausbauen (Stufe 1) Heute fehlt der Zuordnung ein Ziel: die TRO-JSON sagt nicht, welche Stellen ein TRO hat, an die Hardware gehört. Daran scheitern heute Stausensoren und Weichenventile (`doc/E-Planung_Bruecke.md`, "Offen"). - **Slots je TRO:** je Separator, Weiche, Ausgang und Antrieb ein Eintrag mit der Layout-ID des Bauteils und den Rollen, die die Hardwareplanung dort füllen muss. ```json "slots": [ {"slot": "separator1", "layoutId": "0008", "roles": ["sensor", "stopper", "jamSensor?", "partSensor?"]}, {"slot": "switch1", "layoutId": "0037", "exit": 3, "roles": ["valve", "exitSensor?"]}, {"slot": "drive", "layoutId": "0002", "roles": ["carouselRun"]} ] ``` Die Slots ergeben sich aus dem Katalogtyp (`items`) und der Topologie; `?` = optional, fehlt das Gerät, wird der zugehörige `xPlugged*`-Schalter `FALSE`. - **Staubereiche als eigene Liste `jamAreas[]`:** ein Staubereich je TRO-Eingang, mit allen Vorgängern, dem Förderer, auf dem er liegt, und seiner Lage auf dem Förderer (siehe "Staubereiche" unten). Heute nur abgeleitet beim Rendern (`scl_context.SclJam`). Dabei korrigieren: `scl_skeleton` erzeugt heute je Vorgänger einen eigenen Eingang (`arInOutJamEntr1`, `…Entr2`); in den Referenzen laufen mehrere Vorgänger in **einem** Staubereich zusammen, der TRO hat einen Eingang (Ausnahme `2Sep1Swi`, PinStore). - **Weitere Layout-Angaben:** `xIsBorner`, Vorfahrt ja/nein, tragender Förderer mit Rolle (heute `hosts` ohne Rolle), Längen für `tConvEmpty` und Staulängen. - **Ableitungen aus `scl_context` nach `tro_flow`/`model` verschieben,** damit die JSON sie trägt und nicht erst das Rendern sie kennt. - Schema `sps_skel-tro-v4`. **Stand (2026-10-11), erster Teil umgesetzt:** - `model.Slot` und `DerivedTro.slots`: `separator`, `switch`, `scanner`, `drive` mit Rollen, Anzahl aus dem Katalog (`derive_slots`). Weichen-Slots haben noch keine `layoutId`: `tro_flow` hält das Weichen- bzw. Ausschleuselement nicht am TRO fest. - `model.JamArea` und `TroDocument.jamAreas`: je Separator laut Katalog ein Eingangs-Staubereich `J` mit allen Vorgängern, dem Host und der Lage (`derive_jam_areas`). Bei 2Sep-Typen ist die Aufteilung der Vorgänger auf die zwei Eingänge offen (Hinweis im Feld `note`); beim PinStore heißt der Eingang vorläufig nach dem ersten Linien-Separator. - `scl_context` liest die Staubereiche aus der JSON: **ein** `JamEntr` je eigenem Staubereich statt je Vorgänger (Abweichung zu UH01–UH05 behoben), jeder Ausgang nennt im Kommentar den Staubereich des Nachfolgers. Golden Files neu erzeugt. Eine v3-Datei ohne `jamAreas` wird weiter gelesen. - **Noch offen aus Umbau 1:** Layout-ID der Weichen, `xIsBorner`, Vorfahrt, Host-Rolle, Längen (`tConvEmpty`, Staulängen), ``/`` für den SCL-Namen der Staubereiche. ### 2. Neues Skript `hw_import.py` (Stufe 2) - Liest TIA-Import (PLC Tags + Constants), EA-Liste, WSCAD-Import und optional `positionsdraw.json`. - Schreibt ein neues Modell `HardwareDocument`: - **Geräte** je BMK: Adresse, Index-Konstante (`cInBG3306`), Kommentar, VERW, Steuerung, Station/Schrank, Position (falls vorhanden); - **globale Variablen**: alle Tags und Konstanten, Motor↔Motorschutz-Paare, Taster, Not-Halt. - Prüft die Exporte gegeneinander: BMK fehlt in einer Liste, doppelte BMK, Adresse ohne Tag. - Übernimmt aus `e_planning.py`: `load_positionsdraw`, `EDevice`, `pair_drives`. - Erste Quelle: der TIA-Import, weil er Namen und Konstanten 1:1 liefert und die spätere Prüfung des SCL ermöglicht. ### 3. Neues Skript `hw_assign.py` (Stufe 3) - Eingabe: `_tro.json` + `_hw.json`; das CSV wird nicht mehr gelesen. - Zuordnungswege, als Regeln in einer INI (heute `cfg/e_planning.ini`): - **räumlich** über `positionsdraw`: die bestehende Logik aus `e_planning.py` (Lagebezug über `dxf_registration`, Höchst-/Sollabstand), jetzt gegen die Slots; - **Nummernregel**: `MB = BG + 1` bzw. gleiche Nummer, Motor = Motorschutz; - **nur EA-Liste, ohne Positionen**: über die Kommentare ("in Separator 1 (ILS-CV M0108)") und eine von Hand gepflegte Tabelle M-Nummer → Layout-Förderer. - Handkorrekturen in `cfg/hw_overrides.ini` (analog `cfg/tro_overrides.ini`). - Ausgabe `_anlage.json`: Layoutteil unverändert, daneben ```json "bindings": [ {"layoutId": "0008", "role": "sensor", "bmk": "BG3306", "constant": "cInBG3306", "address": "%E330.6", "source": "positionsdraw", "rule": "raeumlich", "distance": 12.0, "status": "zugeordnet"} ] ``` `status`: `zugeordnet` | `offen` | `widerspruch`. - **Stabile Schlüssel:** Zuordnungen hängen an TRO-ID + Slot + Rolle. Die TRO-IDs sind ab der Festlegung durch die SPS-Programmierer stabil (siehe "TRO-IDs" unten). Die Layout-`TeileId` taugt dafür **nicht**: sie ändert sich zwischen zwei CSV-Exporten (siehe dort). `layoutId` in `bindings` dient nur zur Nachverfolgung im aktuellen Stand. - **Gegenprüfung** beider Quellen (Anzahl Separatoren vs. `IN SEP`, Weichen vs. `AE ABZWEIG`, angetriebene Strecken vs. `MA`) in den Bericht. ### 4. `scl_skeleton.py` / `scl_context.py` (Stufe 4) - Liest **eine** Eingabe: `_anlage.json`; ohne Hardwareteil wie heute `_tro.json`. - `--eplan` und die Zusammenführung in `scl_context._separator` entfallen; der Kontext liest Namen aus `bindings`, offene Slots werden `TODO(E-Planung)`. - **Prüfung gegen die globalen Variablen:** jeder Tag und jede `cIn…`-Konstante im SCL muss im TIA-Import stehen, sonst Abbruch mit Liste. - **Neue Bausteine** nach Stufe 2: `FB_CallSensors` aus der Sensorliste, Antriebsaufrufe (`fbCarousel0102`) aus den Motorpaaren, Staubereichs-DBs. ### 5. Defaults als dritte Quelle Zeiten und Projektkonstanten, die heute fest in `scl_skeleton.py` stehen (`t#600ms`, `T#1500ms` …), wandern in den Typ-Katalog (`tro_catalog.py`) bzw. eine Projektdatei `cfg/defaults.ini`. ## Bestehende Programme | Programm | Rolle im neuen Ablauf | |---|---| | `material_flow.py`, `tro_flow.py` | bleiben Stufe 1; Ausbau um Slots und `jamAreas` | | `tro_catalog.py`, `model.py`, `tro_overrides.py`, `dxf_registration.py` | bleiben Bibliotheken; `model.py` bekommt Slots, `jamAreas`, Anlagen-JSON | | `e_planning.py` | wird aufgeteilt (unten) | | `tro_annotate.py` | bleibt als Kontrollansicht, liest künftig die JSON (unten) | | `tro_extract.py` | entfällt bzw. wird umgebaut (unten) | | `scl_skeleton.py`, `scl_context.py` | Stufe 4, Umstellung auf `_anlage.json` | ### `e_planning.py` → `hw_import` + `hw_assign` | Teil heute | Wohin | |---|---| | `load_positionsdraw`, `EDevice`, `pair_drives` | `hw_import.py` | | `resolve_transform`, `load_rules`, `_assign_spatial`, `_assign_by_number`, Bericht | `hw_assign.py`, Ziele = Slots statt CSV-Bauteile | | `cfg/e_planning.ini` | Regeldatei von `hw_assign` | | `EplanDocument` / `_eplan.json` | entfällt, ersetzt durch `bindings[]` | Übergang: `e_planning.py` bleibt kurz als Hülle, die `hw_import` und `hw_assign` nacheinander aufruft, damit `bin/e_planning.bat` weiter funktioniert; danach löschen. ### `tro_annotate.py` → Kontrollansicht für Layout und Zuordnung - **Aus der JSON zeichnen** statt `analyse_tros` selbst aufzurufen: die Zeichnung zeigt dann genau das, woraus das SCL entsteht. - **Zuordnungen einzeichnen:** neben jedem Slot das BMK (`BG3306`, `MB3306`), offene Slots und Widersprüche rot. Für die Hardwareplanung die schnellste Kontrolle der räumlichen Zuordnung. - `--emit-lisp` (`TROEDIT`) bleibt, solange Marker in BricsCAD bearbeitet werden; hängt an der Entscheidung zu `tro_extract`. ### `tro_extract.py` → entfällt im neuen Ablauf Es wäre ein dritter Korrekturweg neben `cfg/tro_overrides.ini` (Layout) und `cfg/hw_overrides.ini` (Zuordnung) und schreibt noch das alte Schema `sps_skel-tro-extract-v1`, das niemand mehr liest. - **Löschen** (Empfehlung), zusammen mit `TROEDIT`/`--emit-lisp`. Korrekturen nur in den INI-Dateien: nachvollziehbar und versioniert. - **Umbauen**, falls die Projektierung in BricsCAD korrigieren will: es schreibt dann Einträge in `tro_overrides.ini`/`hw_overrides.ini`, keine eigene JSON. Bis zur Entscheidung bleibt es geparkt. ## Festlegungen (2026-10-11) ### TRO-IDs **Festgelegt:** Die TRO-IDs vergeben die SPS-Programmierer, die zugleich die Anwender der Skripte sind. Eine ID muss nur **eindeutig** sein. Üblich ist eine Nummerierung entlang des Materialflusses und eine Benennung nach den Bauteilen (Kreisel usw.), vorgeschrieben ist das nicht. Sobald größere Teile des SPS-Codes stehen, ändern sich die IDs nicht mehr; in der frühen Projektphase dürfen sie mehrmals neu vergeben werden. **Befund:** Heute vergibt `tro_flow` laufende Nummern je Lauf (`TRO01`, `TRO02` …, `tro_flow.py` Zeile ~1163); `cfg/tro_overrides.ini` warnt bereits, dass sich die Nummern bei neuer Datenbasis verschieben. Die Layout-`TeileId` ist ebenfalls nicht stabil: Vergleich zweier Exporte derselben Anlage | Vergleich | Bauteile | gleiche Id und Lage | gleiche Lage, andere Id | |---|---|---|---| | `mubea.csv` ↔ `mubea_export.csv` | 119 / 125 | 46 | 27 | | `HundM05.csv` ↔ `HundM05_14.csv` | 45 / 44 | 22 | 16 | Stabil ist nur die **Lage** des Bauteils. **Umgesetzt (2026-10-11, `lib/tro_ids.py`, Schalter in `tro_flow.py`; Einzelheiten in `doc/Python_Scripts.md` §4 "TRO-IDs"):** - Eine versionierte ID-Datei je Anlage, `cfg/_tro_ids.ini`, Abschnitt `[tro]`: je TRO die ID und die Lage seines Anker-Separators (x, y in mm), der Kommentar dahinter nennt Typ, Bezeichnung und Separatoren. Von `tro_flow` angelegt, von den SPS-Programmierern umbenannt. Weitere Abschnitte (später Separator- und Förderernummern) bleiben bei jedem Lauf erhalten. Ein anderer Export derselben Anlage nutzt dieselbe Datei über `--ids`. ```ini [tro] TRO01 = -400.0, 13920.0 ; 1Sep1Swi | Separator 0003 auf Kreisel2 Bahn L | Sep 0003 K0302_1 = 10310.0, -440.1 ; von Hand umbenannt ``` - **Standard (IDs halten):** `tro_flow` liest die ID-Datei und findet jeden TRO über die Lage seiner Separatoren wieder (Toleranz `--id-tolerance`, Standard 100 mm, nächster Treffer zuerst, je TRO ein Eintrag). Gefundene TROs behalten ihre ID, auch umbenannte. Neue TROs bekommen die nächste freie Nummer und werden an `[tro]` angehängt; weggefallene stehen im Bericht (mit dem TRO, der ihren Separator jetzt enthält), bleiben in der Datei, ihre ID wird nicht wiederverwendet. `tro_annotate` liest dieselbe Datei, schreibt sie aber nicht. - **Programmschalter `--renumber` (frühe Phase):** alle IDs neu entlang des Materialflusses vergeben (Tiefensuche ab den Beladestationen, auf Kreisel und Strecke in `TrackIds`-Reihenfolge) und `[tro]` neu schreiben. Ohne ID-Datei geschieht dasselbe beim ersten Lauf. - **Geprüft** mit `mubea.csv` → `mubea_export.csv` (andere TeileIds): 18 TROs wiedergefunden, darunter eine umbenannte ID; `TRO14` als weggefallen gemeldet, weil der Export die Separatoren 0005 und 0036 zu einem `2Sep2Swi` (`TRO13`) zusammenfasst. - **Noch offen:** die Abschnitte `[mubea.csv.tro.TRO03]` usw. in `cfg/tro_overrides.ini` hängen an der alten Nummerierung und passen schon heute nicht mehr (`TRO03` ist dort Sep 0010, im aktuellen Lauf Sep 0011). Sie werden auf die IDs der ID-Datei umgestellt, sobald `tro_flow` die Overrides wieder liest. - Dadurch werden auch die Abschnitte `[.tro.]` in `cfg/tro_overrides.ini` und die Zuordnungen von `hw_assign` stabil. ### Staubereiche **Bisheriges Namensschema** (Referenzen UH01–UH05, `doc/TRO_Katalog/connect.ini`, 286 Staubereiche): ```text "DB_JamArea".stJam_ Kurzform JA_ ``` - `` = **Förderer**, auf dem der Staubereich liegt, ein DB je Förderer: `0102` = Kreisel K0102 (vgl. `#fbCarousel0102`), `0103`/`0104` = Strecken, `4103`/`4104` = Vario-Förderer. - `` = laufende Nummer **entlang des Materialflusses** auf diesem Förderer; Kreisel K0102 hat `_1` … `_6` in Umlaufrichtung. - 249 der 286 folgen diesem Schema. Sonderfälle: Lagerlinien `Storage1.1.2` (24), Übergabe an eine andere Steuerung `IO_Device_UHxx_TO_UHyy…` bzw. lokal `#arCarrierHandoverJam5000` (9), `JamBuffer101` im Leerträgerpuffer (2), F2H (1). **Prüfung der Annahme "ein Staubereich = Ausgang des Vorgängers und Eingang des Nachfolgers":** - **Kein** Staubereich hat mehr als einen Nachfolger-TRO. - **65** Staubereiche haben mehrere Vorgänger: dort laufen Ströme zusammen, z. B. `JA0102_2` mit Vorgängern TRO101, TRO106 und TRO107 (Weichenausgang), alle mit Nachfolger TRO112. - Jeder TRO hat genau einen Eingangs-Staubereich; einzige Ausnahme sind die PinStore-TROs (zusätzlich die Lagerlinie). Ein Staubereich ist also immer **der Eingang genau eines TRO**, aber nicht immer der Ausgang genau eines Vorgängers. Ein Name nach dem Schema `ja_tro3tro4` ist bei Zusammenführungen nicht eindeutig. **Vorschlag:** **Ergänzung (2026-10-11):** In einem Mubea-Layout heißen die Staubereiche nach **ihrem Separator**: der Staubereich vor `Sep1024` heißt `J1024`. Das passt zum Befund oben: der Staubereich ist die Warteschlange vor dem Stopper, also der Eingang genau eines TRO bzw. genau eines Separators. Das Layout liegt nicht im Repo; Quelle der Separatornummer (Zeichnungsbeschriftung, E-Planung, Vergabe durch die SPS-Programmierer) noch klären. - **Kennung nach dem Separator:** `J`, z. B. `J1024`. Das ist feiner als eine Kennung je TRO und deckt TROs mit mehreren Separatoren (`2Sep1Swi` mit `JamEntr1`/`JamEntr2`) ohne Sonderregel ab. Zusammenführungen sind kein Problem, weil der Name nur am Nachfolger hängt. - **Separatornummern wie TRO-IDs behandeln:** in derselben ID-Datei, über die Lage des Separators wiedergefunden, von den SPS-Programmierern vergeben bzw. übernommen; `--renumber` vergibt sie in der frühen Phase neu. - **Name im SCL — festgelegt: Standard sind die SCL-Dateien UH01–UH05,** also `"DB_JamArea".stJam_`. `J` ist nur die Kennung in der JSON (und die Beschriftung im Layout), nicht der SCL-Name. Für den SCL-Namen braucht es je Staubereich: - ``: die **Förderernummer** des Förderers, auf dem der Staubereich liegt (Kreisel K0102 → `0102`); vergeben von den SPS-Programmierern bzw. aus der Hardwareplanung (`KR-K0102`), gepflegt in der ID-Datei wie TRO-IDs und Separatornummern; - ``: laufende Nummer **entlang des Materialflusses** auf diesem Förderer, aus dem Layout abgeleitet (Kreisel in Umlaufrichtung, beginnend an einer festzulegenden Stelle, z. B. nach der Einschleusung). Ohne Förderernummer bleibt der Name `TODO(E-Planung)` wie heute. - Ausnahmen bleiben Sonderfälle mit eigenem Namen: Lagerlinien, Übergabe an eine andere Steuerung, Leerträgerpuffer. ### Korrekturen in BricsCAD **Zurückgestellt.** Eine Alternative wäre, die `_tro.json` zu visualisieren und von Hand zu bearbeiten. Bis dahin bleibt `tro_extract.py` geparkt, `TROEDIT` unverändert. ## Reihenfolge 1. **TRO-IDs stabil machen:** ID-Datei, Wiederfinden über die Lage, `--renumber`. Darauf bauen Overrides, Staubereiche und Zuordnungen auf. **Erledigt** (2026-10-11). 2. Slots und `jamAreas` in die TRO-JSON (Umbau 1), ein Eingangs-Staubereich je TRO — ohne Slots hat die Zuordnung kein Ziel. **Erster Teil erledigt** (2026-10-11, siehe Umbau 1 "Stand"). 3. `hw_import` mit dem TIA-Import als erster Quelle. 4. `hw_assign` mit der räumlichen Logik aus `e_planning.py`; `e_planning.py` als Übergangshülle. 5. `scl_skeleton` auf `_anlage.json` umstellen, `--eplan` entfernen; `tro_annotate` auf JSON umstellen. 6. Defaults (`cfg/defaults.ini`), `FB_CallSensors`, EA-Liste ohne Positionen. Zu jedem Schritt: `bin/`-Wrapper für neue Skripte, Tests unter `tests/` (kleine TIA-Datei als Testdaten), Golden Files bei Schemawechsel bewusst neu erzeugen, `doc/Python_Scripts.md` ergänzen. ## Offene Entscheidungen - **Staubereiche:** woher die Separatornummer (`Sep1024` → `J1024`) stammt; wo auf einem Kreisel die Zählung `` beginnt (Referenz K0102 prüfen). Das SCL-Schema ist festgelegt (UH01–UH05). - **Toleranz** beim Wiederfinden der TROs über die Lage: vorläufig 100 mm (`--id-tolerance`). Ein weiter verschobener Separator wird als "neu" und "weggefallen" gemeldet, der Programmierer übernimmt die ID von Hand in der ID-Datei. Mit echten Layoutänderungen prüfen, ob der Wert passt. - **Korrekturen in BricsCAD oder in der JSON:** zurückgestellt.