tro_annotate --anlage: Staubereiche in der Zeichnung, --read-jams zurueck

Mit --anlage <csv>_anlage.json zeichnet tro_annotate je Staubereich einen
Block JAM_AREA (Layer TRO_JAM): Rechteck mit dem Namen Jam<NNNN>_<k> wie in
der SCL, jam_versatz vor dem Separator Richtung Vorgaenger. Attribute
CONVEYOR, NUMBER, TRO + ENTRY (stabiler Schluessel), AREA, AUTO. Doppelklick
oeffnet den Dialog JAM_EDIT (dxfmakros).

--read-jams uebernimmt im Dialog geaenderte Nummern aus der beschrifteten
Zeichnung nach cfg/<csv>_jam_names.ini (nur Abweichungen von AUTO);
scl_skeleton wendet die Datei mit der Anlagen-JSON an (scl_context:
jam_numbers + Korrekturen, load/save_jam_overrides).

Dematic: 25 Staubereiche gezeichnet; Rundreise TRO20 -> Jam0054_9 bis in die
SCL geprueft. Gemeinsame Zeichnung mit --offset 0,0 beschriften (automatischer
Lagebezug waehlte dy +360 mm).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 12:47:11 +02:00
parent 404d500271
commit e5d1fce41f
7 changed files with 540 additions and 17 deletions
+131
View File
@@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
"""Tests fuer die Staubereiche in tro_annotate.py (--anlage, --read-jams).
Kleine Anlage: ein Kreisel mit zwei TROs hintereinander (K0042). Die Bloecke
JAM_AREA tragen Name, Foerderer, Nummer und den stabilen Schluessel TRO +
Eingang; --read-jams uebernimmt im Dialog JAM_EDIT geaenderte Nummern nach
<csv>_jam_names.ini, und scl_context setzt sie in die SCL ein.
"""
import ezdxf
import tro_annotate
from dxf_registration import Transform
from model import (
AnlageDocument,
Analysis,
ConveyorBinding,
DerivedTro,
LayoutPart,
TroDocument,
)
from scl_context import build_context, load_jam_overrides
IDENT = Transform(dx=0.0, dy=0.0)
def _tro(tro_id, sep, x, vor, nach):
return DerivedTro(tro_id=tro_id, type_name="1Sep", label=tro_id, hosts=["0004-L"],
separators=[sep], scanners=[], reason="Test",
predecessors=vor, successors=nach, position=(x, 0.0, 0.0))
def anlage() -> AnlageDocument:
tros = [_tro("TRO01", "0101", 0.0, ["SYS1"], ["TRO02"]),
_tro("TRO02", "0102", 5000.0, ["TRO01"], ["SYS2"])]
parts = [
LayoutPart(layout_id="0004", kind="Kreisel", name="Kreisel4",
position=(2500.0, 0.0, 0.0), bbox=(8000.0, 800.0, 1.0)),
LayoutPart(layout_id="0101", kind="Separator", name="S1", position=(0.0, 0.0, 0.0)),
LayoutPart(layout_id="0102", kind="Separator", name="S2", position=(5000.0, 0.0, 0.0)),
]
layout = TroDocument.from_analysis(Analysis(tros=tros), source="x.csv", parts=parts)
return AnlageDocument(layout=layout, conveyors=[
ConveyorBinding(eplan_name="K0042", layout_id="0004", motor="MA0042")])
def _zeichnen(overrides=None):
doc = ezdxf.new()
tro_annotate.ensure_appid(doc)
layers = tro_annotate.build_layers(doc, tro_annotate.LAYER_PREFIX)
warnings: list[str] = []
n = tro_annotate.place_jams(doc.modelspace(), doc, anlage(), overrides or {}, IDENT,
layers, 200.0, warnings)
return doc, n, warnings
def _jams(doc) -> dict[str, dict]:
out = {}
for ref in doc.modelspace().query(f'INSERT[name=="{tro_annotate.JAM_BLOCK}"]'):
a = {t.dxf.tag: t.dxf.text for t in ref.attribs}
a["pos"] = (round(ref.dxf.insert.x), round(ref.dxf.insert.y))
out[a["TRO"]] = a
return out
def test_staubereiche_als_block_mit_name_und_schluessel():
doc, n, warnings = _zeichnen()
assert n == 2 and warnings == []
jams = _jams(doc)
assert {k: v["NAME"] for k, v in jams.items()} == {"TRO01": "Jam0042_1",
"TRO02": "Jam0042_2"}
t2 = jams["TRO02"]
assert (t2["CONVEYOR"], t2["NUMBER"], t2["ENTRY"], t2["AREA"], t2["AUTO"]) == (
"0042", "2", "1", "J0102", "0042_2")
# vor dem Separator, Richtung Vorgaenger TRO01 (gegen den Fluss)
assert t2["pos"] == (5000 - round(tro_annotate.JAM_VERSATZ), 0)
# TRO01 hat nur eine Station als Vorgaenger: am Separator
assert jams["TRO01"]["pos"] == (0, 0)
def test_korrektur_wird_gezeichnet():
doc, _, _ = _zeichnen({"TRO02.1": ("0042", 7)})
t2 = _jams(doc)["TRO02"]
assert (t2["NAME"], t2["NUMBER"], t2["AUTO"]) == ("Jam0042_7", "7", "0042_2")
def test_geaenderte_nummer_zurueck_in_die_korrekturdatei(tmp_path):
doc, _, _ = _zeichnen()
# wie der Dialog JAM_EDIT: NUMBER und NAME neu
for ref in doc.modelspace().query(f'INSERT[name=="{tro_annotate.JAM_BLOCK}"]'):
a = {t.dxf.tag: t for t in ref.attribs}
if a["TRO"].dxf.text == "TRO02":
a["NUMBER"].dxf.text = "7"
a["NAME"].dxf.text = "Jam0042_7"
dxf = tmp_path / "x_annotated.dxf"
doc.saveas(dxf)
ini = tmp_path / "x_jam_names.ini"
overrides, meldungen = tro_annotate.read_jams(dxf, ini)
assert overrides == {"TRO02.1": ("0042", 7)}
assert load_jam_overrides(ini) == overrides
assert any("TRO02.1: Jam0042_7" in m for m in meldungen)
# in der SCL: Ein- und Ausgang tragen die korrigierte Nummer
a = anlage()
ctx = build_context(a.layout, "x_anlage.json", anlage=a, jam_overrides=overrides)
tro1, tro2 = ctx.tros
assert tro2.jam_entries[0].db.value == '"DB_JamArea0042".stJam0042_7'
assert tro1.jam_exits[0].db.value == '"DB_JamArea0042".stJam0042_7'
# zurueck auf die abgeleitete Nummer -> Eintrag (und Datei) weg
for ref in doc.modelspace().query(f'INSERT[name=="{tro_annotate.JAM_BLOCK}"]'):
a2 = {t.dxf.tag: t for t in ref.attribs}
if a2["TRO"].dxf.text == "TRO02":
a2["NUMBER"].dxf.text = "2"
doc.saveas(dxf)
overrides, _ = tro_annotate.read_jams(dxf, ini)
assert overrides == {} and not ini.exists()
def test_ungueltige_nummer_wird_nicht_uebernommen(tmp_path):
doc, _, _ = _zeichnen()
for ref in doc.modelspace().query(f'INSERT[name=="{tro_annotate.JAM_BLOCK}"]'):
a = {t.dxf.tag: t for t in ref.attribs}
if a["TRO"].dxf.text == "TRO01":
a["CONVEYOR"].dxf.text = "42"
dxf = tmp_path / "x_annotated.dxf"
doc.saveas(dxf)
overrides, meldungen = tro_annotate.read_jams(dxf, tmp_path / "x_jam_names.ini")
assert overrides == {}
assert any("TRO01.1" in m and "ungueltig" in m for m in meldungen)