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>
This commit is contained in:
Michael Stangl
2026-10-11 22:44:02 +02:00
co-authored by Claude Opus 5.5
parent dd2d98f2ee
commit 0115ef68b7
14 changed files with 489 additions and 267 deletions
+10 -5
View File
@@ -70,8 +70,11 @@ 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
│ ├── 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)
@@ -82,7 +85,8 @@ lib/model.py [Instanz-Ebene / TRO-JSON]
├─ tros: list[DerivedTro]
├─ boundaries: list[DerivedTro]
├─ connections: list[Connection]
└─ candidates: list[TroCandidate]
├─ candidates: list[TroCandidate]
└─ jam_areas: list[JamArea] (JSON: jamAreas)
lib/material_flow.py [CSV-Rohmodell -> Graph, @dataclass]
├── Element (1 CSV-Zeile)
@@ -251,13 +255,14 @@ Kantenarten) → Prüfung gegen den Katalog.
### `TroDocument` — `lib/model.py`
**Wozu:** Die TRO-JSON `<csv>_tro.json` (Schema `sps_skel-tro-v3`) — die
**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). `to_json()` schreibt mit den
(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`
@@ -616,7 +621,7 @@ stehen in der TIA-Gerätekonfiguration, nicht im Tag-Import).
| 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`) |
| 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 | |
+21 -1
View File
@@ -88,6 +88,25 @@ Weichenventile (`doc/E-Planung_Bruecke.md`, "Offen").
JSON sie trägt und nicht erst das Rendern sie kennt.
- Schema `sps_skel-tro-v4`.
**Stand (2026-10-11), erster Teil umgesetzt:**
- `model.Slot` und `DerivedTro.slots`: `separator<n>`, `switch<n>`,
`scanner<n>`, `drive` mit Rollen, Anzahl aus dem Katalog (`derive_slots`).
Weichen-Slots haben noch keine `layoutId`: `tro_flow` hält das Weichen- bzw.
Ausschleuselement nicht am TRO fest.
- `model.JamArea` und `TroDocument.jamAreas`: je Separator laut Katalog ein
Eingangs-Staubereich `J<Separator>` mit allen Vorgängern, dem Host und der
Lage (`derive_jam_areas`). Bei 2Sep-Typen ist die Aufteilung der Vorgänger
auf die zwei Eingänge offen (Hinweis im Feld `note`); beim PinStore heißt
der Eingang vorläufig nach dem ersten Linien-Separator.
- `scl_context` liest die Staubereiche aus der JSON: **ein** `JamEntr` je
eigenem Staubereich statt je Vorgänger (Abweichung zu UH01–UH05 behoben),
jeder Ausgang nennt im Kommentar den Staubereich des Nachfolgers. Golden
Files neu erzeugt. Eine v3-Datei ohne `jamAreas` wird weiter gelesen.
- **Noch offen aus Umbau 1:** Layout-ID der Weichen, `xIsBorner`, Vorfahrt,
Host-Rolle, Längen (`tConvEmpty`, Staulängen), `<NNNN>`/`<k>` für den
SCL-Namen der Staubereiche.
### 2. Neues Skript `hw_import.py` (Stufe 2)
- Liest TIA-Import (PLC Tags + Constants), EA-Liste, WSCAD-Import und optional
@@ -335,7 +354,8 @@ unverändert.
`--renumber`. Darauf bauen Overrides, Staubereiche und Zuordnungen auf.
**Erledigt** (2026-10-11).
2. Slots und `jamAreas` in die TRO-JSON (Umbau 1), ein Eingangs-Staubereich je
TRO — ohne Slots hat die Zuordnung kein Ziel.
TRO — ohne Slots hat die Zuordnung kein Ziel. **Erster Teil erledigt**
(2026-10-11, siehe Umbau 1 "Stand").
3. `hw_import` mit dem TIA-Import als erster Quelle.
4. `hw_assign` mit der räumlichen Logik aus `e_planning.py`; `e_planning.py` als
Übergangshülle.
+12 -7
View File
@@ -308,7 +308,7 @@ colors all come from `lib/tro_catalog.py` — nothing is hard-coded here.
| `--doc` | — | off | Also write a Markdown report (`<name>_tro_doc.md`) with the TRO list, connections, findings and the full type catalog. |
| `--connect` | — | off | Also write the TRO topology as `<name>_tro_connect.ini` (nodes, connections, system boundaries, predecessors/successors per TRO). Pure output, never read back. |
| `--true-scale` | — | off | Only with `--use-cords`: draw to scale. By default empty stretches longer than 1500 mm between nodes are shortened (order and neighbourhood are kept). |
| `--json` | — | off | Also write the TRO-JSON `<name>_tro.json` (schema `sps_skel-tro-v3`, model `TroDocument` in `lib/model.py`): TROs, system boundaries (`SYSn`), connections with their kind, findings. Input for `scl_skeleton.py`. |
| `--json` | — | off | Also write the TRO-JSON `<name>_tro.json` (schema `sps_skel-tro-v4`, model `TroDocument` in `lib/model.py`): TROs with their `slots`, system boundaries (`SYSn`), connections with their kind, `jamAreas`, findings. Input for `scl_skeleton.py`. |
| `--ids` | `DATEI` | `<name>_tro_ids.ini` | ID file of the plant (name in `%SKEL_CFG%`, or a path). Use it to run another export of the same plant against the same IDs, e.g. `--file mubea_export.csv --ids mubea_tro_ids.ini`. |
| `--renumber` | — | off | Number all TROs afresh along the material flow and rewrite the `[tro]` section of the ID file (early project phase). Renamed IDs are lost; other sections of the file are kept. |
| `--id-tolerance` | `MM` (float) | `100.0` | Maximum distance between an ID-file entry and a separator of a TRO for the TRO to count as found again. |
@@ -489,7 +489,8 @@ is missed.
|---|---|
| REGION order (material flow), instance list, FB type per TRO | sensor/actuator addresses (`cInBG`, `MB`, `MA`) |
| number of separator / switch / scanner parameter blocks | separator and switch numbers, `cMainTro` |
| `nTo1Destinations` when the TRO has exactly one switch | jam areas (`DB_JamArea…`) |
| `nTo1Destinations` when the TRO has exactly one switch | jam-area DB names (`DB_JamArea…`) |
| number of jam entries (one per own jam area, not per predecessor) and which jam area each exit leads to (`J<Separator>` in the comment) | |
| destination list per switch exit (graph reachability) | times marked "pruefen" |
| cross-reference to the layout parts (separator numbers, coordinate) | |
@@ -666,8 +667,8 @@ TRO in a plant, and the whole TRO-JSON, look like. It imports only the catalog.
```
tro_catalog.py type level TroDefinition (items, FB block, color, symbol)
▲ get_tro()
model.py instance level TroBase ─► DerivedTro, Connection, TroCandidate,
Analysis, TroDocument
model.py instance level TroBase ─► DerivedTro, Slot, JamArea, Connection,
TroCandidate, Analysis, TroDocument
▲
tro_flow.py (writes) · scl_skeleton.py (reads) · tro_annotate.py (draws)
```
@@ -675,11 +676,13 @@ tro_flow.py (writes) · scl_skeleton.py (reads) · tro_annotate.py (draws)
| Class | What it is |
|---|---|
| `TroBase` | Core of every TRO: `id`, `type`, `confidence`, `separators`, `predecessors`, `successors`, `position` (`{x,y,z}` in JSON). `definition()`, `fb_block` (JSON `fbType`) and `items` are **read from the catalog** on every access, never stored — so they follow a type change and cannot go stale. |
| `DerivedTro` | `TroBase` + what `tro_flow.py` knows from the derivation: `label`, `hosts`, `scanners`, `reason`, `colocated`, `positionFrom`, `positionPoints`. Also used for the system boundaries `SYSn`. |
| `DerivedTro` | `TroBase` + what `tro_flow.py` knows from the derivation: `label`, `hosts`, `scanners`, `reason`, `colocated`, `positionFrom`, `positionPoints`, `slots`. Also used for the system boundaries `SYSn` (no slots). |
| `Slot` | A place on a TRO where hardware belongs: `slot` (`separator1`, `switch1`, `scanner1`, `drive` — the stable key together with the TRO-ID), `layoutId` (TeileId in the current export, for tracing only; empty for switches, whose element `tro_flow` does not keep yet), `roles` the hardware planning must fill (`sensor`, `stopper`, `jamSensor?` …; `?` = optional). Built by `derive_slots()` from the catalog type. |
| `JamArea` | A jam area = the queue in front of a separator, the entry of exactly one TRO: `id` (`J<Separator>`, e.g. `J0042`), `tro`, `entry` (1, 2 for 2Sep types), `separator`, `host`, `predecessors` (all TROs/stations feeding it — merges have several), `note`, `position`. Built by `derive_jam_areas()`; the SCL name `"DB_JamArea<NNNN>".stJam<NNNN>_<k>` needs the conveyor number from the hardware planning and is not stored. |
| `Connection` | Directed edge `from` → `to` with its `kind` (transfer / umlauf / weiche / unbestimmt / normal / bahn). |
| `TroCandidate` | A spot reported in a review where a TRO is **expected** but not derived (formerly `OpenPoint`): `name`, `type` (the expected catalog type), `position`, `belongsTo` (the TRO that holds the part today), `note`. Not a TRO — no separators, no connections, no SCL. Built by `tro_overrides.py` from `[<csv>.openpoint.<Name>]`; empty in the TRO-JSON for now. |
| `Analysis` | Working result of `tro_flow.analyse_tros` (moved here from `tro_flow.py`). |
| `TroDocument` | The TRO-JSON: `schema` (`sps_skel-tro-v3`), `source`, `created`, `note`, `tros`, `boundaries`, `connections`, `candidates`, `findings`. `from_analysis()`, `to_json()`, `from_json()`. |
| `TroDocument` | The TRO-JSON: `schema` (`sps_skel-tro-v4`), `source`, `created`, `note`, `tros`, `boundaries`, `connections`, `candidates`, `jamAreas`, `findings`. `from_analysis()` (derives slots and jam areas), `to_json()`, `from_json()`, `jam_areas_or_derived()` (a v3 file without `jamAreas` still renders). |
`TroBase` and `TroCandidate` share the `position` handling (JSON `{x,y,z}`,
`x`/`y`/`z`, `describe_position()`) through the private mixin `_Positioned`.
@@ -691,7 +694,9 @@ All models validate on creation and assignment and reject unknown keys
(`extra="forbid"`). JSON keys keep the camelCase names of the TRO-JSON (`id`,
`type`, `fbType`, `belongsTo` …); Python code uses the field names
(`tro_id`, `type_name`, `fb_block`, `belongs_to`) — both are accepted on input.
Schema v3 differs from v2 only in `candidates` replacing `openPoints`.
Schema v3 differs from v2 only in `candidates` replacing `openPoints`; v4 adds
`slots` per TRO and `jamAreas`. A v3 file still reads (both default to empty;
`scl_skeleton` then derives the jam areas while rendering).
Tests: `tests/test_model.py` (round trip, catalog-backed fields, rejection of
wrong keys/items, SCL rendering from a document) — run with
+2 -2
View File
@@ -12,7 +12,7 @@ It is written so that it fits the existing main path unchanged:
```
export.csv ─► tro_flow --json ─► <name>_tro.json ─► scl_skeleton ─► <name>_FB_Main.scl
(dxfmakros (derive) (TroDocument, (render) (skeleton with
EXPORTCSV) sps_skel-tro-v3) TODO(E-Planung))
EXPORTCSV) sps_skel-tro-v4) TODO(E-Planung))
```
Nothing upstream of `scl_skeleton` changes. The TRO-JSON stays the only input, and the
@@ -451,7 +451,7 @@ bin\scl_skeleton.bat --json <name>_tro.json [--templates [DIR]] [--strict] [--na
| File | Where | Who writes it |
|---|---|---|
| `<name>.csv` | `%SKEL_DATA%` | dxfmakros `EXPORTCSV` |
| `<name>_tro.json` | `%SKEL_RESULTS%` | `tro_flow --json` (schema `sps_skel-tro-v3`) |
| `<name>_tro.json` | `%SKEL_RESULTS%` | `tro_flow --json` (schema `sps_skel-tro-v4`) |
| `scl_naming.ini`, `<name>_scl_naming.ini` | `%SKEL_CFG%` | you, once per project (optional) |
| `lib/scl_templates/*.scl.j2` | repo | maintained with the PLC engineers |