Files
sps_skel/doc/Klassenhierarchie.md
T
Michael StanglandClaude Opus 5.5 0115ef68b7 TRO-JSON v4: Slots je TRO und Staubereiche (jamAreas)
- model: Slot (separator/switch/scanner/drive + Rollen), JamArea (J<Separator>,
  Eingang eines TRO mit allen Vorgaengern), derive_slots, derive_jam_areas;
  Schema sps_skel-tro-v4, v3-Dateien werden weiter gelesen
- scl_context: ein JamEntr je eigenem Staubereich statt je Vorgaenger (wie
  UH01-UH05), Ausgaenge nennen den Staubereich des Nachfolgers
- Golden Files neu erzeugt, Tests fuer Slots/Staubereiche, Doku nachgezogen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:44:02 +02:00

46 KiB
Raw Blame History

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]
│       └─ slots: list[Slot]                  (Stellen fuer die Hardwareplanung)
├── _Positioned(BaseModel)                    (Mixin: position {x,y,z}, describe_position)
│   ├── TroBase, TroCandidate, JamArea
├── Slot(BaseModel)                           (separator1/switch1/scanner1/drive + Rollen)
├── JamArea(_Positioned)                      (Staubereich J<Sep>: Eingang eines TRO)
├── 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]
    └─ jam_areas: list[JamArea]               (JSON: jamAreas)

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-v4) — 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, je TRO die slots aus derive_slots(), die Staubereiche aus derive_jam_areas()). 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:

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 ein Eingang je eigenem Staubereich (TroDocument.jamAreas, Kennung J<Separator>; mehrere Vorgänger fahren in denselben ein), 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.