- Symbole erzeugen (HW_SYMBOLE, Einzelbefehle HW_SYM_*): Bauteile nach cfg/io_regeln.json (reduzierte Kopie aus kabellaengen), Symbole aus cfg/hw_symbole.json katalog-identisch nachgebaut (lib/hw_symbole.py), REALE_POSITION mit x auf dem Bauteil, Paare uebereinander, Ausweichen in y ohne Ueberlappung - Symbole nummerieren (HW_NUMMERIEREN): lib/create_numbers.py samt error_collector.py und cfg/enumerate.cfg aus kabellaengen uebernommen - Ausgabeziel per Dialog dcl/hw_ausgabe.dcl: aktuelle Zeichnung, bestehende Datei ergaenzen oder neue Datei - Materialfluss mit XDATA SSG_HW_MF, ergaenzt vorhandene Datei - Doku doc/hw_planung.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
132 lines
5.4 KiB
Python
132 lines
5.4 KiB
Python
"""Tests fuer lib/hw_materialfluss.py (Materialfluss-Skizze der HW-Planung)."""
|
|
|
|
import os
|
|
import sys
|
|
|
|
import ezdxf
|
|
import pytest
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
|
|
|
|
import hw_materialfluss as hw # noqa: E402
|
|
|
|
|
|
def _umlaufsinn(punkte):
|
|
""">0 = gegen den Uhrzeigersinn, <0 = im Uhrzeigersinn (Gauss)."""
|
|
return sum(a[0] * b[1] - b[0] * a[1]
|
|
for a, b in zip(punkte, punkte[1:] + punkte[:1]))
|
|
|
|
|
|
def test_kreisel_rechteck_uzs_senkrecht():
|
|
# Kreisel2 aus Mubea: senkrecht (Drehung 90), UZS
|
|
p = hw.kreisel_rechteck({"x": 0.0, "y": 4529.5}, {"dx": 800.0, "dy": 20815.0},
|
|
90.0, "UZS")
|
|
xs = sorted(round(x) for x, _ in p)
|
|
ys = sorted(round(y) for _, y in p)
|
|
assert xs[0] == -400 and xs[-1] == 400
|
|
assert ys[0] == round(4529.5 - 20815 / 2) and ys[-1] == round(4529.5 + 20815 / 2)
|
|
assert _umlaufsinn(p) < 0
|
|
|
|
|
|
def test_kreisel_rechteck_guz_waagrecht():
|
|
p = hw.kreisel_rechteck({"x": 3300.0, "y": -5478.0}, {"dx": 5800.0, "dy": 800.0},
|
|
0.0, "GUZ")
|
|
assert _umlaufsinn(p) > 0
|
|
assert {round(x) for x, _ in p} == {400, 6200}
|
|
|
|
|
|
def test_kreisel_rechteck_schraeg_aus_abstand():
|
|
p = hw.kreisel_rechteck({"x": 0.0, "y": 0.0}, {"dx": 9999.0, "dy": 9999.0},
|
|
45.0, "GUZ", abstand_m=5.0)
|
|
seiten = sorted(round(((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2) ** 0.5)
|
|
for a, b in zip(p, p[1:] + p[:1]))
|
|
assert seiten == [800, 800, 5800, 5800]
|
|
|
|
|
|
def _el(tid, art, x, y, trackids=""):
|
|
return {"teileid": tid, "teileart": art, "mitte": {"x": x, "y": y},
|
|
"trackids": trackids}
|
|
|
|
|
|
def test_strecken_pfad_as_nach_es():
|
|
els = [_el("1", hw.AS_ART, 0, 0), _el("2", "ILS 2.0 Separator", 50, 0),
|
|
_el("3", hw.ES_ART, 100, 0)]
|
|
nach_id = {e["teileid"]: e for e in els}
|
|
strecke = _el("9", "ILS 2.0 Strecke", 0, 0, "1, 2, 3, 77")
|
|
assert hw.strecken_pfad(strecke, nach_id) == [(0, 0), (50, 0), (100, 0)]
|
|
# umgekehrte Kettenfolge -> trotzdem AS zuerst
|
|
strecke["trackids"] = "3, 2, 1"
|
|
assert hw.strecken_pfad(strecke, nach_id) == [(0, 0), (50, 0), (100, 0)]
|
|
|
|
|
|
def test_pfeil_spitze_und_richtung():
|
|
p = hw.pfeil((100.0, 0.0), (1.0, 0.0), laenge=10.0, breite=4.0)
|
|
assert p[1] == (100.0, 0.0)
|
|
assert p[0] == pytest.approx((90.0, 2.0)) and p[2] == pytest.approx((90.0, -2.0))
|
|
|
|
|
|
def test_layer_cfg_abschnitt(tmp_path):
|
|
cfg = tmp_path / "layer.cfg"
|
|
cfg.write_text("[allgemein]\nhw_kreisel = FALSCH, 1\n"
|
|
"[hw_planung]\n# Kommentar\nhw_kreisel = MeinKreisel, 9\n",
|
|
encoding="utf-8")
|
|
layer = hw.layer_cfg_lesen(str(cfg))
|
|
assert layer["hw_kreisel"] == ("MeinKreisel", 9)
|
|
assert layer["hw_weiche"] == hw.LAYER_VORGABEN["hw_weiche"]
|
|
|
|
|
|
def test_csv_nach_dxf(tmp_path):
|
|
kopf = ("Elementnummer;TeileArt;TeileId;Bezeichnung;Planquadrat;Anzahl;Position;"
|
|
"Boundingbox;Insertpoint;K1;K2;K3;K4;Nachbarn;TrackIds;Fehler;Warnungen;Merkmale")
|
|
zeilen = [
|
|
kopf,
|
|
'1;"ILS 2.0 Kreisel";"0001";"K1";"";1;"0.00, 0.00, 0.00";"5800.00, 800.00, 1.00";'
|
|
'"";"";"";"";"";"";"";"";"";{"Drehrichtung": "UZS", "Drehung": 0}',
|
|
'2;"ILS Weiche";"0002";"W";"";1;"10.00, 20.00, 0.00";"100.00, 250.00, 30.00";'
|
|
'"";"";"";"";"";"";"";"";"";{}',
|
|
]
|
|
csv_pfad = tmp_path / "export.csv"
|
|
csv_pfad.write_text("\n".join(zeilen) + "\n", encoding="utf-8")
|
|
dxf_pfad = tmp_path / "Test_HW.dxf"
|
|
assert hw.main([str(csv_pfad), str(dxf_pfad), "--layer-cfg", ""]) == 0
|
|
msp = ezdxf.readfile(str(dxf_pfad)).modelspace()
|
|
assert len(msp.query('LWPOLYLINE[layer=="HW_Kreisel"]')) == 1
|
|
assert len(msp.query('CIRCLE[layer=="HW_Weiche"]')) == 1
|
|
|
|
|
|
def _csv_mit_kreisel(tmp_path):
|
|
kopf = ("Elementnummer;TeileArt;TeileId;Bezeichnung;Planquadrat;Anzahl;Position;"
|
|
"Boundingbox;Insertpoint;K1;K2;K3;K4;Nachbarn;TrackIds;Fehler;Warnungen;Merkmale")
|
|
zeile = ('1;"ILS 2.0 Kreisel";"0001";"K1";"";1;"0.00, 0.00, 0.00";"5800.00, 800.00, 1.00";'
|
|
'"";"";"";"";"";"";"";"";"";{"Drehrichtung": "UZS", "Drehung": 0}')
|
|
csv_pfad = tmp_path / "export.csv"
|
|
csv_pfad.write_text(kopf + "\n" + zeile + "\n", encoding="utf-8")
|
|
return csv_pfad
|
|
|
|
|
|
def test_materialfluss_markiert_und_ersetzt_nur_eigenes(tmp_path):
|
|
"""Erzeugte Objekte tragen XDATA SSG_HW_MF; ein zweiter Lauf ersetzt sie,
|
|
laesst aber fremde Geometrie (z.B. Renamer-Rahmen) stehen."""
|
|
csv_pfad = _csv_mit_kreisel(tmp_path)
|
|
dxf_pfad = tmp_path / "Test_HW.dxf"
|
|
assert hw.main([str(csv_pfad), str(dxf_pfad), "--layer-cfg", ""]) == 0
|
|
doc = ezdxf.readfile(str(dxf_pfad))
|
|
msp = doc.modelspace()
|
|
assert msp and all(e.has_xdata(hw.XDATA_APP) for e in msp)
|
|
msp.add_line((0, 0), (1, 1), dxfattribs={"layer": "ILS_RENAMER"})
|
|
doc.saveas(str(dxf_pfad))
|
|
|
|
assert hw.main([str(csv_pfad), str(dxf_pfad), "--layer-cfg", ""]) == 0
|
|
msp = ezdxf.readfile(str(dxf_pfad)).modelspace()
|
|
assert len(msp.query('LWPOLYLINE[layer=="HW_Kreisel"]')) == 1
|
|
assert len(msp.query('LINE[layer=="ILS_RENAMER"]')) == 1
|
|
|
|
|
|
def test_materialfluss_einheitenlos(tmp_path):
|
|
"""Zwischendatei fuer die aktuelle Zeichnung: $INSUNITS = 0, sonst
|
|
skaliert BricsCAD sie beim Einfuegen."""
|
|
csv_pfad = _csv_mit_kreisel(tmp_path)
|
|
dxf_pfad = tmp_path / "Test_HW_tmp.dxf"
|
|
assert hw.main([str(csv_pfad), str(dxf_pfad), "--layer-cfg", "", "--einheitenlos"]) == 0
|
|
assert ezdxf.readfile(str(dxf_pfad)).header["$INSUNITS"] == 0
|