diff --git a/lib/dump_vf_kette_inhalt.py b/lib/dump_vf_kette_inhalt.py new file mode 100644 index 0000000..3708b3c --- /dev/null +++ b/lib/dump_vf_kette_inhalt.py @@ -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 [] + + z.B. tests/testdata/hundm05.json (vf_spec_export.py-Format) + 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]} []") + 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() diff --git a/lib/dump_vf_wrapper_inhalt.py b/lib/dump_vf_wrapper_inhalt.py new file mode 100644 index 0000000..3020cba --- /dev/null +++ b/lib/dump_vf_wrapper_inhalt.py @@ -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 +""" +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]} ") + 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()