export_csv: VF_n/GF_n-Einzelelemente (Boegen, Streckenelemente, Umlenk-/Motorstation) als Proxy-Zeilen exportieren
Bisher tauchten nur der Wrapper selbst, verpackte Separatoren und AS/ES- Kettenenden als eigene CSV-Zeilen auf - die uebrigen Kettenbausteine (Boegen, gerade Foerderer-/Gefaellestrecken-Segmente, Umlenk-/Motorstation) blieben nur in der Wrapper-Blockdefinition verpackt. csv:element-proxies- erzeugen (analog zu csv:sep-proxies-erzeugen) fuegt fuer jeden Fund eine temporaere Kopie ein, K1/K2 kommen aus dem EIGENEN KS_EIN/KS_AUS jedes Elements (nicht dem Gesamt-AS/ES der Kette) und bleiben leer, wo keines existiert (z.B. das nie tatsaechlich eingefuegte Vario_Kurve_*). Zusaetzlicher Fix: csv:get-k-kos-strings ignorierte bisher die Skalierung des Top-Level-Inserts - fuer Staustrecke_SP_1000_mm (das per Laenge nicht- uniform skaliert wird) waere K2 damit falsch gewesen, unabhaengig von der tatsaechlich gebauten Segmentlaenge. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
4f1a0e0b5b
commit
f751a90076
@@ -10,8 +10,10 @@ Prueft:
|
||||
Modus 3 uebernimmt deltaH/deltaL aus Modus 2)
|
||||
"""
|
||||
|
||||
import csv
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
|
||||
@@ -232,3 +234,136 @@ class TestGefaellestreckeModusKonsistenz:
|
||||
f'von {quelle_id}={m2["delta_h_mm"]} ab '
|
||||
f'(Toleranz {self.DELTA_H_TOLERANZ_MM} mm)'
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Klasse 4: CSV-Export-Pruefung (gefaellestrecke_tests_export.csv)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# TeileArt-Werte, die einen Sensor bzw. ein Streckenbauteil beschreiben statt
|
||||
# einer GF-Strecke selbst - csv:element-proxies-erzeugen (export.lsp) erfasst
|
||||
# die verpackten Bausteine (Boegen, gerade Foerderer-/Gefaellestrecken-
|
||||
# Segmente, Umlenk-/Motorstation) als je eigene Zeile, wie schon
|
||||
# csv:sep-proxies-erzeugen fuer Separator_SP-Symbole; siehe
|
||||
# test_foerderer.py:NEBEN_TEILEARTEN (analoge Konstante dort, erste GF-eigene
|
||||
# CSV-Struktur-Pruefung hier - vorher gab es fuer GF-Ketten noch keine).
|
||||
NEBEN_TEILEARTEN = {
|
||||
"ILS 2.0 Separator",
|
||||
"ILS 2.0 Bogen",
|
||||
"ILS 2.0 Streckenelement",
|
||||
"ILS 2.0 Umlenkstation",
|
||||
"ILS 2.0 Motorstation",
|
||||
}
|
||||
|
||||
|
||||
class TestGefaellestreckeExportCSV:
|
||||
"""Prueft den CSV-Export aus BricsCAD (gefaellestrecke_tests_export.csv,
|
||||
aus TEST_EXPORT_ALL)."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
csv_path = os.path.join(_output_dir(), "gefaellestrecke_tests_export.csv")
|
||||
if not os.path.exists(csv_path):
|
||||
pytest.skip(
|
||||
"gefaellestrecke_tests_export.csv nicht vorhanden - "
|
||||
"TEST_GEFAELLESTRECKE und anschliessend TEST_EXPORT_ALL in BricsCAD ausfuehren"
|
||||
)
|
||||
self.rows = []
|
||||
with open(csv_path, "r", encoding="utf-8") as f:
|
||||
reader = csv.reader(f, delimiter=";")
|
||||
self.header = next(reader)
|
||||
for row in reader:
|
||||
self.rows.append(row)
|
||||
self.col = {name: i for i, name in enumerate(self.header)}
|
||||
|
||||
def _cell(self, row, spalte):
|
||||
i = self.col.get(spalte)
|
||||
if i is None or i >= len(row):
|
||||
return None
|
||||
return row[i]
|
||||
|
||||
def test_teileart_bekannt(self):
|
||||
"""Jede Zeile ist entweder eine GF-Strecke oder eine der bekannten
|
||||
Neben-TeileArten - keine unerwartete dritte Art."""
|
||||
bekannt = {"ILS 2.0 Gefaellestrecke", "ILS 2.0 Strecke"} | NEBEN_TEILEARTEN
|
||||
for i, row in enumerate(self.rows):
|
||||
teileart = self._cell(row, "TeileArt")
|
||||
if teileart is None:
|
||||
continue
|
||||
assert teileart.strip('"') in bekannt, (
|
||||
f"Zeile {i + 2}: unbekannte TeileArt '{teileart}'"
|
||||
)
|
||||
|
||||
def test_element_zeilen_haben_k1_oder_k2(self):
|
||||
"""Bogen/Umlenkstation/Motorstation-Zeilen sollten zuverlaessig ein
|
||||
eigenes KS_EIN/KS_AUS tragen (anders als Vario_Kurve_*, das laut
|
||||
Code-Kommentar nie tatsaechlich eingefuegt wird und daher hier auch
|
||||
nie auftaucht) - K1 ODER K2 muss gefuellt sein. Streckenelement wird
|
||||
hier NICHT geprueft (dafuer siehe
|
||||
test_streckenelement_laenge_stimmt_mit_k1_k2_ueberein unten)."""
|
||||
pruef_teilearten = {"ILS 2.0 Bogen", "ILS 2.0 Umlenkstation", "ILS 2.0 Motorstation"}
|
||||
for i, row in enumerate(self.rows):
|
||||
teileart = self._cell(row, "TeileArt")
|
||||
if teileart is None or teileart.strip('"') not in pruef_teilearten:
|
||||
continue
|
||||
k1 = self._cell(row, "K1")
|
||||
k2 = self._cell(row, "K2")
|
||||
assert (k1 and k1.strip('"')) or (k2 and k2.strip('"')), (
|
||||
f"Zeile {i + 2}: TeileArt='{teileart}' hat weder K1 noch K2 gefuellt"
|
||||
)
|
||||
|
||||
def test_streckenelement_laenge_stimmt_mit_k1_k2_ueberein(self):
|
||||
"""Regressionstest fuer den csv:get-k-kos-strings-Skalierungsfix:
|
||||
Staustrecke_SP_1000_mm wird beim Bau per Laenge NICHT-UNIFORM skaliert
|
||||
(insert-inclined-scaled-block) - ohne die sx/sy/sz-Beruecksichtigung
|
||||
in csv:get-k-kos-strings waere K2 dort so berichtet, als waere der
|
||||
Block unskaliert (1000mm) eingefuegt, unabhaengig von der tatsaechlich
|
||||
gebauten Segmentlaenge. Der K1->K2-Abstand einer Streckenelement-Zeile
|
||||
muss daher der Boundingbox-Ausdehnung (die reale, gebaute Laenge)
|
||||
entsprechen, NICHT literal 1000mm, sofern das Segment gestreckt wurde."""
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
|
||||
from export_neighbors import _decode_trans_xy
|
||||
|
||||
gefunden = 0
|
||||
for i, row in enumerate(self.rows):
|
||||
teileart = self._cell(row, "TeileArt")
|
||||
if teileart is None or teileart.strip('"') != "ILS 2.0 Streckenelement":
|
||||
continue
|
||||
k1_str = self._cell(row, "K1")
|
||||
k2_str = self._cell(row, "K2")
|
||||
if not k1_str or not k2_str:
|
||||
continue
|
||||
p1 = _decode_trans_xy(k1_str.strip('"'))
|
||||
p2 = _decode_trans_xy(k2_str.strip('"'))
|
||||
if p1 is None or p2 is None:
|
||||
continue
|
||||
k1_k2_laenge = ((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2) ** 0.5
|
||||
# Nur Segmente pruefen, deren reale Laenge sichtbar von 1000mm
|
||||
# abweicht - sonst waere ein (durch den Bug) falsch auf 1000mm
|
||||
# zurueckfallender Wert zufaellig nicht von einem korrekten zu
|
||||
# unterscheiden.
|
||||
if abs(k1_k2_laenge - 1000.0) < 50.0:
|
||||
continue
|
||||
gefunden += 1
|
||||
boundingbox = self._cell(row, "Boundingbox")
|
||||
assert boundingbox, f"Zeile {i + 2}: Boundingbox fehlt"
|
||||
dx, dy, dz = (float(v) for v in boundingbox.strip('"').split(","))
|
||||
bbox_diagonale = (dx ** 2 + dy ** 2) ** 0.5
|
||||
# Toleranz: die Boundingbox-Diagonale ist ein Oberwert (das
|
||||
# Element kann geneigt/gedreht sein), der K1-K2-Abstand darf sie
|
||||
# nicht wesentlich ueberschreiten - waere er trotz Streckung immer
|
||||
# ~1000mm (der Bug), wuerde er bei stark gestreckten Segmenten
|
||||
# deutlich UNTER der Bounding-Box-Diagonale liegen.
|
||||
assert k1_k2_laenge <= bbox_diagonale + 50.0, (
|
||||
f"Zeile {i + 2}: K1->K2-Abstand={k1_k2_laenge:.0f}mm groesser als "
|
||||
f"Boundingbox-Diagonale={bbox_diagonale:.0f}mm"
|
||||
)
|
||||
assert k1_k2_laenge >= 50.0, (
|
||||
f"Zeile {i + 2}: K1->K2-Abstand={k1_k2_laenge:.0f}mm verdaechtig klein "
|
||||
f"(Skalierungsfix eventuell nicht wirksam)"
|
||||
)
|
||||
if gefunden == 0:
|
||||
pytest.skip(
|
||||
"keine Streckenelement-Zeile mit von 1000mm abweichender "
|
||||
"K1-K2-Laenge gefunden - Testdaten enthalten kein gestrecktes Segment"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user