- Klassenhierarchie.md, Kapitel Daten: vollstaendige Rohangaben aus Anlagen-Hardware (CSV-Spalten, Merkmale je TeileArt) und Hardwareplanung (EA-Liste, TIA-Import, WSCAD, positionsdraw) - Datenquellen_Layout_Hardwareplanung.md: was TROs/SCL aus Layout, Hardwareplanung und Defaults bekommen; 1Sep1Swi- und Vario-Aufruf zeilenweise mit Herkunft - Konzept_Layout_Hardware_SCL.md: Zielablauf tro_flow -> hw_import -> hw_assign -> scl_skeleton, Rolle der bestehenden Skripte, Festlegungen zu TRO-IDs (stabil ueber Lage, --renumber) und Staubereichen - CLAUDE.md: beide neuen Dokumente in der Leseliste Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
737 lines
45 KiB
Markdown
737 lines
45 KiB
Markdown
# Klassenhierarchie
|
||
|
||
Bestandsaufnahme aller Python-Klassen in `lib/` (Stand 2026-10-10). 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_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 (eigenständig, ohne Abhängigkeit auf obige)
|
||
tro_overrides.py (importiert model.Connection / model.TroCandidate)
|
||
e_planning.py (Brücke zur E-Planung; importiert dxf_registration, liest das
|
||
CSV über material_flow; scl_context kennt nur EplanDocument)
|
||
```
|
||
|
||
`tro_flow.py` und `tro_annotate.py` definieren keine eigenen Klassen (nur
|
||
Funktionen). `scl_skeleton.py` hat genau eine (`SclTemplates`, Jinja-Anbindung).
|
||
`material_flow.py` hat Graph-Dataclasses — das ist CSV-Rohmodell, nicht
|
||
TRO-Modell.
|
||
|
||
## Vererbung (ASCII)
|
||
|
||
```
|
||
RuntimeError (stdlib) BaseModel (pydantic, stdlib: pydantic)
|
||
| |
|
||
+-- RegistrationError +-- _Positioned (model.py, Mixin)
|
||
(dxf_registration.py) |
|
||
+-- TroBase
|
||
| +-- DerivedTro
|
||
+-- TroCandidate
|
||
```
|
||
|
||
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]
|
||
├── _Positioned(BaseModel) (Mixin: position {x,y,z}, describe_position)
|
||
│ ├── TroBase, TroCandidate
|
||
├── Connection(BaseModel) (from/to/kind zwischen zwei TRO-IDs)
|
||
├── TroCandidate(_Positioned) (gemeldete Stelle, an der ein TRO erwartet wird)
|
||
├── 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]
|
||
└─ candidates: list[TroCandidate]
|
||
|
||
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: tro_id ODER separator)
|
||
├── SplitSpec (TRO aus einem bestehenden heraustrennen)
|
||
├── MergeSpec (mehrere TROs zu einem zusammenfassen)
|
||
└── Overrides (Sammelcontainer für eine CSV-Datei)
|
||
├─ tros: dict[str, TroOverride]
|
||
├─ splits: list[SplitSpec]
|
||
├─ merges: list[MergeSpec]
|
||
├─ connections: list[model.Connection]
|
||
└─ candidates: list[model.TroCandidate]
|
||
|
||
lib/dxf_registration.py [CSV<->DXF Koordinatentransform]
|
||
├── RegistrationError(RuntimeError)
|
||
└── 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)
|
||
|
||
lib/e_planning.py [Brücke Layout <-> E-Planung]
|
||
├── EDevice(BaseModel) (ein BMK aus *_positionsdraw.json: VERW, Bezeichnung, Position)
|
||
├── LayoutPoint (@dataclass: punktartiges Layout-Bauteil in CSV-Koordinaten)
|
||
├── Rule (@dataclass: eine Regel aus cfg/e_planning.ini)
|
||
├── Assignment(BaseModel) (BMK in einer Rolle an einem Layout-Bauteil, mit Abstand)
|
||
├── Drive(BaseModel) (Motor MA ↔ Motorschutz FC)
|
||
└── EplanDocument(BaseModel) (= <csv>_eplan.json)
|
||
├─ transform: Transform (aus dxf_registration)
|
||
├─ assignments: list[Assignment]
|
||
└─ drives: list[Drive]
|
||
```
|
||
|
||
## Auffälligkeiten für ein mögliches Refactoring
|
||
|
||
1. ~~`OpenPoint` existiert zweimal~~ — erledigt 2026-10-10: zu
|
||
`model.TroCandidate` zusammengefasst (`expect` → `type_name`, `x/y/z` →
|
||
`position`); `ConnectionSpec` durch `model.Connection` ersetzt;
|
||
`tro_overrides.py` importiert beide aus `model.py`.
|
||
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 doppelte
|
||
`OpenPoint`-Definition (1) ist behoben; offen bleibt nur optional `StrEnum`
|
||
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.
|
||
|
||
## Daten
|
||
|
||
Bestandsaufnahme aller Daten, die in den Referenz-Aufrufstellen und FB-Schnittstellen
|
||
unter `doc/TRO_Katalog/scl_templates/*.scl` vorkommen (Stand 2026-10-10), und woher
|
||
der Generator sie bekommen muss. Grundlage: die sieben Aufrufstellen
|
||
(`FB_ILS_MTRO_1Sep`, `_1Sep1Swi`, `_1Sep2Swi`, `_1Sep_SSCC`, `_PinStore_Auto`,
|
||
`_Vario`, `_Vario_workStation_CallSite`) und die `VAR_INPUT/OUTPUT/IN_OUT`-Abschnitte der
|
||
vier vollständig vorliegenden FBs (`FB_ILS_MTRO_2Sep1Swi`, `FB_ILS_MTRO_Vario_workStation`,
|
||
`FB_EmptyCarrBuffer`, `FB_LoadingBoom_INBOUND`). Die Beispielwerte stammen aus diesen
|
||
Dateien.
|
||
|
||
**Quellen** (Spalte "Quelle"):
|
||
|
||
| Kürzel | Quelle | Was sie liefert |
|
||
|---|---|---|
|
||
| **L** | Anlagen-Hardware = mechanisches Layout (CSV-Export ILS 2.0, `data/*.csv`) | Bauteile (`TeileArt`: Separator, Scanner, Weiche, Kreisel, Strecke, Gefällestrecke, BTMT Be-/Entladung, Ein-/Ausschleuselement), `Nachbarn`/`TrackIds`, `Merkmale` (Drehrichtung, Antriebfahrtrichtung, Höhen, Längen, Anzahl Separatoren/Scanner, Zuordnung). Daraus abgeleitet: TRO-Typ, Topologie, Reihenfolge, Ziele. |
|
||
| **E** | Hardwareplanung = Elektroplanung (EA-Liste, TIA-Import, WSCAD-Import, `*_positionsdraw.json`; siehe `doc/HundM/EA-Listen-Analyse.md`) | Betriebsmittelkennzeichen und Variablennamen (`BG…` Sensoren, `MB…` Ventile, `MA…` Motoren, `FC…` Motorschutz, `SF…` Taster, `BX…` Scanner), Hardware-Kennungen, welche optionalen Sensoren verbaut sind, Zuordnung zu Schaltschrank und Not-Halt-Kreis. |
|
||
| **K** | Namenskonvention | Name folgt aus TRO-Nummer bzw. Staubereichs-/Weichennummer (`cMainTro<n>`, `cSep<n>.<i>`, `"DB_Interface_HMI".stTRO.TRO<n>` …). Keine externe Quelle nötig, sobald Nummernkreis und Regel feststehen. |
|
||
| **P** | Parameter / Projektstandard | Weder Layout noch E-Planung: Zeiten, Schwellwerte, Projektkonstanten, WCS-/Inbetriebnahme-Logik. Kandidaten für Typ-Defaults (`doc/TRO_Typen.md`). |
|
||
|
||
`L + E` heisst: das Layout sagt, **dass/wie viele** es gibt und **wo**, die
|
||
E-Planung liefert den **Namen/die Adresse**. Das ist der häufigste Fall.
|
||
|
||
Die zwei folgenden Abschnitte listen **alle Angaben**, die die beiden externen
|
||
Quellen heute enthalten (Stand 2026-10-11, geprüft an `data/*.csv` und an den
|
||
Exporten unter `kabellaengen/work/`: `easy_sps-1_EA/_TIA/_WSCAD.xlsx`,
|
||
`Mubea_positionsdraw.json`). Was sich daraus für TROs und SCL gewinnen lässt und
|
||
zwei kommentierte SCL-Beispiele: `doc/Datenquellen_Layout_Hardwareplanung.md`.
|
||
|
||
### Angaben 1: Anlagen-Hardware (mechanisches Layout, CSV)
|
||
|
||
Eine Zeile je Bauteil, Trennzeichen `;`, `utf-8-sig`. Gelesen von
|
||
`material_flow.load_elements()`.
|
||
|
||
#### Spalten (alle Bauteilarten)
|
||
|
||
| Spalte | Beispiel | Bedeutung | Nutzen für TRO/SCL |
|
||
|---|---|---|---|
|
||
| `Elementnummer` | `1` | laufende Zeilennummer des Exports | keiner (nicht stabil) |
|
||
| `TeileArt` | `ILS 2.0 Separator` | Bauteilart (Liste unten) | Knotenart, TRO-Typ |
|
||
| `TeileId` | `0008` | stabile ID im Layout | Schlüssel für Topologie, Overrides, E-Brücke |
|
||
| `Bezeichnung` | `VF_1`, `Kreisel :3` | Klartext aus der Zeichnung | Anzeige, REGION-Kommentar |
|
||
| `Planquadrat` | `B/1-C/4` | Raster der Zeichnung | Bericht/Suche |
|
||
| `Anzahl` | `1` | Stückzahl | keiner |
|
||
| `Position` | `5878.63, 4330.96, 2128.70` | Einfügepunkt x, y, z in mm | Lagebezug zur E-Planung (`dxf_registration`), Abstände |
|
||
| `Boundingbox` | `2837.82, 6404.92, 731.58` | Ausdehnung x, y, z in mm | Lage von Weichen/Bahnen |
|
||
| `Insertpoint` | `TV0UEkxg…` | kodierter Einfügepunkt (Base64) | intern |
|
||
| `K1`–`K4` | `AK41AC7XAFQk` | Anschluss-Koordinatensysteme (Base64, je x/y/z) | Anschlussseite, Lage der AS/ES (`decode_kpoint`) |
|
||
| `Nachbarn` | `0006-L, 0014` | berührende Bauteile, bei Kreiseln mit Bahn `-L`/`-R` | Topologie (Kanten) |
|
||
| `TrackIds` | `0044, 0027, 0026` | Anbauteile eines Containers in Baureihenfolge bzw. Umlaufrichtung | Reihenfolge Separator/AS/ES auf Strecke und Kreisel |
|
||
| `Fehler`, `Warnungen` | `nur ein Partner`, `unverbunden` | Prüfmeldungen des Exports | Qualitätsprüfung |
|
||
| `Merkmale` | JSON | artspezifische Eigenschaften (unten) | siehe unten |
|
||
|
||
#### Merkmale je `TeileArt`
|
||
|
||
| TeileArt | Merkmale | Nutzen für TRO/SCL |
|
||
|---|---|---|
|
||
| `ILS 2.0 Kreisel` | `Name`, `Kreiselart` (`STANDARD`), `Drehrichtung` (`UZS`/`GUZ`), `Drehung`, `Höhe in m`, `Abstand (Kreiselachse A - Kreiselachse) in Meter`, `Anzahl der Separatoren`, `Anzahl der Scanner`, `Anzahl der Rampen`, `Geruest fuer Einzelmodul` | Host eines TRO (`xInCarouselRun1/2`), Umlaufrichtung → Reihenfolge, Länge → Staulänge; Anzahlen zur Plausibilisierung |
|
||
| `ILS 2.0 Strecke` | `Typ` (`Streckengruppe`), `Seite_AS`, `Seite_ES`, `Winkel`, `VF_Winkel`, `DeltaH_mm`, `DeltaL_mm`, `L_VF_m` (angetriebene Teilstücke), `L_GF_m` (Gefälleteilstücke), `Montagehoehe_m`, `Hoehe_Von_mm`, `Hoehe_Bis_mm`, `Antriebfahrtrichtung` (`horizontal`/`Auf`), `Anzahl_Separator`, `Anzahl_Scanner`, `Separatorlist` | angetrieben (`L_VF_m`, `Auf`) → Vario/Antrieb; Längen → `tConvEmpty`, Staulänge; Anzahlen zur Plausibilisierung |
|
||
| `ILS 2.0 Gefaellestrecke` | wie Strecke, `L_VF_m` und `Antriebfahrtrichtung` leer | Host "Gefälle" → `1Sep`, `xInCarouselRun1 := TRUE` |
|
||
| `ILS 2.0 Separator` | `Zuordnung` (Host, ggf. mit Bahn `-L`/`-R`), `ARTINR`, `Höhe`, `Drehung`, `Typ` | **TRO-Anker**: jeder Separator ist ein TRO oder Teil davon; Host → TRO-Typ |
|
||
| `ILS 2.0 Scanner` | `Zuordnung`, `Naechster Separator`, `ARTINR`, `Höhe`, `Drehung`, `Typ` | Scanner am TRO (`xPluggedScanner`, `stInBarcodeReader`, REGION-Titel) |
|
||
| `ILS Weiche` | `Kreisel A`, `Kreisel B`, `Achse von Kreisel`, `Beruehrung` (`Kreis/Kreis`), `Erzeugt aus` | Kreisel-Kreisel-Weiche → `1SepNSwi`, `xCar2NecRight/Left`, zweiter Kreisel `xInCarouselRun2` |
|
||
| `ILS Ausschleuselement` | `Kreisel`, `Strecke`/`Förderer`, `Streckenende` (`Anfang`), `Element` (`AS_Element_90_links_3D`), `ARTINR`, `Erzeugt aus` | Abgang Kreisel → Strecke: Weichenausgang `Exit3/4`, Ziel je Abgang |
|
||
| `ILS Einschleuselement` | wie Ausschleuselement, `Streckenende` `Ende` | Zusammenführung Strecke → Kreisel: Vorfahrt (`xPrioDir`, PriorityManager) |
|
||
| `ILS 2.0 BTMT Beladung` / `Entladung` | `ARTINR`, `Höhe`, `Drehung` | Systemgrenze `SYSn`, `xIsBorner`, Ziele; Ladeschleife |
|
||
| `Eingang` / `Ausgang` | `ARTINR`, `Höhe`, `Drehung` | Systemgrenze zu Nachbaranlage, Übergabe-Staubereich |
|
||
|
||
`ARTINR` (Artikelnummer) unterscheidet Bauarten, z. B. Separator `200000077`,
|
||
Scanner `829534306`; daran ließe sich die Weichenbauart (`xNewSwitch`) prüfen.
|
||
|
||
Zusätzlich gibt es den **Sivas-Stücklisten-Export** (`data/*_sivas.csv`:
|
||
`SivasNummer`, `Dispogruppe`, `Laenge[mm]`, `IDs`, `details` mit
|
||
Förder-/Gefällegruppen, Kurven, `MotorSeite`). Er ist eine Stückliste, keine
|
||
Topologie, und wird nicht gelesen; brauchbar wäre `MotorSeite` und die
|
||
Gruppenlängen.
|
||
|
||
**Nicht im Layout:** Betriebsmittelkennzeichen, E/A-Adressen, welche optionalen
|
||
Sensoren verbaut sind, Schaltschrank/Steuerung, Zeiten, Ziel-Nummern des WCS.
|
||
|
||
### Angaben 2: Hardwareplanung (EA-Liste, TIA-Import, WSCAD-Import, positionsdraw)
|
||
|
||
Vier Exporte derselben E-Planung. Gemeinsamer Schlüssel ist das
|
||
**Betriebsmittelkennzeichen (BMK)** `BG3306`, `MB3306` …; nur `positionsdraw`
|
||
kennt eine Lage, nur EA/TIA/WSCAD kennen die E/A-Adresse.
|
||
|
||
**`<projekt>-<n>_EA.xlsx`** — Blatt "Ein-, Ausgaenge", je SPS `n` eine Datei
|
||
|
||
| Spalte | Beispiel | Bedeutung |
|
||
|---|---|---|
|
||
| `Name` | `E324.0` | E/A-Adresse |
|
||
| `Kommentar` | `Stausensor 1 (ILS-CV M0108)` | Funktionsrolle, lfd. Nr., Fördereinheit (M-Nummer), Bereich |
|
||
| `Zeile` | `2` | Verweis Schaltplan |
|
||
|
||
**`<projekt>-<n>_TIA.xlsx`** — Importdatei für das TIA Portal
|
||
|
||
| Blatt / Spalte | Beispiel | Bedeutung | Nutzen für SCL |
|
||
|---|---|---|---|
|
||
| PLC Tags `Name` | `BG3240`, `MB3306`, `MA0062`, `FC0062` | BMK = Variablenname | direkt die Namen in `FB_Main` (`=> "MB3306"`, `:= "FC0145"`) |
|
||
| PLC Tags `Path` | `Import` | Variablentabelle | Ordner der Variablentabelle |
|
||
| PLC Tags `Data Type` | `Bool` | Datentyp (nur Bool) | – |
|
||
| PLC Tags `Logical Address` | `%E324.0`, `%A006.2` | Absolutadresse | Prüfung, `FB_CallSensors` |
|
||
| PLC Tags `Comment` | `Stausensor 1 (ILS-CV M0108)` | wie EA-Kommentar | Rolle, M-Nummer |
|
||
| PLC Tags `Hmi Visible/Accessible/Writeable` | `True` | HMI-Flags | – |
|
||
| PLC Tags `Typeobject ID`, `Version ID` | leer | – | – |
|
||
| Constants `Name` | `cInBG3240`, `cInFC0062`, `cOutMA0062` | Index-Konstante je Signal | genau die Indizes in `"DB_Inputs".Sensors["cInBG3306"]` |
|
||
| Constants `Data Type`, `Value` | `Int`, `1` | Array-Index | Dimensionierung `DB_Inputs` |
|
||
|
||
**`<projekt>-<n>_WSCAD.xlsx`** — Importdatei für das E-CAD
|
||
|
||
| Spalte | Beispiel | Bedeutung | Nutzen für SCL |
|
||
|---|---|---|---|
|
||
| `Anschluss` | `E324.0` | E/A-Adresse | Schlüssel zu EA/TIA |
|
||
| `Kommentar` | wie EA | | |
|
||
| `Bezug` | `=A01+UC0101-KF1DI1` | Anlage, Feldstation (`UC…`), Baugruppe (`KF1DI1`, `KF1FDQ2` = F-Karte) | Steuerungs-/Schaltschrankzuordnung, Safety-Signale |
|
||
| `Symboltext`, `Sondertext 1..8` | meist leer | | |
|
||
|
||
**`<zeichnung>_positionsdraw.json`** — Kabellängen-Tool, E-Symbole aus der Zeichnung
|
||
|
||
| Schlüssel / Attribut | Beispiel | Bedeutung | Nutzen für TRO/SCL |
|
||
|---|---|---|---|
|
||
| `sensors{BMK@SPS}` | `BG0542@UNKNOWN` | Feldgeräte (BG, MB, MA …) | Hauptquelle der Brücke |
|
||
| `.IO` / `.B` | `BG0542` | BMK | Schlüssel zu TIA |
|
||
| `.pos` | `[-42369.8, -11437.2]` | Lage in mm | **Zuordnung zum Layout-Bauteil** (`e_planning.py`) |
|
||
| `.VERW` | `IN SEP`, `SEP`, `STAU_REF-LT`, `AE ABZWEIG`, `ABZWEIG-K104`, `BT BELADEN`, `SYNC P1..4`, `ABFR.FINGER`, `ÜBERGABE`, `ÜBERNAHME`, `BANDBRUCH`, `KETTENBRUCH`, `ZAPPLER P1`, `MOT_0,37kW` | Verwendung (kontrolliertes Vokabular) | **Rolle** des Signals im TRO |
|
||
| `.BEZEICHNUNG` | `STAUSENSOR STR. 01`, `PRÜFUNG BT. POS.1 (KR-K0102 EINFAHRT PUFFER)` | Klartext | Strecken-/Kreiselnummer, Plausibilisierung |
|
||
| `.BESCHR` | `STAUSENSOR` | Gerätebeschreibung | – |
|
||
| `.KENNZEICHNUNG` | `=A1+UH01-KF102` | Steuerung `UH01`, E/A-Station `KF102` | Steuerung, TRO-Nummernkreis, Schaltschrank |
|
||
| `.ARTINR` | `829434300` | Artikelnummer Gerät | Gerätetyp |
|
||
| `.SPS` | `''`, `A/B` | SPS-Nummer (oft leer) | Steuerung |
|
||
| `.A`, `.C`, `.ID`, `.MENGE`, `.POSITION`, `.GRUPPE`, `.ETIKETTE`, `.AUFLOESE`, `.TEXT-D/E/ES/F` | | Blockattribute des Symbols | – |
|
||
| `schaltschrank_elemente{…}` | `FC0200` `MOTORSCHUTZ CONVEYER #100`, `SF…` Taster, `PF…` Leuchten, `QA7000` Hauptschütze, `GF` Encoder, `BP` Druckschalter | Elemente im Schrank, ohne Feldlage | Motorschutz ↔ Motor, Bedienelemente, Encoder |
|
||
| `mappings` | `UH01: [BMK …]` bzw. Verteiler → BMK | Zuordnung zur Steuerung/Verteiler aus der Verkabelung | Steuerungsaufteilung |
|
||
| `distributors`, `racks`, `tunnels` | | Verteiler, Trassen | – |
|
||
| `warnings`, `not_found`, `double_ids` | | Prüfprotokoll | Qualitätsprüfung |
|
||
|
||
**BMK-Präfixe:** `BG` Sensor, `SF` Taster/Schalter, `DI` Diagnose, `BP`
|
||
Druckschalter, `GF` Encoder, `BX` Scanner (nur PROFINET, nicht in den Digitallisten),
|
||
`MB` Ventil (Stopper, Weiche, Ausschleuser), `MA`/`QA` Motor/Schütz, `DQ`
|
||
Sonderausgang, `FC` Motorschutz, `PF` Leuchtmelder.
|
||
|
||
**Nicht in der Hardwareplanung:** Topologie (wer auf wen folgt), TRO-Grenzen,
|
||
Ziele/Weichenrichtungen, Zeiten, Analogwerte und PROFINET-Geräte (Cognex-Variablen
|
||
stehen in der TIA-Gerätekonfiguration, nicht im Tag-Import).
|
||
|
||
### TRO-Kopf und Instanzaufruf
|
||
|
||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||
|---|---|---|---|
|
||
| Existenz, Anzahl, Reihenfolge der TROs | `REGION TRO 100 …` | L | `tro_flow` → `DerivedTro`; REGION-Reihenfolge `scl_context.flow_order()` |
|
||
| TRO-Typ / FB-Typ | `FB_ILS_MTRO_1Sep1Swi` | L (→ Katalog) | Typ aus Separator-Host (`doc/500573_Mubea/TRO_Identifikation_500573.md`), FB aus `tro_catalog` |
|
||
| TRO-Nummer, Instanzname | `#TRO107` | L + E | Anzahl aus L; Nummernkreis je Steuerung (100er = UH01, 200er = UH02 …) ist Projektvorgabe |
|
||
| REGION-Titel | `TRO 267 (1Sep2Switch)+Scanner(BX2100)` | L + E | Typ aus L, Scanner-BMK aus E |
|
||
| `nInMainTroNo` | `"cMainTro100"` | K | Wert der Konstante in der PLC-Konstantentabelle |
|
||
| `nOutStateLast` | `"DB_GLOB_TroState".arTroState["cMainTro100"]` | K | |
|
||
| `stInOutHMI` | `"DB_Interface_HMI".stTRO.TRO100` | K | PinStore/Puffer: eigener UDT `UDT_HMI_TRO_PinStore` |
|
||
| `xInRelease` | `"DB_TRO_TEST".TRO100`, `TRUE`, WCS-Verbindung aktiv | P | Freigabe-/Testlogik, Inbetriebnahme |
|
||
| `xInAllRdyToStart`, `xInCancel`, `xInReset` | `TRUE`, `"cTodoFalse"` | P | meist Konstante |
|
||
| `stInSettings.xIsBorner` (`xIsBorner1/2`) | `TRUE` | L | erster TRO nach Einfahrt in die Anlage (Beladestation / Systemgrenze `SYSn`) |
|
||
| `stInSettings.nBornerDest(1/2)` | – | P | Standardziel für neu angelegte Carrier (WCS) |
|
||
| `stInSettings.bSepType` (`bSep1Type/2Type`) | `.%X2 := TRUE` | E + P | X0 erstes Bein halten, X1 Beladekontrolle, X2 Beladeerfassung — hängt an verbauter Sensorik und Prozess |
|
||
| `stInSettings.nTroStopper1/2`, `nTroSwitchExit2/3` | – | L + K | Nummern der Nachbar-TROs |
|
||
| `stInSettings.sJamSrc` / `sJamDest` | – | L + K | Namen der benachbarten Staubereiche |
|
||
| `stInSettings.xCar2NecRight/Left` | – | L | zweiter Kreisel nötig für Richtung rechts/links (Topologie an einer Kreisel-Weiche) |
|
||
| `tPartSenHigh`, `tInSenHigh`, `tDummyTimeNormal`, `tDummyTimeBorner` | FB-Defaults 500 ms / 3 s / 0 ms | P | Typ-Defaults |
|
||
|
||
### Separator (`stInSeparator<n>`, `xOutStopper<n>`)
|
||
|
||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||
|---|---|---|---|
|
||
| Anzahl Separatoren je TRO | `stInSeparator1`, `stInSeparator2` | L | `ILS 2.0 Separator` + `Zuordnung`, Typ-Katalog `items` |
|
||
| `Settings.nSeparatorNo` | `"cSep107.1"` | K | Layout-Separator-ID (`TeileId`) ist **nicht** diese Nummer |
|
||
| Sensor im Stopper `stSenInSep` | `"DB_Inputs".Sensors["cInBG3242"]` | E | |
|
||
| Sensor nach Stopper `stSenSepLeft`, Beladesensor `stSenPart`, Scan-Start `stSenStartScan` | `["cInBG4465"]` | E | nur wenn verbaut |
|
||
| Stopperventil `xOutStopper<n>` / `xOutStopper` | `=> "MB3242"` | E | BG- und MB-Nummer sind in der E-Planung meist gleich (BG3242/MB3242) |
|
||
| `Settings.xPluggedJam` | `TRUE` | E | Stausensor verbaut? |
|
||
| `Settings.xPluggedPart` | `FALSE` | E | Beladesensor verbaut? |
|
||
| `Settings.xPluggedConfirm`, `xPluggedNullBT` | `FALSE` | E | |
|
||
| `Settings.xPluggedScanner` | `TRUE` | L | Scanner am Separator (`DerivedTro.scanners`) |
|
||
| `Settings.tDelayToNextItem` | `"TimeForSepWaiting_Short"` / `_Medium` | P (+ L) | Wahl Short/Medium hängt am Host (Kreisel/Strecke/Gefälle) |
|
||
| `Settings.tTrailingTime`, `tHandlingTime` | `t#600ms`, `t#200ms`, `"cTimeSepTrailing"` | P | Typ-Default, IBN |
|
||
| `Settings.tJam` | `"TimeForSepJam"` | P | Projektkonstante |
|
||
| `Settings.tSenFree`, `tSenWait` | `T#1500ms`, `T#4000ms` | P | |
|
||
|
||
### Staubereiche (Jam)
|
||
|
||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||
|---|---|---|---|
|
||
| Anzahl Ein-/Ausgänge und Zuordnung Exit-Nr ↔ Nachbar | `arInOutJamEntr1`, `arInOutJamExit2..4` | L | je Vorgänger ein Eingang, je Nachfolger/Weichenabgang ein Ausgang (`SclJam`) |
|
||
| Gleicher Staubereich auf beiden Seiten einer Kante | Exit von A = Entr von B | L | Identität aus der Topologie |
|
||
| Staubereichs-DB und -Index | `"DB_JamArea0102".stJam0102_1.arCarrier` / `.stData` | K (+ E) | Nummerierung der Staubereiche ist Projektvorgabe |
|
||
| Stausensor je Ausgang | `stInSenJam2 := "DB_Inputs".Sensors["cInBG3243"]` | E | |
|
||
| Übergabe-Staubereich an Steuerungsgrenze | `#arCarrierHandoverJam5001`, `#stJamDataHandoverJam5001` | L + E | Steuerungsaufteilung aus der E-Planung |
|
||
| `xInJamAtExit2/3` | – | P | NIU |
|
||
|
||
### Weiche (`stInSwitch<n>`, `FC_Direction`)
|
||
|
||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||
|---|---|---|---|
|
||
| Anzahl Weichen je TRO | `stInSwitch1`, `stInSwitch2` | L | `ILS Weiche`, Ausschleuselement, Typ-Katalog |
|
||
| `Settings.nSwitchNo` | `"cSwi107.1"` | K | |
|
||
| `Settings.nTo1Destinations` | `3` | L | Erreichbarkeit im TRO-Graphen (`reachable_via()`); bei mehreren Weichen Planungsentscheidung |
|
||
| Zielliste je Abgang | `cDest…` | L + P | welche Ziele: L; Zielnummern: Projekt/WCS |
|
||
| `Settings.xPluggedExSen3/4`, `stSenExit3/4` | `FALSE` | E | Abgangssensoren verbaut? |
|
||
| `Settings.xNewSwitch` | `TRUE` | E | Weichenbauart (ggf. über `ARTINR` im CSV prüfbar) |
|
||
| Weichenventile `xOutSw<n>ExTo3/4`, `xOutSw1EntrTo1/2` | `=> "MB3304"` | E | |
|
||
| `FC_Direction`-Aufruf | `nInSwitchNo := "cSwi107.1"`, `InOutCarrier := …stJam0102_1.arCarrier[1]` | K + L | Carrier aus dem Eingangs-Staubereich |
|
||
| Vorfahrt `stInSettings.xPrioDir<n>`, `stInPriorityManagerDir<n>` | `"DB_Prozessdaten".stPriorityManager.udt_TRO107.TroIn` | L + K | Vorfahrt nötig, wo zwei Ströme zusammenlaufen (Kreuzung/Zusammenführung) |
|
||
| `stInPriorityManager.xReleaseOk` | `TRUE` ("braucht keine Vorfahrtsregelung") | L | TRUE, wenn keine Zusammenführung |
|
||
|
||
### Scanner / Barcode / SSCC
|
||
|
||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||
|---|---|---|---|
|
||
| Scanner vorhanden, Anzahl | `stInBarcodeReader`, `stInBarcodeReader1/2` | L | `ILS 2.0 Scanner`, `Anzahl_Scanner` |
|
||
| Scanner-BMK und Index | `#arCognexInterface["cScannerBX2100"]` | E | |
|
||
| Cognex-Variablen | `"bx2100-TriggerReady"`, `-TriggerAck`, `-Results Available`, `-General Fault`, `-TriggerEnable`, `-Trigger`, `-ResultsAck` | E | aus dem BMK nach Regel bildbar, wenn die TIA-Variablentabelle dem Muster folgt |
|
||
| Hardware-Kennung Ergebnisse | `"=A01+UC021-BX2100~Ergebnisse_–_64_Bytes_1"` | E | |
|
||
| Kein Scanner | `#stCognexInterfaceDummy` | L | |
|
||
| `Settings.xActivateCheckASCI`, `tTimeout` | `TRUE`, `T#5s` | P | |
|
||
| `Settings.nStation`, `nScannerPurpose` | `111`, `0` | P | WCS |
|
||
| Zweck SSCC-Scanner, `xInPluggedSSCC` | `#stCognexIntfTro105` | E + P | Layout kennt den zweiten Scanner, nicht seinen Zweck |
|
||
| `stSenEndMeasurement` | `["cInBG3281"]` | E | |
|
||
| `FC_CARR_GLOB_CORRECTION` | `nInStation := 0`, Carrier aus Eingangs-Staubereich | P + L | |
|
||
| WCS-Telegramm, HMI-SSCC-Ausgabe | `FC_Send_GOHDReqM`, `"DB_Interface_HMI".stSSCC.SSCC1107` | P | WCS-Schnittstelle |
|
||
|
||
### Vario (`stInVario`) und Arbeitsplatz
|
||
|
||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||
|---|---|---|---|
|
||
| Vario vorhanden | `FB_ILS_MTRO_Vario` | L | angetriebene Strecke / Variofoerderer |
|
||
| Sensoren `stSenCarrInPos`, `stSenFinger`, `stSenLastPos`, `stSenJam`, `stSenNextSep`, `stSenSepVario` | `["cInBG3026"]` … | E | |
|
||
| Motor `xOutVarioMotor`, Motorschutz `xInMotorProtection` | `"MA0145"`, `"FC0145"` | E | |
|
||
| Finger-Staubereich `arInOutJamFinger` | `"DB_JamArea4103".stJam4103_1` | L + K | |
|
||
| `Settings.tConvEmpty` | `T#45000ms` | L + P | aus Förderlänge (`L_VF_m`) und Geschwindigkeit ableitbar |
|
||
| `Settings.nCountCarrier`, `tWaitForJog`, `tWaitForStop`, `tCarrierInPosDelay`, `tCarrierInPosEdgeDelay`, `tRasterHigh/Low`, `xJamSenDelayActive`, `tJamSenDelay`, `xContinousEmptying`, `SettingsVarioFingerMonitoring` | `1`, `T#300ms` … | P | |
|
||
| Arbeitsplatz `stInStationSensor`, `xInReleaseStation`, `fbConvVario.xBeladenError` | `["cInBG3314"]`, `#"rTrig_Workstation 1".CLK` | E | Taster/Sensor am Arbeitsplatz |
|
||
|
||
### Antrieb, Sicherheit, Betriebsart
|
||
|
||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||
|---|---|---|---|
|
||
| Fördertechnik, die den TRO trägt | `xInCarouselRun1 := #fbCarousel0102.xOutConveyorFullSpeed`; Gefälle: `TRUE` | L | Host-Kreisel aus `DerivedTro.hosts`; Instanzname nach K |
|
||
| `xInCarouselRun2` | zweiter Kreisel | L | TRO an der Berührung zweier Kreisel |
|
||
| `xInConveyorRunIn/Out` (PinStore) | `#fbSortConv1101`, `#fbExitConv1103` | L + K | |
|
||
| `xInSftyOk` | `"DB_InterfaceSafety".stFromSafety.EStopMainOF1.Q` | E | Not-Halt-Kreis |
|
||
| `stInOutMachineState` | `#stInOutControlUnitCabinetOF1` | E | Schaltschrank (OF1/OF2) |
|
||
| `xInAutomatic`, `xInErrorQuit`, `xInQuitLocal` | `#t_Automatik_OF1 AND "SF0120"`, `"SF0002"`, `"SF0121" OR "SF0117"` | E | Bedienelemente |
|
||
| `nInTestTarget` | `"IBN".nTestTarget111` | P | |
|
||
|
||
### Pin-Lager (`FB_ILS_MTRO_PinStore_Auto`) und Leerträgerpuffer (`FB_EmptyCarrBuffer`)
|
||
|
||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||
|---|---|---|---|
|
||
| Anzahl Lagerlinien | `nNoOfLines := 19`, `arInSep[1..5]` | L | Separatoren auf Kreisel mit Pinband / `Separatorlist` |
|
||
| Positionen je Linie, Staulänge | `nNoOfPositions := 70`, `nJamLength := 4` / `"cStorage111ExitJam2"` | L + P | aus Linienlänge und Teilung |
|
||
| Liniensensoren `arInSep[n].stSenTrolley`, `arInJamSensor[n]`, `stInSenJamExit` | `["cInBG3270"]` | E | |
|
||
| Linienventile `arOutSeparatorStore[n]` | `"MB3270"` | E | |
|
||
| Pin-Separator `stInSeparatorPin`, `xOutSeparatorPin` | `["cInBG3000"]`, `"MB3000"` | E | |
|
||
| Referenzsensor "Zappler" `stInSensorReference` | `["cInBG3001"]` | E | |
|
||
| Encoder `stInEncoderParameter` | `#fbEncoderGF2200.nOutPosActDif` | E | |
|
||
| `nDestGeneral`, `nConvStations`, `nStationsMax`, `nStorageThreshold` | `"cDest11100"`, `"cHooksSorterMaxPin111"` | P | WCS/Projekt |
|
||
| `nInBufferNr`, `xInBufferReq/Release/ReleaseNext`, `xInPause` | `101`/`102` | P | Pufferlogik |
|
||
| Lager-/Stations-DBs | `"DB_Storage1.1.1".arLine`, `"DB_ConveyorStations"`, `"DB_HandshakeStorage"` | K + P | |
|
||
| Linien-Zeiten, Simulation | `"cTimeSepHandlingStorageLine"`, `stSimu.*` | P | |
|
||
|
||
### Ladeschleife (`FB_LoadingBoom_INBOUND`)
|
||
|
||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||
|---|---|---|---|
|
||
| Ladeschleife vorhanden, Lage | – | L | Beladestation (`ILS 2.0 BTMT Beladung`), prüfen |
|
||
| Bedienelemente `stInControl.*` | Taster, Knebel-/Schlüsselschalter | E | |
|
||
| Ausleger/Stützfuß `stInBoom.*`, `stInFoot.*` | Endlagen, Motorschutz, Bumper | E | |
|
||
| Analogwerte `stInTilt.wTilt`, `stInDistance.wDistance` | Neigung, Abstand | E | |
|
||
| Sensoren `stInSen2mThreshold`, `stInSenJamEntr/Exit1/Exit2`, `stInSenStopper1/2`, `stInSenGrid` | | E | |
|
||
| Ausgänge `stOutStopper.*`, `stOutBoom.*`, `stOutFoot.*`, `xOutLedMovement`, `xOutLight`, `xOutLedBumper`, `xOutCarousel` | | E | |
|
||
| Schwellwerte `nDiff`, `nZeroOk`, `nStartSlow/Fast`, `nDistanceZero`, `tDiff`, `tZero`, `nTiltComp` | | P | Inbetriebnahme |
|
||
| `stInSettings.*` Zeiten | `tBoomInput := T#3500ms` … | P | |
|
||
| Exit-Staubereich `arInOutCarrierExit`, `stInOutCarrierExit` | | L + K | |
|
||
|
||
### Zusammenfassung
|
||
|
||
- **Aus dem Hardwarelayout (L)** kommt die **Struktur**: welche TROs es gibt, ihr Typ
|
||
und damit FB und Anzahl der Parameterblöcke (Separatoren, Weichen, Scanner, Vario,
|
||
Lagerlinien), Vorgänger/Nachfolger und damit Ein-/Ausgangs-Staubereiche,
|
||
REGION-Reihenfolge, Ziele je Weichenabgang (`nTo1Destinations`), Vorfahrt ja/nein,
|
||
Systemgrenzen (`xIsBorner`), die tragende Fördertechnik (`xInCarouselRun`, Gefälle →
|
||
`TRUE`), ob ein Scanner am Separator sitzt (`xPluggedScanner`) und Längen als
|
||
Grundlage für Zeiten/Staulängen. Das meiste davon ist heute in `DerivedTro` →
|
||
`SclTro` abgebildet; Vorfahrt, `xIsBorner` und Längen noch nicht.
|
||
- **Aus der Elektroplanung (E)** kommen **alle Namen und Adressen**: jeder Sensor
|
||
(`BG`), jedes Ventil (`MB`), Motor (`MA`), Motorschutz (`FC`), Taster (`SF`), Scanner
|
||
(`BX`) samt Cognex-Variablen und Hardware-Kennung, die `xPlugged*`-Schalter (was ist
|
||
tatsächlich verbaut), Weichenbauart, Not-Halt-Kreis, Schaltschrank und
|
||
Steuerungsgrenzen. Im Render-Kontext sind das die `SclName` mit `todo=True`
|
||
(`TODO(E-Planung)`). Offen ist die Brücke: die EA-Liste kennt keine Topologie; es
|
||
braucht eine Zuordnung Layout-Bauteil (Separator-`TeileId`) ↔ BMK (`BG3242`/`MB3242`).
|
||
Diese Brücke baut `lib/e_planning.py` über die Symbolpositionen in
|
||
`*_positionsdraw.json` des Kabellängen-Tools (`doc/E-Planung_Bruecke.md`);
|
||
heute kommen damit Separator-Sensor und Stopperventil in die SCL
|
||
(`scl_skeleton --eplan`).
|
||
- **Namenskonvention (K)**: `cMainTro`, `cSep`, `cSwi`, Staubereichs-DBs, HMI- und
|
||
State-Pfade — sobald Nummernkreise feststehen, ohne weitere Quelle erzeugbar.
|
||
- **Parameter (P)**: Zeiten, Projektkonstanten, WCS-Ziele/Stationen, Test-/IBN-Signale —
|
||
weder L noch E; gehören als Typ-Defaults in den Katalog bzw. in eine
|
||
Projektkonfiguration.
|