28 KiB
Python Scripts — CLI Tooling Overview
As of: 2026-08-20
lib/ currently holds eight Python modules (five of them runnable CLI tools,
three 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.
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 ──► <name>_material_flow.dot / .svg / .md
│ (read_elements, build_graph — reused by tro_flow.py)
▼
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)
material_flow.pymodels the material flow of the mechanical objects only (Gefällestrecke, Strecke, Kreisel).tro_flow.pyis its sister program: it derives the control objects (TROs) from the same graph and draws their flow instead.tro_annotate.pytakes the TROs fromtro_flow.pyand 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.lib/tro_overrides.py— reads the hand-maintained%SKEL_CFG%\tro_overrides.ini(type corrections, open points, TRO merges) that survive re-runs oftro_flow.py. See §6a.
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 inTRO_Katalog.md, built fromdoc/TRO_Katalog/connect.ini.
2. Running the scripts
Each CLI tool is invoked through its bin/<name>.bat (Windows) / bin/<name>.sh
(Linux/macOS) wrapper, which:
- calls
setenvto setSPS_SKEL,SKEL_DATA,SKEL_RESULTS,SKEL_CFG,SKEL_LIBand prependSKEL_LIBtoPYTHONPATH; - activates
.venvif it exists (bin/install_pycreates it); - runs the module with
py(falling back topython).
bin\material_flow.bat --file mubea.csv --tosvg --use-cords --doc
bin\tro_flow.bat --file mubea.csv --tosvg --use-cords --doc
bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend
(--use-cords lays both graphs out at the real plant coordinates; drop it for
Graphviz's computed left-to-right arrangement.)
Conventions shared by the CLI tools:
--file/--dxfaccept either a bare filename — resolved against%SKEL_DATA%— or a full path.- All generated output is written to
%SKEL_RESULTS%. tro_annotate.pyadditionally 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.0fortro_annotate.pyonly) — runbin\install_py.batonce. --tosvgneeds Graphviz'sdot(orneato, fortro_flow.py --use-cords) onPATH, or theGRAPHVIZ_DOTenvironment variable pointing at thedotexecutable.bin/material_flow.bat/.shandbin/tro_flow.bat/.shcheck for the right engine up front when--tosvgis passed and print an earlyWARNUNGif it's missing (GRAPHVIZ_DOTonly coversdot, notneato) — this is just a heads-up before the CSV is even parsed; the Python script still runs and reports the same failure in detail (exit code2) if the SVG step is reached.
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 theDrehrichtungfeature (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. - Einschleuselement / Ausschleuselement / Weiche (from the richer export)
→ no node of their own; they supply the direction of the Kreisel↔Strecke edges
(
Ausschleusung= Kreisel→Strecke at the strecke's Anfang,Einschleusung= Strecke→Kreisel at its Ende, read from theirKreisel/Strecke/StreckenendeMerkmale) and mark the Weiche on a Kreisel↔Kreisel link. A strecke whose two ends touch the same Kreisel (return loop) is split across that Kreisel's two lanes. This makes Kreisel↔Strecke direction deterministic instead of height-inferred. - BTMT Be-/Entladestation (entry/exit points) → one node each, recognised by
TeileArt/BezeichnungcontainingBeladung/BTMT-Beladung(→ entry, a material source) orEntladung/SC_Entladung/Abwurf(→ exit, a sink). An entry gets an outgoing edge, an exit an incoming edge, to the object named in itsNachbarn; if the export lists no neighbour (dxfmakros does not export BTMT yet — see below), the nearest object by 2-D distance is used and a note is printed. Recognition is sps_skel-side only — nothing in dxfmakros changes; it simply activates when such a row appears in the CSV.
Note on BTMT / entry-exit data. dxfmakros'
EXPORTCSVcurrently does not emit the BTMT stations (their block names match no export pattern; documented as "Export noch offen"). Until that is addressed, a freshly exportedmubea.csvwill contain the 61 conveyor items but no BTMT rows, so no entry/exit nodes appear. The recognition above is ready for when BTMT rows are present.
In the rendered graph, parallel Gefällestrecken between the same two Kreisel
lanes with the same slope (Hoehe_Von_mm/Hoehe_Bis_mm) — a typical
line-storage bank — are drawn as a single Gefällestrecke-styled box with a
double border (peripheries=2) instead of one box per line. Its label states
the id range and count of the merged lines plus the combined Separator/Scanner
id ranges, e.g. Gefaellestrecke 0017 - 0036 (20x) / Separator (20): 0037 - 0056. This is purely a rendering simplification (_group_parallel_gefaelle
in lib/material_flow.py) — the underlying graph, --doc report and
--connect output are unaffected and still list every line individually.
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. |
--use-cords (alias --use-coords) |
— | off | Place every node at its plant coordinate and render with neato -n (fixed positions) instead of dot, so the material graph matches the plant layout — the same option tro_flow.py has. The two Kreisel lanes (-L/-R) are nudged apart by LANE_NUDGE_MM; scale is COORD_SCALE (0.25 pt/mm, identical to tro_flow.py). Only has an effect together with --tosvg; fails (exit 1) if any flow node has no coordinate. |
--doc |
— | off | Also write a Markdown report (<name>_material_flow.md) with object list, connections and plausibility findings. |
--connect |
— | off | Also write the topology as <name>_connect.ini (item/flow level): […nodes] (Kreisel lanes, Strecke, Gefällestrecke), […connections] with a kind per edge (normal/umlauf/weiche/einschleusung/ausschleusung), and […externals] — the BTMT entry/exit if present, otherwise the open lane-ends listed as entry candidates. A hand-editable bridge into the TRO step, styled after doc/TRO_Katalog/connect.ini. |
--show-bbs |
— | off | Also write <name>_bbs.svg: a plain 2-D top-down view of every CSV row (not just flow nodes — Separator/Scanner/Ein-/Ausschleuselement too), one axis-aligned rectangle per row from the Position column (its center, X/Y) and the Boundingbox column (its width/depth, X/Y — the Z extent is ignored), with the row's Bezeichnung centered in it. No rotation, no Graphviz — hand-built SVG; the viewBox stays in plant millimetres (Y flipped so plant "up" renders as screen "up") but the printed page size is scaled to DIN A3 (landscape or portrait, whichever matches the layout's aspect ratio) instead of the plant's real-world extent. Larger objects are drawn first so small ones (Separator, Scanner, …) stay visible on top. |
Output (in %SKEL_RESULTS%): <name>_material_flow.dot (always, with pos
attributes when --use-cords was used), <name>_material_flow.svg (with --tosvg),
<name>_material_flow.md (with --doc), <name>_connect.ini (with --connect),
<name>_bbs.svg (with --show-bbs).
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 (<name>_tro_doc.md) with the TRO list, connections, findings and the full type catalog. |
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).
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
TroSymbolintro_catalog.py) so types are distinguishable even without color; - visible attributes
ID,TYPE(andFB_BLOCKwith--fb); hidden attributesITEMS,CONFIDENCE,SEPARATORSfor 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_<Typ> 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 |
<csv-stem>_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 <SPS_SKEL>/cad. |
Output (in %SKEL_RESULTS%, unless --check): <name>_annotated.dxf,
<name>_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.
5a. tro_extract.py — read the TROs back out of the drawing into JSON
The counter-direction to tro_annotate.py:
tro_annotate.py CSV + derivation -> markers in the drawing
tro_extract.py drawing -> JSON
It reads only the drawing, never the CSV. So what comes back is what is
actually in the DXF — including anything adjusted by hand in BricsCAD (markers
moved, type changed via TRO_EDIT). That is the point: after a review, the
drawing is the current state, not the export.
It finds the objects tro_annotate.py tagged with XDATA (AppID
SPS_SKEL_TRO): the TRO_SYM_<type> block references (attributes
ID/TYPE/ITEMS/CONFIDENCE/SEPARATORS, plus FB_BLOCK when annotated
with --fb), the flow arrows ("<from>-><to>"), and the open-point markers
("OFFEN:<name>"). Each TRO also gets predecessors/successors computed
from the arrows.
Coordinates appear twice: positionDxf is the drawing coordinate,
position the plant coordinate. The conversion uses the registration written
by tro_annotate.py (<stem>_registration.json; the _annotated suffix and
the file's own dxf_file field are both taken into account when locating it).
If no registration is found, position is left out rather than guessed,
and a warning says so.
Switches: --dxf NAME (default export_annotated.dxf, looked up in
%SKEL_RESULTS%), --out NAME (default <drawing>_tro.json). Exit codes:
0 ok, 1 input error, 2 no TRO markers in the drawing.
fbType is worth one note: unless the drawing was annotated with --fb, the
FB block is not an attribute in the DXF. It is then looked up from
tro_catalog.py and marked "fbTypeFrom": "tro_catalog" — so a consumer can
tell a value that came out of the drawing from one that was resolved.
5b. scl_skeleton.py — generate the FB_Main skeleton as SCL
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
scl_skeleton.py JSON -> FB_Main.scl
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.
It will not compile as delivered, and that is deliberate. Every value that
has to come from the electrical planning is a visible gap marked
TODO(E-Planung) — never an invented value, because a plausible-looking wrong
sensor address is far worse than an empty one. Grep for that marker and nothing
is missed.
| filled from the layout | left as TODO(E-Planung) |
|---|---|
| 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…) |
| destination list per switch exit (graph reachability) | times marked "pruefen" |
| cross-reference to the layout parts (separator numbers, coordinate) |
Two derivations worth knowing about. The destination list per switch exit is
computed by walking the TRO graph from each exit without going back through the
switch — that is exactly what FC_Direction needs to map destination to
direction, and it does not require the I/O list. And nTo1Destinations is
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
<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.
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. Eleven types are defined in TRO_CATALOG. Ten of them are
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).
2Sep2Swiis the exception — it is not backed by any SCL. It was added on 2026-08-12 for project 500573 (Mubea), to model the transfer point at the Kreisel2/Kreisel1 touch as a single control object with 2 separators, 2 scanners and 2 switches.FB_ILS_MTRO_2Sep2Swidoes not exist in this repository or indoc/TRO_Katalog/scl_templates/, and has to be created in the ILSLib — otherwise a future SCL generator will emit a call to a missing block. The closest verified type is2Sep1Swi(2 separators, 2 scanners, 1 switch), which matches the hardware actually present in that export.
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.
6a. tro_overrides.py — manual corrections to the derived TROs (library, no CLI)
tro_flow.py derives TRO types from the mechanical layout alone. Some things
are simply not in the CSV export: a switch that the export does not carry, a
scanner that lifts the type to 1Sep_SSCC, or a spot that a walkthrough
identified as its own transfer point. Those findings come from a site review,
and they must survive the next run — the .dot/.svg/.md/.ini outputs are
all regenerated from scratch every time, so editing them is not an option.
They therefore live in %SKEL_CFG%\tro_overrides.ini (hand-maintained,
never generated). Section names are prefixed with the CSV file they apply to,
so several plants — and several exports of the same plant — can coexist:
[export.csv.tro.SEP0011] ; type correction, keyed on separator 0011
type = 1Sep1Swi ; a type from lib/tro_catalog.py
reason = Weiche vor Ort bestaetigt (Review 2026-08-10)
confidence = bestaetigt ; default when omitted
[mubea.csv.openpoint.Qualitaetskontrolle] ; explicitly NOT a TRO yet
x = -1026.00 ; plant coordinates in mm (CSV system)
y = 5250.05
z = 1478.50
expect = 1Sep1Swi ; type expected here
belongs_to = TRO09 ; TRO that owns the part today
note = ...
A fourth kind folds several derived TROs into one control object:
[export.csv.merge.Beruehrpunkt_K2_K1_gesamt]
type = 2Sep2Swi
separators = 0005, 0057 ; at least two, addressed by separator
host = 0002-R ; primary flow node; the others are kept
label = ...
reason = ...
A third kind creates a TRO the derivation missed entirely:
[export.csv.split.Beruehrpunkt_K2_K1] ; pull a separator out of its TRO
type = 1Sep1Swi
separators = 0057 ; comma-separated
scanners = ; optional, follow the separators
host = 0001-L ; flow node the new TRO sits on
label = ...
reason = ...
Three kinds of entry, deliberately different in weight:
tro— changes the type of an already-derived TRO. Applied after the TRO numbers are assigned (the sections address them by ID) and before the FB block and component list are pulled from the catalog, so the type change drags both along. A manually set type also suppresses the automatic "check whether1Sep_SSCCis needed" hint for that TRO.split— creates a new TRO from separators taken out of an existing one. For transfer points the derivation misses because the separator'sZuordnungpoints at a conveyor (Strecke/Gefaellestrecke) instead of the Kreisel lane it actually switches on. Applied before the numbers are assigned, so the new TROs slot into the normal ordering — which means adding a split renumbers everything after it. That is exactly why type corrections should be keyedSEP<number>, notTRO<number>.merge— folds several TROs into one. Runs after the splits, so a freshly split-out TRO can be part of a merge. The first affected TRO survives and absorbs the others' separators, scanners and hosts;hostpicks which flow node is primary, the rest stay attached so the linking still sees both sides of a transition. Like splits, this renumbers.openpoint— a spot reported in a review that is not turned into a TRO. The structure stays untouched; the point is only marked, in the doc (section 6) and in the annotated drawing on layerTRO_OPENPOINTas a red crossed circle with plain-text label. This keeps "derived" and "asserted" distinguishable until the spot has been checked in BricsCAD.
Consumers: tro_flow.py (type corrections + doc section 6), tro_annotate.py
(corrected marker types + open-point markers), material_flow.py (writes the
[<plant>.manual] section into connect.ini). Unknown TRO IDs and types are
not applied silently — they are reported as warnings in the console output and
in doc section 6.3, which catches the common trap: TRO IDs are running
numbers of one run and are only valid for the CSV named in the section. Swap
the data basis and the numbers shift.
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.
ANCHOR_BLOCKS holds name patterns (fnmatch, case-insensitive), not fixed
names, because two naming worlds occur in this project:
| Drawing | Separator blocks | Scanner blocks |
|---|---|---|
original customer drawing (500573_60_1.dxf) |
S-SP, S-LP |
SCAN |
macro-generated (Mubea.dxf, from dxfmakros) |
Separator_SP_2D/_3D |
Scanner_2D/_3D |
Staustrecke_Separator_SP_* is deliberately not an anchor: it is a
conveyor module with a built-in separator, so its insertion point is the module
origin rather than the separator, which would feed a second cluster — offset by
exactly that distance — into the vote.
Note that the two roles can carry slightly different systematic offsets (in
Mubea.dxf: separators land on +100/0, scanners on +400/0), so a mixed fit
shows a larger residual_max than either role alone. That is expected; the
mean residual is the number to watch, and only pairs within FIT_TOL (50 mm)
enter the Procrustes fit.
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:
- 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 sharededge_legend.svg; - one Graphviz topology graph per controller (
uh01..uh05), built fromdoc/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.