# 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`. ### 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. **Vorschlag:** - Eine versionierte ID-Datei je Anlage, z. B. `cfg/_tro_ids.ini`: je TRO die ID und die Lage seines Anker-Separators (x, y in mm), optional ein Name. Von `tro_flow` angelegt, von den SPS-Programmierern umbenannt. ```ini ; Beispiel, Werte erfunden [mubea] TRO107 = 12450.0, 3310.0 ; Kreisel 2 vor Beladung TRO108 = 13250.0, 3310.0 ``` - **Standard (IDs halten):** `tro_flow` liest die ID-Datei und findet jeden TRO über die Lage des Anker-Separators wieder (Toleranz einstellbar). Gefundene TROs behalten ihre ID. Neue TROs bekommen eine vorläufige ID (nächste freie Nummer entlang des Materialflusses) und stehen im Bericht; weggefallene TROs stehen im Bericht, ihre ID wird nicht automatisch wiederverwendet. - **Programmschalter `--renumber` (frühe Phase):** alle IDs neu entlang des Materialflusses vergeben und die ID-Datei neu schreiben. - 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:** - **Kennung in der JSON nach dem Nachfolger:** `JA_`, z. B. `JA_TRO112`; ein zweiter Eingang (PinStore) `JA_TRO208_2`. Eindeutig, solange die TRO-IDs eindeutig sind, und mit ihnen stabil. - **Name im SCL nach dem bisherigen Schema** `"DB_JamArea".stJam_`: `` lässt sich aus dem Layout ableiten (Reihenfolge auf dem Förderer in Flussrichtung), `` ist die Förderernummer. Die vergeben wie die TRO-IDs die SPS-Programmierer bzw. sie kommt aus der Hardwareplanung (`KR-K0102`); sie steht in derselben ID-Datei (`[mubea.foerderer]`, `K0102 = `). Ohne Förderernummer bleibt der Name `TODO(E-Planung)` wie heute. - **Alternative,** falls das alte Schema nicht nötig ist: SCL-Name direkt aus der Kennung (`"DB_JamArea".JA_TRO112`); braucht keine Förderernummern, weicht aber von den bestehenden Projekten und deren `FC_Call_Jams` ab. Entscheidung offen. ### 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. 2. Slots und `jamAreas` in die TRO-JSON (Umbau 1), ein Eingangs-Staubereich je TRO — ohne Slots hat die Zuordnung kein Ziel. 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 - **SCL-Name der Staubereiche:** bisheriges Schema mit Förderernummer (Empfehlung) oder neues Schema direkt aus `JA_`. - **Toleranz** beim Wiederfinden der TROs über die Lage; Verhalten, wenn ein Separator weiter verschoben wurde (Vorschlag: als "neu" + "weggefallen" melden, der Programmierer übernimmt die ID von Hand). - **Korrekturen in BricsCAD oder in der JSON:** zurückgestellt.