[ADD] Nachbau ILS-3.0-Showroom: tests/testdata/showroom_ils.json aus Showroom_ILS_3_0.dxf (6 Kreisel, 11 Gefaellestrecken, Separatoren/Scanner; Hoehen noch Platzhalter), Treiber tests/test_showroom_ils.lsp (TEST_SHOWROOM_ILS, Menue Tests -> ILS 3.0 Showroom), in alltests.json bis zu den Hoehen deaktiviert; [ADD] dxf_scan_components.py: --flach (waagrechte Staustrecken mit AS an genau einem Ende als GF), --kreisel-achse (Kreisel ohne AN8/SP8, z.B. Schleifen mit Weichenboegen), zerstueckelte Staustrecken-Linien werden vereint, ES am unteren Ende wird erkannt und verbraucht, AS/ES-Seite ohne Seitenangabe im Namen aus der Lage zur Fahrtrichtung, Kurznamen AE-*/EE-*; [FIX] Kreisel-Paarung: zwei Kreisel auf derselben Achse wurden ueber die Luecke gepaart (800 + 12832 statt 9033 + 3000) - Tausch-Nachbesserung auf minimale Abstandssumme; [CHANGE] test_dematic_arvato.lsp: Lauf als dematic_arvato:bau-datei herausgeloest (vom Showroom-Treiber mitgenutzt); [ADD] Tests in test_dxf_scan_gf.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 15:00:45 +02:00
parent f1583d2d41
commit 4d0d689815
9 changed files with 824 additions and 47 deletions
+1
View File
@@ -197,6 +197,7 @@ Aus dem Menue "Tests" bzw. der Kommandozeile; Details in `doc/LispTests.md`.
| `TEST_HUNDM05` (`_2D`/`_3D`) | HundM05-Anlage aus der Spec bauen |
| `TEST_MUBEA` | Mubea-Nachbau aus `tests/testdata/mubea.json` |
| `TEST_DEMATIC_ARVATO` | Dematic/Arvato-Nachbau (Flowpuffer/Kreisel) |
| `TEST_SHOWROOM_ILS` | ILS-3.0-Showroom-Nachbau (Kreisel, Gefaellestrecken; Hoehen noch Platzhalter) |
---
+209 -17
View File
@@ -153,6 +153,15 @@ GF_LINIE_MIN_DZ = 100.0
# rechts), nicht der kleinste Abstand.
GF_AS_MAX_ENTLANG = 2000.0
GF_AS_MAX_QUER = 700.0
# Wie weit (mm) das AS/ES VOR dem Linienende liegen darf, also schon auf der
# Kreisel-Seite. In Dematic_arvato liegt es innerhalb der Linie; im
# Showroom_ILS_3_0-Plan endet die Staustrecke 305 mm vor dem Element, das auf
# der Kreisel-Kette sitzt. Kleiner als das Linienraster quer zur Kette
# (Nachbar-GF der Gegenrichtung lag dort 1200 mm entfernt).
GF_AS_MAX_VOR = 800.0
# Toleranz (mm) fuer zusammenstossende Linienenden beim Zusammenfassen
# zerstueckelter Staustrecken (linien_zusammenfassen).
GF_LINIE_STOSS_TOL = 1.0
# Groesste Luecke (mm) zwischen Linienende und Kreisel-Kette, bis zu der die
# Linie an diesen Kreisel angeschlossen wird. Gezeichnet ist nur die
@@ -303,8 +312,9 @@ FAMILIEN = [
("omniflo_weiche", ["Weiche *", "Weichen-TEF*"]),
("bogen", ["3D_ILS_Bogen_*", "3D_FF_Bogen_*", "TEF_Bogen_*",
"tef30*", "tef90*"]),
# "AE-D"/"EE-R": Kurznamen der aelteren ILS-Plaene (Showroom_ILS_3_0.dxf)
("schleuselement", ["Ausschleuselement*", "Einschleuselement*",
"EE_Neu*", "AE_Neu*"]),
"EE_Neu*", "AE_Neu*", "AE-*", "EE-*"]),
("sensor", ["S-LP", "S-SP", "SCAN", "SCAN_*", "Scanner*",
"Separator*", "Stopper*", "Stopperschleuse*"]),
]
@@ -399,12 +409,12 @@ def mappe_block(bname):
geraten)
# Aus-/Einschleuselement -> AS_Element / ES_Element
if "ausschleus" in low or low.startswith("ae_"):
if "ausschleus" in low or low.startswith(("ae_", "ae-")):
winkel, geschaetzt = winkel_aus_name(bname, DEFAULT_SCHLEUS_WINKEL)
if geschaetzt:
geraten.append("Winkel=%d" % winkel)
return "AS_Element_%d_%s" % (winkel, seite_aus()), geraten
if "einschleus" in low or low.startswith("ee_"):
if "einschleus" in low or low.startswith(("ee_", "ee-")):
winkel, geschaetzt = winkel_aus_name(bname, DEFAULT_SCHLEUS_WINKEL)
if geschaetzt:
geraten.append("Winkel=%d" % winkel)
@@ -1047,17 +1057,25 @@ def kreisel_paare_bilden(an_liste, sp_liste):
19 m daneben gepaart). Zusaetzlich muessen beide auf einer gemeinsamen
Achse liegen (Querversatz <= KREISEL_MAX_QUER).
Danach Tausch-Nachbesserung auf minimale Abstandssumme (siehe unten).
Rueckgabe: (paare [(an, sp, abstand)] nach AN-Position sortiert,
AN8 ohne Partner, SP8 ohne Partner)
"""
def abstand(an, sp):
"""Mittenabstand, oder None wenn nicht auf gemeinsamer Achse."""
dx = abs(sp["x"] - an["x"])
dy = abs(sp["y"] - an["y"])
if min(dx, dy) > KREISEL_MAX_QUER:
return None
return math.hypot(dx, dy)
kandidaten = []
for ia, an in enumerate(an_liste):
for isp, sp in enumerate(sp_liste):
dx = abs(sp["x"] - an["x"])
dy = abs(sp["y"] - an["y"])
if min(dx, dy) > KREISEL_MAX_QUER:
continue
kandidaten.append((math.hypot(dx, dy), ia, isp))
d = abstand(an, sp)
if d is not None:
kandidaten.append((d, ia, isp))
kandidaten.sort()
an_frei = set(range(len(an_liste)))
sp_frei = set(range(len(sp_liste)))
@@ -1066,7 +1084,29 @@ def kreisel_paare_bilden(an_liste, sp_liste):
if ia in an_frei and isp in sp_frei:
an_frei.discard(ia)
sp_frei.discard(isp)
paare.append((an_liste[ia], sp_liste[isp], dist))
paare.append([an_liste[ia], sp_liste[isp], dist])
# Nachbesserung: zwei Paare tauschen ihre SP8, solange die Summe der
# Abstaende dadurch kleiner wird. "Kuerzestes Paar zuerst" allein
# scheitert an zwei Kreiseln auf DERSELBEN Achse: die Luecke zwischen
# SP8 des einen und AN8 des anderen ist kuerzer als beide Kreisel und
# wird zuerst gepaart (Showroom_ILS_3_0: 800 + 12832 statt 9033 + 3000).
# Die echte Zuordnung hat immer die kleinere Summe - jede vertauschte
# Paarung ueberspannt mindestens eine Luecke doppelt.
verbessert = True
while verbessert:
verbessert = False
for i in range(len(paare)):
for j in range(i + 1, len(paare)):
a1, s1, d1 = paare[i]
a2, s2, d2 = paare[j]
n1, n2 = abstand(a1, s2), abstand(a2, s1)
if n1 is None or n2 is None or n1 + n2 >= d1 + d2 - 1e-6:
continue
paare[i] = [a1, s2, n1]
paare[j] = [a2, s1, n2]
verbessert = True
paare = [tuple(p) for p in paare]
paare.sort(key=lambda p: (p[0]["x"], p[0]["y"]))
return (paare,
[an_liste[i] for i in sorted(an_frei)],
@@ -1320,6 +1360,10 @@ def as_am_ende(p, richtung, as_liste, belegt):
auf dieser Seite liegen - das trennt ihn vom Nachbar-AS der Linie daneben.
Bereits vergebene AS (belegt = Menge von id()) zaehlen nicht.
Funktioniert genauso fuer Einschleuselemente am unteren Ende (es_liste in
gf_eintraege_aus_linien), dann mit der Richtung vom unteren Ende in die
Linie hinein.
Rueckgabe: (treffer, zielblock, geraten) oder None.
"""
beste = None
@@ -1329,7 +1373,8 @@ def as_am_ende(p, richtung, as_liste, belegt):
vx, vy = t["x"] - p[0], t["y"] - p[1]
entlang = vx * richtung[0] + vy * richtung[1]
quer = richtung[0] * vy - richtung[1] * vx
if not (0.0 <= entlang <= GF_AS_MAX_ENTLANG and abs(quer) <= GF_AS_MAX_QUER):
if not (-GF_AS_MAX_VOR <= entlang <= GF_AS_MAX_ENTLANG
and abs(quer) <= GF_AS_MAX_QUER):
continue
block, geraten = mappe_block(t["block"])
if "Seite=%s" % DEFAULT_SEITE not in (geraten or []):
@@ -1376,8 +1421,78 @@ def kreisel_anschluss(p, richtung, kreisel):
return beste
def linien_zusammenfassen(linien):
"""Kollineare, aneinanderstossende Linien desselben Layers vereinen.
Eine Staustrecke ist im Plan oft in Stuecke zerlegt (Showroom_ILS_3_0:
vier LINEs je GF, an den Separatoren getrennt). Als Einzelstuecke fehlte
jedem das AS am einen oder das ES am anderen Ende. Vereint wird nur bei
gleicher Richtung (in 3D) und Stoss innerhalb GF_LINIE_STOSS_TOL.
"""
def richtung(ln):
d = [ln["b"][i] - ln["a"][i] for i in range(3)]
n = math.sqrt(sum(c * c for c in d))
return [c / n for c in d] if n else None
def kollinear(r1, r2):
kreuz = (r1[1] * r2[2] - r1[2] * r2[1], r1[2] * r2[0] - r1[0] * r2[2],
r1[0] * r2[1] - r1[1] * r2[0])
# 1e-3 rad (~0.06 Grad): die Plaene sind nicht exakt gezeichnet -
# im Showroom-Plan sitzt das Separatorstueck 0,3 mm neben der Achse,
# das verkippt das bis dahin vereinte Stueck um ~2.6e-4.
return math.sqrt(sum(c * c for c in kreuz)) < 1e-3
def gleich(p, q):
return math.dist(p, q) <= GF_LINIE_STOSS_TOL
rest = [{"layer": ln["layer"], "a": list(ln["a"]), "b": list(ln["b"])}
for ln in linien]
fertig = []
while rest:
akt = rest.pop(0)
gewachsen = True
while gewachsen:
gewachsen = False
ra = richtung(akt)
for ln in rest:
rb = richtung(ln)
if (ln["layer"] != akt["layer"] or ra is None or rb is None
or not kollinear(ra, rb)):
continue
# Die vier moeglichen Stoesse; das Ergebnis behaelt die
# beiden aeusseren Enden.
for p_akt, q_akt in (("a", "b"), ("b", "a")):
for p_ln, q_ln in (("a", "b"), ("b", "a")):
if gleich(akt[p_akt], ln[p_ln]):
akt = {"layer": akt["layer"], "a": akt[q_akt],
"b": ln[q_ln]}
gewachsen = True
break
if gewachsen:
break
if gewachsen:
rest.remove(ln)
break
fertig.append(akt)
return fertig
def seite_aus_lage(t, p, r):
"""Seite eines AS/ES aus seiner Lage zur GF-Fahrtrichtung.
t = Element, p = Punkt auf der Achse, r = Fahrtrichtung der GF (bergab).
Links der Fahrtrichtung -> "links", sonst "rechts" - dieselbe Zuordnung
wie in Dematic_arvato ("Ausschleuselement links" liegt links der
Fahrtrichtung, siehe as_am_ende). Nur fuer Bloecke ohne Seite im Namen
("AE-D"/"EE-R"); gespiegelte Inserts landen dadurch automatisch auf der
Gegenseite.
"""
vx, vy = t["x"] - p[0], t["y"] - p[1]
return "links" if r[0] * vy - r[1] * vx > 0 else "rechts"
def gf_eintraege_aus_linien(linien, as_liste, kreisel=None, sep_liste=None,
winkel_default=3.0):
winkel_default=3.0, es_liste=None, flach=False):
"""Gefaellestrecken (Modus 1, Schema 2.1) aus Staustrecken-Linien.
Eine Gefaellestrecke laeuft immer bergab: Start ist das HOEHERE
@@ -1407,15 +1522,35 @@ def gf_eintraege_aus_linien(linien, as_liste, kreisel=None, sep_liste=None,
die Gefaellestrecke selbst mit, als Einzel-Insert stuenden sie doppelt in
der Zeichnung (und im Sivas-Export). Der AS eines uebersprungenen
Steigfoerderers bleibt unverbraucht.
es_liste: Einschleuselemente; eines am unteren Ende liefert es_seite/
es_winkel und wird ebenfalls verbraucht. Ohne es_liste bleiben beide
Defaults (Dematic_arvato hat kein ES im Plan).
flach: 2D-Plan ohne Hoehen (alle Linien waagrecht, z.B.
Showroom_ILS_3_0). Dann ist eine waagrechte Linie eine GF, wenn genau an
EINEM Ende ein Ausschleuselement sitzt - dieses Ende ist oben. Ohne flach
werden waagrechte Linien wie bisher uebergangen.
"""
eintraege, hinweise = [], []
belegt = set()
waagrecht = 0
kandidaten = []
for ln in linien:
for ln in linien_zusammenfassen(linien):
a, b = ln["a"], ln["b"]
if abs(a[2] - b[2]) < GF_LINIE_MIN_DZ:
waagrecht += 1
lxy = math.hypot(b[0] - a[0], b[1] - a[1])
if not (flach and lxy > 0.0):
waagrecht += 1
continue
r_ab = ((b[0] - a[0]) / lxy, (b[1] - a[1]) / lxy)
as_a = as_am_ende(a, r_ab, as_liste, belegt)
as_b = as_am_ende(b, (-r_ab[0], -r_ab[1]), as_liste, belegt)
if (as_a is None) == (as_b is None):
waagrecht += 1
continue
hoch, tief = (a, b) if as_a is not None else (b, a)
kandidaten.append((hoch, tief, lxy, ln["layer"]))
continue
hoch, tief = (a, b) if a[2] > b[2] else (b, a)
lxy = math.hypot(tief[0] - hoch[0], tief[1] - hoch[1])
@@ -1495,6 +1630,13 @@ def gf_eintraege_aus_linien(linien, as_liste, kreisel=None, sep_liste=None,
belegt.add(id(t))
teile = block.split("_") # AS_Element_<winkel>_<seite>
eintrag["as_winkel"], eintrag["as_seite"] = teile[2], teile[3]
if "Seite=%s" % DEFAULT_SEITE in geraten:
eintrag["as_seite"] = seite_aus_lage(t, hoch, r)
geraten = [g for g in geraten if not g.startswith("Seite=")]
block = "AS_Element_%s_%s" % (eintrag["as_winkel"],
eintrag["as_seite"])
kommentar += ("; as_seite %s aus der Lage zur Fahrtrichtung"
% eintrag["as_seite"])
kommentar += ("; AS aus Block '%s' bei (%.2f, %.2f) -> %s, als "
"Einzel-Insert entfernt (baut die GF selbst)"
% (t["block"], t["x"], t["y"], block))
@@ -1520,7 +1662,28 @@ def gf_eintraege_aus_linien(linien, as_liste, kreisel=None, sep_liste=None,
kommentar += ("; Separator auf der Achse entfernt (die GF baut "
"ihren eigenen): %s" % ", ".join(weg))
kommentar += "; es_seite/es_winkel sind Defaults (kein ES im DXF)."
unten = (as_am_ende(tief, (-r[0], -r[1]), es_liste, belegt)
if es_liste else None)
if unten is not None:
t, block, geraten = unten
belegt.add(id(t))
teile = block.split("_") # ES_Element_<winkel>_<seite>
eintrag["es_winkel"], eintrag["es_seite"] = teile[2], teile[3]
if "Seite=%s" % DEFAULT_SEITE in geraten:
eintrag["es_seite"] = seite_aus_lage(t, tief, r)
geraten = [g for g in geraten if not g.startswith("Seite=")]
block = "ES_Element_%s_%s" % (eintrag["es_winkel"],
eintrag["es_seite"])
kommentar += ("; es_seite %s aus der Lage zur Fahrtrichtung"
% eintrag["es_seite"])
kommentar += ("; ES aus Block '%s' bei (%.2f, %.2f) -> %s, als "
"Einzel-Insert entfernt (baut die GF selbst)"
% (t["block"], t["x"], t["y"], block))
if geraten:
kommentar += " - geraten: %s" % ", ".join(geraten)
kommentar += "."
else:
kommentar += "; es_seite/es_winkel sind Defaults (kein ES im DXF)."
eintrag["comment"] = kommentar
eintraege.append(eintrag)
@@ -1532,12 +1695,20 @@ def gf_eintraege_aus_linien(linien, as_liste, kreisel=None, sep_liste=None,
def baue_eintraege(treffer, defaults, boegen, weichen,
bekannte_bloecke=None, linien=None, kreisel_hoehen=None):
bekannte_bloecke=None, linien=None, kreisel_hoehen=None,
kreisel_achsen=None, flach=False):
"""Erkannte Vorkommen auf die Schemata aus object_data.md abbilden.
kreisel_hoehen: Liste (x, y, z) vom Anwender - z gilt fuer den Kreisel,
dessen Einfuegepunkt (Eintrag x/y) hoechstens KREISEL_HOEHE_TOL entfernt
liegt, und fuer die Gefaellestrecken, die an ihm anschliessen.
kreisel_achsen: Liste (x1, y1, x2, y2) vom Anwender - Radmitten eines
Kreisels ohne AN8/SP8 im Plan (Showroom_ILS_3_0: Schleifen, die an beiden
Enden mit Weichenboegen schliessen). Wird als STANDARD-Kreisel
angenaehert, x1/y1 gilt als AN8-Seite.
flach: siehe gf_eintraege_aus_linien.
"""
nach_familie = {}
for t in treffer:
@@ -1555,6 +1726,12 @@ def baue_eintraege(treffer, defaults, boegen, weichen,
for an in an_rest:
hinweise.append("AN8 bei (%.2f, %.2f) ohne SP8-Partner" % (an["x"], an["y"]))
eintraege.append(sensor_eintrag(an, bekannte_bloecke, hinweise, treffer))
for x1, y1, x2, y2 in kreisel_achsen or []:
an = {"block": "(manuell AN8)", "layer": "--kreisel-achse",
"x": x1, "y": y1, "z": 0.0, "darstellung": "2D"}
sp = {"block": "(manuell SP8)", "layer": "--kreisel-achse",
"x": x2, "y": y2, "z": 0.0, "darstellung": "2D"}
kreisel_paare.append((an, sp, math.hypot(x2 - x1, y2 - y1)))
for i, (an, sp, dist) in enumerate(kreisel_paare, start=1):
rotation = math.degrees(math.atan2(sp["y"] - an["y"], sp["x"] - an["x"])) % 360.0
# AN8/SP8 sind flache 2D-Symbole auf Z=0 - die Einbauhoehe steht nicht
@@ -1632,9 +1809,12 @@ def baue_eintraege(treffer, defaults, boegen, weichen,
if linien:
as_liste = [t for t in nach_familie.get("schleuselement", [])
if mappe_block(t["block"])[0].startswith("AS_Element")]
es_liste = [t for t in nach_familie.get("schleuselement", [])
if mappe_block(t["block"])[0].startswith("ES_Element")]
gf, belegt, gf_hinweise = gf_eintraege_aus_linien(
linien, as_liste, kreisel_geo, nach_familie.get("sensor", []),
defaults.get("gefaelle", {}).get("default_winkel", 3.0))
defaults.get("gefaelle", {}).get("default_winkel", 3.0),
es_liste, flach)
eintraege.extend(gf)
hinweise.extend(gf_hinweise)
for familie in ("schleuselement", "sensor"):
@@ -1873,6 +2053,17 @@ def main():
"(wie im Ausgabe-JSON); gilt auch fuer Start/Ende "
"der daran anschliessenden Gefaellestrecken. "
"Mehrfach angebbar")
parser.add_argument("--kreisel-achse", nargs=4, type=float,
action="append", metavar=("X1", "Y1", "X2", "Y2"),
help="Kreisel ohne AN8/SP8 im Plan (z.B. Schleife mit "
"Weichenboegen an beiden Enden): Radmitten der "
"beiden Enden, X1/Y1 = AN8-Seite. Wird als "
"STANDARD-Kreisel angenaehert. Mehrfach angebbar")
parser.add_argument("--flach", action="store_true",
help="2D-Plan ohne Hoehen: waagrechte Staustrecken-"
"Linien mit Ausschleuselement an genau einem "
"Ende als Gefaellestrecke ausgeben (Hoehen dann "
"ueber --kreisel-hoehe)")
parser.add_argument("--projekt",
help="Projektkennung in den erzeugten IDs "
"(Default: Name der DXF-Datei ohne Endung)")
@@ -1937,7 +2128,8 @@ def main():
eintraege, hinweise = baue_eintraege(treffer, defaults, boegen, weichen,
bekannte_bloecke, linien,
args.kreisel_hoehe)
args.kreisel_hoehe, args.kreisel_achse,
args.flach)
if args.ohne_hoehen:
print("%s --ohne-hoehen: Hoehe in %d Eintraegen auf 0 gesetzt"
% (PREFIX, hoehen_entfernen(eintraege)))
+28 -16
View File
@@ -741,48 +741,54 @@
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00158">
<Macro>
<Name>ILS 3.0 Showroom (Vorlage)</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_showroom_ils.lsp")) (c:TEST_SHOWROOM_ILS)</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00159">
<Macro>
<Name>KS 2D/3D pruefen (CSV)</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/Lisp/ks_pruefung.lsp")) KS_PRUEFEN</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00170">
<MenuMacro UID="MM_00171">
<Macro>
<Name>Kreisel bearbeiten</Name>
<Command>(ssg-ensure "KreiselInsert") KreiselEdit</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00171">
<MenuMacro UID="MM_00172">
<Macro>
<Name>Omniflo Element bearbeiten</Name>
<Command>(ssg-ensure "OmniModulInsert") OMNI_EDIT</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00172">
<MenuMacro UID="MM_00173">
<Macro>
<Name>Gefaellestrecke bearbeiten</Name>
<Command>(ssg-ensure "Gefaellestrecke") GEFAELLESTRECKE_EDIT</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00173">
<MenuMacro UID="MM_00174">
<Macro>
<Name>VarioFoerderer bearbeiten</Name>
<Command>(ssg-ensure "VarioFoerderer") VARIOFOERDERER_EDIT</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00174">
<MenuMacro UID="MM_00175">
<Macro>
<Name>TRO-Marker bearbeiten</Name>
<Command>(ssg-ensure "Tro_Edit") TRO_EDIT</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00175">
<MenuMacro UID="MM_00176">
<Macro>
<Name>Verbindung bearbeiten</Name>
<Command>(ssg-ensure "Connection_Edit") CONNECTION_EDIT</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00177">
<MenuMacro UID="MM_00178">
<Macro>
<Name>SSG Bearbeiten (Auto)</Name>
<Command>(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT</Command>
@@ -1640,56 +1646,62 @@
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>KS 2D/3D pruefen (CSV)</NameRef>
<NameRef>ILS 3.0 Showroom (Vorlage)</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00158"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>KS 2D/3D pruefen (CSV)</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00159"/>
</MenuItem>
</PopMenuItem>
</PopMenu>
<PopMenu UID="PM_00169">
<PopMenu UID="PM_00170">
<Alias>POP501</Alias>
<Name>SSG_LIB Edit Context</Name>
<PopMenuItem>
<NameRef>Kreisel bearbeiten</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00170"/>
<MacroRef MenuMacroID="MM_00171"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Omniflo Element bearbeiten</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00171"/>
<MacroRef MenuMacroID="MM_00172"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Gefaellestrecke bearbeiten</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00172"/>
<MacroRef MenuMacroID="MM_00173"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>VarioFoerderer bearbeiten</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00173"/>
<MacroRef MenuMacroID="MM_00174"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>TRO-Marker bearbeiten</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00174"/>
<MacroRef MenuMacroID="MM_00175"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Verbindung bearbeiten</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00175"/>
<MacroRef MenuMacroID="MM_00176"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem IsSeparator="true"/>
<PopMenuItem>
<NameRef>SSG Bearbeiten (Auto)</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00177"/>
<MacroRef MenuMacroID="MM_00178"/>
</MenuItem>
</PopMenuItem>
</PopMenu>
+1
View File
@@ -155,6 +155,7 @@
[Mubea Gesamtmodell]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_mubea.lsp")) (c:TEST_MUBEA)
[HundM05 Kreisel-Abschnitt]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp")) (c:TEST_HUNDM05)
[Dematic/Arvato Flowpuffer (Vorlage)]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_dematic_arvato.lsp")) (c:TEST_DEMATIC_ARVATO)
[ILS 3.0 Showroom (Vorlage)]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_showroom_ils.lsp")) (c:TEST_SHOWROOM_ILS)
[<-KS 2D/3D pruefen (CSV)]^C^C(load (strcat (getenv "DXFMAKRO") "/Lisp/ks_pruefung.lsp")) KS_PRUEFEN
***DOUBLECLICK
+2 -1
View File
@@ -11,5 +11,6 @@
{ "name": "mubea", "save": "dxf", "module": "KreiselInsert" },
{ "name": "hundm05", "save": "dxf", "module": "VarioFoerderer" },
{ "name": "dematic_arvato", "save": "dxf", "module": "VarioFoerderer", "disabled": true },
{ "name": "hm_recformat", "save": "dxf", "module": "VarioFoerderer", "disabled": true }
{ "name": "hm_recformat", "save": "dxf", "module": "VarioFoerderer", "disabled": true },
{ "name": "showroom_ils", "save": "dxf", "module": "VarioFoerderer", "disabled": true }
]
+21 -13
View File
@@ -255,25 +255,27 @@
"fehler_text" (vl-catch-all-error-message res)))
res))
(defun c:TEST_DEMATIC_ARVATO ( / datei daten out zaehler st)
(setq datei (dematic_arvato:datei))
;; Alle Eintraege einer Nachbau-Datei bauen und die Records zurueckgeben
;; (nil bei Fehler vor dem Bau). Gemeinsamer Lauf fuer jeden Treiber mit
;; diesem Eintragsschema - auch tests/test_showroom_ils.lsp.
;; befehl = Name fuer Meldungen/ssg-start, titel = Kopfzeile, datei = JSON
(defun dematic_arvato:bau-datei (befehl titel datei / daten out records zaehler st)
(princ "\n\n================================================================")
(princ "\n TEST_DEMATIC_ARVATO - Abschnitt Flowpuffer/Kreisel")
(princ (strcat "\n " befehl " - " titel))
(princ (strcat "\n " datei))
(princ "\n================================================================")
(cond
((null (car (atoms-family 1 '("VSP-BAU-JOURNAL"))))
(princ "\n[TEST_DEMATIC_ARVATO] FEHLER: vf_spec.lsp nicht geladen"
" (VarioFoerderer laden).")
(setq *dematic-arvato-records* nil))
(princ (strcat "\n[" befehl "] FEHLER: vf_spec.lsp nicht geladen"
" (VarioFoerderer laden)."))
nil)
((null (findfile datei))
(princ (strcat "\n[TEST_DEMATIC_ARVATO] FEHLER: " datei " nicht gefunden!"))
(setq *dematic-arvato-records* nil))
(princ (strcat "\n[" befehl "] FEHLER: " datei " nicht gefunden!"))
nil)
(T
(setq daten (ssg-json-laden datei))
(princ (strcat "\n " (itoa (length daten)) " Eintraege geladen"))
(ssg-start "TEST_DEMATIC_ARVATO"
'(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA")))
(ssg-start befehl '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0)
(setvar "ATTREQ" 0)
(setvar "ATTDIA" 0)
@@ -285,10 +287,10 @@
(foreach obj daten
(setq out (cons (dematic_arvato:bau-eintrag obj) out)))
(ssg-end)
(setq *dematic-arvato-records* (reverse out))
(setq records (reverse out))
;; Zusammenfassung je Status
(setq zaehler '())
(foreach r *dematic-arvato-records*
(foreach r records
(setq st (cond ((ssg-val r "status")) ("?")))
(if (assoc st zaehler)
(setq zaehler (subst (cons st (1+ (cdr (assoc st zaehler))))
@@ -298,7 +300,13 @@
(foreach z (reverse zaehler)
(princ (strcat "\n " (car z) ": " (itoa (cdr z)))))
(princ "\n================================================================")
(command "_.ZOOM" "_E")))
(command "_.ZOOM" "_E")
records)))
(defun c:TEST_DEMATIC_ARVATO ()
(setq *dematic-arvato-records*
(dematic_arvato:bau-datei "TEST_DEMATIC_ARVATO" "Abschnitt Flowpuffer/Kreisel"
(dematic_arvato:datei)))
(princ))
;; Ergebnis-Export (Konvention <name>:export-results fuer test_run_all.lsp)
+94
View File
@@ -166,3 +166,97 @@ def test_ohne_as_defaults_und_hinweis():
assert eintraege[0]["hz_grad"] == 0.0
assert eintraege[0]["as_seite"] == dsc.DEFAULT_SEITE
assert any("ohne Ausschleuselement" in h for h in hinweise)
# --- Showroom_ILS_3_0.dxf: flacher 2D-Plan, Werte dort gemessen -------------
def _station(x, y):
return {"x": x, "y": y}
def test_kreisel_paare_zwei_kreisel_auf_einer_achse():
# x=26998: AN8 y=3466 / SP8 y=12499 und AN8 y=13299 / SP8 y=16299. Die
# Luecke SP8(12499)-AN8(13299) = 800 ist das kuerzeste Paar - ohne
# Tausch-Nachbesserung kamen 800 + 12833 statt 9033 + 3000 heraus.
an = [_station(26998.0, 3466.4), _station(26998.0, 13298.9)]
sp = [_station(26998.0, 16298.9), _station(26998.0, 12498.9)]
paare, an_rest, sp_rest = dsc.kreisel_paare_bilden(an, sp)
assert an_rest == [] and sp_rest == []
abstaende = sorted(round(p[2], 1) for p in paare)
assert abstaende == [3000.0, 9032.5]
def test_zerstueckelte_linie_wird_vereint():
# GF bei x=34006 in vier Stuecken, das Separatorstueck 0,3 mm daneben
stuecke = [
_linie((34005.93, 13893.90, 0.0), (34005.93, 14193.90, 0.0)),
_linie((34005.93, 15043.90, 0.0), (34005.93, 15593.90, 0.0)),
_linie((34006.24, 14744.31, 0.0), (34006.24, 15044.31, 0.0)),
_linie((34005.93, 14193.90, 0.0), (34005.93, 14743.90, 0.0)),
]
vereint = dsc.linien_zusammenfassen(stuecke)
assert len(vereint) == 1
ys = sorted([vereint[0]["a"][1], vereint[0]["b"][1]])
assert ys == pytest.approx([13893.90, 15593.90], abs=0.01)
def test_parallele_nachbarlinie_wird_nicht_vereint():
linien = [_linie((0.0, 0.0, 0.0), (0.0, 1000.0, 0.0)),
_linie((800.0, 1000.0, 0.0), (800.0, 2000.0, 0.0))]
assert len(dsc.linien_zusammenfassen(linien)) == 2
def _kreisel_d():
# Schleife oben (Kette unten y=15898.9), Radmitten per --kreisel-achse
return {"id": "K_D", "a": (27797.93, 16298.9), "u": (1.0, 0.0),
"laenge": 8908.0, "r": 400.0, "z_anwender": None}
def _kreisel_e():
# Schleife Mitte (Kette oben y=13493.9)
return {"id": "K_E", "a": (28772.93, 13093.9), "u": (1.0, 0.0),
"laenge": 6533.0, "r": 400.0, "z_anwender": None}
def test_flacher_plan_richtung_und_seiten_aus_as_es():
# Waagrechte Linie; AE-D sitzt 305 mm VOR dem oberen Ende auf der
# Kreisel-Kette, 180 mm links der Fahrtrichtung (Fahrt -y, links = +x).
# EE-R am unteren Ende 212 mm rechts davon.
linie = _linie((30455.93, 13893.9, 0.0), (30455.93, 15593.9, 0.0))
ae = {"familie": "schleuselement", "block": "AE-D",
"x": 30636.27, "y": 15898.9, "z": 0.0, "rot": 270.0}
ee = {"familie": "schleuselement", "block": "EE-R",
"x": 30243.76, "y": 13493.9, "z": 0.0, "rot": 90.0}
eintraege, belegt, _ = dsc.gf_eintraege_aus_linien(
[linie], [ae], [_kreisel_d(), _kreisel_e()], es_liste=[ee], flach=True)
assert len(eintraege) == 1
gf = eintraege[0]
assert gf["hz_grad"] == 270.0
assert gf["start_mm"][:2] == [30455.93, 15898.9]
assert gf["deltaL_mm"] == pytest.approx(2405.0, abs=0.01)
assert (gf["as_seite"], gf["es_seite"]) == ("links", "rechts")
assert (gf["kreisel_start"], gf["kreisel_ende"]) == ("K_D", "K_E")
assert belegt == {id(ae), id(ee)}
def test_flacher_plan_ohne_flach_schalter_keine_gf():
linie = _linie((30455.93, 13893.9, 0.0), (30455.93, 15593.9, 0.0))
ae = {"familie": "schleuselement", "block": "AE-D",
"x": 30636.27, "y": 15898.9, "z": 0.0, "rot": 270.0}
eintraege, _, _ = dsc.gf_eintraege_aus_linien([linie], [ae])
assert eintraege == []
def test_flacher_plan_ohne_as_keine_gf():
# Staulinie am Kreisel B (x=36348) - kein AS an einem Ende
linie = _linie((36347.6, 11313.9, 0.0), (36347.6, 14913.9, 0.0))
eintraege, _, hinweise = dsc.gf_eintraege_aus_linien([linie], [], flach=True)
assert eintraege == []
assert any("waagrechte" in h for h in hinweise)
def test_kurznamen_ae_ee():
assert dsc.familie_von("AE-D") == "schleuselement"
assert dsc.familie_von("EE-R") == "schleuselement"
assert dsc.mappe_block("AE-D")[0].startswith("AS_Element_90_")
assert dsc.mappe_block("EE-R")[0].startswith("ES_Element_90_")
+58
View File
@@ -0,0 +1,58 @@
;; ============================================================
;; test_showroom_ils.lsp - Nachbau des ILS-3.0-Showrooms.
;;
;; Datenquelle: tests/testdata/showroom_ils.json, erzeugt aus der
;; Fremdzeichnung (flacher 2D-Plan, keine Hoehen):
;; python lib/dxf_scan_components.py \
;; C:/develop/sps_skel/data/Showroom_ILS_3_0.dxf data \
;; tests/testdata/showroom_ils.json --projekt Showroom \
;; --flach --ohne-hoehen \
;; --kreisel-achse 27797.93 16298.90 36705.93 16298.90 \
;; --kreisel-achse 28772.93 13093.90 35305.93 13093.90
;;
;; Inhalt: 6 Kreisel, 11 Gefaellestrecken (Modus 1), Separatoren/Scanner.
;; - Kreisel_Showroom_05/06 sind Schleifen ohne AN8/SP8 im Plan (an beiden
;; Enden Weichenboegen) - per --kreisel-achse als STANDARD-Kreisel
;; angenaehert (Radmitten = Mittelpunkte der PIN-Kreise).
;; - Weichen (WB-*/WM-*/WL-*/WR-*) werden bewusst NICHT nachgebaut.
;; - HOEHEN FEHLEN NOCH: alle z = 0, die GF laufen mit dem Platzhalter
;; winkel_grad 3.0. Sobald die Kreisel-Hoehen bekannt sind, den Scanner
;; mit --kreisel-hoehe X Y Z je Kreisel neu laufen lassen - dann bekommen
;; die GF start_mm.z/hoehe_ende_mm und der Treiber rechnet den Winkel.
;;
;; Keine eigene Bau-Logik: Eintragsschema und Lauf wie TEST_DEMATIC_ARVATO
;; (dematic_arvato:bau-datei aus tests/test_dematic_arvato.lsp).
;;
;; Aufruf in BricsCAD:
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_showroom_ils.lsp"))
;; TEST_SHOWROOM_ILS
;; ============================================================
(if (not (boundp '*showroom-ils-records*)) (setq *showroom-ils-records* nil))
;; Beim LADEN holen (siehe test_dematic_arvato.lsp: per load definierte
;; Funktionen sind erst nach Rueckkehr zum Top-Level sichtbar).
(if (null (car (atoms-family 1 '("DEMATIC_ARVATO:BAU-DATEI"))))
(load (strcat (getenv "DXFMAKRO") "/tests/test_dematic_arvato.lsp")))
(defun showroom_ils:datei ()
(strcat (getenv "DXFMAKRO") "/tests/testdata/showroom_ils.json"))
(defun c:TEST_SHOWROOM_ILS ()
(setq *showroom-ils-records*
(dematic_arvato:bau-datei "TEST_SHOWROOM_ILS" "ILS 3.0 Showroom"
(showroom_ils:datei)))
(princ))
;; Ergebnis-Export (Konvention <name>:export-results fuer test_run_all.lsp)
(defun showroom_ils:export-results (out-dir / pfad)
(if (null *showroom-ils-records*)
(princ "\n Keine Showroom-Ergebnisse vorhanden.")
(progn
(vl-mkdir out-dir)
(setq pfad (strcat out-dir "/showroom_ils_results.json"))
(vsp-results-schreiben *showroom-ils-records* pfad)))
(princ))
(princ "\nTEST_SHOWROOM_ILS geladen. Aufruf: TEST_SHOWROOM_ILS")
(princ)
+410
View File
@@ -0,0 +1,410 @@
[
{
"id": "Kreisel_Showroom_01",
"function": "insert",
"x": 26997.93,
"y": 3066.4,
"z": 0.0,
"abstand": 9032.5,
"rotation": 90.0,
"typ": "STANDARD",
"darstellung": "2D",
"comment": "aus DXF: Radmitte AN8 (Layer 1ANTR-800) (26997.93, 3466.40) -> SP8 (Layer 1SP-800) (26997.93, 12498.90), Mittenabstand 9032.50, rotation 90.00 Grad; x/y = AN8-Mitte minus Radius. ACHTUNG hoehe=11039.9 ist NICHT belegt: AN8/SP8 sind flache 2D-Symbole auf Z=0 und nur 0 3D-Nachbarn in 3000 mm - zu wenige fuer eine belegbare Hoehe. [Hoehe entfernt: --ohne-hoehen]",
"hoehe_unbekannt": true
},
{
"id": "Kreisel_Showroom_02",
"function": "insert",
"x": 26997.93,
"y": 12898.9,
"z": 0.0,
"abstand": 3000.0,
"rotation": 90.0,
"typ": "STANDARD",
"darstellung": "2D",
"comment": "aus DXF: Radmitte AN8 (Layer 1ANTR-800) (26997.93, 13298.90) -> SP8 (Layer 1SP-800) (26997.93, 16298.90), Mittenabstand 3000.00, rotation 90.00 Grad; x/y = AN8-Mitte minus Radius. ACHTUNG hoehe=11039.9 ist NICHT belegt: AN8/SP8 sind flache 2D-Symbole auf Z=0 und nur 0 3D-Nachbarn in 3000 mm - zu wenige fuer eine belegbare Hoehe. [Hoehe entfernt: --ohne-hoehen]",
"hoehe_unbekannt": true
},
{
"id": "Kreisel_Showroom_03",
"function": "insert",
"x": 37105.93,
"y": 9888.9,
"z": 0.0,
"abstand": 7200.0,
"rotation": 180.0,
"typ": "STANDARD",
"darstellung": "2D",
"comment": "aus DXF: Radmitte AN8 (Layer 1ANTR-800) (36705.93, 9888.90) -> SP8 (Layer 1SP-800) (29505.93, 9888.90), Mittenabstand 7200.00, rotation 180.00 Grad; x/y = AN8-Mitte minus Radius. ACHTUNG hoehe=11039.9 ist NICHT belegt: AN8/SP8 sind flache 2D-Symbole auf Z=0 und nur 0 3D-Nachbarn in 3000 mm - zu wenige fuer eine belegbare Hoehe. [Hoehe entfernt: --ohne-hoehen]",
"hoehe_unbekannt": true
},
{
"id": "Kreisel_Showroom_04",
"function": "insert",
"x": 36705.93,
"y": 10288.9,
"z": 0.0,
"abstand": 4810.0,
"rotation": 90.0,
"typ": "STANDARD",
"darstellung": "2D",
"comment": "aus DXF: Radmitte AN8 (Layer 1ANTR-800) (36705.93, 10688.90) -> SP8 (Layer 1SP-800) (36705.93, 15498.90), Mittenabstand 4810.00, rotation 90.00 Grad; x/y = AN8-Mitte minus Radius. ACHTUNG hoehe=11039.9 ist NICHT belegt: AN8/SP8 sind flache 2D-Symbole auf Z=0 und nur 0 3D-Nachbarn in 3000 mm - zu wenige fuer eine belegbare Hoehe. [Hoehe entfernt: --ohne-hoehen]",
"hoehe_unbekannt": true
},
{
"id": "Kreisel_Showroom_05",
"function": "insert",
"x": 27397.93,
"y": 16298.9,
"z": 0.0,
"abstand": 8908.0,
"rotation": 0.0,
"typ": "STANDARD",
"darstellung": "2D",
"comment": "aus DXF: Radmitte (manuell AN8) (Layer --kreisel-achse) (27797.93, 16298.90) -> (manuell SP8) (Layer --kreisel-achse) (36705.93, 16298.90), Mittenabstand 8908.00, rotation 0.00 Grad; x/y = AN8-Mitte minus Radius. ACHTUNG hoehe=0.0 ist NICHT belegt: AN8/SP8 sind flache 2D-Symbole auf Z=0 und nur 0 3D-Nachbarn in 3000 mm - zu wenige fuer eine belegbare Hoehe.",
"hoehe_unbekannt": true
},
{
"id": "Kreisel_Showroom_06",
"function": "insert",
"x": 28372.93,
"y": 13093.9,
"z": 0.0,
"abstand": 6533.0,
"rotation": 0.0,
"typ": "STANDARD",
"darstellung": "2D",
"comment": "aus DXF: Radmitte (manuell AN8) (Layer --kreisel-achse) (28772.93, 13093.90) -> (manuell SP8) (Layer --kreisel-achse) (35305.93, 13093.90), Mittenabstand 6533.00, rotation 0.00 Grad; x/y = AN8-Mitte minus Radius. ACHTUNG hoehe=0.0 ist NICHT belegt: AN8/SP8 sind flache 2D-Symbole auf Z=0 und nur 0 3D-Nachbarn in 3000 mm - zu wenige fuer eine belegbare Hoehe.",
"hoehe_unbekannt": true
},
{
"test_id": "GF_Showroom_01",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [30455.93, 15898.9, 0.0],
"as_seite": "links",
"es_seite": "rechts",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_05",
"kreisel_ende": "Kreisel_Showroom_06",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (30455.93, 15593.90) -> (30455.93, 13893.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_05 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite links aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (30636.27, 15898.90) -> AS_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (30456, 13994), S-SP bei (30456, 14807); es_seite rechts aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (30243.76, 13493.90) -> ES_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"test_id": "GF_Showroom_02",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [31255.93, 12693.9, 0.0],
"as_seite": "rechts",
"es_seite": "links",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_06",
"kreisel_ende": "Kreisel_Showroom_03",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (31255.93, 12388.90) -> (31255.93, 10688.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_03 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite rechts aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (31075.60, 12693.90) -> AS_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (31256, 10789), S-SP bei (31256, 11602); es_seite links aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (31468.11, 10288.90) -> ES_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"test_id": "GF_Showroom_03",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [31255.93, 15898.9, 0.0],
"as_seite": "links",
"es_seite": "rechts",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_05",
"kreisel_ende": "Kreisel_Showroom_06",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (31255.93, 15593.90) -> (31255.93, 13893.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_05 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite links aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (31436.27, 15898.90) -> AS_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (31256, 13994), S-SP bei (31256, 14807); es_seite rechts aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (31043.76, 13493.90) -> ES_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"test_id": "GF_Showroom_04",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [32055.93, 12693.9, 0.0],
"as_seite": "rechts",
"es_seite": "links",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_06",
"kreisel_ende": "Kreisel_Showroom_03",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (32055.93, 12388.90) -> (32055.93, 10688.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_03 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite rechts aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (31875.60, 12693.90) -> AS_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (32056, 10789), S-SP bei (32056, 11602); es_seite links aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (32268.11, 10288.90) -> ES_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"test_id": "GF_Showroom_05",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [32055.93, 15898.9, 0.0],
"as_seite": "links",
"es_seite": "rechts",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_05",
"kreisel_ende": "Kreisel_Showroom_06",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (32055.93, 15593.90) -> (32055.93, 13893.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_05 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite links aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (32236.27, 15898.90) -> AS_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (32056, 13994), S-SP bei (32056, 14807); es_seite rechts aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (31843.76, 13493.90) -> ES_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"test_id": "GF_Showroom_06",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [33205.93, 12693.9, 0.0],
"as_seite": "rechts",
"es_seite": "links",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_06",
"kreisel_ende": "Kreisel_Showroom_03",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (33205.93, 12388.90) -> (33205.93, 10688.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_03 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite rechts aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (33025.60, 12693.90) -> AS_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (33206, 10789), S-SP bei (33206, 11602); es_seite links aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (33418.11, 10288.90) -> ES_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"test_id": "GF_Showroom_07",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [33205.93, 15898.9, 0.0],
"as_seite": "links",
"es_seite": "rechts",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_05",
"kreisel_ende": "Kreisel_Showroom_06",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (33205.93, 15593.90) -> (33205.93, 13893.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_05 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite links aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (33386.27, 15898.90) -> AS_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (33206, 13994), S-SP bei (33206, 14807); es_seite rechts aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (32993.76, 13493.90) -> ES_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"test_id": "GF_Showroom_08",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [34005.93, 12693.9, 0.0],
"as_seite": "rechts",
"es_seite": "links",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_06",
"kreisel_ende": "Kreisel_Showroom_03",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (34005.93, 12388.90) -> (34005.93, 10688.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_03 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite rechts aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (33825.60, 12693.90) -> AS_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (34006, 10789), S-SP bei (34006, 11602); es_seite links aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (34218.11, 10288.90) -> ES_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"test_id": "GF_Showroom_09",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [34005.93, 15898.9, 0.0],
"as_seite": "links",
"es_seite": "rechts",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_05",
"kreisel_ende": "Kreisel_Showroom_06",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (34005.93, 15593.90) -> (34005.93, 13893.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_05 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite links aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (34186.27, 15898.90) -> AS_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (34006, 13994), S-SP bei (34006, 14807); es_seite rechts aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (33793.76, 13493.90) -> ES_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"test_id": "GF_Showroom_10",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [34805.93, 12693.9, 0.0],
"as_seite": "rechts",
"es_seite": "links",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_06",
"kreisel_ende": "Kreisel_Showroom_03",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (34805.93, 12388.90) -> (34805.93, 10688.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_03 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite rechts aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (34625.60, 12693.90) -> AS_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (34806, 10789), S-SP bei (34806, 11602); es_seite links aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (35018.11, 10288.90) -> ES_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"test_id": "GF_Showroom_11",
"modus": 1,
"deltaL_mm": 2405.0,
"hz_grad": 270.0,
"start_mm": [34805.93, 15898.9, 0.0],
"as_seite": "links",
"es_seite": "rechts",
"as_winkel": "90",
"es_winkel": "90",
"kreisel_start": "Kreisel_Showroom_05",
"kreisel_ende": "Kreisel_Showroom_06",
"winkel_grad": 3.0,
"hoehe_unbekannt": true,
"comment": "aus DXF: LINE auf Layer '6-SP' (34805.93, 15593.90) -> (34805.93, 13893.90), Laenge XY 1700.00; am oberen Ende um 305.0 mm bis zur Kette von Kreisel_Showroom_05 verlaengert; am unteren Ende um 400.0 mm bis zur Kette von Kreisel_Showroom_06 verlaengert; Hoehen unbekannt - winkel_grad 3.0 ist Platzhalter; as_seite links aus der Lage zur Fahrtrichtung; AS aus Block 'AE-D' bei (34986.27, 15898.90) -> AS_Element_90_links, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90; Separator auf der Achse entfernt (die GF baut ihren eigenen): S-SP bei (34806, 13994), S-SP bei (34806, 14807); es_seite rechts aus der Lage zur Fahrtrichtung; ES aus Block 'EE-R' bei (34593.76, 13493.90) -> ES_Element_90_rechts, als Einzel-Insert entfernt (baut die GF selbst) - geraten: Winkel=90."
},
{
"function": "insert",
"block": "S-LP",
"x": 26597.93,
"y": 11973.9,
"z": 0.0,
"rotation": 270.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "S-LP",
"x": 26597.93,
"y": 13982.92,
"z": 0.0,
"rotation": 90.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'S-LP', Layer 'TGW_HB_2OG-AM_5'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "S-LP",
"x": 27397.93,
"y": 5450.07,
"z": 0.0,
"rotation": 90.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'S-LP', Layer 'TGW_HB_2OG-AM_5'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "S-LP",
"x": 27397.93,
"y": 15773.9,
"z": 0.0,
"rotation": 270.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "S-LP",
"x": 29397.93,
"y": 13493.9,
"z": 0.0,
"rotation": 0.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "S-LP",
"x": 30343.39,
"y": 9488.9,
"z": 0.0,
"rotation": 0.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "S-LP",
"x": 32083.74,
"y": 9488.9,
"z": 0.0,
"rotation": 0.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "S-LP",
"x": 35905.93,
"y": 10288.9,
"z": 0.0,
"rotation": 180.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "S-LP",
"x": 37105.93,
"y": 14978.9,
"z": 0.0,
"rotation": 270.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "Scanner",
"x": 29398.73,
"y": 13176.71,
"z": 0.0,
"rotation": 270.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "Scanner",
"x": 30343.39,
"y": 9208.9,
"z": 0.0,
"rotation": 270.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "Scanner",
"x": 32083.74,
"y": 9208.9,
"z": 0.0,
"rotation": 270.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "Scanner",
"x": 35905.93,
"y": 10008.9,
"z": 0.0,
"rotation": 270.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
},
{
"function": "insert",
"block": "Scanner",
"x": 36825.93,
"y": 14978.9,
"z": 0.0,
"rotation": 180.0,
"darstellung": "2D",
"hoehe_unbekannt": true,
"comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt) [Hoehe entfernt: --ohne-hoehen]"
}
]