# Python Scripts — CLI Tooling Overview **As of:** 2026-08-03 `lib/` currently holds five Python modules (three of them runnable CLI tools, two 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, and an annotated copy of the BricsCAD drawing. 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 layout concept, I/O-list analysis) see `TRO_Typen.md`, `Json_Layout-Konzept.md`, `EA-Listen-Analyse.md`, `BricsCAD_TRO_Symbol.md` and `doc/TRO_Katalog/TRO_Katalog.md`. ## 1. How the pieces fit together ``` CSV export (in %SKEL_DATA%) │ ▼ material_flow.py ──► _material_flow.dot / .svg / .md │ (read_elements, build_graph — reused by tro_flow.py) ▼ tro_flow.py ──► _tro_flow.dot / .svg, _tro_doc.md, _tro_errors.md │ (derives TROs, using the type catalog in tro_catalog.py) ▼ tro_annotate.py ──► _annotated.dxf, _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) ``` - `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** (TROs) from the same graph and draws their flow instead. - `tro_annotate.py` takes the TROs from `tro_flow.py` and burns them into a **copy** of the BricsCAD drawing as attributed blocks (never touches the original). 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/dxf_registration.py`** — derives, verifies and persists the rigid (rotation + offset) transform between the CSV export's coordinate system and the DXF drawing's. Separate, documentation-only tool (not part of the generator pipeline): - **`doc/TRO_Katalog/tro_graphs/gen_tro_graphs.py`** — regenerates the schematic per-type SVGs, the edge-type legend and the per-controller (UH01–UH05) topology graphs embedded in `TRO_Katalog.md`, built from `doc/TRO_Katalog/connect.ini`. ## 2. Running the scripts Each CLI tool is invoked through its `bin/.bat` (Windows) / `bin/.sh` (Linux/macOS) wrapper, which: 1. calls `setenv` to set `SPS_SKEL`, `SKEL_DATA`, `SKEL_RESULTS`, `SKEL_CFG`, `SKEL_LIB` and prepend `SKEL_LIB` to `PYTHONPATH`; 2. activates `.venv` if it exists (`bin/install_py` creates it); 3. runs the module with `py` (falling back to `python`). ```bat bin\material_flow.bat --file mubea.csv --tosvg bin\tro_flow.bat --file mubea.csv --tosvg --doc bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend ``` Conventions shared by all three tools: - `--file` / `--dxf` accept either a bare filename — resolved against `%SKEL_DATA%` — or a full path. - All generated output is written to `%SKEL_RESULTS%`. - `tro_annotate.py` additionally persists the verified CSV↔DXF coordinate offset to `%SKEL_CFG%\dxf_registration.json`. - Every run prints a `====` bordered report to stdout (counts, warnings, output file paths); non-fatal findings are printed as `?`/`!` lines and also do not stop the run. - Requires the packages pinned in `requirements.txt` (`pydantic>=2.0.0`; `ezdxf>=1.4.0` for `tro_annotate.py` only) — run `bin\install_py.bat` once. - `--tosvg` needs Graphviz's `dot` (or `neato`, for `tro_flow.py --use-cords`) on `PATH`, or the `GRAPHVIZ_DOT` environment variable pointing at the `dot` executable. ## 3. `material_flow.py` — material-flow graph of the mechanical layout Reads the CSV export and builds a directed graph of the mechanical objects: - **Gefällestrecke** / **Strecke** (conveyor segments) → one node each. - **Kreisel** (rotary/turntable) → two nodes (`-L` / `-R`), linked by the `Drehrichtung` feature (`UZS` = clockwise = right→left, `GUZ` = counter-clockwise = left→right). - **Separator** / **Scanner** → no node of their own; shown via `Zuordnung` (assignment) on the label of their owning node. Flow direction between conveyor segments is derived from height change (`Hoehe_Von_mm`/`Hoehe_Bis_mm`) or, failing that, from `Antriebfahrtrichtung` ("Auf"/"Ab"); connections whose direction can't be determined are drawn bidirectional and reported as a warning. `--doc` additionally cross-checks stated `Anzahl_Separator`/`Anzahl_Scanner` counts against what was actually attached. | Switch | Argument | Default | Meaning | |---|---|---|---| | `--file` | `NAME` | `export.csv` | CSV input file, resolved against `%SKEL_DATA%`, or a full path. | | `--tosvg` | — | off | Also render the `.dot` file to `.svg` via Graphviz `dot`. | | `--doc` | — | off | Also write a Markdown report (`_material_flow.md`) with object list, connections and plausibility findings. | **Output** (in `%SKEL_RESULTS%`): `_material_flow.dot` (always), `_material_flow.svg` (with `--tosvg`), `_material_flow.md` (with `--doc`). **Exit codes:** `0` ok · `1` input/CLI error · `2` SVG rendering failed (Graphviz). ## 4. `tro_flow.py` — derive and draw the TROs Sister program to `material_flow.py` — same `--file`/`--tosvg`/`--doc` switches, but instead of the mechanical objects it derives the **TROs** (Transfer Route Objects, i.e. control-logic units) from the same graph and draws *their* flow. Derivation rules (see `doc/500573_Mubea/TRO_Identifikation_500573.md` for the source analysis): | Host of the separator | TRO type | |---|---| | Gefällestrecke, ≥ 2 lines sharing an in-/outfeed | `PinStore_Auto` (one block per line group) | | Gefällestrecke, single line | `1Sep` | | Strecke (driven) | `Vario` (one per segment) | | Kreisel lane | `1Sep` | | — additionally: 2 or 3 outgoing paths | upgraded to `1Sep1Swi` / `1Sep2Swi` | A scanner mounted on a separator does **not** change the type (stays `1Sep`); the `1Sep_SSCC` special case can't be recognized from the mechanical layout alone and is only raised as a finding. TRO types, FB blocks, sub-components and colors all come from `lib/tro_catalog.py` — nothing is hard-coded here. | Switch | Argument | Default | Meaning | |---|---|---|---| | `--file` | `NAME` | `export.csv` | CSV input file, resolved against `%SKEL_DATA%`, or a full path. | | `--tosvg` | — | off | Also render the TRO flow diagram to `.svg`. | | `--use-cords` (alias `--use-coords`) | — | off | Place every TRO at the centroid of the components it was built from and render with `neato -n` (fixed positions) instead of `dot`'s computed layout, so the diagram matches the plant layout. Only has an effect together with `--tosvg`; fails if any TRO has no coordinate. | | `--doc` | — | off | Also write a Markdown report (`_tro_doc.md`) with the TRO list, connections, findings and the full type catalog. | **Output** (in `%SKEL_RESULTS%`): `_tro_flow.dot` (always, with `pos` attributes when `--use-cords` was used), `_tro_flow.svg` (with `--tosvg`), `_tro_doc.md` (with `--doc`), `_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 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 with: - one colored marker block per TRO, each type with its own shape (circle, triangle, square, diamond, …; see `TroSymbol` in `tro_catalog.py`) so types are distinguishable even without color; - visible attributes `ID`, `TYPE` (and `FB_BLOCK` with `--fb`); hidden attributes `ITEMS`, `CONFIDENCE`, `SEPARATORS` for downstream tooling; - optional directional flow arrows between TROs (`--flow`) and a legend (`--legend`). The original drawing is never modified. Everything this tool adds lives on its own `TRO_`-prefixed layers and carries XDATA under the app id `SPS_SKEL_TRO`; a re-run removes exactly those tagged objects (not by layer name) and rebuilds them, so hand-drawn content on the same layers survives. Once at least one marker of a given type has been annotated, the `dxfmakros` BricsCAD menu (`SSG_LIB > TRO`) can insert further markers of that type by hand — `TRO_INSERT` reuses the `TRO_SYM_` block definition this tool created and only asks for a point, an id and (optionally) a different type; `TRO_EDIT` changes `ID`/`TYPE` on an existing marker. Neither command draws new shapes — a type with no prior annotated instance in the drawing must be run through this tool at least once first. CSV and DXF live in different coordinate systems; the offset between them is handled by `dxf_registration.py` — derived once, verified on every run, and cached per CSV/DXF filename pair in `%SKEL_CFG%\dxf_registration.json` unless overridden. | Switch | Argument | Default | Meaning | |---|---|---|---| | `--file` | `NAME` | `export.csv` | CSV export, resolved against `%SKEL_DATA%`, or a full path. | | `--dxf` | `NAME` | — | Original drawing as DXF (path, or name resolved against `%SKEL_DATA%`). Required unless `--emit-lisp` is used alone. DWG is not supported. | | `--out` | `NAME` | `_annotated.dxf` | Output filename in `%SKEL_RESULTS%`. | | `--flow` | — | off | Draw directional flow arrows between the TROs. | | `--fb` | — | off | Show the Siemens FB block name as a visible attribute on the marker. | | `--legend` | — | off | Draw a legend of the color groups. | | `--marker-size` | `MM` (float) | `300.0` | Marker radius in mm. | | `--text-height` | `MM` (float) | `220.0` | Text height in mm. | | `--layer-prefix` | `P` | `TRO_` | Prefix for the layers this tool creates. | | `--offset` | `DX,DY` | — | Set the CSV↔DXF offset by hand instead of deriving/loading it. | | `--re-register` | — | off | Re-derive the coordinate offset even if a verified one is already cached in `%SKEL_CFG%`. | | `--max-residual` | `MM` (float) | `600.0` | Largest residual error of the coordinate registration that is still accepted; above it the run fails (exit 4). | | `--check` | — | off | Dry run: only analyze and report, write no output file. | | `--emit-lisp` | — | off | Regenerate `cad/tro_types.lsp` (the type list for the `TRO_INSERT`/`TRO_EDIT` dialogs) from `tro_catalog.py` and exit — no `--dxf` needed. Written under `%DXFM_LISP%` if set (next to the `dxfmakros` SSG_LIB modules that provide the dialogs), otherwise under `/cad`. | **Output** (in `%SKEL_RESULTS%`, unless `--check`): `_annotated.dxf`, `_registration.json` (the coordinate transform used, with its proof). **Exit codes:** `0` ok · `1` input/CLI error · `4` coordinate registration could not be proven (or exceeds `--max-residual`) — resolve with `--offset` or by raising `--max-residual`. ## 6. `tro_catalog.py` — TRO type catalog (library, no CLI) The single source of truth for everything needed to generate or draw a TRO type: short name (as used in `FB_Main`/`connect.ini`), Siemens FB block, sub-components and their counts, color group (for Graphviz/Mermaid/CAD) and CAD marker shape. Ten types are defined in `TRO_CATALOG`, verified against `data/*.scl` where the source is vendored locally and against `TRO_Typen.md`'s mapping table otherwise (see the module docstring, `CATALOG_AS_OF`, for provenance per type). 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. ## 7. `dxf_registration.py` — CSV↔DXF coordinate registration (library, no CLI) Derives the rigid transform (rotation + offset, no scaling) between the CSV export's and the DXF drawing's coordinate systems, by voting on the offset between same-role anchor points (`Separator` and `Scanner` blocks, see `ANCHOR_BLOCKS`), refining the best candidates via a Procrustes fit, and picking the candidate with the most exact matches. Raises `RegistrationError` if fewer than `MIN_INLIERS` (5) exact matches are found for any candidate. Persists/loads the verified transform per CSV/DXF filename pair under `%SKEL_CFG%\dxf_registration.json`. Used exclusively by `tro_annotate.py`; not run directly. ## 8. `gen_tro_graphs.py` — documentation image generator (doc tooling, not part of the generator) Lives under `doc/TRO_Katalog/tro_graphs/`, not `lib/` — it produces the pictures embedded in `TRO_Katalog.md`, not generator output. Two families: 1. one schematic SVG per TRO type showing the block with its *possible* connection edges (`normal`/`dir2`/`dir3`/`bypass`/`finger`/`plc`/`extern`, each with its own color and line style), plus a shared `edge_legend.svg`; 2. one Graphviz topology graph per controller (`uh01`..`uh05`), built from `doc/TRO_Katalog/connect.ini`, rendered to SVG. No command-line switches — run it directly and re-run after editing `connect.ini`: ``` py doc/TRO_Katalog/tro_graphs/gen_tro_graphs.py ``` Needs Graphviz `dot` on `PATH` (or at the default Windows install location); without it the `.dot` files are still written, just not rendered to `.svg`.