Commit Graph

36 Commits

Author SHA1 Message Date
m.stangl fce0b8d9a1 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.
2026-08-21 12:08:41 +02:00
m.stangl e165beeaa8 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.
2026-08-21 12:08:21 +02:00
m.stangl 9336f18466 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 <noreply@anthropic.com>
2026-08-20 23:37:19 +02:00
m.stangl e630ec12d6 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 <noreply@anthropic.com>
2026-08-20 21:30:25 +02:00
m.stangl ec009efefe Readme des Projektes aktualisiert 2026-08-20 21:30:25 +02:00
s.ayadi 29e2425c16 Add the generated results for mubea for review 2026-08-20 12:18:44 +02:00
s.ayadi da0714ad46 Add the generated results for mubea for review 2026-08-20 12:13:38 +02:00
s.ayadi d7edc55116 latest changes 2026-08-20 11:38:51 +02:00
s.ayadi 8bd0739653 Write connection blocks and a permanent UID per TRO
Each TRO marker gains a UID attribute: a unique, permanent four-digit number in
the same space as the TeileId of the CSV export and the ID attribute of SSG_LIB
(dxfmakros/Lisp/ssg_id.lsp). The visible ID (TRO03) is untouched - it stays the
speaking name that TRO_EDIT and TRO_INSERT work with. The UID is a separate tag
on purpose: IDSCHECK treats ID as numeric and would overwrite TRO03 with a number
while correcting duplicates.

Numbers are allocated above the highest already in use, taken from two sources -
the ID attributes of the drawing and the TeileId of every CSV object. The second
source is the one that matters here: a mechanical drawing not built with SSG_LIB
carries no ID attributes at all (in the Mubea plan only 10 of 669 blocks have any
attributes), so without it the run would start at 0001 and take numbers the CSV
already uses for components. With it the markers start at 0062, above TeileId
0061.

With --flow each edge now also gets a CONNECTION_ARROW block at the midpoint of
the arrow, rotated along it, holding FROM_UID/TO_UID plus FROM_TRO/TO_TRO as
readable text and KIND. That makes the relationship extractable from a DXF export
and stable when a marker is moved or renamed. The shaft and head stay separate
geometry: a block with fixed geometry cannot span two arbitrary points, and
scaling it in X would distort the arrowhead.

Block name prefix CONNECTION_ so the SSG_BLOCKEDIT dispatcher in dxfmakros picks
it up via "CONNECTION_*".

Verified on the real drawing: 10 markers with UIDs 0062-0071, 29 connections with
UIDs 0072-0100, every FROM_UID/TO_UID matching its marker, all 39 UIDs unique.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 15:09:39 +02:00
s.ayadi 64c180d84b Document the new TRO_INSERT BricsCAD command
dxfmakros' TRO menu can now insert new TRO markers by hand (TRO_INSERT),
not just edit ones tro_annotate.py placed; note it here since this is
where the workflow and --emit-lisp switch are documented.
2026-08-03 15:56:16 +02:00
s.ayadi 8b4619cf9f Document lib/ CLI tools and refresh CLAUDE.md to match
Add doc/Python_Scripts.md covering material_flow.py, tro_flow.py,
tro_annotate.py, tro_catalog.py and dxf_registration.py: purpose, CLI
switches, outputs and exit codes.

Update CLAUDE.md, which still described lib/ and doc/ as empty
design-phase scaffolding: reflect the working tooling, the doc/
reorganization (HundM/, 500573_Mubea/, TRO_Katalog/), the moved SCL
templates, and the now-accepted bin/<tool>.bat+.sh wrapper convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 15:32:01 +02:00
s.ayadi ecc9c8fb53 Move the CAD dialog to dxfmakros and target DXFM_LISP
The TRO edit dialog belongs with the other SSG_LIB edit commands, so
cad/tro_edit.lsp and cad/tro_edit.dcl move to dxfmakros (Lisp/Tro_Edit.lsp,
dcl/tro_edit.dcl) where they use the ssg_* helpers, the DXFM_DCL path and the
existing SSG_BLOCKEDIT dispatcher instead of standing on their own. Keeping a
second copy here would only let the two drift apart before the repos are merged.

--emit-lisp now writes the generated type list to DXFM_LISP when that variable is
set, so it lands next to the SSG_LIB modules that read it. Without the variable it
still writes to cad/ and says where the file actually belongs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 12:06:55 +02:00
s.ayadi 0f02fd83aa Give each TRO type its own CAD symbol and add a TROEDIT dialog
Replaces the generic marker with one shape per type, so the types are
distinguishable in the drawing without relying on colour. The shape belongs to
the type, so it sits in tro_catalog next to the colour - colour groups (6 groups),
shape identifies (10 types):

  1Sep circle          1Sep1Swi triangle      1Sep2Swi pentagon
  1Sep_SSCC 2 circles  Vario rectangle        Vario_workStation hexagon
  PinStore_Auto square EmptyCarrBuffer diamond
  LoadingBoom arrow    2Sep1Swi triangle down

The visible attributes are now ID and TYPE, as those are what the dialog edits;
FB_BLOCK stays visible under --fb and ITEMS/CONFIDENCE/SEPARATORS stay hidden
data. Note this renames the former TRO_ID/TRO_TYPE tags, so drawings annotated
with an earlier version need regenerating.

Marker layers now take the *stroke* colour instead of the fill. A CAD symbol is
line work, and the pastel fills of the palette were nearly invisible as lines -
LoadingBoom in particular came out almost white on white.

cad/tro_edit.dcl and cad/tro_edit.lsp add the TROEDIT command: pick a TRO_SYM_*
block, edit ID and TYPE, write back on OK. TYPE is a picklist rather than free
text so it cannot drift from the catalogue; the list comes from cad/tro_types.lsp,
which "tro_annotate.py --emit-lisp" generates from TRO_CATALOG. If that file is
missing the dialog degrades to the block's current type instead of failing.

The dialog deliberately changes attributes only. The marker shape belongs to the
block definition of the type and FB_BLOCK is derived from it, so both follow on
the next annotation run - the dialog says so, and TROEDIT prints a reminder when
the type was changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 13:32:48 +02:00
s.ayadi 977b5c826e Add TRO annotation tool writing an annotated copy of the drawing
Takes the original DXF, the CSV export and the derived TROs and writes a copy of
the drawing with the TRO information overlaid. The original is never touched.

Per TRO a block marker with attributes, not loose geometry: one block definition
per type (TRO_SYM_<type>), inserted at the plant coordinate, carrying TRO_ID,
TRO_TYPE, FB_BLOCK visibly and ITEMS, CONFIDENCE, SEPARATORS as hidden data.
That makes the result queryable and editable inside BricsCAD, and a corrected
TRO_ID can later feed back into the generator.

Layers follow the colour groups of tro_catalog (TRO_MARKER_sep, _vario, _store,
...) with the group colour as true colour and entities BYLAYER, so the layer
manager filters by TRO type. Flow arrows and the legend get their own layers.

Re-runs are idempotent through XDATA, not layer names: every generated entity is
tagged with the SPS_SKEL_TRO appid and a re-run deletes exactly those. Deleting
by layer would also destroy anything a user had put on the same layer.

Switches follow the other tools; --check reports the registration and what would
be drawn without writing, --flow adds the TRO-level arrows, --fb shows the
function block, --offset and --re-register override the stored transform. DWG is
rejected with a clear message since ezdxf cannot read it.

Verified against the real 23.8 MB drawing: 9 markers on the correct layers with
readable attributes, 15 flow arrows, and a re-run replaces its own 46 entities
instead of duplicating them.

Adds ezdxf to requirements and stores the verified Mubea transform in cfg/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:39:54 +02:00
s.ayadi 7a8c25f3b9 Add DXF registration with a provable coordinate transform
The CSV export and the BricsCAD drawing sit in different coordinate systems. A
silent offset here puts every annotation in the wrong place while still looking
plausible, so this module derives the transform, proves it, and refuses when it
cannot.

Approach: vote for the translation over point pairs, refine the best candidates
by Procrustes fit (rotation included), pick the one with the most exact matches,
then report the residuals.

Two things the naive version got wrong, both fixed here:

Matching is constrained by role. The plant is built from regular grids - the
store lines, their infeeds and their outfeeds each form a row of 20 points at
800 mm pitch. Voting over all points is therefore ambiguous: a wrong offset hits
the infeed row exactly as well as the right one hits the separator row, and it
won by a single vote (21 to 20), producing an offset 8.6 m out with a spurious
0.52 degree rotation. Separators are now matched only against separator blocks
(S-SP, S-LP) and scanners only against SCAN, which makes the result unique.

The fit uses exact matches only (50 mm). For extended objects the CSV reports the
bounding-box centre while the block reports its insertion point; such pairs are
100-500 mm apart and would tilt the fit if they were included. They still count
towards the reported residuals.

For the Mubea plant this yields dx +50905.7 mm, dy +8414.5 mm, rotation exactly
0, from 20 exact matches - the same values a hand analysis produced.

A verified transform is stored in SKEL_CFG/dxf_registration.json per CSV/DXF
pair, so later runs are reproducible; a stored transform is still re-checked
against the drawing on every run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 15:39:54 +02:00
s.ayadi 5dcaa8b8fa Take the conveying direction from the element's own height change
The new export fills Hoehe_Von_mm / Hoehe_Bis_mm, so the direction of a transport
element no longer has to be guessed from its neighbours. drive_uphill() now
decides in this order:

  1. Hoehe_Von_mm -> Hoehe_Bis_mm: the rise of the element itself. Only the sign
     is read, since the values are absolute on a Gefaellestrecke (1941 -> 1466)
     but relative on a Strecke (0 -> 474).
  2. Antriebfahrtrichtung "Auf" (up) or "Ab" (down).
  3. A Gefaellestrecke without either runs downhill by definition.

Previously a Gefaellestrecke was assumed downhill outright and everything else
depended on Antriebfahrtrichtung alone; now both are backed by the height data
and the fallback order is explicit.

The node label states what was read and what follows from it, e.g.
"0 -> 474 mm (+474) = aufwaerts", so the arrow can be checked against the export
without opening the CSV.

Adds a plausibility check for the case where Antriebfahrtrichtung and the height
change disagree. The height wins and the conflict is reported, rather than one of
the two silently deciding.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 14:08:58 +02:00
s.ayadi 0392114e10 Orient the carousel arrows from Drehrichtung (UZS / GUZ)
Until now the two lanes of a Kreisel were joined by a pair of edges in both
directions, because the travel direction was unknown. The CSV does carry it in
the Drehrichtung merkmal, so the arrow inside a Kreisel is now directed:

  UZS  clockwise         -> material runs right to left, R -> L
  GUZ  counter-clockwise -> material runs left to right, L -> R

If the merkmal is missing or holds anything else, the circuit stays bidirectional
and that is reported as a warning, so an unknown value cannot silently invent a
direction.

The direction is visible in the drawing: the edge carries the rotation as its
label and the Kreisel cluster header states it in words, e.g.
"Kreisel Kreisel3 [0009] UZS (rechts nach links)".

Since a Kreisel is now traversed one way only, lanes can end up without an
outgoing or without an incoming connection. build_graph reports both cases,
naming the separators that sit on the affected lane, because such a lane means
carriers can either not leave or not reach it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:38:46 +02:00
s.ayadi 035c86ced9 Draw the TRO flow to scale with neato via --use-cords
Adds a --use-cords switch to tro_flow.py (--use-coords accepted too). With it,
every TRO node carries its plant coordinate as a pos attribute and the SVG is
produced by "neato -n" instead of dot, so the diagram looks like the plant
layout rather than a computed left-to-right flow.

Scale is 0.25 pt/mm (COORD_SCALE), which turns the ~11 x 20 m plant into roughly
2800 x 5000 pt - large enough that the TRO boxes do not overlap. Without the
switch the pos attributes stay in the DOT as information only, at 1:100, and dot
ignores them; the default layout is unchanged.

Two details neato -n needs: the legend gets a position of its own, placed below
left of the plant bounding box, because with -n a node without pos would land at
0,0 in the middle of the drawing. And the switch is refused with a clear message
if any TRO has no coordinate, since -n cannot lay out a graph with missing
positions.

Verified that neato -n reproduces the given coordinates: the positions it reports
back differ from the input by a constant offset only (spread 0.08 pt, from
Graphviz moving the drawing into the positive quadrant), and no node distance
changes by more than 0.08 pt over spans of up to 3600 pt.

graph_to_svg() in material_flow.py replaces dot_to_svg() to take an engine and
the no-op flag; dot_to_svg() stays as a thin wrapper. It now also treats output
on stderr as an error, so neato's "node has no position" warnings do not pass
silently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:24:14 +02:00
s.ayadi 19259a0251 Add TRO identification for the Mubea seat-cover store (500573)
Works out which TROs the control system will need for drawing 500573_60_1,
derived from the mechanical DXF and matched against the TRO catalog, plus the
annotated figures it refers to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:13:56 +02:00
s.ayadi 708f21fa95 Update 1Sep_SSCC and LoadingBoom layout images
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:13:56 +02:00
s.ayadi 849e985008 Add TRO flow program deriving the control objects from the layout
Sister program to material_flow.py with the same switches, but --tosvg draws the
flow diagram of the TROs instead of the individual objects. It reuses
read_elements and build_graph, so CSV parsing and direction logic exist once.

A TRO is the control logic of one hand-over point, derived from the separator and
what is attached to it, following doc/500573_Mubea/TRO_Identifikation_500573.md:

  Gefaellestrecke, >= 2 lines sharing feed/discharge -> PinStore_Auto per group
  Gefaellestrecke, single line                       -> 1Sep
  Strecke (driven)                                   -> Vario per segment
  Kreisel lane                                       -> 1Sep

A scanner at the separator deliberately does not change the type: in connect.ini
separators with a scanner are predominantly 1Sep, and 1Sep_SSCC is reserved for
the SSCC/end-measurement/WCS case, which a mechanical layout cannot reveal.
Affected points are reported as hints instead. A host with 2 or 3 outgoing flow
edges upgrades 1Sep to 1Sep1Swi/1Sep2Swi.

Connectivity contracts passive nodes: material runs separator to separator, so a
carousel lane carrying no TRO is traversed rather than treated as a dead end.
Without that, a lone separator on a spur loop would be reported as an orphan
purely as an artifact of the model. Each TRO must connect to at least one other;
otherwise an error file is written and the exit code is 3.

Each TRO also carries a plant coordinate - the centroid of the components it was
built from, so a 1Sep1Swi sits between its separator and its switch. Written into
the DOT as a pos attribute (1:100), which dot ignores and "neato -n" can use to
draw the diagram to scale.

Types, function blocks, components and colours come exclusively from
lib/tro_catalog.py.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:13:45 +02:00
s.ayadi b86c320f2b Add TRO type definition as a self-contained pydantic model
lib/tro_catalog.py is the single source for everything needed to create a TRO.
It reads no files at runtime; the origin of the data is recorded in comments
only, with CATALOG_AS_OF naming the revision it was taken from.

A TRO type is described by four things:
  name      short type as used in FB_Main and connect.ini ("1Sep")
  fb_block  the Siemens function block ("FB_ILS_MTRO_1Sep")
  items     which components sit inside it and how many
  style     colour group for drawings

TroDefinition is a pydantic v2 model with validate_assignment, so the get_*/set_*
methods cannot leave an object in an invalid state, and model_dump()/
model_validate() come for free for the planned JSON layout model.

Component lists for the four locally vendored FBs are verified against their VAR
declarations; the rest are .liblink references and follow the composition table
in TRO_Typen.md. Two findings from that comparison, recorded in the comments:
FB_ILS_MTRO_2Sep1Swi also declares two barcode readers, which the doc table
omits, and FB_EmptyCarrBuffer has no separator of its own - its base is a
separate 1Sep TRO (TRO101_IN next to TRO101 in the reference plant).

Colours follow the "Color legend" of the catalog: green means storage, the Vario
is purple. TroStyle renders both Graphviz attributes and Mermaid classDefs, so
the palette exists once and is ready for the BricsCAD symbol.

Enables pydantic in requirements.txt accordingly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:13:28 +02:00
s.ayadi 4454af5165 Add material flow generator for the mechanical layout
Reads the CSV export of the plant objects (ILS 2.0) from SKEL_DATA and builds a
directed graph of the material flow, written as Graphviz DOT and optionally SVG
into SKEL_RESULTS.

Node model: one node per Gefaellestrecke and Strecke; a Kreisel becomes two
nodes (-L / -R) wired into a circle, since it is a carousel with one lane per
travel direction. Separators and scanners are no nodes of their own; they are
shown on the label of the object they are assigned to.

Edge direction is not in the CSV - the Nachbarn column is symmetric. It is
derived from geometry instead: a Gefaellestrecke runs downhill away from its
highest neighbour, a driven Strecke follows Antriebfahrtrichtung ("Auf" uphill,
"Ab" downhill). Connections that stay ambiguous, e.g. between two Kreisel at the
same height, are drawn as bidirectional edges and reported.

The lane suffix of a Kreisel is always stated in the *partner's* row, so both
viewpoints of a link are merged before orienting it.

Switches:
  --file   name of the CSV in SKEL_DATA, or a full path
  --tosvg  render the DOT via Graphviz
  --doc    write a Markdown documentation of the plant objects

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:13:01 +02:00
s.ayadi f8baaf536e Add SKEL_DOC to the environment setup
The doc folder is now addressed via an environment variable like every other
project folder, so tools can resolve doc paths without hardcoding them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:12:47 +02:00
s.ayadi 10e354cae2 Update documentation images and annotations 2026-07-25 14:29:53 +02:00
m.stangl 70a6e580d4 Bilder gedreht 2026-07-24 11:57:08 +02:00
s.ayadi 98863097ac Refernenz auf alte Datei rausgeworfen 2026-07-23 11:46:47 +02:00
s.ayadi 92e34c4c32 Link zu Bild repariert 2026-07-23 11:31:30 +02:00
s.ayadi 5fca317888 Link zu Bild repariert 2026-07-23 11:30:30 +02:00
s.ayadi 2d7e7ab321 Screenshots vereinfacht, um besser auf die wesentlichen Elemente zu kommen 2026-07-23 11:27:16 +02:00
s.ayadi 9669d29dad Complete TRO documentation with bricsCAD screenshots 2026-07-22 15:56:59 +02:00
s.ayadi a70b5bba9a Update TRO documentation 2026-07-22 09:03:28 +02:00
s.ayadi 94c69cbd0c erste Fassung eines TRO Katalogs erstellt. Analyse des Graphen von HundM neu gezeichnet 2026-07-21 16:57:31 +02:00
m.stangl e901857869 Doku und erster Vorschlag aus dem Fortna Extrakt dazu 2026-07-21 11:59:14 +02:00
m.stangl c7c50190cb Umgebungsvariable heisst jetzt SPS_SKEL 2026-07-20 16:45:19 +02:00
m.stangl 06de3aba70 leeres Projekt mit Umgebung angelegt 2026-07-20 16:28:31 +02:00