Diagnose-Skripte fuer VF/GF-Kettenzusammensetzung ergaenzen
Zeigen die Spec- bzw. Blockdefinitions-Struktur einer VF-/GF-Linienzugkette in der Konsole, um sichtbar zu machen, dass ein einzelner VF_n-Wrapper intern zwischen VF- und GF-Abschnitten wechseln kann (Grundlage fuer den noch ausstehenden Separator-TYP-Fix). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3f89f36e4a
commit
c95a94c175
@@ -0,0 +1,157 @@
|
||||
"""
|
||||
Diagnose: Inhalt jeder VF_n/GF_n-Linienzug-Kette aus Spec + sepliste
|
||||
gegenueberstellen.
|
||||
|
||||
Zeigt fuer jede Kette (spec_id, z.B. VF_hundm05_LZ_01 = VF_1):
|
||||
- die "glied"-Folge aus der Spec (Horizontal-VF/Linie-GF/... in
|
||||
Baureihenfolge, siehe vf_spec.lsp/vsp-journal) - das ist die
|
||||
DOMAENEN-Wahrheit ueber VF- vs. GF-Abschnitte,
|
||||
- die tatsaechliche sepliste (AS/SEP/ES in Baureihenfolge) aus dem
|
||||
zugehoerigen export_raw.json/export.csv-Eintrag des gleichnamigen
|
||||
VF_n-Wrappers.
|
||||
|
||||
Zweck: sichtbar machen, wie viele Separatoren tatsaechlich je Glied-Uebergang
|
||||
entstehen und ob ein einzelner VF_n-Wrapper (ein Blockname!) intern zwischen
|
||||
VF- und GF-Abschnitten wechselt - der Blockname allein (VF_n/GF_n) sagt
|
||||
darueber NICHTS aus, wie sich beim csv:wrapper-strecke-typ-Versuch gezeigt
|
||||
hat (alle Separatoren eines VF_n-Wrappers bekamen "VF", obwohl mehrere davon
|
||||
zwischen echten GF-Abschnitten liegen).
|
||||
|
||||
Aufruf:
|
||||
python dump_vf_kette_inhalt.py <spec.json> [<export_raw.json>]
|
||||
|
||||
<spec.json> z.B. tests/testdata/hundm05.json (vf_spec_export.py-Format)
|
||||
<export_raw.json> optional, z.B. results/export_raw.json - wenn angegeben,
|
||||
wird die sepliste des gleichnamigen VF_n/GF_n-Wrappers
|
||||
daneben gestellt (Zuordnung ueber Reihenfolge: die N-te
|
||||
Kette der Spec-Datei = der N-te VF_n/GF_n-Wrapper in
|
||||
export_raw.json, aufsteigend nach Blockname sortiert -
|
||||
die einzige verlaessliche Zuordnung ohne eigene ID-Bruecke,
|
||||
siehe Hinweis in der Ausgabe).
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def lade_json(pfad):
|
||||
with open(pfad, encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def ketten_aus_spec(spec_liste):
|
||||
"""Gruppiert die flache Spec-Liste in Ketten (ein 'spec_id'-Kopf +
|
||||
seine 'glied'/'sub'-Eintraege bis zum naechsten Kopf). Objekte ohne
|
||||
'spec_id' UND ohne 'glied'/'sub' (z.B. von Hand ergaenzte Anlagenteile
|
||||
mit 'function') werden uebersprungen - siehe vf_spec_export.py."""
|
||||
ketten = []
|
||||
aktuelle = None
|
||||
for obj in spec_liste:
|
||||
if "spec_id" in obj:
|
||||
aktuelle = {"kopf": obj, "eintraege": []}
|
||||
ketten.append(aktuelle)
|
||||
elif aktuelle is not None and ("glied" in obj or "sub" in obj):
|
||||
aktuelle["eintraege"].append(obj)
|
||||
return ketten
|
||||
|
||||
|
||||
def glied_typ(eintrag):
|
||||
"""VF/GF-Familie eines 'glied'-Eintrags (siehe vfl-letzter-typ-familie,
|
||||
vf_linienzug.lsp:5979). 'sub'-Eintraege gehoeren zum zuletzt genannten
|
||||
'glied' und werden nicht separat eingeordnet."""
|
||||
name = eintrag.get("glied", "")
|
||||
if name.startswith("Linie-GF"):
|
||||
return "GF"
|
||||
if name.startswith(("Horizontal-VF", "Linie-VF")):
|
||||
return "VF"
|
||||
if name == "kettenende":
|
||||
return "ES"
|
||||
return name or "?"
|
||||
|
||||
|
||||
def kette_zusammenfassung(kette):
|
||||
kopf = kette["kopf"]
|
||||
zeilen = []
|
||||
zeilen.append(
|
||||
f" Kopf: as_winkel={kopf.get('as_winkel')} as_seite={kopf.get('as_seite')} "
|
||||
f"start_hoehe={kopf.get('start_hoehe')}"
|
||||
)
|
||||
for e in kette["eintraege"]:
|
||||
if "glied" in e:
|
||||
typ = glied_typ(e)
|
||||
sep_vor = e.get("sep_vor", e.get("vf_sep_vor", ""))
|
||||
sep_nach = e.get("sep_nach", e.get("vf_sep_nach", ""))
|
||||
zeilen.append(
|
||||
f" [{typ:2s}] glied={e['glied']:15s} nr={e.get('nr','')!s:3s} "
|
||||
f"dl={e.get('dl','')!s:8s} sep_vor={sep_vor!s:2s} sep_nach={sep_nach!s:2s}"
|
||||
)
|
||||
elif e.get("sub") == "kettenende":
|
||||
zeilen.append(
|
||||
f" [ES] kettenende es_winkel={e.get('es_winkel')} "
|
||||
f"es_seite={e.get('es_seite')} dl={e.get('dl')}"
|
||||
)
|
||||
else:
|
||||
zeilen.append(f" sub={e.get('sub')}")
|
||||
return "\n".join(zeilen)
|
||||
|
||||
|
||||
def wrapper_aus_export_raw(export_raw):
|
||||
wrapper = [
|
||||
b for b in export_raw
|
||||
if re.match(r"^(VF|GF)_\d+$", b.get("block_name", ""))
|
||||
]
|
||||
wrapper.sort(key=lambda b: (b["block_name"].split("_")[0],
|
||||
int(b["block_name"].split("_")[1])))
|
||||
return wrapper
|
||||
|
||||
|
||||
def sepliste_zusammenfassung(wrapper_block):
|
||||
sepliste = wrapper_block.get("sepliste", [])
|
||||
if not sepliste:
|
||||
return " (keine sepliste-XDATA am Wrapper - Altbestand oder Fehler)"
|
||||
zeilen = []
|
||||
for s in sepliste:
|
||||
zeilen.append(
|
||||
f" lfdnr={s['lfdnr']:2d} typ={s['typ']:3s} id={s.get('id','?'):6s} "
|
||||
f"x={s['x']:10.2f} y={s['y']:10.2f}"
|
||||
)
|
||||
return "\n".join(zeilen)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) not in (2, 3):
|
||||
print(f"Aufruf: python {sys.argv[0]} <spec.json> [<export_raw.json>]")
|
||||
sys.exit(1)
|
||||
|
||||
spec = lade_json(sys.argv[1])
|
||||
ketten = ketten_aus_spec(spec)
|
||||
|
||||
export_raw = None
|
||||
wrapper_liste = []
|
||||
if len(sys.argv) == 3:
|
||||
export_raw = lade_json(sys.argv[2])
|
||||
wrapper_liste = wrapper_aus_export_raw(export_raw)
|
||||
if len(wrapper_liste) != len(ketten):
|
||||
print(
|
||||
f"[WARNUNG] {len(ketten)} Ketten in der Spec, aber "
|
||||
f"{len(wrapper_liste)} VF_n/GF_n-Wrapper in export_raw.json - "
|
||||
f"Zuordnung nach Reihenfolge ist damit UNSICHER.\n"
|
||||
)
|
||||
|
||||
for i, kette in enumerate(ketten):
|
||||
kopf = kette["kopf"]
|
||||
print(f"\n{'=' * 70}")
|
||||
print(f"Kette {i + 1}: {kopf['spec_id']}"
|
||||
+ (f" <-> {wrapper_liste[i]['block_name']} "
|
||||
f"(handle={wrapper_liste[i].get('handle')})"
|
||||
if i < len(wrapper_liste) else " <-> (kein Wrapper zugeordnet)"))
|
||||
print(f"{'=' * 70}")
|
||||
print("--- Spec (Domaenen-Wahrheit VF/GF) ---")
|
||||
print(kette_zusammenfassung(kette))
|
||||
if i < len(wrapper_liste):
|
||||
print("--- sepliste (tatsaechlich exportierte Separator-Reihenfolge) ---")
|
||||
print(sepliste_zusammenfassung(wrapper_liste[i]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
Diagnose: Inhalt jedes VF_n/GF_n-Wrapper-Blocks in Baureihenfolge ausgeben.
|
||||
|
||||
Zeigt fuer jeden Wrapper (VF_n/GF_n) alle direkt in seiner Blockdefinition
|
||||
enthaltenen Sub-INSERTs (rekursiv, wie ssg-collect-nested-inserts es tut) in
|
||||
der Reihenfolge, in der sie in der Blockdefinition stehen - inkl. Blockname
|
||||
und lokalem Einfuegepunkt. Damit sichtbar wird, welche Foerderer-/
|
||||
Gefaellestrecke-Segmente (VF-Baustein-Bloecke vs. GF-Baustein-Bloecke)
|
||||
tatsaechlich zwischen den verpackten Separator_SP-Symbolen liegen - ein
|
||||
einzelner VF_n-Wrapper kann intern mehrfach zwischen Foerderer- und
|
||||
Gefaellestrecke-Abschnitten wechseln, sein Blockname (VF_n) sagt darueber
|
||||
nichts aus.
|
||||
|
||||
Aufruf: python dump_vf_wrapper_inhalt.py <dxf-oder-dwg>
|
||||
"""
|
||||
import sys
|
||||
|
||||
import ezdxf
|
||||
|
||||
|
||||
# Muster grob nach Herkunft eingeteilt, rein zur Anzeige - keine
|
||||
# Export-Klassifikation. GF-Baustein-Bloecke tragen "Staustrecke"/"Gefaelle"
|
||||
# im Namen (siehe Gefaellestrecke.lsp/vf_linienzug.lsp GF-Zweige), reine
|
||||
# VF-Bausteine div. Praefixe (siehe vf_standard.lsp/vf_etage.lsp/
|
||||
# vf_linienzug.lsp horizontale/geneigte Foerderer-Bloecke).
|
||||
def klassifiziere(block_name):
|
||||
bn = block_name.upper()
|
||||
if bn.startswith("STAUSTRECKE_SEPARATOR"):
|
||||
return "SEP (Trennsteg)"
|
||||
if "SEPARATOR_SP" in bn:
|
||||
return "SEP (Symbol)"
|
||||
if bn.startswith(("AS_ELEMENT", "ES_ELEMENT")):
|
||||
return "AS/ES"
|
||||
if "GEFAELLE" in bn or bn.startswith("STAUSTRECKE"):
|
||||
return "GF-Baustein"
|
||||
if bn.startswith(("VARIO_", "VF_", "UMLENK", "MOTOR", "ANTRIEB")):
|
||||
return "VF-Baustein"
|
||||
return ""
|
||||
|
||||
|
||||
def dump_block(doc, block_name, tiefe=0):
|
||||
block = doc.blocks.get(block_name)
|
||||
if block is None:
|
||||
print(" " * tiefe + f"[Blockdefinition {block_name} nicht gefunden]")
|
||||
return
|
||||
for e in block:
|
||||
if e.dxftype() != "INSERT":
|
||||
continue
|
||||
name = e.dxf.name
|
||||
x, y, z = e.dxf.insert
|
||||
klasse = klassifiziere(name)
|
||||
klasse_str = f" <- {klasse}" if klasse else ""
|
||||
print(" " * tiefe + f"{name:40s} x={x:10.2f} y={y:10.2f}{klasse_str}")
|
||||
# rekursiv absteigen (verschachtelte Wrapper/Zwischenbloecke)
|
||||
if doc.blocks.get(name) is not None:
|
||||
dump_block(doc, name, tiefe + 1)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Aufruf: python {sys.argv[0]} <dxf-oder-dwg>")
|
||||
sys.exit(1)
|
||||
|
||||
pfad = sys.argv[1]
|
||||
doc = ezdxf.readfile(pfad)
|
||||
msp = doc.modelspace()
|
||||
|
||||
wrapper = [
|
||||
e for e in msp.query("INSERT")
|
||||
if e.dxf.name.startswith(("VF_", "GF_"))
|
||||
]
|
||||
wrapper.sort(key=lambda e: e.dxf.name)
|
||||
|
||||
if not wrapper:
|
||||
print("Keine VF_n/GF_n-Wrapper-Bloecke im Modellraum gefunden.")
|
||||
return
|
||||
|
||||
for e in wrapper:
|
||||
print(f"\n=== {e.dxf.name} (handle={e.dxf.handle}) ===")
|
||||
dump_block(doc, e.dxf.name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user