diff --git a/tests/testdata/object_data.md b/tests/testdata/object_data.md index de14118..3a643cd 100644 --- a/tests/testdata/object_data.md +++ b/tests/testdata/object_data.md @@ -6,6 +6,13 @@ noetig sind, um das jeweilige Bauteil programmgesteuert (per JSON) zu erzeugen. Fuer jeden Typ wird die tatsaechlich aufgerufene LISP-Funktion samt Parameterliste referenziert. +Jede Objekt-Sektion enthaelt ein **vollstaendiges** JSON-Beispiel: es listet +*alle* Werte auf, die man setzen muss, damit die dahinterliegende +Produktionsfunktion mit saemtlichen Parametern aufgerufen werden kann — so, +als gaebe es keine `tests/test_*.lsp`-Wrapper. Wo Koordinaten (x/y/z) als +Eingang noetig sind, stehen sie explizit im Beispiel. Optionale Felder sind +mit ihrem Default annotiert; laesst man sie weg, greift der Default. + Wichtige Unterscheidung, die sich beim Durcharbeiten herauskristallisiert hat: - **Scriptbar** = es existiert eine Produktions-Funktion, die alle noetigen @@ -24,49 +31,103 @@ Grad). Richtung `hz`/`hz_grad`: 0=Ost(+X), 90=Nord(+Y), 180=West(-X), --- +## 0. Uebersicht — alle skriptbaren Objekte + +| # | Objekt | Produktionsfunktion (Bau) | Scriptbar? | Koordinaten-Eingang | Sektion | +|---|---|---|---|---|---| +| 1.1 | Kreisel Insert | `kreisel-insert-script (pt abstand rotation typ hoehe)` | **ja** | 1 Punkt `x,y,z` | [#11](#11-kreisel-insert-freier-punkt) | +| 1.2 | Kreisel Connect | `kreisel-connect-script (ptStart ptEnd typ hoehe)` | **ja** | 2 Punkte `start_*`/`end_*` | [#12](#12-kreisel-connect-start-endpunkt) | +| 2.1 | Gefaellestrecke Modus 1 | `gf-modus12-abschluss` → `gefaellestrecke-einfuegen` | **ja** | 1 Punkt `start_mm` | [#21](#21-gefaellestrecke-modus-1--manuelle-werteingabe) | +| 2.2 | Gefaellestrecke Modus 2 | `gf-modus12-abschluss` (deltaL/hz aus 2 Punkten) | **ja** | 3 Punkte (`linie_*`, `start_mm`) | [#22](#22-gefaellestrecke-modus-2--3d-linie-als-richtungsreferenz) | +| 2.3 | Gefaellestrecke Modus 3 | `gf-linienzug-modus ()` | **nein** | vorgezeichnete LINE/ARC-Auswahl | [#23](#23-gefaellestrecke-modus-3--linienzug--nicht-scriptbar) | +| 3.1 | VarioFoerderer Standard | `variofoerderer-einfuegen (…)` | **ja** | 1 Punkt `startpunkt` | [#31](#31-variofoerderer-standard--scriptbar) | +| 3.2 | VarioFoerderer Etage | `etage-foerderanlage-einfuegen (…)` | **ja** | 1 Punkt `startpunkt` | [#32](#32-variofoerderer-etage--scriptbar) | +| 3.3 | VarioFoerderer Linienzug | `vf-linienzug-modus ()` | **nein** | interaktiver Assistent | [#33](#33-variofoerderer-linienzug--nicht-scriptbar) | +| 3.4 | Vario_Kette_Merge | `vfl-kette-verfolgen` (Re-Packaging) | n/a | 1 Entity-Pick | [#34](#34-vario_kette_merge--kein-neubau) | +| 4.1 | Omniflo Bogen/Weiche | `omni:insert-dxf (sivasnr hoehe drehung)` + `_.INSERT` | Kern ja, Befehl nein | 1 Punkt `x,y,z` (nur via Wrapper) | [#41](#41-omniflo-boegenweichen) | +| 4.2 | Omniflo Aluprofil-Gerade | `omni:make-gerade (blockname pt pt2 …)` | Kern ja | 2 Punkte `pt`/`pt2` | [#42](#42-omniflo-aluprofil-gerade-ap60ap110apg110) | +| 4.3 | Omniflo TEF/Zubehoer | `omni:insert-dxf` (fester Sivasnr) | Position ja, Punkt interaktiv | 1 Punkt (nur via Wrapper) | [#43](#43-omniflo-tefzubehoer) | +| 4.4 | Omniflo TV_*/APBW_* | — (DUMMY) | **nein** | — | [#44](#44-omniflo-tv_-apbw_) | +| 5 | Separator/Scanner/Sensor | `ils-insert-sensor (fname)` + `_.INSERT` | **nein** (Schleife) | 1 Punkt `x,y,z` (nur via Wrapper) | [#5](#5-separatorscannersensor) | + +Legende Scriptbar-Spalte: +- **ja** — direkt per JSON aufrufbar. +- **Kern ja, Befehl nein** — die Bau-Funktion nimmt alle Werte an, aber der + ausgelieferte `c:…`-Befehl fragt den Einfuegepunkt per Maus (`pause`) ab; ein + JSON-Treiber muss den `_.INSERT` selbst nachbauen. +- **nein** — nur interaktiv; JSON beschreibt hoechstens die gewuenschte + Geometrie, nicht einen Funktions-Parametersatz. + +--- + ## 1. Kreisel — `Lisp/KreiselInsert.lsp` -Scriptbar. Zwei Varianten: +Scriptbar. Zwei Varianten. -### 1.1 Insert (freier Punkt + Abstand) +### 1.1 Kreisel Insert (freier Punkt) Produktionsfunktion: `kreisel-insert-script (pt abstand rotation typ hoehe)` | Feld | Typ | Pflicht | Bedeutung | Default | |---|---|---|---|---| | `x`, `y` | real (mm) | **ja** | Einfuegepunkt (Welt-XY), zusammen `pt` | - | -| `z` | real (mm) | nein | Hoehe; entfaellt `hoehe`, wird `pt`s Z verwendet | `*kreisel-default-hoehe*` (config `kreisel.default_hoehe`, 2000.0) | +| `z` | real (mm) | nein | Z von `pt`; wird verwendet, wenn `hoehe` fehlt | - | +| `hoehe` | real (mm) | nein | Ueberschreibt Z; sonst `pt`s Z | `*kreisel-default-hoehe*` (config `kreisel.default_hoehe`, 2000.0) | | `abstand` | real (mm) | nein | Abstand zwischen AN-/SP-Kreiselhaelfte (Kreisel-Baulaenge) | `*kreisel-default-laenge*` (config `kreisel.default_laenge`, 2300.0) | -| `rotation` | real (Grad) | nein | freie Rotation des Bauteils, kein Rasterzwang (0/90/180/270 sind nur ueblich, nicht erzwungen) | 0.0 | -| `typ` | string | nein | `"STANDARD"` oder `"PIN"` (einzige zwei Optionen aus `kreisel-ask-typ`) | `"STANDARD"` | +| `rotation` | real (Grad) | nein | freie Rotation, kein Rasterzwang | 0.0 | +| `typ` | string | nein | `"STANDARD"` oder `"PIN"` (einzige Optionen aus `kreisel-ask-typ`) | `"STANDARD"` | Konstanten (Config `[kreisel]`): `durchmesser` (800.0mm, fuer Connect-Abstandsberechnung), `default_laenge`, `default_hoehe`. -Minimal-JSON (wie `kreisel_tests.json`): +Vollstaendiges JSON (alle Parameter gesetzt): ```json -{ "id": "K1", "function": "insert", - "x": 500, "y": 500, "z": 2500, - "abstand": 2000, "rotation": 0.0, "typ": "STANDARD" } +{ + "id": "K1", + "function": "insert", + "x": 500, + "y": 500, + "z": 2500, + "hoehe": 2500, + "abstand": 2300, + "rotation": 0.0, + "typ": "STANDARD" +} ``` `id` und `expect_*`-Felder (`expect_block_prefix`, `expect_hoehe`, `expect_kreiselart`) sind **keine** Produktionsparameter — reine Test-Erwartungswerte fuer die Ergebnispruefung, nicht von `kreisel-insert-script` konsumiert. -### 1.2 Connect (Start-/Endpunkt, Abstand wird berechnet) +### 1.2 Kreisel Connect (Start-/Endpunkt) Produktionsfunktion: `kreisel-connect-script (ptStart ptEnd typ hoehe)` -| Feld | Typ | Pflicht | Bedeutung | -|---|---|---|---| -| `start_x`,`start_y`,`start_z` | real (mm) | **ja** | aeusserster Punkt AN-Seite (Antriebsstation) | -| `end_x`,`end_y`,`end_z` | real (mm) | **ja** | aeusserster Punkt SP-Seite (Spannstation) | -| `typ` | string | nein | `"STANDARD"`/`"PIN"` | Default `"STANDARD"` | -| `hoehe` | real (mm) | nein | Default: Z von `ptStart`, sonst `*kreisel-default-hoehe*` | +| Feld | Typ | Pflicht | Bedeutung | Default | +|---|---|---|---|---| +| `start_x`,`start_y`,`start_z` | real (mm) | **ja** | aeusserster Punkt AN-Seite (Antriebsstation) | - | +| `end_x`,`end_y`,`end_z` | real (mm) | **ja** | aeusserster Punkt SP-Seite (Spannstation) | - | +| `typ` | string | nein | `"STANDARD"`/`"PIN"` | `"STANDARD"` | +| `hoehe` | real (mm) | nein | Einfuegehoehe | Z von `ptStart`, sonst `*kreisel-default-hoehe*` | -`abstand`/`rotation` werden **intern** aus der Distanz beider Punkte -berechnet (`abstand = distanz - *kreisel-durchmesser*`; Rotation aus dem -Winkel der Verbindungslinie) — bei Connect KEIN Eingabefeld. +`abstand`/`rotation` werden **intern** aus den beiden Punkten berechnet +(`abstand = distanz - *kreisel-durchmesser*`; Rotation aus dem Winkel der +Verbindungslinie) — bei Connect KEIN Eingabefeld. + +Vollstaendiges JSON (alle Parameter gesetzt): +```json +{ + "id": "K2", + "function": "connect", + "start_x": 0, + "start_y": 0, + "start_z": 2000, + "end_x": 5000, + "end_y": 0, + "end_z": 2000, + "typ": "STANDARD", + "hoehe": 2000 +} +``` --- @@ -86,72 +147,101 @@ sich gegenseitig fest (`L_stau = (deltaL - aus-dx - ein-dx - sep-x) / cos(rad)`, Default 300.0). Es gibt **keinen** Parameter fuer eine gewuenschte Ziel-Hoehendifferenz — die ergibt sich aus `L_stau`/`winkel`. -### 2.1 Modus 1 — Manuelle Werteingabe +### 2.1 Gefaellestrecke Modus 1 — Manuelle Werteingabe Eingangsfunktion (nach DCL-Dialog `gefaellestrecke.dcl`): `gf-modus12-abschluss (deltaL winkel hz-winkel as-seite es-seite startpunkt as-winkel es-winkel dialog-bestaetigt geruest-einzelmodul geruest-typ)` | Feld | Typ | Pflicht | Bedeutung | Default | |---|---|---|---|---| -| `deltaL` / `deltaL_mm` | real (mm) | **ja** | horizontale Gesamtlaenge (AS+Staustrecke+Separator+ES) | config `gefaelle.default_delta_l` 5000.0 | -| `winkel` / `winkel_grad` | real (Grad) | nein | Gefaelle-Neigungswinkel | config `gefaelle.default_winkel` 3.0 | +| `deltaL_mm` | real (mm) | **ja** | horizontale Gesamtlaenge (AS+Staustrecke+Separator+ES) | config `gefaelle.default_delta_l` 5000.0 | +| `winkel_grad` | real (Grad) | nein | Gefaelle-Neigungswinkel | config `gefaelle.default_winkel` 3.0 | | `hz_grad` | real (Grad) | nein | Fahrtrichtung (0/90/180/270 ueblich, frei waehlbar) | 0.0 | | `start_mm` | [x,y,z] (mm) | **ja** | Startpunkt; Z = Einfuegehoehe | - | | `as_seite` | string | nein | `"links"`/`"rechts"` (AUS-Element-Seite) | `"links"` | | `es_seite` | string | nein | `"links"`/`"rechts"` (EIN-Element-Seite) | `"links"` | | `as_winkel` | string | nein | `"90"`/`"30"` (AUS-Element-Variante) | `"90"` | | `es_winkel` | string | nein | `"90"`/`"30"` (EIN-Element-Variante) | `"90"` | -| `geruest_einzelmodul` | string "0"/"1" | nein | Geruest je Einzelmodul (Dialogfeld) | "1" | -| `geruest_typ` | string | nein | Geruest-Option (`*ssg-geruest-optionen*`) | Default-Index | +| `geruest_einzelmodul` | string "0"/"1" | nein | Geruest je Einzelmodul | `"1"` | +| `geruest_typ` | string | nein | einer aus `*ssg-geruest-optionen*` (s.u.) | `"Schoenenberger Geruest"` (Index 2) | -Minimal-JSON (wie `gefaellestrecke_tests.json` Modus 1): +`*ssg-geruest-optionen*` (aus `ssg_core.lsp`): `"IPE-Geruest abgestuft"`, +`"Obergeruest oben"`, `"Schoenenberger Geruest"`, `"nur Doppelrohrtraeger"`. +Interner Parameter `dialog-bestaetigt` ist immer `T` (kein Datenfeld). + +Vollstaendiges JSON (alle Parameter gesetzt): ```json -{ "test_id": "M1_hz000", "modus": 1, "hz_grad": 0.0, +{ + "test_id": "GF_M1", + "modus": 1, + "deltaL_mm": 10000, + "winkel_grad": 3.0, + "hz_grad": 0.0, "start_mm": [0, 0, 5000], - "as_seite": "links", "es_seite": "links", - "as_winkel": "90", "es_winkel": "90" } + "as_seite": "links", + "es_seite": "links", + "as_winkel": "90", + "es_winkel": "90", + "geruest_einzelmodul": "1", + "geruest_typ": "Schoenenberger Geruest" +} ``` -`deltaL_mm`/`winkel_grad`/`z_start_mm` stehen in der Testdatei als -gemeinsamer Kopf-Eintrag (ein Objekt ohne `test_id`) statt pro Testfall, -weil alle Modus-1-Faelle in `gefaellestrecke_tests.json` dieselbe Laenge -teilen — Produktionsseitig ist `deltaL` aber pro Aufruf ein normaler, -nicht geteilter Parameter. +Hinweis: In `gefaellestrecke_tests.json` stehen `deltaL_mm`/`winkel_grad`/ +`z_start_mm` als gemeinsamer Kopf-Eintrag (ein Objekt ohne `test_id`), weil +alle Modus-1-Faelle dieselbe Laenge teilen. Produktionsseitig ist `deltaL` +aber pro Aufruf ein normaler, nicht geteilter Parameter — daher hier +vollstaendig im Einzelfall aufgefuehrt. -### 2.2 Modus 2 — 3D-Linie als Richtungsreferenz +### 2.2 Gefaellestrecke Modus 2 — 3D-Linie als Richtungsreferenz Gleiche Ziel-Funktion (`gf-modus12-abschluss`), aber `deltaL`/`hz-winkel` -werden **nicht** direkt angegeben, sondern aus zwei Linienpunkten -berechnet: +werden **nicht** direkt angegeben, sondern aus zwei Linienpunkten berechnet: ``` deltaX = ende.x - start.x ; deltaY = ende.y - start.y -deltaL = hypot(deltaX, deltaY) +deltaL = hypot(deltaX, deltaY) hz-winkel = angle(deltaX, deltaY) [Grad, 0=Ost] ``` In Produktion werden diese zwei Punkte interaktiv per `get-line-start-end-points` (Linie in der Zeichnung anklicken) bestimmt — fuer einen JSON-Treiber genuegen aber zwei beliebige Punkte, da die Berechnung rein geometrisch ist. -| Feld | Typ | Pflicht | Bedeutung | -|---|---|---|---| -| `linie_start_mm` | [x,y,z] | **ja** | erster Linienpunkt (nur X/Y verwendet) | -| `linie_ende_mm` | [x,y,z] | **ja** | zweiter Linienpunkt (nur X/Y verwendet) | -| `start_mm` | [x,y,z] | **ja** | Einfuegepunkt der Kette (unabhaengig von der Linie waehlbar) | -| `winkel`, `as_seite`, `es_seite`, `as_winkel`, `es_winkel` | wie Modus 1 | nein/ja | siehe oben | +| Feld | Typ | Pflicht | Bedeutung | Default | +|---|---|---|---|---| +| `linie_start_mm` | [x,y,z] | **ja** | erster Linienpunkt (nur X/Y verwendet) | - | +| `linie_ende_mm` | [x,y,z] | **ja** | zweiter Linienpunkt (nur X/Y verwendet) | - | +| `start_mm` | [x,y,z] | **ja** | Einfuegepunkt der Kette (unabhaengig von der Linie) | - | +| `winkel_grad` | real (Grad) | nein | Neigungswinkel | 3.0 | +| `as_seite`,`es_seite` | string | nein | `"links"`/`"rechts"` | `"links"` | +| `as_winkel`,`es_winkel` | string | nein | `"90"`/`"30"` | `"90"` | Geruestoptionen sind in Modus 2 (reine Konsoleneingabe) nicht abfragbar, -bleiben auf Default (`"0"`, `"Schoenenberger Geruest"`). +bleiben auf Default (`geruest_einzelmodul="0"`, `geruest_typ="Schoenenberger Geruest"`). -### 2.3 Modus 3 — Linienzug aus LINE/ARC — **nicht scriptbar** +Vollstaendiges JSON: +```json +{ + "test_id": "GF_M2", + "modus": 2, + "winkel_grad": 3.0, + "linie_start_mm": [0, 0, 0], + "linie_ende_mm": [10000, 0, 0], + "start_mm": [0, 0, 5000], + "as_seite": "links", + "es_seite": "links", + "as_winkel": "90", + "es_winkel": "90" +} +``` + +### 2.3 Gefaellestrecke Modus 3 — Linienzug — **nicht scriptbar** Eingangsfunktion: `gf-linienzug-modus ()` — **keine Parameter**. Ablauf in Produktion: -1. `(ssget '((0 . "LINE,ARC")))` — der Nutzer muss die Leitlinie - (Kette aus bereits in der Zeichnung vorhandenen LINE-/ARC-Entities) - **vorher zeichnen und dann per Maus auswaehlen**. Es gibt keine - Funktion, die eine Leitlinie aus Koordinaten/Bogen-Parametern - (`start_mm`/`ende_mm`/`bogen: {winkel_grad, radius_mm, seite}`, wie in - `gefaellestrecke_tests.json`s `M3_Bogen` zu sehen) direkt als Argument - entgegennimmt. +1. `(ssget '((0 . "LINE,ARC")))` — der Nutzer muss die Leitlinie (Kette aus + bereits in der Zeichnung vorhandenen LINE-/ARC-Entities) **vorher zeichnen + und dann per Maus auswaehlen**. Es gibt keine Funktion, die eine Leitlinie + aus Koordinaten/Bogen-Parametern (`start_mm`/`ende_mm`/`bogen`) direkt als + Argument entgegennimmt. 2. `getpoint` fuer Startpunkt (Kettenanfang, Z = obere Anschlusshoehe) und Endpunkt-Referenz (Z = untere Anschlusshoehe); `deltaH` wird aus der Differenz der beiden Z-Werte gebildet. @@ -161,22 +251,34 @@ Ablauf in Produktion: `gf-analysiere-kette`) leitet Ein-/Ausgangsrichtung sowie Segment-Laengenkorrekturen ab. -Um Modus 3 aus JSON zu treiben, muesste man selbst zuerst die LINE/ARC- -Geometrie per `entmake` erzeugen und dann eine eigene (nicht-interaktive) -Nachbildung von `gf-linienzug-modus` schreiben — genau das tut -`tests/test_gefaellestrecke.lsp`, was hier aber explizit nicht als -Parameterquelle verwendet wurde. Die Felder `start_mm`, `ende_mm`, `bogen` -in `gefaellestrecke_tests.json` beschreiben also die **gewuenschte -Leitlinien-Geometrie**, nicht direkte Funktionsparameter einer -Produktionsroutine. +JSON beschreibt hier nur die **gewuenschte Leitlinien-Geometrie**, nicht +direkte Funktionsparameter. Um Modus 3 aus JSON zu treiben, muss man zuerst +die LINE/ARC-Geometrie per `entmake` erzeugen und dann eine eigene +(nicht-interaktive) Nachbildung von `gf-linienzug-modus` schreiben — genau +das tut `tests/test_gefaellestrecke.lsp`. + +JSON (Intent-Beschreibung, kein Parametersatz): +```json +{ + "test_id": "M3_Bogen", + "modus": 3, + "start_mm": [30000, -50000, 2000], + "ende_mm": [38500, -43500, 1800], + "bogen": {"winkel_grad": 90, "radius_mm": 500, "seite": "links"}, + "as_seite": "links", + "es_seite": "links", + "as_winkel": "90", + "es_winkel": "90" +} +``` --- ## 3. VarioFoerderer — `Lisp/vf_core.lsp` + `vf_standard.lsp`/`vf_etage.lsp`/`vf_linienzug.lsp` Dispatcher `c:VarioFoerderer` waehlt einen registrierten Anlagetyp -(`"standard"`, `"etage"`, `"linienzug"`). Registry-Vertrag fuer -scriptbare Typen: +(`"standard"`, `"etage"`, `"linienzug"`). Registry-Vertrag fuer scriptbare +Typen: ``` berechne-fn (deltaL deltaH richtung seite) -> (best-winkel best-L_GF best-L_VF ergebnis-liste) einfuege-fn (deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz) -> endpunkt @@ -184,65 +286,99 @@ einfuege-fn (deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seit Der Wrapper `vf-block-erstellen` fasst danach zentral alles zu einem `VF_n`-Block zusammen (kein JSON-Feld dafuer noetig — laeuft automatisch). -### 3.1 Standard — scriptbar +`best-winkel`, `L_GF1`, `L_GF2`, `L_VF` stammen normalerweise aus dem Solver +(`berechne-fn`). Fuer einen scriptbaren Aufruf gibt man daher die +**Eingangsgroessen** (`deltaL`, `deltaH`, `richtung`, `seite`, optional +`winkel`, `gf_verteilung`) sowie `startpunkt`+`hz` an; der Solver liefert die +inneren Laengen/Winkel. Zwingt man `winkel`, wird dieser Kandidat statt der +Auto-Wahl verwendet. + +### 3.1 VarioFoerderer Standard — scriptbar - Winkel-/Laengen-Solver: `berechne-standard (deltaL deltaH richtung seite)` → wrappt `berechne-alle-winkel (deltaL deltaH richtung feste-hz)`. Kandidatenwinkel (config `vario.bogen_winkel`, Default): - `3 6 9 12 15 18 21 27 33 39 45 51` Grad — **nur diese Werte sind - moeglich**, kein freier Winkel. + `3 6 9 12 15 18 21 27 33 39 45 51` Grad — **nur diese Werte sind moeglich**, kein freier Winkel. - Bau-Funktion: `variofoerderer-einfuegen (deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz)` -| Feld | Typ | Pflicht | Bedeutung | -|---|---|---|---| -| `deltaL` | real (mm) | **ja** | horizontale Gesamtlaenge | -| `deltaH` | real (mm) | **ja** | Ziel-Hoehendifferenz (hier IST deltaH ein direkter Eingang, anders als bei Gefaellestrecke!) | -| `richtung` | string | **ja** | `"Auf"` oder `"Ab"` | -| `winkel` | int | nein | einer der 12 Kandidatenwinkel; ohne Angabe waehlt der Solver automatisch den **kleinsten gueltigen** Winkel | -| `L_GF1`, `L_GF2` | real (mm) | nein | Aufteilung der Gefaellestrecke vorne/hinten. Falls nicht vorgegeben: `gleichmaessig` = beide `L_GF/2` (Solver-Ergebnis `L_GF` gesamt). In Produktion per Dialog waehlbar: `0`=gleichmaessig, `1`=alles vorne (Rest hinten), `2`=alles hinten (Rest vorne), `3`=manuell (`L_GF1` frei, `L_GF2` = Rest) | -| `startpunkt` (x,y,z) | real (mm) | **ja** | Einfuegepunkt | -| `seite` | string | nein | `"links"`/`"rechts"` (AS/ES-Element-Seite) | Default `"rechts"` (Dialog) | +| Feld | Typ | Pflicht | Bedeutung | Default | +|---|---|---|---|---| +| `deltaL` | real (mm) | **ja** | horizontale Gesamtlaenge | - | +| `deltaH` | real (mm) | **ja** | Ziel-Hoehendifferenz (hier IST deltaH ein direkter Eingang, anders als bei Gefaellestrecke!) | - | +| `richtung` | string | **ja** | `"Auf"` oder `"Ab"` | - | +| `winkel` | int | nein | einer der 12 Kandidatenwinkel; ohne Angabe waehlt der Solver den **kleinsten gueltigen** | (Solver) | +| `gf_verteilung` | string | nein | `"gleichmaessig"`/`"vorne"`/`"hinten"` (Modus `0`/`1`/`2`); bestimmt `L_GF1`/`L_GF2` | `"gleichmaessig"` | +| `startpunkt` | [x,y,z] (mm) | **ja** | Einfuegepunkt | - | +| `seite` | string | nein | `"links"`/`"rechts"` (AS/ES-Element-Seite) | `"rechts"` (Dialog) | | `hz` | real (Grad) | nein | Fahrtrichtung | 0.0 | -| `motorseite` | string | nein | `"links"`/`"rechts"` — nur Dialog/Anzeige, kein Geometrie-Parameter von `variofoerderer-einfuegen` selbst | +| `motorseite` | string | nein | `"links"`/`"rechts"` — nur Dialog/Anzeige, kein Geometrie-Parameter von `variofoerderer-einfuegen` | `"links"` | -Minimal-JSON (wie `foerderer_tests.json`): +Verteilungsmodi ausfuehrlich (in Produktion per Dialog): `0`=gleichmaessig +(beide `L_GF/2`), `1`=alles vorne (Rest hinten), `2`=alles hinten (Rest +vorne), `3`=manuell (`L_GF1` frei, `L_GF2`=Rest). + +Vollstaendiges JSON (alle Parameter gesetzt): ```json -{ "test_id": "VF_Auf_1000", "richtung": "Auf", "deltaL": 7000, "deltaH": 1000 } +{ + "test_id": "VF_Auf_2000", + "typ": "standard", + "richtung": "Auf", + "deltaL": 7000, + "deltaH": 2000, + "winkel": 27, + "gf_verteilung": "gleichmaessig", + "startpunkt": [0, 0, 0], + "seite": "rechts", + "hz": 0.0, + "motorseite": "links" +} ``` -Optionale Erweiterungsfelder aus derselben Datei: `seite` (Default -`"links"` im Testfall), `winkel` (erzwingt einen bestimmten Kandidaten -statt Auto-Wahl), `gf_verteilung` (`"gleichmaessig"`/`"vorne"`/`"hinten"`, -entspricht Verteilungsmodus `0`/`1`/`2` oben). +Minimal genuegt (Solver fuellt den Rest): `richtung`, `deltaL`, `deltaH` (+ +`startpunkt`, falls nicht am Ursprung). -### 3.2 Etage — scriptbar +### 3.2 VarioFoerderer Etage — scriptbar - Solver: `berechne-etage (deltaL deltaH richtung seite)` → `berechne-winkel-etage (deltaL deltaH richtung)`. Gleiche 12 Kandidatenwinkel wie Standard. - Bau-Funktion: `etage-foerderanlage-einfuegen (deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz)` - — **identische Signatur** wie Standard (Registry-Vertrag), Unterschied - liegt im internen Aufbau (30-Grad-AS/ES-Elemente statt 90, AS/ES direkt + — **identische Signatur** wie Standard (Registry-Vertrag), Unterschied liegt + im internen Aufbau (30-Grad-AS/ES-Elemente statt 90, AS/ES direkt uebereinander, zusaetzlicher fixer "Gefaellebogen" je nach `seite` gegenueber dem AS-Element). Felder: identisch zu 3.1 (`deltaL`, `deltaH`, `richtung`, `winkel`, -`L_GF1`/`L_GF2`, `startpunkt`, `seite`, `hz`). +`gf_verteilung`, `startpunkt`, `seite`, `hz`). Nur `typ` unterscheidet sich. -### 3.3 Linienzug — **nicht scriptbar** +Vollstaendiges JSON: +```json +{ + "test_id": "VF_Etage_Auf_2000", + "typ": "etage", + "richtung": "Auf", + "deltaL": 7000, + "deltaH": 2000, + "winkel": 27, + "gf_verteilung": "gleichmaessig", + "startpunkt": [0, 0, 0], + "seite": "rechts", + "hz": 0.0, + "motorseite": "links" +} +``` + +### 3.3 VarioFoerderer Linienzug — **nicht scriptbar** Eingangsfunktion `vf-linienzug-modus ()` — **keine Parameter**, rein interaktiver Mehrschritt-Assistent (Kettenaufbau aus GF-Boegen, Vario-Kurven, VF-Einheiten, Horizontal-Stuecken). Jeder Schritt fragt per -`getpoint`/`getstring`/`getint`/`getreal` einzeln ab (Startpunkt, Hoehe, -AS/ES-Winkel+Seite, naechstes Element, Linien-Endpunkt via -Fahrtrichtung+Laenge, GF-Verteilung, Separator vor/nach, Kettenende -ja/nein, ...). Es existiert **keine** einzelne Produktionsfunktion, die -eine ganze Kette aus einer Liste von Segment-Beschreibungen entgegennimmt. +`getpoint`/`getstring`/`getint`/`getreal` einzeln ab. Es existiert **keine** +einzelne Produktionsfunktion, die eine ganze Kette aus einer Liste von +Segment-Beschreibungen entgegennimmt. -`linienzug_tests.json` bildet genau diese Prompt-Sequenz als -Frage-Antwort-Protokoll ab (`typ`: `point_abs`/`point_rel`/`real`/ -`string`/`int`, jeweils mit `wert`/`dL`+`hz` und einem `kommentar`, der -beschreibt, WELCHE Frage/Bedeutung die Antwort hat) — das ist die einzige -Moeglichkeit, den interaktiven Ablauf nicht-interaktiv nachzubilden, kein -direkter Parametersatz einer Bau-Funktion. Die Reihenfolge und Bedeutung -der Prompts (Kurzfassung, siehe `vf_linienzug.lsp` fuer Details): +`linienzug_tests.json` bildet die Prompt-Sequenz als Frage-Antwort-Protokoll +ab (`typ`: `point_abs`/`point_rel`/`real`/`string`/`int`, jeweils mit +`wert`/`dL`+`hz` und einem `kommentar`, der beschreibt, WELCHE Frage die +Antwort beantwortet) — das ist die einzige Moeglichkeit, den interaktiven +Ablauf nicht-interaktiv nachzubilden, kein direkter Parametersatz einer +Bau-Funktion. Prompt-Reihenfolge (Kurzfassung, Details in `vf_linienzug.lsp`): 1. Startpunkt (`point_abs`), Hoehe Z (`real`) 2. AS-Element setzen? Ja/Nein, wenn ja: Winkel 90/30, Seite links/rechts @@ -252,121 +388,155 @@ der Prompts (Kurzfassung, siehe `vf_linienzug.lsp` fuer Details): Separator vor/nach, GF-Verteilung, Vario-Kurve Winkel/Seite/Variante, ...) 4. Kettenende: ES-Winkel (90/30) + ES-Seite (links/rechts) -### 3.4 Vario_Kette_Merge — kein Neubau, sondern Re-Packaging +JSON (Prompt-Protokoll, Auszug): +```json +[ + { "typ": "point_abs", "wert": [0, 0, 5000], "kommentar": "Startpunkt + Hoehe Z" }, + { "typ": "string", "wert": "90", "kommentar": "AS-Element Winkel 90/30" }, + { "typ": "string", "wert": "links", "kommentar": "AS-Element Seite" }, + { "typ": "point_rel", "dL": 8000, "hz": 0.0, "kommentar": "Endpunkt naechstes Linien-Segment" }, + { "typ": "string", "wert": "90", "kommentar": "ES-Element Winkel" }, + { "typ": "string", "wert": "links", "kommentar": "ES-Element Seite" } +] +``` -`c:Vario_Kette_Merge`: benoetigt nur **einen** Start-Entity-Pick -(`entsel`), sucht danach selbst alle KS_EIN/KS_AUS-kompatiblen -Nachbarblöcke in der ganzen Zeichnung ab (`vfl-kette-verfolgen`, -Toleranzen 2mm/100mm) und fasst sie zu einem neuen `VF_n` zusammen. Kein -JSON-Parametersatz noetig/möglich — reine Nachbearbeitung bereits -vorhandener Geometrie. +### 3.4 Vario_Kette_Merge — kein Neubau + +`c:Vario_Kette_Merge`: benoetigt nur **einen** Start-Entity-Pick (`entsel`), +sucht danach selbst alle KS_EIN/KS_AUS-kompatiblen Nachbarbloecke in der +ganzen Zeichnung ab (`vfl-kette-verfolgen`, Toleranzen 2mm/100mm) und fasst +sie zu einem neuen `VF_n` zusammen. Kein JSON-Parametersatz noetig/moeglich — +reine Nachbearbeitung bereits vorhandener Geometrie. --- ## 4. Omniflo — `Lisp/OmniModulInsert.lsp` -### 4.1 Boegen/Weichen — Kern scriptbar, Produktions-Befehle NICHT +### 4.1 Omniflo Boegen/Weichen Kern-Funktion: `omni:insert-dxf (sivasnr-str hoehe drehung)` -| Param | Typ | Pflicht | Bedeutung | -|---|---|---|---| -| `sivasnr-str` | string | **ja** | Komponenten-ID; **ist gleichzeitig der Dateiname** `.dxf` unter `%DXFM_OMNIFLO%` — keine separate Namenstabelle | -| `hoehe` | string (mm) | nein | **absolute** Z-Koordinate des Einfuegepunkts + `HOEHE`-Attribut; leer/`nil` -> `config omniflo.default_hoehe` bzw. hart "2000" | -| `drehung` | string (Grad) | nein | feste Rotation; `nil` -> in den echten `c:OMNI_*`-Befehlen wird interaktiv per Maus gedreht | +| Feld | Typ | Pflicht | Bedeutung | Default | +|---|---|---|---|---| +| `sivasnr` | string | **ja** | Komponenten-ID; **ist gleichzeitig der Dateiname** `.dxf` unter `%DXFM_OMNIFLO%` | - | +| `hoehe` | string/real (mm) | nein | **absolute** Z-Koordinate + `HOEHE`-Attribut | config `omniflo.default_hoehe` bzw. hart "2000" | +| `drehung` | real (Grad) | nein | feste Rotation | in `c:OMNI_*` interaktiv per Maus | +| `x`, `y`, `z` | real (mm) | (Wrapper) | Einfuegepunkt — **kein** Parameter von `omni:insert-dxf`; muss ueber einen eigenen `_.INSERT`-Aufruf gesetzt werden | - | -**Aber**: Alle echten `c:OMNI_APB_*`/`c:OMNI_W*_*`-Befehle rufen -`omni:insert-dxf` **ohne X/Y-Parameter** auf — der Einfuegepunkt kommt in -`_.INSERT` immer per `pause` (Mausklick). Es gibt in Produktion **keinen** -Aufrufpfad, der X/Y programmatisch uebergibt; `omni:insert-dxf` selbst -akzeptiert auch keinen Punkt-Parameter (der Punkt wird IN der Funktion -interaktiv abgefragt). Die Felder `x`/`y` in `omniflo_tests.json` haben -daher **keine direkte Entsprechung** in einer Produktions-Parameterliste — -sie beschreiben nur, wo das Element im Testlayout stehen soll; ein -JSON-Treiber muesste den `_.INSERT`-Aufruf selbst nachbauen (z.B. wie -`mubea:build-separator` in `tests/test_mubea.lsp` es fuer Separatoren tut). +**Wichtig**: Alle echten `c:OMNI_APB_*`/`c:OMNI_W*_*`-Befehle rufen +`omni:insert-dxf` **ohne X/Y** auf — der Einfuegepunkt kommt in `_.INSERT` +immer per `pause` (Mausklick). Es gibt in Produktion **keinen** Aufrufpfad, +der X/Y programmatisch uebergibt; `omni:insert-dxf` selbst akzeptiert keinen +Punkt-Parameter. Die Felder `x`/`y`/`z` beschreiben also nur, wo das Element +stehen soll — ein JSON-Treiber muss den `_.INSERT` selbst nachbauen (wie +`mubea:build-separator` in `tests/test_mubea.lsp`). -Sivasnr-Herkunft: `data/json/omniflo_boegen.json` (Felder u.a. `Sivasnr`, -`ProfilTyp`, `Radius`, `KurvenWinkel`, `Breite`, `Länge`, `SivasnrTEF`, -`Antriebsart`) und `data/json/omniflo_weichen.json` (zusaetzlich -`WeichenTyp`, `Schaltungstyp`, `KurvenRichtung`, `WeichenkörperLänge`) — -diese Tabellen dienen nur der Dialog-Filterung/Anzeige, nicht der -Namensaufloesung selbst (die ist `sivasnr == dateiname`). +Sivasnr-Herkunft: `data/json/omniflo_boegen.json` und +`data/json/omniflo_weichen.json` (dienen der Dialog-Filterung/Anzeige, nicht +der Namensaufloesung — die ist `sivasnr == dateiname`). -Minimal-JSON, wie es fuer einen scriptbaren Wrapper um `omni:insert-dxf` -sinnvoll waere (entspricht `omniflo_tests.json`): +Vollstaendiges JSON (alle fachlich noetigen Werte inkl. Position): ```json -{ "id": "821104025", "type": "bogen", "sivasnr": "821104025", - "x": 219.4, "y": 223.06, "hoehe": 2000, "drehung": 0.0 } +{ + "id": "821104025", + "type": "bogen", + "sivasnr": "821104025", + "x": 219.4, + "y": 223.06, + "z": 2000, + "hoehe": 2000, + "drehung": 0.0 +} ``` -`description`/`row` sind reine Dokumentations-/Gruppierungsfelder aus der -Testdatei, keine Produktionsparameter. +`description`/`row` sind reine Dokumentations-/Gruppierungsfelder, keine +Produktionsparameter. -### 4.2 Aluprofil-Gerade (AP60/AP110/APG110) — Kern scriptbar +### 4.2 Omniflo Aluprofil-Gerade (AP60/AP110/APG110) Kern-Funktion: `omni:make-gerade (blockname pt pt2 srcAttribs textHeight textGap textEnt)` -| Param | Typ | Pflicht | Bedeutung | -|---|---|---|---| -| `blockname` | string | **ja** | `"AP60"` / `"AP110"` / `"APG110"` | -| `pt`, `pt2` | (x,y,z) | **ja** | Start-/Endpunkt der Geraden (absolute Weltkoordinaten); Laenge/Rotation werden **aus der Differenz berechnet**, keine eigenen Winkel-/Laengenfelder | -| `srcAttribs` | alist | nein | aus Vorlagen-DWG gelesen (`omni:read-src-attribs blockname`); bei `nil` entsteht ein Block ohne ATTDEFs | -| `textHeight`,`textGap` | real | nein | Konfig `omniflo.text_height`(100.0)/`text_gap`(20.0) | +| Feld | Typ | Pflicht | Bedeutung | Default | +|---|---|---|---|---| +| `blockname` | string | **ja** | `"AP60"` / `"AP110"` / `"APG110"` | - | +| `pt` | [x,y,z] | **ja** | Startpunkt (absolute Weltkoordinaten) | - | +| `pt2` | [x,y,z] | **ja** | Endpunkt; Laenge/Rotation aus Differenz `pt→pt2` berechnet | - | +| `textHeight` | real | nein | Textgroesse | config `omniflo.text_height` (100.0) | +| `textGap` | real | nein | Textabstand | config `omniflo.text_gap` (20.0) | -Der oeffentliche Befehl `omni:insert-block (blockname laengemax)` fragt -`pt`/`pt2` interaktiv per `getpoint` ab und prueft `laengemax` (Config -`omniflo.laengemax_ap60/ap110/apg110`) — `omni:make-gerade` selbst ist -aber mit fest vorgegebenen Punkten aufrufbar, hier waere aus -`linienzug_tests.json`-artigen Daten (`profil`, `x`/`y`, `x_ende`/`y_ende`, -`hoehe`) ein Treiber moeglich, wie es die Eintraege vom Typ `"gerade"` in -`linienzug_tests.json` nahelegen (dort allerdings fuer eine andere -Test-Zwecksetzung — Reihen-Layout, nicht direkter Funktionsaufruf). +`srcAttribs` wird aus der Vorlagen-DWG gelesen (`omni:read-src-attribs blockname`); +`textEnt` ist ein vorhandener Text-Entity zum Aktualisieren (bei Neuaufbau +`nil`). Beide sind keine JSON-Datenfelder. Der oeffentliche Befehl +`omni:insert-block (blockname laengemax)` fragt `pt`/`pt2` interaktiv per +`getpoint` ab — `omni:make-gerade` selbst ist mit festen Punkten aufrufbar. -### 4.3 TEF/Zubehoer (Stopper, Scanner, Buerstenbremse, Umlenk-/Antriebsstationen) +Vollstaendiges JSON: +```json +{ + "type": "gerade", + "blockname": "AP110", + "pt": [2000.0, 0.0, 2000.0], + "pt2": [3000.0, 0.0, 2000.0], + "textHeight": 100.0, + "textGap": 20.0 +} +``` +(In `omniflo_strecke_tests.json` wird die Gerade als `profil`/`x`/`y`/ +`x_ende`/`y_ende`/`hoehe` beschrieben — dieselben Punkte, nur andere +Feldnamen fuer den Reihen-Layout-Test.) +### 4.3 Omniflo TEF/Zubehoer + +Stopper, Scanner, Buerstenbremse, Umlenk-/Antriebsstationen: `c:OMNI_STOPPER`/`c:OMNI_SCANNER`/... rufen `omni:insert-dxf` mit **hartcodiertem** `sivasnr-str` (z.B. `"827062022"`) und `hoehe=nil, drehung=nil` auf — Punkt weiterhin interaktiv. Kein Nutzer-JSON-Feld fuer -`sivasnr` noetig (fest im Code), nur Position/Hoehe/Rotation waeren -grundsaetzlich scriptbar (siehe 4.1), aber nicht ueber die vorhandenen -`c:OMNI_*`-Befehle. +`sivasnr` noetig (fest im Code); Position/Hoehe/Rotation waeren +grundsaetzlich scriptbar (wie 4.1), aber nicht ueber die vorhandenen +`c:OMNI_*`-Befehle. JSON identisch strukturiert wie 4.1 (mit `sivasnr` fest +je Zubehoer-Typ). -### 4.4 TV_* (Transferwagen-Verbinder) / APBW_* (Boegen fuer Weichen) +### 4.4 Omniflo TV_* / APBW_* -**Nicht implementiert.** Alle `c:OMNI_TV_*`- und `c:OMNI_APBW_*`-Befehle -sind reine `[DUMMY]`-Platzhalter ohne echte Bau-Logik — kein -Parametersatz vorhanden, unabhaengig davon was eine Testdatei dafuer -vorsehen wuerde. +Transferwagen-Verbinder (`c:OMNI_TV_*`) und Boegen fuer Weichen +(`c:OMNI_APBW_*`): **nicht implementiert** — reine `[DUMMY]`-Platzhalter ohne +Bau-Logik, kein Parametersatz. -`c:OMNI_UPDATE_ATTRIBS` ist in `Lisp/export.lsp` definiert (aktualisiert -`HOEHE`/`DREHUNG` aller Omniflo-Elemente aus der Zeichnung) — kein -Bau-/Einfuege-Befehl und daher kein JSON-Parametersatz. +`c:OMNI_UPDATE_ATTRIBS` (in `Lisp/export.lsp`) aktualisiert `HOEHE`/`DREHUNG` +aller Omniflo-Elemente aus der Zeichnung — kein Bau-/Einfuege-Befehl, daher +kein JSON-Parametersatz. --- -## 5. Separator/Scanner — `Lisp/SSG_LIB_Commands.lsp` +## 5. Separator/Scanner/Sensor — `Lisp/SSG_LIB_Commands.lsp` Funktion: `ils-insert-sensor (fname)` -| Param | Typ | Pflicht | Bedeutung | -|---|---|---|---| -| `fname` | string | **ja** | Blockdateiname ohne Erweiterung, z.B. `"Separator_SP"`, `"Scanner"` — aufgeloest ueber `ssg-ils-block-datei` (2D/3D-Suffix + Fallback) | +| Feld | Typ | Pflicht | Bedeutung | Default | +|---|---|---|---|---| +| `block` / `fname` | string | **ja** | Blockdateiname ohne Erweiterung, z.B. `"Separator_SP"`, `"Scanner"`, `"S-LP"` — aufgeloest ueber `ssg-ils-block-datei` (2D/3D-Suffix + Fallback) | - | +| `x`, `y`, `z` | real (mm) | (Wrapper) | Einfuegepunkt — **kein** Parameter von `ils-insert-sensor` | - | +| `rotation` | real (Grad) | (Wrapper) | Rotation — **kein** Parameter (kommt per `pause`) | - | -**Nicht scriptbar**: die Funktion fragt Einfuegepunkte in einer Schleife -per `getpoint` ab (`_.INSERT pfad pt "" "" pause` — auch die Rotation -kommt per Maus/`pause`), bis der Nutzer mit ENTER/ESC abbricht. Es gibt -keinen Parameter fuer Punkt, Anzahl oder Rotation. Ein JSON-Treiber muss -den `_.INSERT`-Aufruf direkt selbst nachbilden (Blockdatei-Pfad ueber -`ssg-ils-block-datei` bzw. `ssg-ils-block-laden`, dann `_.INSERT pfad pt "" "" rot` -mit festem `rot` statt `pause`) — das ist der Ansatz, den -`tests/test_mubea.lsp`s `mubea:build-separator` verwendet (zur Info, nicht -als Quelle fuer diesen Parametersatz genutzt, da bereits aus -`ils-insert-sensor` selbst ableitbar: Blockname + Punkt + Rotation sind -die einzigen fachlich noetigen Werte). +**Nicht scriptbar**: die Funktion fragt Einfuegepunkte in einer Schleife per +`getpoint` ab (`_.INSERT pfad pt "" "" pause` — auch die Rotation kommt per +Maus/`pause`), bis der Nutzer mit ENTER/ESC abbricht. Es gibt keinen +Parameter fuer Punkt, Anzahl oder Rotation. Ein JSON-Treiber muss den +`_.INSERT`-Aufruf direkt selbst nachbilden (Blockdatei-Pfad ueber +`ssg-ils-block-datei` bzw. `ssg-ils-block-laden`, dann +`_.INSERT pfad pt "" "" rot` mit festem `rot` statt `pause`) — der Ansatz von +`mubea:build-separator` in `tests/test_mubea.lsp`. Fachlich noetig sind nur +Blockname + Punkt + Rotation. -Minimal-JSON (wie in `mubea.json`s Separator-Eintraegen): +Vollstaendiges JSON (wie in `mubea.json`s Separator-Eintraegen): ```json -{ "function": "insert", "block": "S-LP", "x": 10310, "y": -14158, "z": 1467, "rotation": 270 } +{ + "function": "insert", + "block": "S-LP", + "x": 10310, + "y": -14158, + "z": 1467, + "rotation": 270 +} ``` ---