model.py: gemeinsames TRO-Modell; Hauptweg CSV -> tro_flow --json -> scl_skeleton
- lib/model.py (neu, pydantic): TroBase -> DerivedTro, Connection, OpenPoint, Analysis (aus tro_flow verschoben), TroDocument = TRO-JSON (Schema sps_skel-tro-v2). items/fbType werden immer ueber den Typ aus tro_catalog gelesen, nie gespeichert; abweichende Werte in einer JSON sind ein Fehler. JSON-Schluessel wie bisher (id, type, fbType ...), extra="forbid". - tro_flow: Tro/Analysis aus model.py; neuer Schalter --json schreibt <name>_tro.json (TroDocument) - scl_skeleton: liest die JSON ueber TroDocument statt per Dict-Zugriff, Bauteile ueber TroItem-Konstanten; ungueltige JSON -> Exit 1 ohne SCL, alte tro_extract-JSON (v1) mit Hinweis auf tro_flow --json - tro_annotate: nur noch Ansicht; derselbe analyse_tros-Aufruf wie tro_flow (inkl. sepliste-Gegenprobe), damit Zeichnung und JSON uebereinstimmen - tro_extract: geparkt (unveraendert) - tests/test_model.py + conftest.py, pytest in requirements.txt aktiviert - Doku: Python_Scripts.md (§6b model.py), Commandlines.md, CLAUDE.md, Aufrufkommentare in bin/ (scl_skeleton.bat zeigte tro_flow-Aufrufe) Geprueft: alle tro_flow/tro_annotate-Ausgaben (8 CSVs) bis auf Zeitstempel identisch zum vorherigen Stand; scl_skeleton alt/neu auf derselben JSON byte-identisch (export, mubea, HundM05); 10 Tests gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
90cafec99c
commit
97a0db6707
+29
-15
@@ -28,15 +28,19 @@ activates `.venv` if present and runs `lib/<name>.py` with all arguments passed
|
||||
through. Every tool accepts `--help`.
|
||||
|
||||
```
|
||||
CSV ─► material_flow ─► tro_flow ─► tro_annotate ─► [edit in BricsCAD] ─► tro_extract ─► scl_skeleton
|
||||
(+ DXF) (DXF only) (JSON → SCL)
|
||||
CSV ─► material_flow ─► tro_flow --json ─► <name>_tro.json ─► scl_skeleton ─► <name>_FB_Main.scl
|
||||
│ (model.py) (validates)
|
||||
└─► tro_annotate (+ DXF) ─► <name>_annotated.dxf viewer only
|
||||
|
||||
parked: tro_extract (DXF → JSON), see 2.4
|
||||
```
|
||||
|
||||
Shared conventions:
|
||||
|
||||
- Input names (`--file`, `--dxf`) are a bare filename resolved against
|
||||
`%SKEL_DATA%`, or a full path. `tro_extract`/`scl_skeleton` look up their
|
||||
input in `%SKEL_RESULTS%` instead (it is the output of the previous step).
|
||||
`%SKEL_DATA%`, or a full path. `scl_skeleton` (and the parked `tro_extract`)
|
||||
look up their input in `%SKEL_RESULTS%` instead (it is the output of the
|
||||
previous step).
|
||||
- All output goes to `%SKEL_RESULTS%`.
|
||||
- `--tosvg` needs Graphviz `dot` (`neato` with `--use-cords`) on `PATH`, or
|
||||
`GRAPHVIZ_DOT` pointing at `dot`.
|
||||
@@ -78,16 +82,20 @@ Input: CSV export. Details: [`Python_Scripts.md` §4](Python_Scripts.md#4-tro_fl
|
||||
| `--true-scale` | — | off | Only with `--use-cords`: draw to scale (default shortens empty stretches > 1500 mm). |
|
||||
| `--doc` | — | off | Write Markdown report `<name>_tro_doc.md`. |
|
||||
| `--connect` | — | off | Write TRO topology as `<name>_tro_connect.ini`. |
|
||||
| `--json` | — | off | Write the TRO-JSON `<name>_tro.json` (model `lib/model.py`) — input for `scl_skeleton`. |
|
||||
|
||||
Exit codes: `0` ok · `1` input/CLI error · `2` SVG rendering failed ·
|
||||
`3` TRO validation errors (`<name>_tro_errors.md` written).
|
||||
|
||||
```bat
|
||||
bin\tro_flow.bat --file mubea.csv --tosvg --use-cords --doc
|
||||
bin\tro_flow.bat --file mubea.csv --json
|
||||
```
|
||||
|
||||
### 2.3 `tro_annotate` — burn the TROs into a copy of the drawing
|
||||
|
||||
Viewer only: shows the same TROs as `tro_flow` in the drawing for a visual
|
||||
check; the drawing is not read back as data.
|
||||
Input: CSV export + original BricsCAD DXF. The original is never modified.
|
||||
Details: [`Python_Scripts.md` §5](Python_Scripts.md#5-tro_annotatepy--burn-the-tros-into-a-copy-of-the-cad-drawing).
|
||||
|
||||
@@ -117,6 +125,10 @@ bin\tro_annotate.bat --emit-lisp
|
||||
|
||||
### 2.4 `tro_extract` — read the TROs back out of the drawing into JSON
|
||||
|
||||
**Parked** — kept, but not part of the main path. Its JSON (schema
|
||||
`sps_skel-tro-extract-v1`) is rejected by `scl_skeleton`; use
|
||||
`tro_flow --json` instead.
|
||||
|
||||
Input: the (possibly hand-edited) annotated DXF only, never the CSV.
|
||||
Details: [`Python_Scripts.md` §5a](Python_Scripts.md#5a-tro_extractpy--read-the-tros-back-out-of-the-drawing-into-json).
|
||||
|
||||
@@ -133,8 +145,9 @@ bin\tro_extract.bat --dxf mubea_annotated.dxf
|
||||
|
||||
### 2.5 `scl_skeleton` — generate the `FB_Main` SCL skeleton
|
||||
|
||||
Input: TRO-JSON from `tro_extract`. Output is a skeleton with
|
||||
`TODO(E-Planung)` gaps, not a compilable program.
|
||||
Input: TRO-JSON from `tro_flow --json`, validated against `lib/model.py` — a
|
||||
wrong or renamed key stops the run (exit `1`) instead of producing SCL. Output
|
||||
is a skeleton with `TODO(E-Planung)` gaps, not a compilable program.
|
||||
Details: [`Python_Scripts.md` §5b](Python_Scripts.md#5b-scl_skeletonpy--generate-the-fb_main-skeleton-as-scl).
|
||||
|
||||
| Switch | Argument | Default | Effect |
|
||||
@@ -143,11 +156,11 @@ Details: [`Python_Scripts.md` §5b](Python_Scripts.md#5b-scl_skeletonpy--generat
|
||||
| `--out` | `NAME` | `<source>_FB_Main.scl` | Output SCL filename. |
|
||||
| `--start` | `TRO` | — | TRO where the REGION order begins. Only needed while the flow is a closed loop without a loading/unloading station (`SYSn`). |
|
||||
|
||||
Exit codes: `0` ok · `1` input/CLI error.
|
||||
Exit codes: `0` ok · `1` input/CLI error or JSON does not match the model.
|
||||
|
||||
```bat
|
||||
bin\scl_skeleton.bat --json mubea_annotated_tro.json
|
||||
bin\scl_skeleton.bat --json mubea_annotated_tro.json --start TRO01
|
||||
bin\scl_skeleton.bat --json mubea_tro.json
|
||||
bin\scl_skeleton.bat --json mubea_tro.json --start TRO01
|
||||
```
|
||||
|
||||
## 3. End-to-end example
|
||||
@@ -155,18 +168,19 @@ bin\scl_skeleton.bat --json mubea_annotated_tro.json --start TRO01
|
||||
```bat
|
||||
bin\activate_venv.bat
|
||||
bin\material_flow.bat --file mubea.csv --tosvg --doc
|
||||
bin\tro_flow.bat --file mubea.csv --tosvg --use-cords --doc
|
||||
bin\tro_flow.bat --file mubea.csv --tosvg --use-cords --doc --json
|
||||
bin\scl_skeleton.bat --json mubea_tro.json
|
||||
REM optional, for a visual check in BricsCAD:
|
||||
bin\tro_annotate.bat --file mubea.csv --dxf Mubea.dxf --flow --legend
|
||||
REM ... optionally review / edit results\mubea_annotated.dxf in BricsCAD ...
|
||||
bin\tro_extract.bat --dxf mubea_annotated.dxf
|
||||
bin\scl_skeleton.bat --json mubea_annotated_tro.json
|
||||
```
|
||||
|
||||
Tests: `python -m pytest tests/` (with `.venv` active).
|
||||
|
||||
## 4. Documentation tooling (not part of the pipeline)
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| `py doc/TRO_Katalog/tro_graphs/gen_tro_graphs.py` | Regenerates the per-type SVGs, the edge legend and the UH01–UH05 topology graphs for `TRO_Katalog.md` from `doc/TRO_Katalog/connect.ini`. No switches, no `bin/` wrapper. Needs Graphviz `dot`. |
|
||||
|
||||
The libraries `lib/tro_catalog.py`, `lib/dxf_registration.py` and
|
||||
`lib/tro_overrides.py` have no command line of their own.
|
||||
The libraries `lib/tro_catalog.py`, `lib/model.py`, `lib/dxf_registration.py`
|
||||
and `lib/tro_overrides.py` have no command line of their own.
|
||||
|
||||
+92
-25
@@ -1,13 +1,14 @@
|
||||
# Python Scripts — CLI Tooling Overview
|
||||
|
||||
**As of:** 2026-09-17
|
||||
**As of:** 2026-10-09
|
||||
|
||||
`lib/` currently holds nine Python modules (five of them runnable CLI tools,
|
||||
four supporting libraries), plus one documentation-generator script that lives
|
||||
`lib/` currently holds ten Python modules (five of them runnable CLI tools,
|
||||
five supporting libraries), plus one documentation-generator script that lives
|
||||
under `doc/TRO_Katalog/tro_graphs/`. Together the CLI tools turn a CSV export
|
||||
of the mechanical layout (ILS 2.0) into a material-flow graph, a derived TRO
|
||||
list/diagram, an annotated copy of the BricsCAD drawing, a TRO-JSON read back
|
||||
out of that drawing, and an `FB_Main` SCL skeleton generated from that JSON.
|
||||
list/diagram, a TRO-JSON (shared model `lib/model.py`), an `FB_Main` SCL
|
||||
skeleton generated from that JSON, and an annotated copy of the BricsCAD
|
||||
drawing for visual checking.
|
||||
|
||||
This document is the practical "what does each script do and which switches
|
||||
does it take" reference. For the underlying domain model (TRO types, JSON
|
||||
@@ -26,18 +27,25 @@ material_flow.py ──► <name>_material_flow.dot / .svg / .md
|
||||
▼
|
||||
tro_flow.py ──► <name>_tro_flow.dot / .svg, _tro_doc.md, _tro_errors.md
|
||||
│ (derives TROs, using the type catalog in tro_catalog.py)
|
||||
▼
|
||||
tro_annotate.py ──► <name>_annotated.dxf, <name>_registration.json
|
||||
│ (also needs the BricsCAD DXF; uses dxf_registration.py for the CSV↔DXF
|
||||
│ coordinate offset and tro_catalog.py for marker shape/color per TRO type)
|
||||
▼ (drawing may be hand-edited in BricsCAD before the next step)
|
||||
tro_extract.py ──► <drawing>_tro.json
|
||||
│ (reads only the drawing; resolves plant coords via the registration)
|
||||
▼
|
||||
scl_skeleton.py ──► <source>_FB_Main.scl
|
||||
(one REGION per TRO; E-planning values left as TODO(E-Planung) gaps)
|
||||
│
|
||||
├── --json ──► <name>_tro.json (model.TroDocument — MAIN PATH)
|
||||
│ │
|
||||
│ ▼
|
||||
│ scl_skeleton.py ──► <name>_FB_Main.scl
|
||||
│ (validates the JSON against model.py; one REGION per TRO;
|
||||
│ E-planning values left as TODO(E-Planung) gaps)
|
||||
│
|
||||
└── same derivation ──► tro_annotate.py ──► <name>_annotated.dxf (VIEWER ONLY)
|
||||
(also needs the BricsCAD DXF; uses dxf_registration.py for the CSV↔DXF
|
||||
coordinate offset and tro_catalog.py for marker shape/color per TRO type)
|
||||
|
||||
parked: tro_extract.py drawing ──► JSON (not extended; see §5a)
|
||||
```
|
||||
|
||||
The drawing is a **view** for checking the result, not a data source: the
|
||||
TRO-JSON is written by `tro_flow.py --json` straight from the CSV, and
|
||||
`tro_annotate.py` derives exactly the same TROs to draw them.
|
||||
|
||||
- `material_flow.py` models the material flow of the **mechanical** objects only
|
||||
(Gefällestrecke, Strecke, Kreisel).
|
||||
- `tro_flow.py` is its sister program: it derives the **control objects**
|
||||
@@ -51,6 +59,10 @@ Supporting libraries (no CLI of their own, imported by the tools above):
|
||||
- **`lib/tro_catalog.py`** — the single source of truth for TRO types: short
|
||||
name, Siemens FB block, sub-components and counts, color group, CAD marker
|
||||
shape.
|
||||
- **`lib/model.py`** — the shared pydantic model of TRO *instances* and of the
|
||||
TRO-JSON (`TroBase`, `DerivedTro`, `Connection`, `OpenPoint`, `Analysis`,
|
||||
`TroDocument`). Written by `tro_flow.py --json`, read and validated by
|
||||
`scl_skeleton.py`. See §6b.
|
||||
- **`lib/dxf_registration.py`** — derives, verifies and persists the rigid
|
||||
(rotation + offset) transform between the CSV export's coordinate system and
|
||||
the DXF drawing's.
|
||||
@@ -77,7 +89,8 @@ Each CLI tool is invoked through its `bin/<name>.bat` (Windows) / `bin/<name>.sh
|
||||
|
||||
```bat
|
||||
bin\material_flow.bat --file mubea.csv --tosvg --use-cords --doc
|
||||
bin\tro_flow.bat --file mubea.csv --tosvg --use-cords --doc
|
||||
bin\tro_flow.bat --file mubea.csv --tosvg --use-cords --doc --json
|
||||
bin\scl_skeleton.bat --json mubea_tro.json
|
||||
bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend
|
||||
```
|
||||
|
||||
@@ -281,6 +294,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-v2`, model `TroDocument` in `lib/model.py`): TROs, system boundaries (`SYSn`), connections with their kind, findings. Input for `scl_skeleton.py`. |
|
||||
|
||||
TROs, types and connections always come from the layout alone;
|
||||
`cfg/tro_overrides.ini` is not read. Errors in the derivation are fixed in
|
||||
@@ -288,16 +302,21 @@ TROs, types and connections always come from the layout alone;
|
||||
|
||||
**Output** (in `%SKEL_RESULTS%`): `<name>_tro_flow.dot` (always, with `pos`
|
||||
attributes when `--use-cords` was used), `<name>_tro_flow.svg` (with
|
||||
`--tosvg`), `<name>_tro_doc.md` (with `--doc`), `<name>_tro_errors.md` (only
|
||||
if TROs are unconnected or of an unknown type).
|
||||
`--tosvg`), `<name>_tro_doc.md` (with `--doc`), `<name>_tro.json` (with
|
||||
`--json`), `<name>_tro_errors.md` (only if TROs are unconnected or of an
|
||||
unknown type).
|
||||
|
||||
**Exit codes:** `0` ok · `1` input/CLI error · `2` SVG rendering failed
|
||||
(Graphviz) · `3` TRO validation errors found (error file written).
|
||||
|
||||
## 5. `tro_annotate.py` — burn the TROs into a copy of the CAD drawing
|
||||
|
||||
**Viewer only.** The drawing shows the result for a visual check in BricsCAD;
|
||||
it is no longer a data source for the SCL (that is `tro_flow.py --json`).
|
||||
|
||||
Takes the CSV export, the original BricsCAD drawing (DXF) and the TROs
|
||||
derived the same way as `tro_flow.py`, and writes a **copy** of the drawing
|
||||
derived with exactly the same call as `tro_flow.py` (including the separator
|
||||
cross-check from `<name>_raw.json`), and writes a **copy** of the drawing
|
||||
with:
|
||||
|
||||
- one colored marker block per TRO, each type with its own shape (circle,
|
||||
@@ -353,6 +372,13 @@ could not be proven (or its residual error exceeds 600 mm) — resolve with
|
||||
|
||||
## 5a. `tro_extract.py` — read the TROs back out of the drawing into JSON
|
||||
|
||||
> **Parked since 2026-10-09.** The TRO-JSON for `scl_skeleton.py` now comes
|
||||
> from `tro_flow.py --json`. `tro_extract.py` is kept but not extended; its
|
||||
> output (schema `sps_skel-tro-extract-v1`) is **not** accepted by
|
||||
> `scl_skeleton.py` any more, which says so and asks for `tro_flow --json`. If
|
||||
> hand edits in the drawing are ever needed again, it should write the same
|
||||
> `model.TroDocument` format.
|
||||
|
||||
The counter-direction to `tro_annotate.py`:
|
||||
|
||||
```
|
||||
@@ -394,12 +420,18 @@ Last step of the chain:
|
||||
|
||||
```
|
||||
material_flow.py objects -> material flow
|
||||
tro_flow.py flow -> TROs
|
||||
tro_annotate.py TROs -> markers in the drawing
|
||||
tro_extract.py drawing -> JSON
|
||||
tro_flow.py flow -> TROs -> <name>_tro.json (--json)
|
||||
scl_skeleton.py JSON -> FB_Main.scl
|
||||
```
|
||||
|
||||
The JSON is read through `model.TroDocument` and **validated**: an unknown or
|
||||
renamed key, or `items`/`fbType` that do not match the TRO type in
|
||||
`tro_catalog.py`, stops the run with exit code `1` and the validation error —
|
||||
no SCL is written. A file with a different schema (e.g. the old
|
||||
`tro_extract.py` output) gets a short message asking for `tro_flow --json`.
|
||||
Part counts (`items`) and the FB type are taken from the catalog via the TRO
|
||||
type.
|
||||
|
||||
Produces a **skeleton, not a running program**: one `REGION` per TRO with the
|
||||
instance call and every parameter line in the right order, following the real
|
||||
call sites in `doc/TRO_Katalog/scl_templates/*.scl`.
|
||||
@@ -426,14 +458,16 @@ only auto-filled for a single-switch TRO; where several switches share the
|
||||
exits, how they divide them is a planning decision, so it stays a gap rather
|
||||
than a guess.
|
||||
|
||||
Switches: `--json NAME` (default `export_tro.json`), `--out NAME` (default
|
||||
Switches: `--json NAME` (default `export_tro.json`, looked up in
|
||||
`%SKEL_RESULTS%`; written by `tro_flow.py --json`), `--out NAME` (default
|
||||
`<source>_FB_Main.scl`), `--start TRO`. The last one matters while the plant has
|
||||
no loading/unloading station: the flow is then a closed loop with no entry, so
|
||||
the order has to be broken somewhere arbitrary. The chosen start and the reason
|
||||
are recorded in the file header.
|
||||
|
||||
**Stations (`SYSn`).** Loading/unloading stations from `tro_flow.py` appear in
|
||||
the JSON as `SYSn` neighbours, not as TROs. They get no REGION and no instance.
|
||||
the JSON under `boundaries` and as `SYSn` neighbours of the TROs, not in
|
||||
`tros`. They get no REGION and no instance.
|
||||
A TRO fed by a `SYSn` is the start of the order ("Materialeingang ueber SYSn"),
|
||||
so `--start` is no longer needed once the export contains a station. In the
|
||||
jam-area comments they are marked "(Station, kein TRO)", and an exit station
|
||||
@@ -461,7 +495,40 @@ verified against `data/*.scl` where the source is vendored locally, and against
|
||||
Everything is a Pydantic v2 model
|
||||
(`TroDefinition`/`TroStyle`), so it validates on every assignment and comes
|
||||
with `model_dump()`/`model_validate()` for the planned JSON layout model for
|
||||
free. Consumed by `tro_flow.py` and `tro_annotate.py`; not run directly.
|
||||
free. Consumed by `model.py`, `tro_flow.py`, `tro_annotate.py` and
|
||||
`scl_skeleton.py`; not run directly.
|
||||
|
||||
## 6b. `model.py` — shared model of TRO instances and the TRO-JSON (library, no CLI)
|
||||
|
||||
`tro_catalog.py` says what a TRO **type** is; `model.py` says what one concrete
|
||||
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, OpenPoint,
|
||||
Analysis, TroDocument
|
||||
▲
|
||||
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`. |
|
||||
| `Connection` | Directed edge `from` → `to` with its `kind` (transfer / umlauf / weiche / unbestimmt / normal / bahn). |
|
||||
| `OpenPoint` | A reported spot that is not a TRO yet (empty in the JSON for now; meant to replace the class of the same name in `tro_overrides.py`). |
|
||||
| `Analysis` | Working result of `tro_flow.analyse_tros` (moved here from `tro_flow.py`). |
|
||||
| `TroDocument` | The TRO-JSON: `schema` (`sps_skel-tro-v2`), `source`, `created`, `note`, `tros`, `boundaries`, `connections`, `openPoints`, `findings`. `from_analysis()`, `to_json()`, `from_json()`. |
|
||||
|
||||
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`, `openPoints` …); Python code uses the field names
|
||||
(`tro_id`, `type_name`, `fb_block`, `open_points`) — both are accepted on input.
|
||||
|
||||
Tests: `tests/test_model.py` (round trip, catalog-backed fields, rejection of
|
||||
wrong keys/items, SCL rendering from a document) — run with
|
||||
`python -m pytest tests/`.
|
||||
|
||||
## 6a. `tro_overrides.py` — manual corrections to the derived TROs (library, no CLI)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user