diff --git a/CLAUDE.md b/CLAUDE.md index cecfdfe..3a15910 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,23 +9,33 @@ electrical layout (list of sensors, stoppers, switches, etc.) and generates a ** of Siemens SCL code** ("TRO" — Transfer Route Object — blocks for a conveyor/material-flow control system) that can be imported directly into TIA Portal. -The repository is currently in the **design/analysis phase**: `lib/`, `tests/`, `examples/`, -and `cfg/` are empty scaffolding directories (see "Standard Programm Template" structure -below). No generator code has been written yet. What exists today is: +The repository has moved past pure analysis: `lib/` now holds working Python tooling that +derives a material-flow graph and a TRO list from a CSV export (ILS 2.0) and can annotate a +copy of the BricsCAD drawing with the result. What does **not** exist yet is the actual +SCL-skeleton generator — the step that would emit TIA-Portal-importable SCL from a JSON +layout model. `tests/` and `examples/` are still empty scaffolding (see "Standard Programm +Template" below). What exists today is: -- `bin/` — environment/venv management scripts (see "Environment scripts" below) -- `data/*.scl` — real SCL code extracted from an existing plant ("HundM_Fortna", 5 - controllers UH01–UH05), used as reference templates for the planned generator +- `bin/` — environment/venv management scripts, plus one `.bat`/`.sh` wrapper pair per CLI + tool in `lib/` (see "Environment scripts" below) +- `lib/*.py` — CLI tools and libraries that turn a CSV export + BricsCAD DXF into a + material-flow graph, a derived TRO list/diagram, and an annotated copy of the drawing. + See **`doc/Python_Scripts.md`** for what each script does and which switches it takes. +- `data/` — input CSV exports (gitignored, not committed) +- `cad/tro_types.lsp` — generated type list for the BricsCAD `TROEDIT` dialog (written by + `tro_annotate.py --emit-lisp`) +- `cfg/dxf_registration.json` — persisted, verified CSV↔DXF coordinate transform - `doc/*.md` — analysis documents that reverse-engineer the SCL patterns and propose the - JSON schema / code-generation approach + JSON schema / code-generation approach for the generator that's still to be written -Read `doc/` before writing any generator code — it contains the actual domain model this -project is meant to implement. +Read `doc/` before writing any generator code — start with `doc/Python_Scripts.md` for the +existing tooling, then the domain-model documents below; together they contain the actual +domain model this project is meant to implement. ## Environment scripts (bin/) -Every script has a `.bat` (Windows) and `.sh` (Linux/macOS) pair. **None of them invoke a -concrete Python script** — they only manage the environment and virtualenv. +Every script has a `.bat` (Windows) and `.sh` (Linux/macOS) pair. The four scripts below are +environment/venv management only — they never invoke a concrete Python script: | Script | Purpose | |---|---| @@ -34,6 +44,13 @@ concrete Python script** — they only manage the environment and virtualenv. | `activate_venv.bat` / `activate_venv.sh` | Calls `setenv`, activates `.venv` (errors if missing — run `install_py` first) | | `get_cmd.bat` / `get_cmd.sh` | Calls `setenv`, opens a new shell with the environment variables set | +**Convention:** every CLI tool added to `lib/` gets its own `.bat`/`.sh` wrapper pair in +`bin/`, named after the module (e.g. `lib/tro_flow.py` → `bin/tro_flow.bat` / +`bin/tro_flow.sh`). Each wrapper calls `setenv`, activates `.venv` if present, then runs the +module with `py`/`python3 "%*"`/`"$@"`. This supersedes the old "env-only" rule — see +`bin/material_flow.bat`, `bin/tro_flow.bat`, `bin/tro_annotate.bat` (+ `.sh`) for the current +pattern, and `doc/Python_Scripts.md` for what each tool does and which switches it takes. + Typical workflow on Windows: ``` @@ -48,13 +65,14 @@ bash bin/install_py.sh source bin/activate_venv.sh ``` -Requires Python 3.10+. `requirements.txt` currently has no real dependencies pinned yet -(placeholders for `pydantic`, `pytest`). +Requires Python 3.10+. `requirements.txt` pins `pydantic>=2.0.0` (used throughout `lib/` +for validated models) and `ezdxf>=1.4.0` (DXF read/write, `tro_annotate.py` only); `pytest` +is commented out as not yet needed. There is no build/lint/test command configured yet — `tests/` is empty and no test -runner or linter is set up. Once code exists in `lib/`, use `pytest` (already anticipated -in `requirements.txt`) and set `PYTHONPATH` via `bin/setenv` first so imports resolve -against `lib/`. +runner or linter is set up. Once tests exist, use `pytest` (already anticipated in +`requirements.txt`, just commented out) and set `PYTHONPATH` via `bin/setenv` first so +imports resolve against `lib/`. ## Domain model (from doc/) @@ -62,8 +80,12 @@ The target system is a Siemens TIA Portal / SCL project for automated conveyor/c routing, modeled around **TRO** (Transfer Route Object) blocks. Key documents, read in this order for onboarding: -1. **`doc/Json_Layout-Konzept.md`** — the core proposal: a JSON file as single source of - truth (`plc`, `controlUnits`, `sensors[]`, `conveyors[]`, `tros[]`, `loadingBooms[]`, +0. **`doc/Python_Scripts.md`** — the existing CLI tooling (`lib/material_flow.py`, + `lib/tro_flow.py`, `lib/tro_annotate.py`, plus the `lib/tro_catalog.py` and + `lib/dxf_registration.py` libraries they share): what each script does, its switches, + inputs/outputs. This is working code, not a proposal — read it before touching `lib/`. +1. **`doc/HundM/Json_Layout-Konzept.md`** — the core proposal: a JSON file as single source + of truth (`plc`, `controlUnits`, `sensors[]`, `conveyors[]`, `tros[]`, `loadingBooms[]`, `emptyCarrBuffers[]`, `routing`, `jamAreas[]`, `scanners[]`, `connections[]`, `destinations[]`) from which the repetitive, per-topology SCL code in `FB_Main`, `FB_CallSensors`, `FC_Direction`, `FC_Call_Jams` would be generated. @@ -72,34 +94,48 @@ this order for onboarding: combinations of one base type (`1Sep` = one separator/stopper) plus reusable sub-blocks (`FB_ILS_STRO_Sep`, `FB_ILS_STRO_Switch`, `FB_ILS_STRO_Vario`, `FB_BarcodeReaderCognex`, `FB_CarrAccumulate1Sep`). Only `LoadingBoom` is structurally - independent. -3. **`doc/EA-Listen-Analyse.md`** — analyzes the raw I/O list Excel exports + independent. `lib/tro_catalog.py` is the executable form of this catalog (name, FB + block, sub-components, color, CAD symbol per type). +3. **`doc/HundM/EA-Listen-Analyse.md`** — analyzes the raw I/O list Excel exports (`*_EA.xlsx`/`*_TIA.xlsx`/`*_WSCAD.xlsx`) and companion position/cabling JSON exports, and what can vs. cannot be auto-derived from them (signal naming prefixes `BG/SF/DI/BP` for inputs, `MB/MA/QA/DQ/FC/PF` for outputs; topology/timing/customCode cannot be derived from I/O lists alone — those must come from the JSON model or CAD symbol). -4. **`doc/BricsCAD_TRO_Symbol.md`** — proposes a BricsCAD block symbol whose attributes - feed the JSON `tros[]` entries; documents which fields are captured on the symbol vs. - derived from drawing topology or type-based timing defaults. -5. **`doc/SCL_Analyse_Standardisierung.md`** — cross-controller duplication analysis; +4. **`doc/HundM/BricsCAD_TRO_Symbol.md`** — proposes a BricsCAD block symbol whose + attributes feed the JSON `tros[]` entries; documents which fields are captured on the + symbol vs. derived from drawing topology or type-based timing defaults. + `lib/tro_annotate.py` implements a first version of this (marker blocks with `ID`/ + `TYPE`/`FB_BLOCK` attributes), plus the `TROEDIT` BricsCAD dialog (`cad/tro_types.lsp`, + generated by `tro_annotate.py --emit-lisp`). +5. **`doc/HundM/SCL_Analyse_Standardisierung.md`** — cross-controller duplication analysis; identifies ~22 blocks duplicated identically across all 5 controllers (candidates for a shared library) and flags "version chaos" areas (e.g. `FB_StockRemovalBLKModul*` variants) that should NOT be naively merged. -6. **`doc/suggestion.md`** — a follow-up proposal to collapse the 7 `FB_ILS_MTRO_*` +6. **`doc/HundM/suggestion.md`** — a follow-up proposal to collapse the 7 `FB_ILS_MTRO_*` variants into a single `FB_ILS_MTRO` block driven by an array/config descriptor instead of hand-duplicated numbered members (`...1`, `...2`, `Dir1..Dir4`). References a not-yet-written `lib/create_skel.py` (`guess_fbtype()`) as the intended generator - entry point — this is the planned shape of the generator, not existing code. + entry point — this is still the planned shape of the SCL-emitting generator itself; + the current `lib/` tooling derives TROs from a layout but does not yet emit SCL. +7. **`doc/500573_Mubea/TRO_Identifikation_500573.md`** — the derivation rules + `lib/tro_flow.py` implements: how to infer a TRO's type from its separator's host + object (Gefällestrecke/Strecke/Kreisel) when no I/O list or `FB_Main` exists yet. +8. **`doc/TRO_Katalog/TRO_Katalog.md`** — central image/concept catalog per TRO type + (schematic, real layout image, SCL template, JSON example, CAD symbol attributes); + its generated images come from `doc/TRO_Katalog/tro_graphs/gen_tro_graphs.py` + (doc-tooling, not part of the generator — see `doc/Python_Scripts.md` §8) built from + `doc/TRO_Katalog/connect.ini`. ### Used libraries (referenced but not vendored) Most `FB_ILS_MTRO_*` types exist in this repo **only as `.liblink`** references to an -external `ILSLib` library — the actual FB source is not in this Git repo. `data/*.scl` -contains the real **call-site/instantiation code** (parametrization in `FB_Main`) for -those types, not the FB body. Only a few types have local, fully-vendored SCL source -(`FB_ILS_MTRO_Vario_workStation`, `FB_EmptyCarrBuffer`, `FB_LoadingBoom_INBOUND`, -`FB_ILS_MTRO_2Sep1Swi` — the last one unused/orphaned). Treat `data/*.scl` as **read-only -reference material** for pattern extraction, not code to execute or modify. +external `ILSLib` library — the actual FB source is not in this Git repo. +`doc/TRO_Katalog/scl_templates/*.scl` contains the real **call-site/instantiation code** +(parametrization in `FB_Main`) for those types, not the FB body. Only a few types have +local, fully-vendored SCL source (`FB_ILS_MTRO_Vario_workStation`, `FB_EmptyCarrBuffer`, +`FB_LoadingBoom_INBOUND`, `FB_ILS_MTRO_2Sep1Swi` — the last one unused/orphaned). Treat +`doc/TRO_Katalog/scl_templates/*.scl` as **read-only reference material** for pattern +extraction, not code to execute or modify. ## Standard Programm Template @@ -107,18 +143,24 @@ This project follows the user's standard Python project scaffold convention: ``` sps_skel/ - bin/ environment scripts (see above) - cfg/ config files (INI/JSON) — empty for now - data/ input data, gitignored except the .scl reference templates - doc/ documentation + bin/ environment scripts, plus one .bat/.sh wrapper pair per lib/ CLI tool + cad/ generated BricsCAD LISP support (tro_types.lsp) for the TROEDIT dialog + cfg/ config files (INI/JSON); dxf_registration.json holds the persisted, + verified CSV<->DXF coordinate transform + data/ input CSV exports — gitignored, not committed + doc/ documentation — see doc/Python_Scripts.md for the CLI tools, plus the + domain-model documents listed above examples/ example files — empty for now - lib/ Python source — empty for now, this is where the generator belongs + lib/ Python source, importable via SKEL_LIB on PYTHONPATH — CLI tools and + libraries that derive material flow / TRO lists / CAD annotations from a + layout (see doc/Python_Scripts.md); the JSON-driven SCL generator itself + is not yet written log/ gitignored - results/ gitignored + results/ gitignored — CLI tool output (.dot/.svg/.md/.dxf) tests/ unit tests — empty for now ``` -When adding the generator, put it under `lib/` (importable via `SKEL_LIB` on -`PYTHONPATH`), keep `bin/*.bat`/`*.sh` scripts environment-only (do not add -project-specific script invocations to them per the user's convention), and add tests -under `tests/`. +When adding the SCL generator, put it under `lib/` (importable via `SKEL_LIB` on +`PYTHONPATH`), add a `bin/.bat`/`.sh` wrapper pair for it following the pattern of +`bin/tro_flow.bat`/`.sh` (see "Environment scripts" above), document its switches in +`doc/Python_Scripts.md`, and add tests under `tests/`. diff --git a/doc/Python_Scripts.md b/doc/Python_Scripts.md new file mode 100644 index 0000000..f9805ee --- /dev/null +++ b/doc/Python_Scripts.md @@ -0,0 +1,249 @@ +# 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. + +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 `TROEDIT` dialog) 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 dialog), 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`.