From ec009efefeb8958028a5b4e3c26c28d1213ebb91 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Thu, 20 Aug 2026 21:14:05 +0200 Subject: [PATCH 1/5] Readme des Projektes aktualisiert --- README.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 3c2138e..c104363 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,47 @@ -# sps_skel +# sps_skel -> Das Programm erhält ein mechanische Layout von der Projektierung oder -> Angebotswesen zusammen mit dem elektronischen Layout mit der Liste aller -> Sensoren, Stopper, etc. Daraus erstellt es dann ein Skelett für die SPS -> Programmierung, welches direkt ins TIA Portal importiert werden kann. +> Das Programm erhält ein mechanisches Layout von der Projektierung oder dem +> Angebotswesen zusammen mit dem elektrischen Layout (Liste aller Sensoren, +> Stopper, Weichen, etc.). Daraus soll es ein Skelett für die SPS-Programmierung +> (Siemens SCL, TIA Portal) erzeugen — aufgebaut aus **TRO**-Bausteinen +> (Transfer Route Object), den wiederkehrenden Steuerungseinheiten einer +> Fördertechnik-Anlage. + +## Stand des Projekts + +Der eigentliche SCL-Generator (JSON-Layout → TIA-Portal-Code) ist **noch nicht +geschrieben**. Was bereits funktioniert, ist die Ableitung der Vorstufen dazu: +aus einem CSV-Export der mechanischen Planung (ILS 2.0) werden automatisch + +- der **Materialfluss-Graph** der mechanischen Objekte (Strecken, Gefällestrecken, + Kreisel), +- die daraus abgeleitete **TRO-Liste** samt eigenem Flussdiagramm, und +- eine **annotierte Kopie der BricsCAD-Zeichnung** mit einem Marker-Symbol pro + TRO + +erzeugt. Diese drei Werkzeuge liegen als CLI-Tools in `lib/` und sind über +`bin/`-Wrapper aufrufbar (siehe unten). Details, Domänenmodell und die +Roadmap zum Generator stehen in `doc/`, allen voran `doc/Python_Scripts.md`. ## Projektstruktur sps_skel/ - bin/ Skripte zur Umgebungsverwaltung + bin/ Umgebungsskripte + ein .bat/.sh-Wrapper-Paar je CLI-Tool setenv.bat/.sh Umgebungsvariablen setzen install_py.bat/.sh venv erstellen + pip install activate_venv.bat/.sh venv aktivieren get_cmd.bat/.sh Shell mit Umgebung oeffnen - cfg/ Konfigurationsdateien (INI/JSON) - data/ Eingabedaten (nicht im Git) - doc/ Dokumentation + material_flow.bat/.sh Wrapper fuer lib/material_flow.py + tro_flow.bat/.sh Wrapper fuer lib/tro_flow.py + tro_annotate.bat/.sh Wrapper fuer lib/tro_annotate.py + cad/ generiertes BricsCAD-LISP (tro_types.lsp) fuer den TROEDIT-Dialog + cfg/ Konfigurationsdateien (INI/JSON); dxf_registration.json + data/ Eingabedaten - CSV-Exporte, DXF (nicht im Git) + doc/ Dokumentation, siehe doc/Python_Scripts.md examples/ Beispieldateien - lib/ Python-Quellcode / Bibliothek + lib/ Python-Quellcode (CLI-Tools + Bibliotheken) log/ Log-Dateien (nicht im Git) - results/ Ergebnisse / Ausgaben (nicht im Git) + results/ Ergebnisse der CLI-Tools (.dot/.svg/.md/.dxf, nicht im Git) tests/ Unit Tests .gitignore LICENSE @@ -35,9 +57,11 @@ | `SKEL_LIB` | Python-Quellcode | | `SKEL_CFG` | Konfigurationsdateien | | `SKEL_DATA` | Eingabedaten | +| `SKEL_DOC` | Dokumentation | | `SKEL_LOG` | Log-Dateien | | `SKEL_RESULTS` | Ergebnisse | | `SKEL_EXAMPLES` | Beispieldateien | +| `SKEL_TESTS` | Unit Tests | | `PYTHONPATH` | Erweitert um `SKEL_LIB` | ## Installation @@ -45,6 +69,8 @@ ### Voraussetzungen - Python 3.10 oder hoeher +- Graphviz (`dot`) auf `PATH` fuer `--tosvg`, optional aber empfohlen +- fuer `tro_annotate.py`: `ezdxf` (wird per `requirements.txt` installiert) ### Setup (Windows) @@ -71,6 +97,28 @@ bin\activate_venv.bat # Windows source bin/activate_venv.sh # Linux / macOS +### CLI-Tools ausführen + +Jedes Tool wird über seinen `bin/.bat`/`.sh`-Wrapper aufgerufen; dieser +setzt die Umgebung, aktiviert `.venv` und ruft dann das Python-Modul auf. +`--file`/`--dxf` akzeptieren entweder einen Dateinamen (wird gegen +`%SKEL_DATA%` aufgelöst) oder einen vollen Pfad; Ausgaben landen in +`%SKEL_RESULTS%`. + + REM 1) Materialfluss der mechanischen Objekte + bin\material_flow.bat --file mubea.csv --tosvg --doc + + REM 2) TRO-Liste + eigenes Flussdiagramm ableiten + bin\tro_flow.bat --file mubea.csv --tosvg --doc + + REM 3) TROs als Marker-Symbole in eine Kopie der CAD-Zeichnung einbrennen + bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend + +Alle Switches, Ausgabedateien und Exit-Codes der drei Tools sind in +[`doc/Python_Scripts.md`](doc/Python_Scripts.md) dokumentiert; das +Domänenmodell (TRO-Typen, geplantes JSON-Layout, I/O-Listen-Analyse) findet +sich in den übrigen Dokumenten unter `doc/`. + ## Lizenz MIT License - siehe LICENSE From e630ec12d6107fdcc0a026c5677fb94afab877d8 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Thu, 20 Aug 2026 21:29:49 +0200 Subject: [PATCH 2/5] Document the planned DXF-to-JSON-to-SCL generator pipeline Add a Roadmap section to README.md and CLAUDE.md describing the two still-to-be-written lib/ tools (tro_export.py, scl_gen.py) that will turn the annotated CAD drawing into a JSON layout model and then into TIA-Portal-importable SCL, including the --skip-json shortcut. Co-Authored-By: Claude Sonnet 5 --- CLAUDE.md | 39 +++++++++++++++++- README.md | 118 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 119 insertions(+), 38 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3a15910..2e89fc6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,8 +13,10 @@ The repository has moved past pure analysis: `lib/` now holds working Python too 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: +layout model — nor the tool that derives that JSON model from the annotated drawing; see +"Roadmap" below for the planned two-tool pipeline that closes this gap. `tests/` and +`examples/` are still empty scaffolding (see "Standard Programm Template" below). What +exists today is: - `bin/` — environment/venv management scripts, plus one `.bat`/`.sh` wrapper pair per CLI tool in `lib/` (see "Environment scripts" below) @@ -137,6 +139,39 @@ local, fully-vendored SCL source (`FB_ILS_MTRO_Vario_workStation`, `FB_EmptyCarr `doc/TRO_Katalog/scl_templates/*.scl` as **read-only reference material** for pattern extraction, not code to execute or modify. +## Roadmap: DXF → JSON → SCL (planned, not implemented) + +`tro_annotate.py` is where the current pipeline stops today: the user can keep hand-editing +the annotated DXF copy afterwards (via the BricsCAD `TRO_INSERT`/`TRO_EDIT` commands, see +`doc/HundM/BricsCAD_TRO_Symbol.md`) — moving TROs, adding new ones, or changing a type. Two +more `lib/` tools are planned to carry that drawing the rest of the way to importable SCL: + +1. **`lib/tro_export.py`** (planned) — reads the (possibly hand-edited) annotated DXF plus + the CSV export and derives the JSON layout file described in + `doc/HundM/Json_Layout-Konzept.md` (`plc`, `controlUnits`, `sensors[]`, `conveyors[]`, + `tros[]`, `loadingBooms[]`, `emptyCarrBuffers[]`, `routing`, `jamAreas[]`, `scanners[]`, + `connections[]`, `destinations[]`). Per-TRO timing (`trailingTime`, `handlingTime`, + `senFree`, `senWait`, `jamTime`, ...) comes from the type-based default table in + `doc/TRO_Typen.md` unless the CAD symbol carries an `OVERRIDE_TIMING_JSON` value (see + `doc/HundM/BricsCAD_TRO_Symbol.md`), in which case the override wins. The resulting JSON + file is meant to be hand-edited afterwards — that's the intended place to tweak defaults + or individual timings before code generation. +2. **`lib/scl_gen.py`** (planned) — reads the JSON layout file and emits the `.scl` files + (`FB_Main`, `FB_CallSensors`, `FC_Direction`, `FC_Call_Jams` per controller) ready for + TIA Portal import. Takes a `--skip-json` switch for the case where no manual JSON edits + are needed: it then reads the DXF + CSV directly (running the same derivation as + `tro_export.py` internally) and emits SCL immediately, without writing or reading an + intermediate JSON file. + +Both tools follow the existing `lib/` conventions once written: a `bin/.bat`/`.sh` +wrapper pair (see "Environment scripts" above) and a switches/outputs section in +`doc/Python_Scripts.md`. Note this is a separate concept from the `create_skel.py` / +`guess_fbtype()` generator sketched in `doc/HundM/suggestion.md` and +`doc/HundM/Json_Layout-Konzept.md` §14.5 — that one is designed to derive its skeleton JSON +from HundM's Excel I/O-list exports (`*_TIA.xlsx`, `*_positions.json`, ...), a different +input source than this repo's CSV+DXF pipeline. The JSON *schema* it targets is the same +(`doc/HundM/Json_Layout-Konzept.md`); only the derivation source differs. + ## Standard Programm Template This project follows the user's standard Python project scaffold convention: diff --git a/README.md b/README.md index c104363..f31740d 100644 --- a/README.md +++ b/README.md @@ -23,30 +23,64 @@ erzeugt. Diese drei Werkzeuge liegen als CLI-Tools in `lib/` und sind über `bin/`-Wrapper aufrufbar (siehe unten). Details, Domänenmodell und die Roadmap zum Generator stehen in `doc/`, allen voran `doc/Python_Scripts.md`. +## Roadmap + +`tro_annotate.py` ist aktuell die letzte Stufe: Der Nutzer kann die annotierte +DXF-Kopie danach von Hand weiterbearbeiten — TROs verschieben, ergänzen oder +deren Typ ändern (`TRO_INSERT`/`TRO_EDIT` im BricsCAD-Menü, siehe +`doc/HundM/BricsCAD_TRO_Symbol.md`). Geplant sind zwei weitere Tools, die diese +Zeichnung bis zum fertigen SCL-Code weiterführen: + +1. **`tro_export.py`** (geplant) — liest die (ggf. von Hand nachbearbeitete) + annotierte DXF zusammen mit dem CSV-Export und leitet daraus die + JSON-Layout-Datei nach dem in `doc/HundM/Json_Layout-Konzept.md` + beschriebenen Schema ab (`plc`, `controlUnits`, `sensors[]`, `conveyors[]`, + `tros[]`, `loadingBooms[]`, `emptyCarrBuffers[]`, `routing`, `jamAreas[]`, + `scanners[]`, `connections[]`, `destinations[]`). Timing-Werte pro TRO + (`trailingTime`, `handlingTime`, `senFree`, `senWait`, `jamTime`, ...) + kommen dabei standardmäßig aus der typspezifischen Default-Tabelle + (`doc/TRO_Typen.md`); trägt das CAD-Symbol einen `OVERRIDE_TIMING_JSON`-Wert + (siehe `doc/HundM/BricsCAD_TRO_Symbol.md`), gewinnt der Override. Die + erzeugte JSON-Datei ist danach von Hand editierbar, um einzelne Defaults + oder Zeiten anzupassen, bevor daraus SCL generiert wird. +2. **`scl_gen.py`** (geplant) — liest die JSON-Layout-Datei und erzeugt daraus + die `.scl`-Skeleton-Dateien für den direkten TIA-Portal-Import (`FB_Main`, + `FB_CallSensors`, `FC_Direction`, `FC_Call_Jams` je Steuerung). Mit dem + Schalter `--skip-json` läuft dieser Schritt auch ohne manuell + nachbearbeitete JSON-Datei: `scl_gen.py` liest dann DXF und CSV direkt (wie + `tro_export.py`) und erzeugt sofort SCL mit den Typ-Defaults, ohne eine + Zwischen-JSON-Datei zu schreiben oder einzulesen — sinnvoll, wenn an den + Defaults nichts manuell angepasst werden muss. + +Beide Tools bekommen wie üblich ein `bin/.bat`/`.sh`-Wrapper-Paar und +werden in `doc/Python_Scripts.md` dokumentiert, sobald sie existieren. + ## Projektstruktur - sps_skel/ - bin/ Umgebungsskripte + ein .bat/.sh-Wrapper-Paar je CLI-Tool - setenv.bat/.sh Umgebungsvariablen setzen - install_py.bat/.sh venv erstellen + pip install - activate_venv.bat/.sh venv aktivieren - get_cmd.bat/.sh Shell mit Umgebung oeffnen - material_flow.bat/.sh Wrapper fuer lib/material_flow.py - tro_flow.bat/.sh Wrapper fuer lib/tro_flow.py - tro_annotate.bat/.sh Wrapper fuer lib/tro_annotate.py - cad/ generiertes BricsCAD-LISP (tro_types.lsp) fuer den TROEDIT-Dialog - cfg/ Konfigurationsdateien (INI/JSON); dxf_registration.json - data/ Eingabedaten - CSV-Exporte, DXF (nicht im Git) - doc/ Dokumentation, siehe doc/Python_Scripts.md - examples/ Beispieldateien - lib/ Python-Quellcode (CLI-Tools + Bibliotheken) - log/ Log-Dateien (nicht im Git) - results/ Ergebnisse der CLI-Tools (.dot/.svg/.md/.dxf, nicht im Git) - tests/ Unit Tests - .gitignore - LICENSE - README.md - requirements.txt +```text +sps_skel/ + bin/ Umgebungsskripte + ein .bat/.sh-Wrapper-Paar je CLI-Tool + setenv.bat/.sh Umgebungsvariablen setzen + install_py.bat/.sh venv erstellen + pip install + activate_venv.bat/.sh venv aktivieren + get_cmd.bat/.sh Shell mit Umgebung oeffnen + material_flow.bat/.sh Wrapper fuer lib/material_flow.py + tro_flow.bat/.sh Wrapper fuer lib/tro_flow.py + tro_annotate.bat/.sh Wrapper fuer lib/tro_annotate.py + cad/ generiertes BricsCAD-LISP (tro_types.lsp) fuer den TROEDIT-Dialog + cfg/ Konfigurationsdateien (INI/JSON); dxf_registration.json + data/ Eingabedaten - CSV-Exporte, DXF (nicht im Git) + doc/ Dokumentation, siehe doc/Python_Scripts.md + examples/ Beispieldateien + lib/ Python-Quellcode (CLI-Tools + Bibliotheken) + log/ Log-Dateien (nicht im Git) + results/ Ergebnisse der CLI-Tools (.dot/.svg/.md/.dxf, nicht im Git) + tests/ Unit Tests + .gitignore + LICENSE + README.md + requirements.txt +``` ## Umgebungsvariablen @@ -74,28 +108,38 @@ Roadmap zum Generator stehen in `doc/`, allen voran `doc/Python_Scripts.md`. ### Setup (Windows) - bin\install_py.bat +```bat +bin\install_py.bat +``` ### Setup (Linux / macOS) - bash bin/install_py.sh +```sh +bash bin/install_py.sh +``` ## Nutzung ### Umgebung setzen - bin\setenv.bat # Windows - source bin/setenv.sh # Linux / macOS +```bat +bin\setenv.bat # Windows +source bin/setenv.sh # Linux / macOS +``` ### Shell mit gesetzten Variablen oeffnen - bin\get_cmd.bat # Windows - source bin/get_cmd.sh # Linux / macOS +```bat +bin\get_cmd.bat # Windows +source bin/get_cmd.sh # Linux / macOS +``` ### venv aktivieren - bin\activate_venv.bat # Windows - source bin/activate_venv.sh # Linux / macOS +```bat +bin\activate_venv.bat # Windows +source bin/activate_venv.sh # Linux / macOS +``` ### CLI-Tools ausführen @@ -105,14 +149,16 @@ setzt die Umgebung, aktiviert `.venv` und ruft dann das Python-Modul auf. `%SKEL_DATA%` aufgelöst) oder einen vollen Pfad; Ausgaben landen in `%SKEL_RESULTS%`. - REM 1) Materialfluss der mechanischen Objekte - bin\material_flow.bat --file mubea.csv --tosvg --doc +```bat +REM 1) Materialfluss der mechanischen Objekte +bin\material_flow.bat --file mubea.csv --tosvg --doc - REM 2) TRO-Liste + eigenes Flussdiagramm ableiten - bin\tro_flow.bat --file mubea.csv --tosvg --doc +REM 2) TRO-Liste + eigenes Flussdiagramm ableiten +bin\tro_flow.bat --file mubea.csv --tosvg --doc - REM 3) TROs als Marker-Symbole in eine Kopie der CAD-Zeichnung einbrennen - bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend +REM 3) TROs als Marker-Symbole in eine Kopie der CAD-Zeichnung einbrennen +bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend +``` Alle Switches, Ausgabedateien und Exit-Codes der drei Tools sind in [`doc/Python_Scripts.md`](doc/Python_Scripts.md) dokumentiert; das From 9336f184665b22e5b59fb0c56b02135e63b5ccba Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Thu, 20 Aug 2026 23:37:19 +0200 Subject: [PATCH 3/5] Docs: reflect implemented DXF->JSON->SCL first version lib/scl_skeleton.py and lib/tro_extract.py already run the pipeline end-to-end (annotated DXF -> TRO-JSON -> FB_Main SCL skeleton), but CLAUDE.md and README.md still described these as unwritten tools (tro_export.py / scl_gen.py). Update the current-state docs: - CLAUDE.md: project purpose, reading order, roadmap section, and standard-template notes now describe the built pipeline plus the parts still open (full layout JSON schema, other per-controller blocks, timing defaults, --skip-json). - README.md: bin/ tree and usage examples for tro_extract/scl_skeleton. - doc/Python_Scripts.md: intro counts (8 modules / 5 CLI tools), header date, pipeline diagram, and tro_overrides.py in the lib list. Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 120 ++++++++++++++++++++++++------------------ README.md | 68 +++++++++++------------- doc/Python_Scripts.md | 24 ++++++--- 3 files changed, 117 insertions(+), 95 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2e89fc6..3551a10 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,25 +10,30 @@ of Siemens SCL code** ("TRO" — Transfer Route Object — blocks for a conveyor control system) that can be imported directly into TIA Portal. 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 — nor the tool that derives that JSON model from the annotated drawing; see -"Roadmap" below for the planned two-tool pipeline that closes this gap. `tests/` and -`examples/` are still empty scaffolding (see "Standard Programm Template" below). What -exists today is: +runs the **full DXF → JSON → SCL pipeline in a first version**. From a CSV export (ILS 2.0) +it derives a material-flow graph and a TRO list, annotates a copy of the BricsCAD drawing +with the result, reads the (possibly hand-edited) drawing back into a TRO-JSON, and emits an +`FB_Main` **SCL skeleton** from that JSON. The skeleton is deliberately not a running +program — every value that must come from the electrical planning is left as a visible +`TODO(E-Planung)` gap rather than guessed. What is **not** done yet is the fuller layout-JSON +schema (`doc/HundM/Json_Layout-Konzept.md`) as an intermediate format and the generation of +the other per-controller blocks (`FB_CallSensors`, `FC_Direction`/`FC_Call_Jams` as +standalone files); see "Roadmap" below. `tests/` and `examples/` are still empty scaffolding +(see "Standard Programm Template" below). What exists today is: - `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. + material-flow graph, a derived TRO list/diagram, an annotated copy of the drawing, a + TRO-JSON read back out of that drawing, and an `FB_Main` SCL skeleton. 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 +- `cfg/dxf_registration.json` — persisted, verified CSV↔DXF coordinate transform; + `cfg/tro_overrides.ini` — hand-maintained corrections to the derived TROs (see `tro_overrides.py`) - `doc/*.md` — analysis documents that reverse-engineer the SCL patterns and propose the - JSON schema / code-generation approach for the generator that's still to be written + fuller JSON layout schema for the parts of the pipeline still to be built out 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 @@ -83,9 +88,10 @@ routing, modeled around **TRO** (Transfer Route Object) blocks. Key documents, r this order for onboarding: 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/`. + `lib/tro_flow.py`, `lib/tro_annotate.py`, `lib/tro_extract.py`, `lib/scl_skeleton.py`, + plus the `lib/tro_catalog.py`, `lib/dxf_registration.py` and `lib/tro_overrides.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[]`, @@ -116,9 +122,10 @@ this order for onboarding: 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 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. + a `lib/create_skel.py` (`guess_fbtype()`) as the intended generator entry point. That + specific module does not exist in this repo — it is the HundM Excel-based generator + sketch, a different input source (see "Roadmap" below). This repo's own SCL emitter is + `lib/scl_skeleton.py`, which already emits an `FB_Main` skeleton from the TRO-JSON. 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. @@ -139,38 +146,47 @@ local, fully-vendored SCL source (`FB_ILS_MTRO_Vario_workStation`, `FB_EmptyCarr `doc/TRO_Katalog/scl_templates/*.scl` as **read-only reference material** for pattern extraction, not code to execute or modify. -## Roadmap: DXF → JSON → SCL (planned, not implemented) +## Roadmap: DXF → JSON → SCL (first version implemented) -`tro_annotate.py` is where the current pipeline stops today: the user can keep hand-editing -the annotated DXF copy afterwards (via the BricsCAD `TRO_INSERT`/`TRO_EDIT` commands, see -`doc/HundM/BricsCAD_TRO_Symbol.md`) — moving TROs, adding new ones, or changing a type. Two -more `lib/` tools are planned to carry that drawing the rest of the way to importable SCL: +The DXF → JSON → SCL pipeline now runs end-to-end in a first version. After `tro_annotate.py` +burns the TROs into a copy of the drawing, the user can hand-edit that DXF in BricsCAD (via +the `TRO_INSERT`/`TRO_EDIT` commands, see `doc/HundM/BricsCAD_TRO_Symbol.md`) — moving TROs, +adding new ones, changing a type — and two further `lib/` tools carry the drawing the rest of +the way to importable SCL: -1. **`lib/tro_export.py`** (planned) — reads the (possibly hand-edited) annotated DXF plus - the CSV export and derives the JSON layout file described in - `doc/HundM/Json_Layout-Konzept.md` (`plc`, `controlUnits`, `sensors[]`, `conveyors[]`, - `tros[]`, `loadingBooms[]`, `emptyCarrBuffers[]`, `routing`, `jamAreas[]`, `scanners[]`, - `connections[]`, `destinations[]`). Per-TRO timing (`trailingTime`, `handlingTime`, - `senFree`, `senWait`, `jamTime`, ...) comes from the type-based default table in - `doc/TRO_Typen.md` unless the CAD symbol carries an `OVERRIDE_TIMING_JSON` value (see - `doc/HundM/BricsCAD_TRO_Symbol.md`), in which case the override wins. The resulting JSON - file is meant to be hand-edited afterwards — that's the intended place to tweak defaults - or individual timings before code generation. -2. **`lib/scl_gen.py`** (planned) — reads the JSON layout file and emits the `.scl` files - (`FB_Main`, `FB_CallSensors`, `FC_Direction`, `FC_Call_Jams` per controller) ready for - TIA Portal import. Takes a `--skip-json` switch for the case where no manual JSON edits - are needed: it then reads the DXF + CSV directly (running the same derivation as - `tro_export.py` internally) and emits SCL immediately, without writing or reading an - intermediate JSON file. +1. **`lib/tro_extract.py`** (implemented) — reads the (possibly hand-edited) annotated DXF + **only** (not the CSV) and derives a TRO-JSON (`_tro.json`): the TROs tagged with + XDATA, their `predecessors`/`successors` computed from the flow arrows, and plant + coordinates resolved through the persisted registration. See `doc/Python_Scripts.md` §5a. +2. **`lib/scl_skeleton.py`** (implemented) — reads that TRO-JSON and emits an `FB_Main` SCL + skeleton (`_FB_Main.scl`): one `REGION` per TRO with the instance call and every + parameter line in the right order, plus inline `FC_Direction` calls for switch TROs. + Everything derivable from the layout is filled (REGION order, FB type, parameter-block + counts, destination list per switch exit, `nTo1Destinations` for single-switch TROs); + everything that must come from the electrical planning is a `TODO(E-Planung)` gap. + Switches `--json`, `--out`, `--start` (the last picks an entry point while the plant is a + closed loop with no loading/unloading station). See `doc/Python_Scripts.md` §5b. -Both tools follow the existing `lib/` conventions once written: a `bin/.bat`/`.sh` -wrapper pair (see "Environment scripts" above) and a switches/outputs section in -`doc/Python_Scripts.md`. Note this is a separate concept from the `create_skel.py` / -`guess_fbtype()` generator sketched in `doc/HundM/suggestion.md` and -`doc/HundM/Json_Layout-Konzept.md` §14.5 — that one is designed to derive its skeleton JSON -from HundM's Excel I/O-list exports (`*_TIA.xlsx`, `*_positions.json`, ...), a different -input source than this repo's CSV+DXF pipeline. The JSON *schema* it targets is the same -(`doc/HundM/Json_Layout-Konzept.md`); only the derivation source differs. +What is **not** built out yet, relative to the original two-tool design: + +- the fuller **layout-JSON schema** of `doc/HundM/Json_Layout-Konzept.md` (`plc`, + `controlUnits`, `sensors[]`, `conveyors[]`, `tros[]`, `loadingBooms[]`, + `emptyCarrBuffers[]`, `routing`, `jamAreas[]`, `scanners[]`, `connections[]`, + `destinations[]`) as the intermediate format — `tro_extract.py` currently emits a + TRO-focused JSON, not this full schema; +- the **other per-controller blocks** (`FB_CallSensors`, `FC_Direction`/`FC_Call_Jams` as + standalone files) — only `FB_Main` is generated today; +- **type-based timing defaults** from `doc/TRO_Typen.md` and the `OVERRIDE_TIMING_JSON` CAD + attribute (timings are emitted as `TODO(E-Planung): pruefen` placeholders for now); +- a **`--skip-json`** convenience path that would read DXF + CSV directly and emit SCL + without an intermediate JSON file. + +Note the `create_skel.py` / `guess_fbtype()` generator sketched in `doc/HundM/suggestion.md` +and `doc/HundM/Json_Layout-Konzept.md` §14.5 is a **separate concept** and is not implemented +here: it is designed to derive its skeleton JSON from HundM's Excel I/O-list exports +(`*_TIA.xlsx`, `*_positions.json`, ...), a different input source than this repo's CSV+DXF +pipeline. The JSON *schema* it targets is the same (`doc/HundM/Json_Layout-Konzept.md`); only +the derivation source differs. ## Standard Programm Template @@ -188,14 +204,14 @@ sps_skel/ examples/ example files — empty for now 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 + layout, read the TROs back into JSON, and emit an FB_Main SCL skeleton + (see doc/Python_Scripts.md) log/ gitignored - results/ gitignored — CLI tool output (.dot/.svg/.md/.dxf) + results/ gitignored — CLI tool output (.dot/.svg/.md/.dxf/.scl/.json) tests/ unit tests — empty for now ``` -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/`. +When extending the pipeline (the remaining pieces in "Roadmap" above), put new modules under +`lib/` (importable via `SKEL_LIB` on `PYTHONPATH`), add a `bin/.bat`/`.sh` wrapper pair +for each 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/README.md b/README.md index f31740d..e61b43c 100644 --- a/README.md +++ b/README.md @@ -9,51 +9,37 @@ ## Stand des Projekts -Der eigentliche SCL-Generator (JSON-Layout → TIA-Portal-Code) ist **noch nicht -geschrieben**. Was bereits funktioniert, ist die Ableitung der Vorstufen dazu: -aus einem CSV-Export der mechanischen Planung (ILS 2.0) werden automatisch +Aus einem CSV-Export der mechanischen Planung (ILS 2.0) werden automatisch - der **Materialfluss-Graph** der mechanischen Objekte (Strecken, Gefällestrecken, Kreisel), -- die daraus abgeleitete **TRO-Liste** samt eigenem Flussdiagramm, und +- die daraus abgeleitete **TRO-Liste** samt eigenem Flussdiagramm, - eine **annotierte Kopie der BricsCAD-Zeichnung** mit einem Marker-Symbol pro - TRO + TRO (danach in BricsCAD von Hand nachbearbeitbar — TROs verschieben, + ergänzen, Typ ändern), +- daraus zurückgelesen ein **JSON-Modell der TROs** (liest nur die Zeichnung, + erfasst also auch von Hand vorgenommene Korrekturen), und +- daraus ein **SCL-Gerüst für `FB_Main`** — ein `REGION` pro TRO mit + Instanzaufruf und allen Parametern in der richtigen Reihenfolge, aber + bewusst nicht lauffähig: jeder Wert, der aus der E-Planung stammen muss + (Sensor-/Aktoradressen, Separator-/Weichennummern, Staubereiche, ungeprüfte + Zeiten), ist als `TODO(E-Planung)` markiert statt geraten. -erzeugt. Diese drei Werkzeuge liegen als CLI-Tools in `lib/` und sind über +Diese fünf Werkzeuge liegen als CLI-Tools in `lib/` und sind über `bin/`-Wrapper aufrufbar (siehe unten). Details, Domänenmodell und die -Roadmap zum Generator stehen in `doc/`, allen voran `doc/Python_Scripts.md`. +verbleibende Roadmap stehen in `doc/`, allen voran `doc/Python_Scripts.md`. ## Roadmap -`tro_annotate.py` ist aktuell die letzte Stufe: Der Nutzer kann die annotierte -DXF-Kopie danach von Hand weiterbearbeiten — TROs verschieben, ergänzen oder -deren Typ ändern (`TRO_INSERT`/`TRO_EDIT` im BricsCAD-Menü, siehe -`doc/HundM/BricsCAD_TRO_Symbol.md`). Geplant sind zwei weitere Tools, die diese -Zeichnung bis zum fertigen SCL-Code weiterführen: - -1. **`tro_export.py`** (geplant) — liest die (ggf. von Hand nachbearbeitete) - annotierte DXF zusammen mit dem CSV-Export und leitet daraus die - JSON-Layout-Datei nach dem in `doc/HundM/Json_Layout-Konzept.md` - beschriebenen Schema ab (`plc`, `controlUnits`, `sensors[]`, `conveyors[]`, - `tros[]`, `loadingBooms[]`, `emptyCarrBuffers[]`, `routing`, `jamAreas[]`, - `scanners[]`, `connections[]`, `destinations[]`). Timing-Werte pro TRO - (`trailingTime`, `handlingTime`, `senFree`, `senWait`, `jamTime`, ...) - kommen dabei standardmäßig aus der typspezifischen Default-Tabelle - (`doc/TRO_Typen.md`); trägt das CAD-Symbol einen `OVERRIDE_TIMING_JSON`-Wert - (siehe `doc/HundM/BricsCAD_TRO_Symbol.md`), gewinnt der Override. Die - erzeugte JSON-Datei ist danach von Hand editierbar, um einzelne Defaults - oder Zeiten anzupassen, bevor daraus SCL generiert wird. -2. **`scl_gen.py`** (geplant) — liest die JSON-Layout-Datei und erzeugt daraus - die `.scl`-Skeleton-Dateien für den direkten TIA-Portal-Import (`FB_Main`, - `FB_CallSensors`, `FC_Direction`, `FC_Call_Jams` je Steuerung). Mit dem - Schalter `--skip-json` läuft dieser Schritt auch ohne manuell - nachbearbeitete JSON-Datei: `scl_gen.py` liest dann DXF und CSV direkt (wie - `tro_export.py`) und erzeugt sofort SCL mit den Typ-Defaults, ohne eine - Zwischen-JSON-Datei zu schreiben oder einzulesen — sinnvoll, wenn an den - Defaults nichts manuell angepasst werden muss. - -Beide Tools bekommen wie üblich ein `bin/.bat`/`.sh`-Wrapper-Paar und -werden in `doc/Python_Scripts.md` dokumentiert, sobald sie existieren. +Offen ist vor allem die JSON-Layout-Datei nach dem in +`doc/HundM/Json_Layout-Konzept.md` beschriebenen Schema (`plc`, +`controlUnits`, `sensors[]`, `conveyors[]`, `tros[]`, `loadingBooms[]`, +`emptyCarrBuffers[]`, `routing`, `jamAreas[]`, `scanners[]`, `connections[]`, +`destinations[]`) als von Hand editierbares Zwischenformat vor der +SCL-Erzeugung, sowie die Generierung der übrigen Bausteine je Steuerung +(`FB_CallSensors`, `FC_Direction`, `FC_Call_Jams`) — `scl_skeleton.py` deckt +bisher nur `FB_Main` ab. Siehe `CLAUDE.md` (Abschnitt "Roadmap") für den +aktuellen Stand der Planung dazu. ## Projektstruktur @@ -67,6 +53,8 @@ sps_skel/ material_flow.bat/.sh Wrapper fuer lib/material_flow.py tro_flow.bat/.sh Wrapper fuer lib/tro_flow.py tro_annotate.bat/.sh Wrapper fuer lib/tro_annotate.py + tro_extract.bat/.sh Wrapper fuer lib/tro_extract.py + scl_skeleton.bat/.sh Wrapper fuer lib/scl_skeleton.py cad/ generiertes BricsCAD-LISP (tro_types.lsp) fuer den TROEDIT-Dialog cfg/ Konfigurationsdateien (INI/JSON); dxf_registration.json data/ Eingabedaten - CSV-Exporte, DXF (nicht im Git) @@ -158,6 +146,14 @@ bin\tro_flow.bat --file mubea.csv --tosvg --doc REM 3) TROs als Marker-Symbole in eine Kopie der CAD-Zeichnung einbrennen bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend + +REM (Zeichnung optional in BricsCAD von Hand nachbearbeiten) + +REM 4) TROs aus der (ggf. bearbeiteten) Zeichnung zurueck in JSON lesen +bin\tro_extract.bat --dxf export_annotated.dxf + +REM 5) FB_Main-SCL-Geruest aus dem JSON erzeugen +bin\scl_skeleton.bat --json export_tro.json --start TRO07 ``` Alle Switches, Ausgabedateien und Exit-Codes der drei Tools sind in diff --git a/doc/Python_Scripts.md b/doc/Python_Scripts.md index 0f1cb6b..dd900f3 100644 --- a/doc/Python_Scripts.md +++ b/doc/Python_Scripts.md @@ -1,12 +1,13 @@ # Python Scripts — CLI Tooling Overview -**As of:** 2026-08-03 +**As of:** 2026-08-20 -`lib/` currently holds five Python modules (three of them runnable CLI tools, -two supporting libraries), plus one documentation-generator script that lives +`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, and an annotated copy of the BricsCAD drawing. +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 @@ -27,8 +28,14 @@ tro_flow.py ──► _tro_flow.dot / .svg, _tro_doc.md, _tro_error │ (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) + │ (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 ──► _tro.json + │ (reads only the drawing; resolves plant coords via the registration) + ▼ +scl_skeleton.py ──► _FB_Main.scl + (one REGION per TRO; E-planning values left as TODO(E-Planung) gaps) ``` - `material_flow.py` models the material flow of the **mechanical** objects only @@ -47,6 +54,9 @@ Supporting libraries (no CLI of their own, imported by the tools above): - **`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 of `tro_flow.py`. See §6a. Separate, documentation-only tool (not part of the generator pipeline): @@ -74,7 +84,7 @@ 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 all three tools: +Conventions shared by the CLI tools: - `--file` / `--dxf` accept either a bare filename — resolved against `%SKEL_DATA%` — or a full path. From e165beeaa863e2306a577f7c229b123558512ea7 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Fri, 21 Aug 2026 12:08:21 +0200 Subject: [PATCH 4/5] Group parallel Gefaellestrecken in the material-flow diagram A bank of gravity lanes between the same two Kreisel (same slope) was drawn as one box per lane, making large storage banks unreadable. render_dot now collapses such lanes into a single Gefaellestrecke-styled block with a double border, labelled with the id range and combined Separator/Scanner ranges. Purely visual - the graph, --doc report and --connect output still list every lane individually. --- .../graphs/mubea_material_flow.svg | 653 ++++-------------- .../graphs/mubea_material_flow_report.md | 106 +-- doc/Python_Scripts.md | 10 + lib/material_flow.py | 117 +++- 4 files changed, 298 insertions(+), 588 deletions(-) diff --git a/doc/500573_Mubea/graphs/mubea_material_flow.svg b/doc/500573_Mubea/graphs/mubea_material_flow.svg index 6dcfaa6..042109a 100644 --- a/doc/500573_Mubea/graphs/mubea_material_flow.svg +++ b/doc/500573_Mubea/graphs/mubea_material_flow.svg @@ -4,634 +4,219 @@ - - + + Materialfluss - -Materialfluss - export.csv + +Materialfluss - mubea.csv 0001-L - -Kreisel1  [0001-L] -Bahn L (links) -h = 1.470 m + +Kreisel1  [0001-L] +Bahn L (links) +h = 1.470 m 0002-R - -Kreisel2  [0002-R] -Bahn R (rechts) -h = 1.470 m + +Kreisel2  [0002-R] +Bahn R (rechts) +h = 1.470 m 0001-L->0002-R - - - + + + -Weiche +Weiche 0001-R - -Kreisel1  [0001-R] -Bahn R (rechts) -h = 1.470 m -Separator (1): 0061 + +Kreisel1  [0001-R] +Bahn R (rechts) +h = 1.470 m +Separator (1): 0061 - + 0001-R->0001-L - - - + + + -UZS +UZS 0001-R->0002-R - - - + + + -Weiche +Weiche 0002-L - -Kreisel2  [0002-L] -Bahn L (links) -h = 1.470 m -Separator (3): 0003, 0011, 0012 -Scanner (3): 0004, 0007, 0009 + +Kreisel2  [0002-L] +Bahn L (links) +h = 1.470 m +Separator (3): 0003, 0011, 0012 +Scanner (3): 0004, 0007, 0009 0016 - -VarioFoerderer :1  [0016] -Strecke -h = 0.000 m -0 -> 0 mm (+0) = aufwaerts -Separator (2): 0058, 0059 -Scanner (1): 0060 + +VarioFoerderer :1  [0016] +Strecke +h = 0.000 m +0 -> 0 mm (+0) = aufwaerts +Separator (2): 0058, 0059 +Scanner (1): 0060 - + 0002-L->0016 - - + + 0002-R->0002-L - - + + -UZS +UZS 0013 - -VarioFoerderer :3  [0013] -Strecke -h = 0.237 m -0 -> 474 mm (+474) = aufwaerts -Separator (2): 0015, 0062 + +VarioFoerderer :3  [0013] +Strecke +h = 0.237 m +0 -> 474 mm (+474) = aufwaerts +Separator (2): 0015, 0062 - + 0002-R->0013 - - + + 0014 - -VarioFoerderer :2  [0014] -Strecke -h = 0.237 m -0 -> 474 mm (+474) = aufwaerts -Separator (2): 0057, 0065 -Scanner (1): 0006 + +VarioFoerderer :2  [0014] +Strecke +h = 0.237 m +0 -> 474 mm (+474) = aufwaerts +Separator (2): 0057, 0065 +Scanner (1): 0006 - + 0002-R->0014 - - + + 0010-L - -Kreisel3  [0010-L] -Bahn L (links) -h = 1.940 m + +Kreisel3  [0010-L] +Bahn L (links) +h = 1.940 m - + -0017 - -Gefaellestrecke :20  [0017] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0037 +grp_0017 + + +Gefaellestrecke 0017 -> 0036 (20x) +Gruppe paralleler Schwerkraftlinien +h = 1.941 m +1941 -> 1466 mm (-475) = abwaerts +Separator (21): 0005 - 0056 +Scanner (1): 0066 - - -0010-L->0017 - - - - - -0018 - -Gefaellestrecke :19  [0018] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0038 - - - -0010-L->0018 - - - - - -0019 - -Gefaellestrecke :18  [0019] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0039 - - - -0010-L->0019 - - - - - -0020 - -Gefaellestrecke :17  [0020] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0040 - - - -0010-L->0020 - - - - - -0021 - -Gefaellestrecke :16  [0021] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0041 - - - -0010-L->0021 - - - - - -0022 - -Gefaellestrecke :15  [0022] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0042 - - - -0010-L->0022 - - - - - -0023 - -Gefaellestrecke :14  [0023] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0043 - - - -0010-L->0023 - - - - - -0024 - -Gefaellestrecke :13  [0024] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0044 - - - -0010-L->0024 - - - - - -0025 - -Gefaellestrecke :12  [0025] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0045 - - - -0010-L->0025 - - - - - -0026 - -Gefaellestrecke :11  [0026] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0046 - - - -0010-L->0026 - - - - - -0027 - -Gefaellestrecke :10  [0027] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0047 - - - -0010-L->0027 - - - - - -0028 - -Gefaellestrecke :9  [0028] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0048 - - - -0010-L->0028 - - - - - -0029 - -Gefaellestrecke :8  [0029] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0049 - - - -0010-L->0029 - - - - - -0030 - -Gefaellestrecke :7  [0030] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0050 - - - -0010-L->0030 - - - - - -0031 - -Gefaellestrecke :6  [0031] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0051 - - - -0010-L->0031 - - - - - -0032 - -Gefaellestrecke :5  [0032] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0052 - - - -0010-L->0032 - - - - - -0033 - -Gefaellestrecke :4  [0033] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0053 - - - -0010-L->0033 - - - - - -0034 - -Gefaellestrecke :3  [0034] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0054 - - - -0010-L->0034 - - - - - -0035 - -Gefaellestrecke :2  [0035] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (1): 0055 - - - -0010-L->0035 - - - - - -0036 - -Gefaellestrecke :1  [0036] -Gefaellestrecke -h = 1.941 m -1941 -> 1466 mm (-475) = abwaerts -Separator (2): 0005, 0056 -Scanner (1): 0066 - - + -0010-L->0036 - - +0010-L->grp_0017 + + 0010-R - -Kreisel3  [0010-R] -Bahn R (rechts) -h = 1.940 m -Separator (2): 0063, 0064 + +Kreisel3  [0010-R] +Bahn R (rechts) +h = 1.940 m +Separator (2): 0063, 0064 - + 0010-R->0010-L - - - + + + -UZS +UZS - + 0013->0010-L - - + + - + 0014->0010-L - - + + - -0016->0002-R - - - - - -0017->0002-R - - - - - -0018->0002-R - - - - - -0019->0002-R - - - - - -0020->0002-R - - - - - -0021->0002-R - - - - - -0022->0002-R - - - - - -0023->0002-R - - - - - -0024->0002-R - - - - - -0025->0002-R - - - - - -0026->0002-R - - - - - -0027->0002-R - - - - - -0028->0002-R - - - - - -0029->0002-R - - - - - -0030->0002-R - - - - - -0031->0002-R - - - - - -0032->0002-R - - - - - -0033->0002-R - - - - - -0034->0002-R - - - - -0035->0002-R - - +0016->0002-R + + - + -0036->0002-R - - +grp_0017->0002-R + + - + legende -durchgezogen -Materialfluss -gestrichelt blau -Kreisel-Umlauf, Richtung aus Drehrichtung (UZS = rechts nach links, GUZ = links nach rechts) -gestrichelt gelb -Richtung nicht bestimmbar +durchgezogen +Materialfluss +gestrichelt blau +Kreisel-Umlauf, Richtung aus Drehrichtung (UZS = rechts nach links, GUZ = links nach rechts) +gestrichelt gelb +Richtung nicht bestimmbar +doppelte Umrandung +Gruppe paralleler Gefaellestrecken (gleicher Zu-/Ablauf, gleiche Steigung) diff --git a/doc/500573_Mubea/graphs/mubea_material_flow_report.md b/doc/500573_Mubea/graphs/mubea_material_flow_report.md index e062c5a..a32a313 100644 --- a/doc/500573_Mubea/graphs/mubea_material_flow_report.md +++ b/doc/500573_Mubea/graphs/mubea_material_flow_report.md @@ -1,8 +1,8 @@ -# Materialfluss - export.csv +# Materialfluss - mubea.csv -**Erzeugt:** 2026-08-07 13:20 von `lib/material_flow.py` -**Quelle:** `C:\develop\sps_skel\data\export.csv` +**Erzeugt:** 2026-08-21 11:53 von `lib/material_flow.py` +**Quelle:** `C:\10-Develop\gitrepos\sps_skel\data\mubea.csv` > Materialfluss der mechanischen Objekte. Die daraus abgeleiteten Steuerungsobjekte (TROs) dokumentiert `lib/tro_flow.py --doc`. @@ -15,7 +15,7 @@ | Anbauteile | Scanner: 6, Separator: 33 | | Plausibilitaetshinweise | 7 | -Graph: [`export_material_flow.dot`](export_material_flow.dot) - [`export_material_flow.svg`](export_material_flow.svg) +Graph: [`mubea_material_flow.dot`](mubea_material_flow.dot) - [`mubea_material_flow.svg`](mubea_material_flow.svg) Ein **Kreisel** ist ein Umlauf und wird als zwei Knoten modelliert - eine Bahn je Fahrtrichtung (`-L` links, `-R` rechts), die zu einem Kreis verbunden sind. @@ -67,55 +67,55 @@ Hoehe: bei Kreiseln aus dem Merkmal `Höhe in m`, bei Gefaellestrecke und Streck |---|---|---| | `0001-L` | `0002-R` | Richtung unbestimmt (Weiche) | | `0001-R` | `0002-R` | Richtung unbestimmt (Weiche) | -| `0010-R` | `0010-L` | Kreisel-Umlauf (UZS) | -| `0002-R` | `0002-L` | Kreisel-Umlauf (UZS) | | `0001-R` | `0001-L` | Kreisel-Umlauf (UZS) | -| `0010-L` | `0036` | Materialfluss | -| `0036` | `0002-R` | Materialfluss | -| `0010-L` | `0035` | Materialfluss | -| `0035` | `0002-R` | Materialfluss | -| `0010-L` | `0034` | Materialfluss | -| `0034` | `0002-R` | Materialfluss | -| `0010-L` | `0033` | Materialfluss | -| `0033` | `0002-R` | Materialfluss | -| `0010-L` | `0032` | Materialfluss | -| `0032` | `0002-R` | Materialfluss | -| `0010-L` | `0031` | Materialfluss | -| `0031` | `0002-R` | Materialfluss | -| `0010-L` | `0030` | Materialfluss | -| `0030` | `0002-R` | Materialfluss | -| `0010-L` | `0029` | Materialfluss | -| `0029` | `0002-R` | Materialfluss | -| `0010-L` | `0028` | Materialfluss | -| `0028` | `0002-R` | Materialfluss | -| `0010-L` | `0027` | Materialfluss | -| `0027` | `0002-R` | Materialfluss | -| `0010-L` | `0026` | Materialfluss | -| `0026` | `0002-R` | Materialfluss | -| `0010-L` | `0025` | Materialfluss | -| `0025` | `0002-R` | Materialfluss | -| `0010-L` | `0024` | Materialfluss | -| `0024` | `0002-R` | Materialfluss | -| `0010-L` | `0023` | Materialfluss | -| `0023` | `0002-R` | Materialfluss | -| `0010-L` | `0022` | Materialfluss | -| `0022` | `0002-R` | Materialfluss | -| `0010-L` | `0021` | Materialfluss | -| `0021` | `0002-R` | Materialfluss | -| `0010-L` | `0020` | Materialfluss | -| `0020` | `0002-R` | Materialfluss | -| `0010-L` | `0019` | Materialfluss | -| `0019` | `0002-R` | Materialfluss | -| `0010-L` | `0018` | Materialfluss | -| `0018` | `0002-R` | Materialfluss | -| `0010-L` | `0017` | Materialfluss | -| `0017` | `0002-R` | Materialfluss | -| `0002-L` | `0016` | Materialfluss | -| `0016` | `0002-R` | Materialfluss | -| `0002-R` | `0014` | Materialfluss | -| `0014` | `0010-L` | Materialfluss | -| `0002-R` | `0013` | Materialfluss | -| `0013` | `0010-L` | Materialfluss | +| `0002-R` | `0002-L` | Kreisel-Umlauf (UZS) | +| `0010-R` | `0010-L` | Kreisel-Umlauf (UZS) | +| `0010-L` | `0036` | Materialfluss (Ausschleusung) | +| `0036` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0035` | Materialfluss (Ausschleusung) | +| `0035` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0034` | Materialfluss (Ausschleusung) | +| `0034` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0033` | Materialfluss (Ausschleusung) | +| `0033` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0032` | Materialfluss (Ausschleusung) | +| `0032` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0031` | Materialfluss (Ausschleusung) | +| `0031` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0030` | Materialfluss (Ausschleusung) | +| `0030` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0029` | Materialfluss (Ausschleusung) | +| `0029` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0028` | Materialfluss (Ausschleusung) | +| `0028` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0027` | Materialfluss (Ausschleusung) | +| `0027` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0026` | Materialfluss (Ausschleusung) | +| `0026` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0025` | Materialfluss (Ausschleusung) | +| `0025` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0024` | Materialfluss (Ausschleusung) | +| `0024` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0023` | Materialfluss (Ausschleusung) | +| `0023` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0022` | Materialfluss (Ausschleusung) | +| `0022` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0021` | Materialfluss (Ausschleusung) | +| `0021` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0020` | Materialfluss (Ausschleusung) | +| `0020` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0019` | Materialfluss (Ausschleusung) | +| `0019` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0018` | Materialfluss (Ausschleusung) | +| `0018` | `0002-R` | Materialfluss (Einschleusung) | +| `0010-L` | `0017` | Materialfluss (Ausschleusung) | +| `0017` | `0002-R` | Materialfluss (Einschleusung) | +| `0002-L` | `0016` | Materialfluss (Ausschleusung) | +| `0016` | `0002-R` | Materialfluss (Einschleusung) | +| `0002-R` | `0014` | Materialfluss (Ausschleusung) | +| `0014` | `0010-L` | Materialfluss (Einschleusung) | +| `0002-R` | `0013` | Materialfluss (Ausschleusung) | +| `0013` | `0010-L` | Materialfluss (Einschleusung) | Die Richtung innerhalb eines Kreisels kommt aus dem Merkmal `Drehrichtung`: **UZS** (im Uhrzeigersinn) laesst das Material rechts nach links laufen, **GUZ** (gegen den Uhrzeigersinn) links nach rechts. @@ -133,7 +133,7 @@ Abweichungen zwischen den Merkmalen des CSV-Exports und den tatsaechlich vorhand ## 5. Warnungen des Laufs -- Zeile 52 (Strecke 0016 'VarioFoerderer :1'): alle Nachbarn auf gleicher Hoehe (1.470 m) - Richtung nicht bestimmbar +- Zeile 17 (Strecke 0016 'VarioFoerderer :1'): alle Nachbarn auf gleicher Hoehe (1.470 m) - Richtung nicht bestimmbar - Verbindung 0001-L <-> 0002-R: Richtung nicht bestimmbar (kein Transportobjekt beteiligt) - beidseitig gezeichnet - Verbindung 0001-R <-> 0002-R: Richtung nicht bestimmbar (kein Transportobjekt beteiligt) - beidseitig gezeichnet - Verbindung 0002-L <-> 0016: Richtung nicht bestimmbar (kein Transportobjekt beteiligt) - beidseitig gezeichnet diff --git a/doc/Python_Scripts.md b/doc/Python_Scripts.md index dd900f3..09bd553 100644 --- a/doc/Python_Scripts.md +++ b/doc/Python_Scripts.md @@ -132,6 +132,16 @@ Reads the CSV export and builds a directed graph of the mechanical objects: > will 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 diff --git a/lib/material_flow.py b/lib/material_flow.py index e8a7800..dc9d2a0 100644 --- a/lib/material_flow.py +++ b/lib/material_flow.py @@ -138,6 +138,12 @@ NODE_STYLES = { } DEFAULT_NODE_STYLE = dict(fillcolor="#f2f2f2", fontcolor="#404040", color="#808080") +# Ab dieser Anzahl paralleler Gefaellestrecken zwischen denselben zwei +# Kreisel-Bahnen (gleicher Zulauf, gleicher Ablauf, gleiche Steigung) werden +# sie im Materialfluss-Diagramm zu einem Block zusammengefasst - sonst wird +# eine grosse Linienbank (Speicher) unlesbar breit. +MIN_GEFAELLE_GROUP = 2 + # --------------------------------------------------------------------------- # Datenmodell @@ -999,6 +1005,78 @@ def _pos_attr(node: "Node", use_coords: bool) -> str: return f', pos="{xy[0] * COORD_SCALE:.2f},{xy[1] * COORD_SCALE:.2f}"' +def _flow_neighbours(node_id: str, edges: list[Edge]) -> tuple[str, str] | None: + """ + (Vorgaenger, Nachfolger) eines Knotens im Materialfluss. + + None, wenn der Knoten nicht genau einen ankommenden und einen abgehenden + EDGE_FLOW hat - dann ist er kein einfacher Durchlaufknoten zwischen zwei + festen Nachbarn. + """ + incoming = {e.src for e in edges if e.dst == node_id and e.kind == EDGE_FLOW} + outgoing = {e.dst for e in edges if e.src == node_id and e.kind == EDGE_FLOW} + if len(incoming) != 1 or len(outgoing) != 1: + return None + return next(iter(incoming)), next(iter(outgoing)) + + +def _group_parallel_gefaelle(graph: Graph) -> list[list[Node]]: + """ + Parallele Gefaellestrecken zwischen denselben zwei Kreisel-Bahnen zu + Gruppen zusammenfassen (z. B. eine Bank aus Schwerkraftlinien). + + Kriterium: gleicher Zulauf- und Ablauf-Knoten (jeweils eine Kreisel-Bahn) + und gleiche Steigung (Hoehe_Von/Hoehe_Bis) - "gleich lang und gleiche + Steigung" laesst sich aus dem Export nur ueber die Hoehenangabe pruefen, + die Laenge ergibt sich daraus, dass beide Enden an denselben zwei Knoten + haengen. Rein fuer die Darstellung; Graph-Knoten/-Kanten bleiben unveraendert. + """ + groups: dict[tuple, list[Node]] = {} + for node_id in sorted(graph.nodes): + node = graph.nodes[node_id] + if node.kind != "Gefaellestrecke": + continue + neighbours = _flow_neighbours(node_id, graph.edges) + if neighbours is None: + continue + before, after = neighbours + if graph.nodes[before].kind != "Kreisel" or graph.nodes[after].kind != "Kreisel": + continue + element = node.element + key = (before, after, element.height_from, element.height_to) + groups.setdefault(key, []).append(node) + return [members for members in groups.values() if len(members) >= MIN_GEFAELLE_GROUP] + + +def _id_range(ids: list[str]) -> str: + """Sortierte Ids als Bereich, z. B. ['0037', '0056'] -> '0037 - 0056'.""" + ids = sorted(ids) + return ids[0] if len(ids) == 1 else f"{ids[0]} - {ids[-1]}" + + +def _gefaelle_group_label(members: list[Node]) -> str: + """Sammel-Label einer Gefaellestrecken-Gruppe: Bereich der Linien-Ids, + gemeinsame Steigung, zusammengefasste Separatoren/Scanner.""" + element = members[0].element + ids = sorted(m.node_id for m in members) + lines = [ + f"Gefaellestrecke {ids[0]} -> {ids[-1]} ({len(ids)}x)", + "Gruppe paralleler Schwerkraftlinien", + ] + if element.height is not None: + lines.append(f"h = {element.height:.3f} m") + if element.rise is not None: + uphill = drive_uphill(element)[0] + arrow = {True: "aufwaerts", False: "abwaerts", None: "Richtung offen"}[uphill] + lines.append(f"{element.height_from:.0f} -> {element.height_to:.0f} mm " + f"({element.rise:+.0f}) = {arrow}") + for kind in DEVICE_KINDS: + ids_dev = sorted({d for m in members for d in m.devices.get(kind, [])}) + if ids_dev: + lines.append(f"{kind} ({len(ids_dev)}): {_id_range(ids_dev)}") + return _dot_label(lines) + + def render_dot(graph: Graph, use_coords: bool = False) -> str: out: list[str] = [] add = out.append @@ -1047,14 +1125,48 @@ def render_dot(graph: Graph, use_coords: bool = False) -> str: add(" }") add("") + # Parallele Gefaellestrecken (gleicher Zu-/Ablauf, gleiche Steigung) zu + # je einem Block zusammenfassen, damit eine Linienbank nicht als N + # einzelne Kaesten gezeichnet wird. + gefaelle_groups = _group_parallel_gefaelle(graph) + group_of: dict[str, str] = {} + for members in gefaelle_groups: + group_id = f"grp_{members[0].node_id}" + for member in members: + group_of[member.node_id] = group_id + grouped_ids = set(group_of) + for node in plain: + if node.node_id in grouped_ids: + continue style = NODE_STYLES.get(node.kind, DEFAULT_NODE_STYLE) attrs = ", ".join(f'{key}="{value}"' for key, value in style.items()) add(f' "{node.node_id}" [label="{_node_label(node)}", {attrs}' f'{_pos_attr(node, use_coords)}];') + + for members in gefaelle_groups: + group_id = f"grp_{members[0].node_id}" + style = NODE_STYLES.get("Gefaellestrecke", DEFAULT_NODE_STYLE) + attrs = ", ".join(f'{key}="{value}"' for key, value in style.items()) + pos = "" + if use_coords: + xs = [xy[0] for m in members if (xy := _node_xy(m))] + ys = [xy[1] for m in members if (xy := _node_xy(m))] + if xs and ys: + pos = (f', pos="{sum(xs) / len(xs) * COORD_SCALE:.2f},' + f'{sum(ys) / len(ys) * COORD_SCALE:.2f}"') + add(f' "{group_id}" [label="{_gefaelle_group_label(members)}", {attrs}, ' + f'peripheries=2{pos}];') add("") + seen_group_edges: set[tuple[str, str]] = set() for edge in graph.edges: + src = group_of.get(edge.src, edge.src) + dst = group_of.get(edge.dst, edge.dst) + if edge.src in grouped_ids or edge.dst in grouped_ids: + if src == dst or (src, dst) in seen_group_edges: + continue + seen_group_edges.add((src, dst)) if edge.kind == EDGE_CIRCLE: # Richtung kommt aus der Drehrichtung und steht als Label an der Kante label = f', xlabel="{_dot_escape(edge.note)}"' if edge.note else "" @@ -1070,7 +1182,7 @@ def render_dot(graph: Graph, use_coords: bool = False) -> str: 'fontcolor="#cc8800", tooltip="Weiche (Kreisel-Uebergang)"]') else: attrs = "[]" - add(f' "{edge.src}" -> "{edge.dst}" {attrs};') + add(f' "{src}" -> "{dst}" {attrs};') add("") legend_pos = "" @@ -1094,6 +1206,9 @@ def render_dot(graph: Graph, use_coords: bool = False) -> str: f'{ROTATION_CCW} = {ROTATION_FLOW[ROTATION_CCW]})' '
gestrichelt gelb' 'Richtung nicht bestimmbar' + 'doppelte Umrandung' + 'Gruppe paralleler Gefaellestrecken (gleicher Zu-/Ablauf, ' + "gleiche Steigung)" ">];") add(" }") add("}") From fce0b8d9a1a86430b85770e31a5fe041995c982b Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Fri, 21 Aug 2026 12:08:41 +0200 Subject: [PATCH 5/5] Fix BOM breaking bin/*.bat and add early Graphviz check to --tosvg wrappers setenv.bat (and activate_venv/get_cmd/install_py.bat) had a UTF-8 BOM, which cmd.exe read as part of the @echo off line, breaking every bin script that calls setenv.bat via `call`. Stripped the BOM. material_flow.bat/.sh and tro_flow.bat/.sh now check up front whether the Graphviz engine --tosvg needs (dot, or neato with --use-cords) is resolvable, and print an early WARNUNG with fix instructions if not - before the CSV is even parsed. The Python scripts still run and report the same failure in detail if the SVG step is actually reached. --- bin/activate_venv.bat | 2 +- bin/get_cmd.bat | 2 +- bin/install_py.bat | 2 +- bin/material_flow.bat | 20 ++++++++++++++++++++ bin/material_flow.sh | 20 ++++++++++++++++++++ bin/setenv.bat | 2 +- bin/tro_flow.bat | 20 ++++++++++++++++++++ bin/tro_flow.sh | 20 ++++++++++++++++++++ doc/Python_Scripts.md | 6 +++++- 9 files changed, 89 insertions(+), 5 deletions(-) diff --git a/bin/activate_venv.bat b/bin/activate_venv.bat index d7bbe4e..9c89b06 100644 --- a/bin/activate_venv.bat +++ b/bin/activate_venv.bat @@ -1,4 +1,4 @@ -@echo off +@echo off REM ================================================================ REM SPS_SKEL - Python Virtual Environment aktivieren REM ================================================================ diff --git a/bin/get_cmd.bat b/bin/get_cmd.bat index 1d97463..82b5118 100644 --- a/bin/get_cmd.bat +++ b/bin/get_cmd.bat @@ -1,4 +1,4 @@ -@echo off +@echo off REM ================================================================ REM SPS_SKEL - Shell mit gesetzten Umgebungsvariablen oeffnen REM ================================================================ diff --git a/bin/install_py.bat b/bin/install_py.bat index a8958f7..412cf92 100644 --- a/bin/install_py.bat +++ b/bin/install_py.bat @@ -1,4 +1,4 @@ -@echo off +@echo off REM ================================================================ REM SPS_SKEL - Python Virtual Environment einrichten REM ================================================================ diff --git a/bin/material_flow.bat b/bin/material_flow.bat index 3f77368..fb606ed 100644 --- a/bin/material_flow.bat +++ b/bin/material_flow.bat @@ -24,6 +24,26 @@ REM Python-Interpreter bestimmen (py-Launcher bevorzugt, sonst python) set "PY=py" py --version >nul 2>&1 || set "PY=python" +REM Graphviz pruefen, wenn --tosvg SVG erzeugen soll (dot, bei --use-cords/ +REM --use-coords neato) - nur ein frueher Hinweis; das eigentliche Skript +REM meldet denselben Fehler beim Rendern noch einmal im Detail. +set "GV_ENGINE=dot" +echo %*| findstr /i /c:"--use-cords" /c:"--use-coords" >nul && set "GV_ENGINE=neato" +echo %*| findstr /i /c:"--tosvg" >nul +if not errorlevel 1 ( + where %GV_ENGINE%.exe >nul 2>&1 + if errorlevel 1 ( + set "GV_OK=" + if /i "%GV_ENGINE%"=="dot" if defined GRAPHVIZ_DOT set "GV_OK=1" + if not defined GV_OK ( + echo WARNUNG: Graphviz '%GV_ENGINE%' nicht im PATH gefunden - --tosvg wird vermutlich fehlschlagen. + echo Graphviz installieren ^(https://graphviz.org/download/^), den bin-Ordner + echo zum PATH hinzufuegen, oder GRAPHVIZ_DOT auf die dot.exe setzen ^(nur fuer 'dot'^). + echo. + ) + ) +) + "%PY%" "%SKEL_LIB%\material_flow.py" %* set "RC=%ERRORLEVEL%" diff --git a/bin/material_flow.sh b/bin/material_flow.sh index d457e51..90c26c4 100644 --- a/bin/material_flow.sh +++ b/bin/material_flow.sh @@ -28,4 +28,24 @@ fi PY="python3" command -v python3 >/dev/null 2>&1 || PY="python" +# Graphviz pruefen, wenn --tosvg SVG erzeugen soll (dot, bei --use-cords/ +# --use-coords neato) - nur ein frueher Hinweis; das eigentliche Skript +# meldet denselben Fehler beim Rendern noch einmal im Detail. +case " $* " in + *" --tosvg "*) + GV_ENGINE="dot" + case " $* " in + *" --use-cords "*|*" --use-coords "*) GV_ENGINE="neato" ;; + esac + if ! command -v "$GV_ENGINE" >/dev/null 2>&1; then + if [ "$GV_ENGINE" != "dot" ] || [ -z "${GRAPHVIZ_DOT:-}" ]; then + echo "WARNUNG: Graphviz '$GV_ENGINE' nicht im PATH gefunden - --tosvg wird vermutlich fehlschlagen." >&2 + echo " Graphviz installieren (https://graphviz.org/download/), PATH ergaenzen," >&2 + echo " oder GRAPHVIZ_DOT auf die dot-Programmdatei setzen (nur fuer 'dot')." >&2 + echo "" >&2 + fi + fi + ;; +esac + exec "$PY" "$SKEL_LIB/material_flow.py" "$@" diff --git a/bin/setenv.bat b/bin/setenv.bat index 610b437..2e14c33 100644 --- a/bin/setenv.bat +++ b/bin/setenv.bat @@ -1,4 +1,4 @@ -@echo off +@echo off REM ================================================================ REM SPS_SKEL - Umgebungsvariablen Setup diff --git a/bin/tro_flow.bat b/bin/tro_flow.bat index 9cd3ee9..19043db 100644 --- a/bin/tro_flow.bat +++ b/bin/tro_flow.bat @@ -27,6 +27,26 @@ REM Python-Interpreter bestimmen (py-Launcher bevorzugt, sonst python) set "PY=py" py --version >nul 2>&1 || set "PY=python" +REM Graphviz pruefen, wenn --tosvg SVG erzeugen soll (dot, bei --use-cords/ +REM --use-coords neato) - nur ein frueher Hinweis; das eigentliche Skript +REM meldet denselben Fehler beim Rendern noch einmal im Detail. +set "GV_ENGINE=dot" +echo %*| findstr /i /c:"--use-cords" /c:"--use-coords" >nul && set "GV_ENGINE=neato" +echo %*| findstr /i /c:"--tosvg" >nul +if not errorlevel 1 ( + where %GV_ENGINE%.exe >nul 2>&1 + if errorlevel 1 ( + set "GV_OK=" + if /i "%GV_ENGINE%"=="dot" if defined GRAPHVIZ_DOT set "GV_OK=1" + if not defined GV_OK ( + echo WARNUNG: Graphviz '%GV_ENGINE%' nicht im PATH gefunden - --tosvg wird vermutlich fehlschlagen. + echo Graphviz installieren ^(https://graphviz.org/download/^), den bin-Ordner + echo zum PATH hinzufuegen, oder GRAPHVIZ_DOT auf die dot.exe setzen ^(nur fuer 'dot'^). + echo. + ) + ) +) + "%PY%" "%SKEL_LIB%\tro_flow.py" %* set "RC=%ERRORLEVEL%" diff --git a/bin/tro_flow.sh b/bin/tro_flow.sh index a1b4745..72e1037 100644 --- a/bin/tro_flow.sh +++ b/bin/tro_flow.sh @@ -31,4 +31,24 @@ fi PY="python3" command -v python3 >/dev/null 2>&1 || PY="python" +# Graphviz pruefen, wenn --tosvg SVG erzeugen soll (dot, bei --use-cords/ +# --use-coords neato) - nur ein frueher Hinweis; das eigentliche Skript +# meldet denselben Fehler beim Rendern noch einmal im Detail. +case " $* " in + *" --tosvg "*) + GV_ENGINE="dot" + case " $* " in + *" --use-cords "*|*" --use-coords "*) GV_ENGINE="neato" ;; + esac + if ! command -v "$GV_ENGINE" >/dev/null 2>&1; then + if [ "$GV_ENGINE" != "dot" ] || [ -z "${GRAPHVIZ_DOT:-}" ]; then + echo "WARNUNG: Graphviz '$GV_ENGINE' nicht im PATH gefunden - --tosvg wird vermutlich fehlschlagen." >&2 + echo " Graphviz installieren (https://graphviz.org/download/), PATH ergaenzen," >&2 + echo " oder GRAPHVIZ_DOT auf die dot-Programmdatei setzen (nur fuer 'dot')." >&2 + echo "" >&2 + fi + fi + ;; +esac + exec "$PY" "$SKEL_LIB/tro_flow.py" "$@" diff --git a/doc/Python_Scripts.md b/doc/Python_Scripts.md index 09bd553..16fc36f 100644 --- a/doc/Python_Scripts.md +++ b/doc/Python_Scripts.md @@ -98,7 +98,11 @@ Conventions shared by the CLI tools: `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. + at the `dot` executable. `bin/material_flow.bat`/`.sh` and `bin/tro_flow.bat`/`.sh` + check for the right engine up front when `--tosvg` is passed and print an early + `WARNUNG` if it's missing (`GRAPHVIZ_DOT` only covers `dot`, not `neato`) — 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 code `2`) if the SVG step is reached. ## 3. `material_flow.py` — material-flow graph of the mechanical layout