diff --git a/doc/json-input-lisp.md b/doc/json-input-lisp.md index eb69f7c..910a632 100644 --- a/doc/json-input-lisp.md +++ b/doc/json-input-lisp.md @@ -10,6 +10,14 @@ Die zugehoerigen LISP-Routinen mit Signaturen stehen in `tests/testdata/object_data.md` (geloescht in Commit `e147b89`, auf das Code- Kommentare teilweise noch verweisen). +**Feldnamen als Python-Konstanten**: `lib/testdata_schema.py` buendelt die +Feldnamen/festen Werte der Abschnitte 1 (Kreisel), 2 (Einzelbloecke), 3 (VF +Standard/Etage) und 4.1 (Gefaellestrecke Modus 1) als benannte Konstanten +(`sch.F_ABSTAND`, `sch.SEITE_LINKS`, ...), genutzt von `lib/dxf_anlage_export.py` +(eigene, mit unseren Makros gebaute Zeichnungen). `lib/dxf_scan_components.py` +(fremde Zeichnungen) hat dieselben Feldnamen weiterhin als eigene String- +Literale - gleiches Schema, noch nicht auf das gemeinsame Modul umgestellt. + --- ## 0. Uebersicht diff --git a/lib/dxf_anlage_export.py b/lib/dxf_anlage_export.py index f864696..62ad022 100644 --- a/lib/dxf_anlage_export.py +++ b/lib/dxf_anlage_export.py @@ -63,6 +63,7 @@ import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from vf_journal_export import TRENNER, gruppen # noqa: E402 from dxf_scan_components import arrays_einzeilig, kreisel_anschluss # noqa: E402 +import testdata_schema as sch # noqa: E402 PREFIX = "[dxf_anlage_export]" RUND = 2 @@ -204,17 +205,17 @@ def kreisel_eintrag(e, block_inhalt, projekt): quelle = "Blockdefinition (AN8 -> SP8)" nr = int(_float(a.get("NUMMER"))) or _nummer(e["name"]) return collections.OrderedDict([ - ("id", "Kreisel_%s_%02d" % (projekt, nr)), - ("function", "insert"), - ("x", _r(e["p"][0])), ("y", _r(e["p"][1])), ("z", _r(e["p"][2])), - ("abstand", _r(abstand)), - ("rotation", _r(e["rot"])), - ("typ", a.get("KREISELART") or "STANDARD"), + (sch.F_ID, "Kreisel_%s_%02d" % (projekt, nr)), + (sch.F_FUNCTION, sch.FUNC_INSERT), + (sch.F_X, _r(e["p"][0])), (sch.F_Y, _r(e["p"][1])), (sch.F_Z, _r(e["p"][2])), + (sch.F_ABSTAND, _r(abstand)), + (sch.F_ROTATION, _r(e["rot"])), + (sch.F_TYP, a.get("KREISELART") or sch.TYP_STANDARD), # Umlaufsinn: bestimmt die Reihenfolge im Kreisel-Umlauf und damit den # TRO-Fluss - ohne ihn kam jeder Nachbau als UZS heraus (Dematic 1/4: # GUZ). hundm05:bau-kreisel reicht ihn an kreisel-insert-script weiter. - ("drehrichtung", a.get("DREHRICHTUNG") or "UZS"), - ("comment", "aus Zeichnung: %s (ID %s), abstand aus %s" + (sch.F_DREHRICHTUNG, a.get("DREHRICHTUNG") or sch.DREHRICHTUNG_UZS), + (sch.F_COMMENT, "aus Zeichnung: %s (ID %s), abstand aus %s" % (e["name"], a.get("ID", "?"), quelle)), ]) @@ -251,15 +252,15 @@ def gf_eintrag(e, geo, projekt, hinweise): r = _richtung(hz) ende = (start[0] + deltaL * r[0], start[1] + deltaL * r[1]) eintrag = collections.OrderedDict([ - ("test_id", "GF_%s_%02d" % (projekt, _nummer(e["name"]))), - ("modus", 1), - ("deltaL_mm", _r(deltaL)), - ("hz_grad", _r(hz)), - ("start_mm", [_r(start[0]), _r(start[1]), _r(z_start)]), - ("as_seite", a.get("SEITE_AS") or "links"), - ("es_seite", a.get("SEITE_ES") or "links"), - ("as_winkel", xd[2] or "90"), - ("es_winkel", xd[3] or "90"), + (sch.F_TEST_ID, "GF_%s_%02d" % (projekt, _nummer(e["name"]))), + (sch.F_MODUS, sch.GF_MODUS_WERTEINGABE), + (sch.F_DELTAL_MM, _r(deltaL)), + (sch.F_HZ_GRAD, _r(hz)), + (sch.F_START_MM, [_r(start[0]), _r(start[1]), _r(z_start)]), + (sch.F_AS_SEITE, a.get("SEITE_AS") or sch.SEITE_LINKS), + (sch.F_ES_SEITE, a.get("SEITE_ES") or sch.SEITE_LINKS), + (sch.F_AS_WINKEL, xd[2] or sch.SCHLEUS_WINKEL_90), + (sch.F_ES_WINKEL, xd[3] or sch.SCHLEUS_WINKEL_90), ]) k_start = kreisel_an_punkt(start, r, geo) k_ende = kreisel_an_punkt(ende, (-r[0], -r[1]), geo) @@ -275,7 +276,7 @@ def gf_eintrag(e, geo, projekt, hinweise): else: eintrag["winkel_grad"] = round(winkel, 4) kommentar = "kein Kreisel am Ende - gebauter Winkel uebernommen" - eintrag["comment"] = "aus Zeichnung: %s (ID %s); %s" % ( + eintrag[sch.F_COMMENT] = "aus Zeichnung: %s (ID %s); %s" % ( e["name"], a.get("ID", "?"), kommentar) return eintrag @@ -286,18 +287,18 @@ def vf_standard_eintrag(e, projekt, typ): xd = e["xdata"].get("SSG_VF_EDIT") or [] l_gf = [t for t in (a.get("L_GF_m") or "").split(",") if t.strip()] eintrag = collections.OrderedDict([ - ("test_id", "VF_%s_%02d" % (projekt, _nummer(e["name"]))), - ("typ", typ), - ("richtung", a.get("ANTRIEBFAHRTRICHTUNG") or "Auf"), - ("deltaL", _r(_float(a.get("DELTA_L_mm")))), - ("deltaH", _r(_float(a.get("DELTA_H_mm")))), - ("winkel", int(_float(a.get("VF_WINKEL")))), - ("L_GF1", _r(1000.0 * _float(l_gf[0])) if l_gf else 0.0), - ("seite", a.get("SEITE_AS") or "rechts"), - ("motorseite", (a.get("MOTORSEITE") or "rechts").split(",")[0]), - ("hz", _r(_float(xd[1]) if len(xd) > 1 else 0.0)), - ("x", _r(e["p"][0])), ("y", _r(e["p"][1])), ("z", _r(e["p"][2])), - ("comment", "aus Zeichnung: %s (ID %s), Hoehe %s -> %s; deltaL/" + (sch.F_TEST_ID, "VF_%s_%02d" % (projekt, _nummer(e["name"]))), + (sch.F_VF_TYP, typ), + (sch.F_RICHTUNG, a.get("ANTRIEBFAHRTRICHTUNG") or sch.RICHTUNG_AUF), + (sch.F_DELTA_L, _r(_float(a.get("DELTA_L_mm")))), + (sch.F_DELTA_H, _r(_float(a.get("DELTA_H_mm")))), + (sch.F_WINKEL, int(_float(a.get("VF_WINKEL")))), + (sch.F_L_GF1, _r(1000.0 * _float(l_gf[0])) if l_gf else 0.0), + (sch.F_SEITE, a.get("SEITE_AS") or sch.SEITE_RECHTS), + (sch.F_MOTORSEITE, (a.get("MOTORSEITE") or sch.SEITE_RECHTS).split(",")[0]), + (sch.F_HZ, _r(_float(xd[1]) if len(xd) > 1 else 0.0)), + (sch.F_X, _r(e["p"][0])), (sch.F_Y, _r(e["p"][1])), (sch.F_Z, _r(e["p"][2])), + (sch.F_COMMENT, "aus Zeichnung: %s (ID %s), Hoehe %s -> %s; deltaL/" "deltaH aus den (ganzzahligen) Attributen" % (e["name"], a.get("ID", "?"), a.get("HOEHE_VON_mm", "?"), a.get("HOEHE_BIS_mm", "?"))), @@ -311,10 +312,10 @@ def vf_journal_eintrag(e, projekt, nr): roh = "".join(e["xdata"]["SSG_VF_EDIT"][1:]) tokens = [t for t in roh.split(TRENNER) if t] return collections.OrderedDict([ - ("test_id", "VF_%s_LZ_%02d" % (projekt, nr)), - ("typ", "linienzug_journal"), + (sch.F_TEST_ID, "VF_%s_LZ_%02d" % (projekt, nr)), + (sch.F_VF_TYP, "linienzug_journal"), ("journal", tokens), - ("comment", "aus Zeichnung: %s (ID %s), %d Eingaben, Hoehe %s -> %s; " + (sch.F_COMMENT, "aus Zeichnung: %s (ID %s), %d Eingaben, Hoehe %s -> %s; " "wird 1:1 abgespielt (vsp-bau-journal)" % (e["name"], a.get("ID", "?"), len(tokens), a.get("HOEHE_VON_mm", "?"), a.get("HOEHE_BIS_mm", "?"))), @@ -326,11 +327,11 @@ def einzel_eintrag(e): info = ", ".join("%s %s" % (k, a[k]) for k in ("ID", "ZUORDNUNG", "ARTINR") if a.get(k)) return collections.OrderedDict([ - ("function", "insert"), - ("block", DIM_SUFFIX.sub("", e["name"])), - ("x", _r(e["p"][0])), ("y", _r(e["p"][1])), ("z", _r(e["p"][2])), - ("rotation", _r(e["rot"])), - ("comment", "aus Zeichnung: %s%s" % (e["name"], + (sch.F_FUNCTION, sch.FUNC_INSERT), + (sch.F_BLOCK, DIM_SUFFIX.sub("", e["name"])), + (sch.F_X, _r(e["p"][0])), (sch.F_Y, _r(e["p"][1])), (sch.F_Z, _r(e["p"][2])), + (sch.F_ROTATION, _r(e["rot"])), + (sch.F_COMMENT, "aus Zeichnung: %s%s" % (e["name"], (" (%s)" % info) if info else "")), ]) @@ -390,11 +391,11 @@ def main(): eintraege, hinweise = baue_eintraege(inserts, block_inhalt, projekt) zaehler = collections.Counter( - "Kreisel" if "abstand" in e else - "GF" if "modus" in e else - "VF Linienzug" if e.get("typ") == "linienzug_journal" else - "VF %s" % e["typ"] if "typ" in e and "test_id" in e else - e.get("block", "?") for e in eintraege) + "Kreisel" if sch.F_ABSTAND in e else + "GF" if sch.F_MODUS in e else + "VF Linienzug" if e.get(sch.F_VF_TYP) == "linienzug_journal" else + "VF %s" % e[sch.F_VF_TYP] if sch.F_VF_TYP in e and sch.F_TEST_ID in e else + e.get(sch.F_BLOCK, "?") for e in eintraege) for k, v in sorted(zaehler.items()): print("%s %-22s %d" % (PREFIX, k, v)) for h in hinweise: diff --git a/lib/testdata_schema.py b/lib/testdata_schema.py new file mode 100644 index 0000000..a1e5e11 --- /dev/null +++ b/lib/testdata_schema.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +testdata_schema.py - Feldnamen und feste Werte der JSON-Testdaten-Schemas aus +doc/json-input-lisp.md, als einzige Quelle fuer alle Erzeuger. + +Bisher standen dieselben Feldnamen ("function", "abstand", "deltaL_mm", +"as_seite", ...) als lose String-Literale unabhaengig in dxf_scan_components.py +(fremde Zeichnungen) UND dxf_anlage_export.py (eigene, mit unseren Makros +gebaute Zeichnungen) - ein Tippfehler oder eine Schema-Aenderung in nur einer +der beiden Dateien waere nicht aufgefallen. Dieses Modul buendelt sie an +einer Stelle; die LISP-Treiber (tests/test_*.lsp) lesen die JSON-Keys direkt +als Strings und sind davon nicht betroffen. + +Nur Feldnamen/feste Werte, KEINE Validierung oder Default-Logik - die bleibt +bewusst in den jeweiligen Erzeugern, deren Kontext (fremde vs. eigene +Zeichnung) unterschiedliche Fallbacks braucht. +""" + +# --- Gemeinsame Felder --- +F_FUNCTION = "function" +F_ID = "id" +F_TEST_ID = "test_id" +F_BLOCK = "block" +F_X = "x" +F_Y = "y" +F_Z = "z" +F_ROTATION = "rotation" +F_COMMENT = "comment" + +FUNC_INSERT = "insert" +FUNC_CONNECT = "connect" + +# --- Abschnitt 1 (json-input-lisp.md): Kreisel --- +F_ABSTAND = "abstand" +F_TYP = "typ" +F_DREHRICHTUNG = "drehrichtung" + +TYP_STANDARD = "STANDARD" +TYP_PIN = "PIN" +DREHRICHTUNG_UZS = "UZS" +DREHRICHTUNG_GUZ = "GUZ" + +# --- Abschnitt 3: VarioFoerderer Standard und Etage --- +F_VF_TYP = "typ" +F_RICHTUNG = "richtung" +F_DELTA_L = "deltaL" +F_DELTA_H = "deltaH" +F_WINKEL = "winkel" +F_L_GF1 = "L_GF1" +F_GF_VERTEILUNG = "gf_verteilung" +F_SEITE = "seite" +F_MOTORSEITE = "motorseite" +F_HZ = "hz" + +VF_TYP_STANDARD = "standard" +VF_TYP_ETAGE = "etage" +RICHTUNG_AUF = "Auf" +RICHTUNG_AB = "Ab" +SEITE_LINKS = "links" +SEITE_RECHTS = "rechts" + +# --- Abschnitt 4.1: Gefaellestrecke Modus 1 --- +F_MODUS = "modus" +F_DELTAL_MM = "deltaL_mm" +F_WINKEL_GRAD = "winkel_grad" +F_HZ_GRAD = "hz_grad" +F_START_MM = "start_mm" +F_AS_SEITE = "as_seite" +F_ES_SEITE = "es_seite" +F_AS_WINKEL = "as_winkel" +F_ES_WINKEL = "es_winkel" + +GF_MODUS_WERTEINGABE = 1 +GF_MODUS_RICHTUNG_AUS_PUNKTEN = 2 +GF_MODUS_LINIENZUG = 3 + +SCHLEUS_WINKEL_90 = "90" +SCHLEUS_WINKEL_30 = "30"