[ADD] lib/dxf_anlage_export.py: gebaute und von Hand nachbearbeitete Anlage aus der Zeichnung zurueck in Nachbau-Testdaten (Kreisel, GF, VF standard/etage, VF-Linienzug als unveraendertes Journal, Einzelbloecke) - tests/testdata/dematic_arvato.json jetzt aus Dematic_TST.dxf; [ADD] dxf_scan_components.py: Gefaellestrecken aus Staustrecken-LINEs (Layer 6-SP), Start/Ende auf die Kreisel-Ketten verlaengert, AS-Element und Separator auf der Achse werden verbraucht, Steigfoerderer erkannt, --ohne-hoehen und --kreisel-hoehe; [ADD] vf_spec.lsp: Replay-Kern vsp-bau-journal aus vsp-bau-aus-spec herausgeloest (auch fuer aufgezeichnete Journale); [CHANGE] TEST_DEMATIC_ARVATO baut GF, VF standard/etage und Linienzug-Journale (mubea:gf-bauen/-vario-bauen wiederverwendet); [FIX] GF-Winkel aus Kreisel-Hoehen: AS/ES-Abfall mit falschem Vorzeichen an gf-winkel-berechnen uebergeben (3.79 statt ~2.49 Grad, GF endete 258 mm zu tief); [ADD] Tests test_dxf_scan_gf.py, test_dxf_anlage_export.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 13:35:46 +02:00
parent 2f9cb7eaed
commit ba20d63f63
9 changed files with 2024 additions and 1328 deletions
+140
View File
@@ -0,0 +1,140 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
test_dxf_anlage_export.py - Gebaute Anlage -> Nachbau-Testdaten
(lib/dxf_anlage_export.py). Laeuft ohne BricsCAD und ohne DXF: die
Wrapper-Daten sind die Werte aus Dematic_TST.dxf, von Hand nachgestellt
(Form wie lese_zeichnung sie liefert).
1. Kreisel: abstand exakt aus der Blockdefinition (AN8 -> SP8), nicht aus
dem ganzzahligen ABSTAND-Attribut.
2. GF auf zwei Kreiselketten: Endhoehe = Hoehe des Kreisels am Ende,
der (falsch) gebaute Winkel wird NICHT uebernommen.
3. GF ohne Kreisel am Ende: gebauter Winkel bleibt.
4. VF "standard": Parameter wie vfs-edit-basis-lesen, L_GF1 aus L_GF_m.
5. VF "linienzug": Journal unveraendert als Token-Liste.
6. Einzelblock: _2D/_3D-Suffix entfaellt.
"""
import os
import sys
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "lib"))
import dxf_anlage_export as dae # noqa: E402
def _insert(name, p, rot=0.0, attribs=None, xdata=None):
return {"name": name, "handle": "1", "p": list(p), "rot": rot,
"attribs": attribs or {}, "xdata": xdata or {}, "ps": 0}
KREISEL_OBEN = _insert("KREISEL_2_STANDARD_33044", (35860.37, 12519.72, 2437.0),
180.0, {"ID": "0002", "NUMMER": "2", "ABSTAND": "33044",
"KREISELART": "STANDARD"})
KREISEL_UNTEN = _insert("KREISEL_3_STANDARD_33470", (36661.24, 24878.8, 1826.0),
180.0, {"ID": "0003", "NUMMER": "3", "ABSTAND": "33470",
"KREISELART": "STANDARD"})
BLOCK_INHALT = {
"KREISEL_2_STANDARD_33044": [{"name": "AN8_3D", "p": [400.0, 0.0, 0.0]},
{"name": "SP8_3D", "p": [33444.28, 0.0, 0.0]}],
"KREISEL_3_STANDARD_33470": [{"name": "AN8_3D", "p": [400.0, 0.0, 0.0]},
{"name": "SP8_3D", "p": [33870.18, 0.0, 0.0]}],
}
def _geo():
return [dae.kreisel_geo(dae.kreisel_eintrag(k, BLOCK_INHALT, "T"))
for k in (KREISEL_OBEN, KREISEL_UNTEN)]
def test_kreisel_abstand_aus_blockdefinition():
k = dae.kreisel_eintrag(KREISEL_OBEN, BLOCK_INHALT, "T")
assert k["id"] == "Kreisel_T_02"
assert k["abstand"] == pytest.approx(33044.28, abs=0.01)
assert (k["x"], k["y"], k["z"], k["rotation"]) == (35860.37, 12519.72, 2437.0, 180.0)
ohne_block = dae.kreisel_eintrag(KREISEL_OBEN, {}, "T")
assert ohne_block["abstand"] == 33044.0
def test_gf_zwischen_zwei_kreiseln_bekommt_endhoehe():
gf = _insert("GF_1", (5833.56, 12919.72, 2437.0), 0.0,
{"ID": "0005", "SEITE_AS": "links", "SEITE_ES": "links",
"HOEHE_BIS_mm": "1568"},
{"SSG_GF_EDIT": ["90", "3.7929", "90", "90", "2437", "11559.08"]})
hinweise = []
e = dae.gf_eintrag(gf, _geo(), "T", hinweise)
assert e["test_id"] == "GF_T_01"
assert e["start_mm"] == [5833.56, 12919.72, 2437.0]
assert e["deltaL_mm"] == 11559.08 and e["hz_grad"] == 90.0
assert (e["kreisel_start"], e["kreisel_ende"]) == ("Kreisel_T_02", "Kreisel_T_03")
assert e["hoehe_ende_mm"] == 1826.0
assert "winkel_grad" not in e
assert hinweise == []
def test_gf_ohne_kreisel_behaelt_gebauten_winkel():
gf = _insert("GF_7", (0.0, 0.0, 1000.0), 0.0, {},
{"SSG_GF_EDIT": ["0", "3", "90", "30", "1000", "5000"]})
e = dae.gf_eintrag(gf, _geo(), "T", [])
assert e["winkel_grad"] == 3.0
assert "hoehe_ende_mm" not in e and "kreisel_ende" not in e
assert e["es_winkel"] == "30"
def test_gf_ohne_xdata_wird_gemeldet():
hinweise = []
assert dae.gf_eintrag(_insert("GF_9", (0, 0, 0)), [], "T", hinweise) is None
assert "SSG_GF_EDIT" in hinweise[0]
def test_vf_standard():
vf = _insert("VF_2", (3314.07, 24478.8, 1826.0), 0.0,
{"ID": "0082", "DELTA_L_mm": "11559", "DELTA_H_mm": "611",
"ANTRIEBFAHRTRICHTUNG": "Auf", "SEITE_AS": "links",
"MOTORSEITE": "rechts", "VF_WINKEL": "15",
"L_GF_m": "1.907,1.907"},
{"SSG_VF_EDIT": ["standard", "270"]})
e = dae.vf_standard_eintrag(vf, "T", "standard")
assert e["test_id"] == "VF_T_02"
assert (e["richtung"], e["deltaL"], e["deltaH"], e["winkel"]) == ("Auf", 11559.0, 611.0, 15)
assert e["L_GF1"] == 1907.0 and e["hz"] == 270.0
assert (e["seite"], e["motorseite"]) == ("links", "rechts")
assert (e["x"], e["y"], e["z"]) == (3314.07, 24478.8, 1826.0)
def test_vf_linienzug_journal_unveraendert():
roh = ["linienzug", "PT:42161.679193,2448.319538,0^JREAL:2177^JSTR:1^JSTR:9",
"0^JSTEP:Linie-VF^JDL:9370.410886"]
vf = _insert("VF_26", (42161.68, 2448.32, 2177.0), 0.0, {"ID": "0184"},
{"SSG_VF_EDIT": roh})
e = dae.vf_journal_eintrag(vf, "T", 1)
assert e["typ"] == "linienzug_journal" and e["test_id"] == "VF_T_LZ_01"
# Chunk-Grenze mitten im Token ("STR:9" + "0") wird korrekt zusammengesetzt
assert e["journal"] == ["PT:42161.679193,2448.319538,0", "REAL:2177", "STR:1",
"STR:90", "STEP:Linie-VF", "DL:9370.410886"]
def test_einzelblock_ohne_dimensionssuffix():
e = dae.einzel_eintrag(_insert("Separator_SP_3D", (3038.72, 12119.73, 2473.0),
0.0, {"ID": "0043", "ZUORDNUNG": "0002"}))
assert e["block"] == "Separator_SP"
assert e["function"] == "insert" and e["z"] == 2473.0
assert "ID 0043" in e["comment"]
def test_reihenfolge_vf_vor_gf():
inserts = [
_insert("GF_1", (5833.56, 12919.72, 2437.0), 0.0, {},
{"SSG_GF_EDIT": ["90", "3", "90", "90", "2437", "11559.08"]}),
_insert("Scanner_3D", (1, 2, 3)),
_insert("VF_2", (0, 0, 0), 0.0, {}, {"SSG_VF_EDIT": ["standard", "0"]}),
KREISEL_OBEN, KREISEL_UNTEN,
_insert("*U12", (0, 0, 0)),
]
eintraege, _ = dae.baue_eintraege(inserts, BLOCK_INHALT, "T")
art = ["K" if "abstand" in e else "GF" if "modus" in e else
"VF" if "typ" in e else e["block"] for e in eintraege]
assert art == ["K", "K", "VF", "GF", "Scanner"]