Files
dxfmakros/lib/dump_vf_kette_inhalt.py
T
Michael StanglandClaude Sonnet 5 c95a94c175 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>
2026-09-23 17:14:03 +02:00

158 lines
5.8 KiB
Python

"""
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()