From 05f1adc1b4dc4654bff4ab92da68c50f97ca83c2 Mon Sep 17 00:00:00 2001
From: Paul Wolok
Date: Mon, 26 Jan 2026 09:27:48 +0100
Subject: [PATCH] Refactor und dokumentation
---
doc/plant2dxf_code_explained.md | 25 -
doc/plant2dxf_dokumentation.md | 941 ++++++++++++++++++++++++++++++++
lib/as_es_methoden.py | 7 +-
lib/plant2dxf.py | 16 +-
4 files changed, 958 insertions(+), 31 deletions(-)
delete mode 100644 doc/plant2dxf_code_explained.md
create mode 100644 doc/plant2dxf_dokumentation.md
diff --git a/doc/plant2dxf_code_explained.md b/doc/plant2dxf_code_explained.md
deleted file mode 100644
index a9a0af8..0000000
--- a/doc/plant2dxf_code_explained.md
+++ /dev/null
@@ -1,25 +0,0 @@
-## Wichtige Ezdxf Methoden
-
-1. block.add_blockref(blockname,(kordinaten)dxfattribute={"rotation": rotation,"layer": layer})
-
-- Fügt inserts in ein block hinzu
-
-2. msp.add_blockref(blockname,(kordinaten)dxfattribute={"rotation": rotation,"layer": layer})
-
-- Fügt insert ins Modelspace hinzu
-
-3. doc.blocks.new(blockname, base_point=(0,0,0))
-
-- Fügt einen neune Block ins Document hinzu(gleichen Block kann man nur einmal machen if abrage wär if blockname in doc.blocks)
-
-4. msp.query("DXFOBJEKT)
-
-- Holle jedes spezifische dxfobjekt von dem Modelspace, wird meistens für Inserts verwendet
-
-## Plant2dxf handle Gefällestrecke
-
-1.Definiere Attribute und entnehme die Attribute von den Nachbarn falls nötig.
-2.Abarbeitung fall nur mit einem Kreisel verbunden.
-3. Korektur der rotation und austausch der Höhe falls nötig, wird gemact für die Konsistenz der Erstellung
-
-## Main line
diff --git a/doc/plant2dxf_dokumentation.md b/doc/plant2dxf_dokumentation.md
new file mode 100644
index 0000000..e0b3623
--- /dev/null
+++ b/doc/plant2dxf_dokumentation.md
@@ -0,0 +1,941 @@
+# plant2dxf - Dokumentation
+
+## Übersicht
+
+`plant2dxf` ist ein Python-Skript, das aus einer RuleDesigner-CSV-Datei DXF-Elemente erzeugt. Das Skript liest eine CSV-Datei mit Angaben über alle in einer Anlage enthaltenen Elemente und generiert daraus eine DXF-Datei der kompletten Anlage.
+
+## Hauptfunktionalität
+
+Das Skript verarbeitet CSV-Dateien mit folgenden Informationen:
+
+- **TeileArt**: Typ der Komponente (z.B. "ILS 2.0 Kreisel", "ILS 2.0 Gefällestrecke")
+- **TeileId**: Eindeutige Identifikation des Elements
+- **Planquadrat**: Koordinaten im Format `X: Y:`
+- **Merkmale**: JSON-String mit spezifischen Eigenschaften des Elements
+- **NachbarIds**: Kommagetrennte Liste von IDs benachbarter Elemente
+
+## Benötigte Dateien und Verzeichnisse
+
+### Umgebungsvariablen
+
+Das Skript benötigt folgende Umgebungsvariablen (werden durch `bin/setenv.bat` gesetzt):
+
+- **PROJECT_DATA**: Verzeichnis für Bibliotheksdateien (DXF-Blockbibliotheken)
+ - Standard: `/data`
+ - Enthält Unterverzeichnis `block_libraries/` mit DXF-Bibliotheken
+
+- **PROJECT_WORK**: Arbeitsverzeichnis für CSV-Eingabedateien und DXF-Ausgabedateien
+ - Standard: `/work`
+
+- **PROJECT_CFG**: Verzeichnis für Konfigurationsdateien
+ - Standard: `/cfg`
+ - Enthält: `allgemein.cfg`, `shapes.cfg`
+
+- **PROJECT_LOG**: Verzeichnis für Log-Dateien
+ - Standard: `/log`
+
+### Konfigurationsdateien
+
+#### 1. `cfg/allgemein.cfg`
+
+Allgemeine Konfigurationsdatei, die die Zuweisung von Block-Bibliotheken zu bestimmten Bauteil-Familien steuert.
+
+**Struktur:**
+
+```ini
+[LOG]
+log_level = INFO
+log_format = %%(asctime)s - %%(levelname)s - %%(message)s
+screen_format = %%(message)s
+
+[ILS]
+libfile = ils_lib.dxf
+
+[Omniflo]
+libfile = omniflo_lib.dxf
+
+[BT]
+libfile = ils_lib.dxf
+
+[TEF]
+libfile = omniflo_lib.dxf
+```
+
+**Zweck:**
+
+- Definiert, welche DXF-Bibliotheksdatei für welche TeileArt verwendet wird
+- Bibliotheksdateien werden aus `PROJECT_DATA/block_libraries/` geladen
+- Falls keine Zuordnung gefunden wird, wird die Standard-Bibliothek (`blocks.dxf`) verwendet
+
+#### 2. `cfg/shapes.cfg`
+
+Definiert für jede TeileArt die zu verwendenden Blöcke aus der Bibliotheks-DXF und deren Eigenschaften.
+
+**Struktur:**
+
+```ini
+[ILS 2.0 Kreisel]
+items = SP8, AN8
+offset_symb1 = 0,0
+offset_symb2 = 0,0
+rot_symb1 = 0.0
+rot_symb2 = 0.0
+
+[ILS 2.0 Gefällestrecke]
+items = EE DS, AE DS
+offset_symb1 = 0,-330
+offset_symb2 = 0,1000
+rot_symb1 = 0.0
+rot_symb2 = 0.0
+asoffset = 537.90
+esoffset = 479.95
+
+[ILS 2.0 Variofoerderer]
+Umlenkstation = 500.0, 0.0, 0.0
+Motorstation = 500.0, 0.0, 0.0
+vario_abstand = 66.5
+
+[Ils 2.0 core winkel]
+winkel_boegen = 3
+winkel_motor = 3.0
+winkel_umlenk = 3.0
+winkel_as = 3.0
+winkel_es = 3.0
+
+[Omniflo]
+OFgeradesivas = 821106002
+Tefgeradesivas = 0_B10030
+OFfoerderer = 2
+```
+
+**Parameter:**
+
+- **[TeileArt]**: Abschnittsname muss exakt dem Wert aus der CSV-Spalte `TeileArt` entsprechen
+- **items**: Kommagetrennte Liste der Blocknamen aus der Bibliothek
+- **offset_symbX**: Offset (x,y) für den X-ten Block relativ zur berechneten Position
+- **rot_symbX**: Rotation (in Grad) für den X-ten Block
+- **asoffset/esoffset**: Offsets für AS/ES-Elemente bei Gefällestrecken
+- **winkel_***: Winkel für verschiedene Komponenten (Bögen, Motor, Umlenkstation, AS/ES)
+
+### DXF-Bibliotheken
+
+DXF-Bibliotheken enthalten die Blockdefinitionen für die verschiedenen Komponenten. Sie werden aus `PROJECT_DATA/block_libraries/` geladen.
+
+**Bibliotheksdateien:**
+
+- `ils_lib.dxf`: Blöcke für ILS 2.0 Komponenten
+- `omniflo_lib.dxf`: Blöcke für Omniflo-Komponenten
+- `blocks.dxf`: Standard-Bibliothek (Fallback)
+
+**Wichtige Blöcke:**
+
+- `SCAN`: Scanner-Block
+- `S-LP`: Separator-Block
+- `Pinbereich`: Pinbereich-Block für Kreisel
+- `AN8`, `SP8`: Standard-Blöcke für Kreisel
+- `200000241_AS-Element_90_rechts`, `200000217_AS-Element_90_links`: AS-Elemente
+- `200000146_ES-Element_90_rechts`, `400102632_ES-Element_90_links`: ES-Elemente
+- Verschiedene Omniflo-Blöcke (identifiziert über SivasNummer)
+
+## Unterstützte Elementtypen
+
+### 1. ILS 2.0 Kreisel
+
+**Handler:** `handle_ils_2_0_kreisel()`
+
+**Funktionalität:**
+
+- Erstellt einen Kreisel mit Scanner und Separatoren
+- Positioniert Blöcke basierend auf Anzahl der Scanner/Separatoren
+- Zeichnet Kreisel-Linien und Drehrichtung-Markierung
+- Verwendet Blöcke aus `shapes.cfg` (z.B. SP8, AN8)
+
+**Merkmale:**
+
+- `Abstand (Kreiselachse A - Kreiselachse) in Meter`: Abstand zwischen den beiden Kreiselachsen
+- `Anzahl der Separatoren`: Anzahl der Separator-Blöcke
+- `Anzahl der Scanner`: Anzahl der Scanner-Blöcke
+- `Anzahl der Rampen`: Anzahl der Rampen
+- `Höhe in m`: Höhe des Kreisels
+- `Drehrichtung`: UZS (Uhrzeigersinn) oder GUZS (Gegen-Uhrzeigersinn)
+
+### 2. ILS 2.0 Gefällestrecke
+
+**Handler:** `handle_ils_2_0_gefaellestrecke()`
+
+**Funktionalität:**
+
+- Erstellt eine Gefällestrecke zwischen verschiedenen Höhen
+- Berücksichtigt Verbindungen zu Kreiseln, Kurven und anderen Elementen
+- Fügt AS/ES-Elemente ein, wenn nötig
+- Erstellt Blöcke dynamisch basierend auf Verbindungen
+
+**Merkmale:**
+
+- `Länge in Meter`: Länge der Gefällestrecke
+- `Höhe Anfang`: Start-Höhe in Metern
+- `Höhe Ende`: End-Höhe in Metern
+- `Drehung`: Rotation der Strecke
+- `Anzahl der Zusatzseparatoren`: Zusätzliche Separatoren
+
+**Besonderheiten:**
+
+- Unterscheidet zwischen Verbindung mit einem oder zwei Kreiseln
+- Prüft, ob direkte Verbindung zum Kreisel (ohne Stahlband)
+- Fügt Motor-/Umlenkstationen ein, wenn mit angetriebenen Kurven verbunden
+- Erstellt AS/ES-Elemente basierend auf Drehrichtung der benachbarten Kreisel
+
+### 3. ILS 2.0 VarioFoerderer
+
+**Handler:** `handle_ils_2_0_variofoerderer()`
+
+**Funktionalität:**
+
+- Erstellt einen Vario-Förderer mit verschiedenen Konfigurationen
+- Unterstützt Verbindungen zu Kreiseln, Eckrädern und anderen Vario-Förderern
+- Berücksichtigt Motor- und Umlenkstationen
+- Erstellt Gefälle-Komponenten innerhalb des Förderers
+
+**Merkmale:**
+
+- `Winkel`: Winkel des Förderers
+- `Förderrichtung`: Auf, Ab oder Horizontal
+- `Motorseite`: links oder rechts
+- `Höhe Anfang`, `Höhe Ende`: Höhen der Endpunkte
+- `Gefälle`: Gefälle-Länge und -Winkel
+- `Motor vorhanden`, `Umlenk vorhanden`: Boolean-Werte
+
+**Besonderheiten:**
+
+- Unterscheidet zwischen Verbindung mit einem oder zwei Kreiseln/Eckrädern
+- Erstellt spezielle Blöcke für direkte Kreisel-Verbindungen
+- Berücksichtigt horizontale Ausrichtung bei Eckrad-Verbindungen
+- Fügt Gefällestrecken ein, wenn mit Kreiseln verbunden
+
+### 4. ILS 2.0 Kurve angetrieben
+
+**Handler:** `handle_ils_2_0_kurve_angetrieben()`
+
+**Funktionalität:**
+
+- Erstellt eine angetriebene Kurve (Förderer-Kurve)
+- Verwendet Blocknamen basierend auf Kurvenrichtung, Winkel und Antrieb
+
+**Merkmale:**
+
+- `Kurvenrichtung`: Links oder Rechts
+- `Kurvenwinkel`: Winkel der Kurve in Grad
+- `Antrieb`: TEF-Kurve oder ähnlich
+- `Höhe Anfang`, `Höhe Ende`: Höhen der Endpunkte
+
+### 5. ILS 2.0 Kurve (Gefälle-Kurve)
+
+**Handler:** `handle_ils_2_0_kurve()`
+
+**Funktionalität:**
+
+- Erstellt eine Gefälle-Kurve
+- Verwendet Blocknamen basierend auf Kurvenrichtung und Winkel
+
+**Merkmale:**
+
+- `Kurvenrichtung`: Links oder Rechts
+- `Kurvenwinkel`: Winkel der Kurve in Grad
+- `Höhe Anfang`, `Höhe Ende`: Höhen der Endpunkte
+
+### 6. ILS 2.0 Eckrad
+
+**Handler:** `handle_ils_2_0_eckrad()`
+
+**Funktionalität:**
+
+- Erstellt ein Eckrad basierend auf Drehrichtung
+- Verwendet unterschiedliche Blöcke für UZS/GUZS
+
+**Merkmale:**
+
+- `Drehrichtung`: UZS oder GUZS
+- `Höhe`: Höhe des Eckrads
+
+### 7. BT Elemente (Beladung/Entladung)
+
+**Handler:** `handle_bt___beladung()`, `handle_bt___entladung()`
+
+**Funktionalität:**
+
+- Erstellt BT-Elemente für Beladung oder Entladung
+- Verwendet Standard-Block "AN8"
+
+**Merkmale:**
+
+- `Höhe`: Höhe des Elements
+- `Drehung`: Rotation
+
+### 8. Omniflo
+
+**Handler:** `handle_omniflo()`
+
+**Funktionalität:**
+
+- Erstellt Omniflo-Komponenten basierend auf SivasNummer
+- Unterstützt Geraden, Förderer und spezielle Blöcke
+- Verwendet Blocknamen direkt aus der SivasNummer
+
+**Merkmale:**
+
+- `SivasNummer`: Identifikation des Omniflo-Blocks
+- `Höhe`: Höhe des Elements
+- `Drehung`: Rotation
+- `Länge`: Länge (für Geraden)
+
+**Besonderheiten:**
+
+- Spezielle Behandlung für Geraden (SivasNummer aus Config)
+- Erstellt Linien für Geraden statt Blöcke
+- Unterstützt Förderer-Komponenten
+
+## CSV-Dateiformat
+
+Die CSV-Datei muss folgende Spalten enthalten (Semikolon-getrennt):
+
+```csv
+Elementnummer;TeileArt;TeileId;NachbarIds;Bezeichnung;Planquadrat;Merkmale
+```
+
+**Spalten:**
+
+- **Elementnummer**: Fortlaufende Nummer
+- **TeileArt**: Typ der Komponente (muss exakt mit Config übereinstimmen)
+- **TeileId**: Eindeutige ID (z.B. "shape_...")
+- **NachbarIds**: Kommagetrennte Liste von IDs benachbarter Elemente
+- **Bezeichnung**: Beschreibung des Elements
+- **Planquadrat**: Koordinaten im Format `X: Y:` (in mm)
+- **Merkmale**: JSON-String mit spezifischen Eigenschaften
+
+**Beispiel:**
+
+```csv
+1;"ILS 2.0 Kreisel";"shape_f81e5c4b-a976-a3c0-3304-d2b30da1ab29";"shape_d76e250a-0a46-f6d0-df52-943ab572cc63";"Kreisel:1";"X:9174.15 Y:12039.11";{"Abstand (Kreiselachse A - Kreiselachse) in Meter":"20.265","Anzahl der Separatoren":"2","Anzahl der Scanner":"0","Höhe in m":"2"}
+```
+
+## Verwendung
+
+### Kommandozeilen-Syntax
+
+```bash
+python plant2dxf.py -f [Optionen]
+```
+
+**Optionen:**
+
+- `-f, --file`: CSV-Datei (Pfad oder Dateiname im WORK-Verzeichnis) **[erforderlich]**
+- `-c, --config`: Pfad zur shapes.cfg (Standard: `PROJECT_CFG/shapes.cfg`)
+- `-l, --lib`: Pfad zur DXF-Bibliothek (Standard: `PROJECT_DATA/blocks.dxf`)
+- `-o, --output`: Ausgabe-DXF-Datei (Standard: `.dxf` im WORK-Verzeichnis)
+- `-v, --verbose`: Ausführliche Ausgaben
+
+**Beispiele:**
+
+```bash
+# Einfache Verwendung
+python plant2dxf.py -f anlage.csv
+
+# Mit expliziten Pfaden
+python plant2dxf.py -f anlage.csv -c cfg/shapes.cfg -l data/blocks.dxf -o output.dxf
+
+# Mit verbose-Ausgabe
+python plant2dxf.py -f anlage.csv -v
+```
+
+### Batch-Datei (Windows)
+
+Verwende `bin/plant2dxf.bat` für einfache Ausführung:
+
+```batch
+plant2dxf.bat
+```
+
+## Verarbeitungslogik
+
+### 1. Initialisierung
+
+1. Laden der Konfigurationsdateien (`shapes.cfg`, `allgemein.cfg`)
+2. Erstellen einer neuen DXF-Datei (DXF R2018, Einheit: Millimeter)
+3. Laden der Block-Bibliotheken (mit Caching)
+
+### 2. CSV-Verarbeitung
+
+Für jede Zeile in der CSV-Datei:
+
+1. **Parsen der CSV-Zeile:**
+ - Extraktion von TeileArt, TeileId, Koordinaten, Merkmalen
+ - Parsen des Planquadrats zu X/Y-Koordinaten
+ - Parsen der Merkmale (JSON)
+
+2. **Nachbar-Informationen:**
+ - Analyse der NachbarIds
+ - Bestimmung der Verbindungen zu Kreiseln, Kurven, Eckrädern
+ - Berechnung von Drehrichtungen, Höhen, Abständen
+
+3. **Bibliothekszuordnung:**
+ - Bestimmung der Bibliotheksdatei aus `allgemein.cfg`
+ - Laden der Bibliothek (mit Cache)
+
+4. **Handler-Aufruf:**
+ - Normalisierung des TeileArt-Namens zu Funktionsname
+ - Aufruf der entsprechenden `handle_*`-Funktion
+ - Erstellung der DXF-Elemente
+
+### 3. Element-Erstellung
+
+Jeder Handler:
+
+- Erstellt Objekte aus den Element-Klassen (`Kreisel`, `Gefaellestrecke`, etc.)
+- Analysiert Verbindungen zu Nachbarn
+- Erstellt dynamische Blöcke basierend auf Konfiguration
+- Fügt Blöcke, Linien und andere DXF-Entitäten ein
+- Setzt Attribute (z.B. TeileId)
+
+### 4. Speicherung
+
+- Speicherung der DXF-Datei im angegebenen Ausgabepfad
+- Logging der Verarbeitung
+
+## Wichtige Module und Klassen
+
+### Element-Klassen (`lib/Elemente/`)
+
+- **Kreisel**: Repräsentiert einen Kreisel mit allen Eigenschaften
+- **Gefaellestrecke**: Repräsentiert eine Gefällestrecke
+- **VarioFoerderer**: Repräsentiert einen Vario-Förderer
+- **Angetriebene_Kurve**: Repräsentiert eine angetriebene Kurve
+- **Eckrad**: Repräsentiert ein Eckrad
+- **Bt_element**: Repräsentiert BT-Elemente
+- **Omniflo**: Repräsentiert Omniflo-Komponenten
+
+### Hilfsmodule
+
+- **arbeiten_mit_csv.py**: CSV-Parsing, Koordinaten-Extraktion, Nachbar-Analyse
+- **block_methoden.py**: Block-Import, Rotation, Layer-Verwaltung
+- **as_es_methoden.py**: AS/ES-Element-Erstellung, Höhen-Vertauschung
+- **utils.py**: Umgebungsvariablen, Logger-Setup
+
+## Wichtige ezdxf-Methoden
+
+Das Skript verwendet verschiedene Methoden der `ezdxf`-Bibliothek zur Erstellung und Manipulation von DXF-Elementen. Die wichtigsten Methoden sind:
+
+### 1. Block-Referenz in Block einfügen
+
+```python
+block.add_blockref(blockname, (x, y, z), dxfattribs={"rotation": rotation, "layer": layer, "color": color})
+```
+
+**Beschreibung:** Fügt eine Block-Referenz (INSERT) in einen bestehenden Block ein.
+
+**Parameter:**
+
+- `blockname` (str): Name des Blocks, der eingefügt werden soll
+- `(x, y, z)` (tuple): Koordinaten der Einfügeposition (3D-Tupel)
+- `dxfattribs` (dict): Dictionary mit DXF-Attributen:
+ - `rotation` (float): Rotationswinkel in Grad
+ - `layer` (str): Layer-Name
+ - `color` (int): Farbe (optional)
+
+**Verwendung:** Wird verwendet, um Blöcke innerhalb von anderen Blöcken zu platzieren (z.B. Motorstationen in Gefällestrecken-Blöcken).
+
+**Beispiel:**
+
+```python
+block.add_blockref("Vario_Motorstation_500mm", (0, 250, 0), dxfattribs={"rotation": 270, "layer": "VARIO"})
+```
+
+### 2. Block-Referenz ins Modelspace einfügen
+
+```python
+msp.add_blockref(blockname, (x, y, z), dxfattribs={"rotation": rotation, "layer": layer, "color": color})
+```
+
+**Beschreibung:** Fügt eine Block-Referenz direkt in den Modelspace (die Hauptzeichnung) ein.
+
+**Parameter:**
+
+- `blockname` (str): Name des Blocks, der eingefügt werden soll
+- `(x, y, z)` (tuple): Koordinaten der Einfügeposition (3D-Tupel)
+- `dxfattribs` (dict): Dictionary mit DXF-Attributen (siehe oben)
+
+**Verwendung:** Wird verwendet, um fertige Blöcke in die Zeichnung zu platzieren. Dies ist der häufigste Weg, um Elemente in die DXF-Datei einzufügen.
+
+**Beispiel:**
+
+```python
+msp.add_blockref("ILS_2.0_Gefaellestrecke_8200_2000_UZS_higher",
+ (x, y, hoehe_gefaehlle),
+ dxfattribs={"rotation": rotation, "layer": "6-SP"})
+```
+
+### 3. Neuen Block erstellen
+
+```python
+block = doc.blocks.new(name=blockname, base_point=(0, 0, 0))
+```
+
+**Beschreibung:** Erstellt einen neuen Block im DXF-Dokument.
+
+**Parameter:**
+
+- `name` (str): Name des neuen Blocks (muss eindeutig sein)
+- `base_point` (tuple): Basis-Punkt des Blocks (meist (0, 0, 0))
+
+**Rückgabewert:** Block-Objekt, das verwendet werden kann, um Entitäten hinzuzufügen
+
+**Wichtiger Hinweis:** Ein Block mit demselben Namen kann nur einmal erstellt werden. Vor der Erstellung sollte geprüft werden:
+
+```python
+if blockname not in doc.blocks:
+ block = doc.blocks.new(name=blockname, base_point=(0, 0, 0))
+```
+
+**Verwendung:** Wird verwendet, um dynamische Blöcke zu erstellen, die aus mehreren Komponenten bestehen (z.B. Gefällestrecken mit Motorstationen, Vario-Förderer mit verschiedenen Konfigurationen).
+
+**Beispiel:**
+
+```python
+blockname = f"Ils_2.0_Gefaellestrecke_{laenge}_{hoehe_gefaehlle}_{hat_motor_0}_{hat_umlenk_0}"
+if blockname not in doc.blocks:
+ block = doc.blocks.new(name=blockname, base_point=(0, 0, 0))
+ # Füge Komponenten zum Block hinzu
+ block.add_blockref("Vario_Motorstation_500mm", (0, 0, 0))
+ line = Line.new(dxfattribs={"start": start, "end": ende})
+ line.translate(-x, -y, -hoehe_gefaehlle)
+ block.add_entity(line)
+```
+
+### 4. DXF-Objekte aus Modelspace abfragen
+
+```python
+entities = msp.query("INSERT")
+```
+
+**Beschreibung:** Holt alle spezifischen DXF-Objekte vom Modelspace.
+
+**Parameter:**
+
+- `"INSERT"` (str): DXF-Typ des Objekts (z.B. "INSERT" für Block-Referenzen, "LINE" für Linien, "CIRCLE" für Kreise)
+
+**Rückgabewert:** Liste von DXF-Entitäten des angegebenen Typs
+
+**Verwendung:** Wird verwendet, um bestimmte Objekte aus der Zeichnung zu finden oder zu analysieren. Meistens wird dies für INSERT-Objekte (Block-Referenzen) verwendet.
+
+**Beispiel:**
+
+```python
+# Alle Block-Referenzen im Modelspace finden
+inserts = msp.query("INSERT")
+for insert in inserts:
+ print(f"Block: {insert.dxf.name} an Position {insert.dxf.insert}")
+```
+
+**Weitere DXF-Typen:**
+
+- `"LINE"`: Linien
+- `"CIRCLE"`: Kreise
+- `"ARC"`: Bögen
+- `"TEXT"`: Text
+- `"ATTDEF"`: Attribut-Definitionen
+- `"INSERT"`: Block-Referenzen
+
+### 5. Weitere wichtige ezdxf-Methoden
+
+#### Linie erstellen und hinzufügen
+
+```python
+# Linie direkt im Modelspace
+line = msp.add_line(start=(x1, y1, z1), end=(x2, y2, z2))
+line.dxf.layer = "6-SP"
+
+# Linie in Block erstellen
+line = Line.new(dxfattribs={"start": (x1, y1, z1), "end": (x2, y2, z2)})
+line.translate(-x, -y, -z) # Relativ zum Block-Ursprung verschieben
+block.add_entity(line)
+```
+
+#### Layer erstellen
+
+```python
+if "LAYER_NAME" not in doc.layers:
+ doc.layers.add(name="LAYER_NAME", color=7)
+```
+
+#### Block-Attribute hinzufügen
+
+```python
+bref = msp.add_blockref(blockname, (x, y, z))
+bref.add_attrib(tag="NAME", text="Bezeichnung", insert=(x, y))
+bref.add_auto_attribs({ATTR_TAG: teileid}) # Automatische Attribute aus Block-Definition
+```
+
+#### Entität transformieren
+
+```python
+entity.translate(dx, dy, dz) # Verschieben
+entity.rotate_z(angle) # Rotation um Z-Achse
+entity.scale(factor) # Skalierung
+```
+
+## Konstante Parameter
+
+- **ATTR_TAG**: `"TeileId"` - Attributtag im Block
+- **RADIUS**: `400` - Radius der Kreiselkreise (in mm)
+
+## Layer-Verwaltung
+
+Das Skript erstellt automatisch folgende Layer:
+
+- **VARIO**: Für Vario-Förderer (Farbe: 3)
+- **6-SP**: Für Gefällestrecken (Farbe: 7)
+- Weitere Layer werden aus den Bibliotheks-Blöcken übernommen
+
+## Fehlerbehandlung
+
+- Fehlende Umgebungsvariablen führen zum Programmabbruch
+- Fehlende Bibliotheksdateien werden geloggt, Verarbeitung wird fortgesetzt
+- Fehlerhafte CSV-Zeilen werden übersprungen (mit Warnung)
+- Fehlende Handler für TeileArt werden geloggt, Element wird übersprungen
+
+## Logging
+
+- Log-Dateien werden in `PROJECT_LOG/` gespeichert
+- Format: `plant2dxf_YYYYMMDD_HHMMSS.log`
+- Logging erfolgt sowohl in Datei als auch auf Konsole
+- Format: `%(asctime)s - %(levelname)s - %(message)s`
+
+## Detaillierte Funktionsbeschreibungen
+
+### NachbarId-Entnahme und -Analyse
+
+Die Funktion `get_nachbar_information()` in `arbeiten_mit_csv.py` analysiert die CSV-Datei und erstellt ein Dictionary mit allen Nachbar-Informationen für Gefällestrecken und Vario-Förderer.
+
+#### Ablauf der Nachbar-Analyse
+
+1. **Erste Durchlauf - Sammeln aller Elemente:**
+ - **Gefällestrecken**: Speichert ID und NachbarIds
+ - **Kreisel**: Erstellt Kreisel-Objekte und speichert:
+ - `drehung`: Drehrichtung (UZS/GUZS)
+ - `höhe`: Höhe des Kreisels
+ - `x`, `y`: Koordinaten
+ - `rotation`: Rotationswinkel
+ - `abstand`: Abstand zwischen Kreiselachsen (in Metern)
+ - **Vario-Förderer**: Speichert ID, NachbarIds, Winkel, Höhen, Förderrichtung, Koordinaten
+ - **Angetriebene Kurven**: Speichert ID, Höhen, Kurvenrichtung, TEF-Kurve-Status, Winkel, Koordinaten
+ - **Eckräder**: Speichert ID, Höhe, Koordinaten
+
+2. **Zweiter Durchlauf - Zuordnung der Nachbarn:**
+ Für jede Gefällestrecke oder Vario-Förderer:
+
+ **a) Angetriebene Kurven (TEF-Kurven):**
+ - Prüft, ob Kurven-IDs in den NachbarIds enthalten sind
+ - Speichert für erste Kurve (`voerder_anweisung == 0`):
+ - `X_angetrieben`, `Y_angetrieben`: Koordinaten
+ - `vario_hoehe_0`, `vario_hoehe_1`: Höhen der Kurve
+ - `Kurvenrichtung`: Links oder Rechts
+ - `Tefkurve`: Antriebsart (außen/innen)
+ - `Kurvenwinkel`: Winkel der Kurve
+ - Speichert für zweite Kurve (`voerder_anweisung == 1`):
+ - Gleiche Felder mit Suffix `_1`
+
+ **b) Eckräder:**
+ - Prüft, ob Eckrad-IDs in den NachbarIds enthalten sind
+ - Speichert für erstes Eckrad (`eckrad_anweisung == 0`):
+ - `Eckrad_x`, `Eckrad_y`: Koordinaten
+ - `Eckrad_höhe`: Höhe des Eckrads
+ - Speichert für zweites Eckrad (`eckrad_anweisung == 1`):
+ - Gleiche Felder mit Suffix `_1`
+
+ **c) Kreisel:**
+ - Prüft, ob Kreisel-IDs in den NachbarIds enthalten sind
+ - Speichert für ersten Kreisel (`anweisungen == 0`):
+ - `Drehung0`: Drehrichtung (UZS/GUZS)
+ - `Hoehe0`: Höhe des Kreisels
+ - `x0`, `y0`: Koordinaten
+ - `rotation0`: Rotationswinkel
+ - `abstand0`: Abstand zwischen Kreiselachsen (in Metern)
+ - Speichert für zweiten Kreisel (`anweisungen == 1`):
+ - Gleiche Felder mit Suffix `1` statt `0`
+
+ **d) Vario-Förderer (nur für Vario-Förderer):**
+ - Prüft, ob andere Vario-Förderer in den NachbarIds enthalten sind
+ - Speichert für ersten Vario-Förderer (`geraden_anweisung == 0`):
+ - `X_foerderer`, `Y_foerderer`: Koordinaten
+ - `Winkel`: Winkel des Förderers
+ - `h0`, `h1`: Höhen
+ - `Foerderrichtung`: Auf/Ab/Horizontal
+ - Speichert für zweiten Vario-Förderer (`geraden_anweisung == 1`):
+ - Gleiche Felder mit Suffix `_2`
+
+#### Verwendung der Nachbar-Informationen
+
+Die Nachbar-Informationen werden verwendet für:
+
+- **Gefällestrecken**: Bestimmung der AS/ES-Elemente, Motor/Umlenkstationen, direkte Kreisel-Verbindungen
+- **Vario-Förderer**: Bestimmung der Verbindungsart, Gefälle-Komponenten, Offset-Berechnungen
+
+### Gefällestrecke mit Motor/Umlenkstation - Detaillierte Erstellung
+
+Die Erstellung einer Gefällestrecke mit Motor- oder Umlenkstation erfolgt in mehreren Schritten:
+
+#### 1. Prüfung auf Motor/Umlenkstation-Bedarf
+
+Die Methode `hat_motor_umlenk_station()` prüft, ob eine Gefällestrecke eine Motor- oder Umlenkstation benötigt:
+
+**Logik:**
+
+- Prüft, ob `Kurvenrichtung` in den Nachbar-Informationen vorhanden ist (d.h. verbunden mit angetriebener Kurve)
+- Bestimmt `tefkurve_0` basierend auf Kurvenrichtung und Antriebsart:
+ - `kurvenrichtung == "links"` und `Tefkurve == "außen"` → `tefkurve_0 = "rechts"`
+ - `kurvenrichtung == "rechts"` und `Tefkurve == "innen"` → `tefkurve_0 = "rechts"`
+ - Sonst → `tefkurve_0 = "links"`
+
+**Höhen-basierte Bestimmung:**
+
+- Wenn `upper_hoehe_gefaehlle > lower_hoehe_gefaehlle`:
+ - Wenn `vario_hoehe_0` oder `vario_hoehe_1 == upper_hoehe_gefaehlle` → `hat_motor_0 = True`
+ - Sonst → `hat_umlenk_0 = True`
+- Wenn `upper_hoehe_gefaehlle < lower_hoehe_gefaehlle`:
+ - Wenn `vario_hoehe_0` oder `vario_hoehe_1 == lower_hoehe_gefaehlle` → `hat_motor_0 = True`
+ - Sonst → `hat_umlenk_0 = True`
+
+**Rotations-basierte Bestimmung (bei gleicher Höhe):**
+
+- Wenn beide Höhen gleich sind, wird die Position relativ zur Kurve geprüft
+- Basierend auf Rotation und Koordinaten wird entschieden:
+ - `hat_umlenk_0 = True` + `umlenk_gerade = True` (wenn bestimmte Bedingungen erfüllt)
+ - Oder `hat_motor_0 = True` + `motor_gerade = True`
+
+#### 2. Erstellung des Blocks mit Motor/Umlenkstation
+
+Die Methode `ein_motor_oder_eine_umlenk()` erstellt die Motor- oder Umlenkstation im Block:
+
+**Schritte:**
+
+1. **Import der Vario-Bögen:**
+ - `Vario_Bogen_auf_3°`: Bogen für Aufwärts-Bewegung
+ - `Vario_Bogen_ab_3°`: Bogen für Abwärts-Bewegung
+ - Erstellung der Links-Versionen durch `turn_two_blocks_left()`
+
+2. **Auslesen der Delta-Werte:**
+ - `DELTA_SP_0`, `DELTA_SP_1`: Delta-Werte für Startpunkt
+ - `DELTA_VP_0`, `DELTA_VP_1`: Delta-Werte für Verbindungspunkt
+ - Negative Werte werden in positive umgewandelt
+
+3. **Motor-Station einfügen (`hat_motor_0 == True`):**
+ - **Wenn `tefkurve_0 == "links"`:**
+ - Wenn `motor_gerade == False`:
+ - Fügt `Vario_Bogen_ab_links` ein (Rotation 270°)
+ - Berechnet neuen Startpunkt basierend auf Delta-Werten
+ - Fügt `blockname_motor_links` ein (250mm Offset)
+ - Verschiebt Startpunkt um 500mm * cos(3°) und sin(3°)
+ - Wenn `motor_gerade == True`:
+ - Fügt `Vario_Motorstation_500mm_links` direkt ein
+ - Verschiebt Startpunkt um 500mm
+ - **Wenn `tefkurve_0 == "rechts"`:**
+ - Gleiche Logik mit rechts-Versionen der Blöcke
+
+4. **Umlenk-Station einfügen (`hat_umlenk_0 == True`):**
+ - **Wenn `tefkurve_0 == "links"`:**
+ - Wenn `umlenk_gerade == False`:
+ - Fügt `Vario_Bogen_auf` ein (Rotation 90°)
+ - Berechnet neuen Endpunkt basierend auf Delta-Werten
+ - Fügt `blockname_umlenk_links` ein (250mm Offset)
+ - Verschiebt Endpunkt um 500mm * cos(3°) und sin(3°)
+ - Wenn `umlenk_gerade == True`:
+ - Fügt `Vario_Umlenkstation_500mm_links` direkt ein
+ - Verschiebt Endpunkt um 500mm
+ - **Wenn `tefkurve_0 == "rechts"`:**
+ - Gleiche Logik mit rechts-Versionen der Blöcke
+
+5. **Rückgabe:**
+ - Gibt modifizierte `start` und `ende` Koordinaten zurück
+
+#### 3. Blockname-Generierung
+
+Der Blockname wird dynamisch generiert:
+
+```python
+Ils_2.0_Gefaellestrecke_{laenge}_{hoehe_gefaehlle}_{hat_umlenk_0}_{hat_motor_0}_{tefkurve_0}_{umlenk_gerade}_{motor_gerade}
+```
+
+#### 4. Block-Erstellung
+
+1. Prüft, ob Block bereits existiert (Caching)
+2. Erstellt neuen Block mit Basis-Punkt (0,0,0)
+3. Ruft `ein_motor_oder_eine_umlenk()` auf, um Motor/Umlenkstation einzufügen
+4. Erstellt Linie zwischen modifiziertem Start- und Endpunkt
+5. Verschiebt Linie relativ zum Block-Ursprung
+6. Fügt Block-Referenz in Modelspace ein
+
+### Vario-Förderer ohne Verbindung - Detaillierte Erstellung
+
+Wenn ein Vario-Förderer **nicht** mit einem Kreisel oder Eckrad verbunden ist, wird der einfachste Fall behandelt:
+
+#### 1. Vorbereitung
+
+```python
+halbe_laenge = laenge / 2
+dy = halbe_laenge * math.cos(0) # dy = halbe_laenge (da cos(0) = 1)
+```
+
+#### 2. Blockname-Generierung
+
+**Rechts-Version:**
+
+```python
+Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}
+```
+
+**Links-Version:**
+
+```python
+Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}
+```
+
+**Parameter:**
+
+- `winkel`: Winkel des Förderers (z.B. 3, 6, 9)
+- `voerder_richtung`: Auf, Ab oder Horizontal
+- `laenge`: Länge in mm
+- `hoehe_vario`: Mittlere Höhe ((h0 + h1) / 2)
+- `motor_vorhanden`: True/False
+- `umlenk_vorhanden`: True/False
+- `gefaelle`: Länge der Gefällestrecke (falls vorhanden)
+- `gefahellewinkel`: Winkel der Gefällestrecke (falls vorhanden)
+
+#### 3. Block-Erstellung
+
+1. **Prüfung auf existierenden Block:**
+ - Wenn Block bereits existiert, wird nur Block-Referenz eingefügt
+ - Auswahl zwischen Links- und Rechts-Version basierend auf `Motorseite`
+
+2. **Neuer Block wird erstellt:**
+
+ ```python
+ block = doc.blocks.new(blockname, base_point=(0,0,0))
+ ```
+
+3. **Start- und Endpunkt berechnen:**
+
+ ```python
+ start = (x, y + dy, upper_hoehe_vario)
+ ende = (x, y - dy, lower_hoehe_vario)
+ ```
+
+ - Startpunkt: Mitte + halbe Länge in Y-Richtung, obere Höhe
+ - Endpunkt: Mitte - halbe Länge in Y-Richtung, untere Höhe
+
+4. **Vario-Erstellung aufrufen:**
+
+ ```python
+ VarioFoerderer.VarioFoerderer.vario_erstellung(
+ foerderer, doc, lib_doc, config,
+ block, block_name_links,
+ start, ende,
+ voerder_richtung,
+ winkel_VP_offset_vorne, # None, da keine Verbindung
+ winkel_VP_offset_hinten # None, da keine Verbindung
+ )
+ ```
+
+#### 4. Vario-Erstellung (`vario_erstellung()`)
+
+**Schritte:**
+
+1. **Konfiguration auslesen:**
+ - `winkel_motor`: Winkel für Motorstation (aus Config, z.B. 3.0°)
+ - `winkel_umlenk`: Winkel für Umlenkstation (aus Config, z.B. 3.0°)
+ - `umlenk_laenge`: Länge der Umlenkstation (aus Config, z.B. 500.0mm)
+ - `motor_laenge`: Länge der Motorstation (aus Config, z.B. 500.0mm)
+ - `vario_abstand`: Abstand zwischen Vario-Komponenten (aus Config, z.B. 66.5mm)
+
+2. **Offset-Berechnung:**
+
+ ```python
+ motor_offset_x = umlenk_laenge[0] * math.cos(math.radians(winkel_motor))
+ motor_offset_z = umlenk_laenge[0] * math.sin(math.radians(winkel_motor))
+ umlenk_offset_x = motor_laenge[0] * math.cos(math.radians(winkel_umlenk))
+ umlenk_offset_z = motor_laenge[0] * math.sin(math.radians(winkel_umlenk))
+ ```
+
+3. **Gefälle-Länge anpassen:**
+ - Wenn Motor vorhanden: `gefaelle = gefaelle - motor_offset_x`
+ - Wenn Umlenk vorhanden: `gefaelle = gefaelle - umlenk_offset_x`
+
+4. **Förderrichtung "Auf" oder "Horizontal":**
+ - Erstellt Vario-Komponenten von Start zu Ende
+ - Fügt Motorstation am Start ein (falls vorhanden)
+ - Fügt Gefälle-Komponenten ein (falls vorhanden)
+ - Fügt Umlenkstation am Ende ein (falls vorhanden)
+
+5. **Förderrichtung "Ab":**
+ - Erstellt Vario-Komponenten von Ende zu Start
+ - Fügt Umlenkstation am Ende ein (falls vorhanden)
+ - Fügt Gefälle-Komponenten ein (falls vorhanden)
+ - Fügt Motorstation am Start ein (falls vorhanden)
+
+6. **Links-Version erstellen:**
+ - Erstellt gespiegelte Version des Blocks für Links-Motorseite
+ - Verwendet `turn_two_blocks_left()` für Spiegelung
+
+#### 5. Block-Referenz einfügen
+
+Basierend auf `Motorseite` aus Merkmalen:
+
+- `Motorseite == "links"` → Fügt `block_name_links` ein
+- `Motorseite == "rechts"` → Fügt `blockname` ein
+
+**Position:** `(x, y, hoehe_vario)`
+**Rotation:** `rotation` aus Merkmalen
+
+## Erweiterbarkeit
+
+### Neue TeileArt hinzufügen
+
+1. **Config hinzufügen** (`shapes.cfg`):
+
+ ```ini
+ [Neue TeileArt]
+ items = Block1, Block2
+ offset_symb1 = 0,0
+ rot_symb1 = 0.0
+ ```
+
+2. **Handler-Funktion erstellen** (`plant2dxf.py`):
+
+ ```python
+ def handle_neue_teileart(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols, strecken_nachbarn, config, config_allgemein):
+ """Erstellt eine neue TeileArt in der neuen Dxf"""
+ # Implementierung
+ ```
+
+3. **Bibliothekszuordnung** (`allgemein.cfg`):
+
+ ```ini
+ [Neue TeileArt]
+ libfile = bibliothek.dxf
+ ```
+
+4. **Element-Klasse** (optional, `lib/Elemente/`):
+ - Falls komplexe Logik benötigt wird, eigene Klasse erstellen
+
+## Abhängigkeiten
+
+Siehe `lib/requirements.txt`:
+
+- `ezdxf==1.4.1`: DXF-Bibliothek
+- `svg.path==7.0`: SVG-Pfad-Verarbeitung
+- `pydantic>=2.0.0`: Datenvalidierung
+
+## Bekannte Einschränkungen
+
+- CSV muss UTF-8 kodiert sein
+- Koordinaten müssen im Format `X: Y:` vorliegen
+- Bibliotheksdateien müssen im DXF-Format vorliegen
+- Blocknamen müssen exakt mit Bibliothek übereinstimmen
diff --git a/lib/as_es_methoden.py b/lib/as_es_methoden.py
index 57dcb4d..beb1134 100644
--- a/lib/as_es_methoden.py
+++ b/lib/as_es_methoden.py
@@ -4,7 +4,10 @@ import plant2dxf
import block_methoden
RADIUS = 400
-def vertausch_der_höhe(objekt, upper_hoehe_objekt, lower_hoehe_objekt, rotation):
+def vertausch_der_höhe(objekt):
+ upper_hoehe_objekt = objekt.h1
+ lower_hoehe_objekt = objekt.h0
+ rotation = objekt.drehung
"""Vertausch der Höhen, für ausrechnung blöcke mit as elemente oder andere Berücksichtigungen"""
if upper_hoehe_objekt < lower_hoehe_objekt:
hoehe2 = upper_hoehe_objekt
@@ -16,7 +19,7 @@ def vertausch_der_höhe(objekt, upper_hoehe_objekt, lower_hoehe_objekt, rotation
objekt.drehung = rotation
objekt.h1 = upper_hoehe_objekt
objekt.h0 = lower_hoehe_objekt
- return upper_hoehe_objekt,lower_hoehe_objekt,rotation
+
def erstellung_gefaelle_block_verbunenden_am_einen(msp,x, y, doc, lib_doc, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, hoehe_gefaehlle, drehung0, laenge,blockname,config, hight = None, block_vario = None, vario_richtung = None, verbunden_höher = None,gefaelle_block=None,start = None,ende = None,mit_horizontal_verbunden = None,as_es_rotation= None):
"""Tut ein as/es element in den Block für gefällestrecken wird auch überprüft ob nur ein as es element vorhanden ist"""
halbe_laenge = laenge / 2
diff --git a/lib/plant2dxf.py b/lib/plant2dxf.py
index 0e8b204..54dc5b7 100644
--- a/lib/plant2dxf.py
+++ b/lib/plant2dxf.py
@@ -118,8 +118,11 @@ def handle_ils_2_0_gefaellestrecke(msp, teileid, merkmale, x, y, doc, lib_doc, v
abstand0 = float(gefaellestrecke_nachbarn.get("abstand0")) * 1000
#Austauch der höhen und dementsprechende Koorekur falls nötig, dieser Schritt ist notwendig für die Konsistzenz der Blockerstellung
- upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, rotation = as_es_methoden.vertausch_der_höhe(gefaelle_objekt, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, rotation)
+ as_es_methoden.vertausch_der_höhe(gefaelle_objekt)
# Übrerprüfung ob die Gefällestrecke einen Motor oder Umlenkstation braucht, wenn es mit einer angetriebenen Kurve verbunden ist
+ upper_hoehe_gefaehlle = gefaelle_objekt.h1
+ lower_hoehe_gefaehlle = gefaelle_objekt.h0
+ rotation = gefaelle_objekt.drehung
block_Vario_Umlenkstation_500mm, block_Vario_Motorstation_500mm, blockname_motor_links, blockname_umlenk_links = block_methoden.rotatate_and_left_motor_umlenk(doc, lib_doc,config)
hat_zusatz = Gefaehllestrecke.Gefaellestrecke.hat_motor_umlenk_station (gefaelle_objekt, gefaellestrecke_nachbarn)
hat_motor_0 = hat_zusatz.get("hat_motor_0")
@@ -291,7 +294,10 @@ def handle_ils_2_0_gefaellestrecke(msp, teileid, merkmale, x, y, doc, lib_doc, v
# Behandlung falls es mit einer odr zwei Vario kurve verbunden ist
if blockname not in doc.blocks:
rotation= float(merkmale.get("Drehung"))
- upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, rotation = as_es_methoden.vertausch_der_höhe(gefaelle_objekt, upper_hoehe_gefaehlle, lower_hoehe_gefaehlle, rotation)
+ as_es_methoden.vertausch_der_höhe(gefaelle_objekt)
+ upper_hoehe_gefaehlle = gefaelle_objekt.h1
+ lower_hoehe_gefaehlle = gefaelle_objekt.h0
+ rotation = gefaelle_objekt.drehung
block = doc.blocks.new(name=blockname,base_point = (0,0,0))
dy = halbe_laenge * math.cos(0)
start = [x , y + dy ,upper_hoehe_gefaehlle]
@@ -332,8 +338,10 @@ def handle_ils_2_0_variofoerderer(msp, teileid, merkmale, x, y, doc, lib_doc, ve
lower_hoehe_vario = foerderer.h0
hoehe_vario= foerderer.hight_zwischen
anzahl_seperatoren_oder_scan(msp, x, y, doc, lib_doc, foerderer, hoehe_vario, rotation)
- upper_hoehe_vario, lower_hoehe_vario, rotation = as_es_methoden.vertausch_der_höhe(foerderer, upper_hoehe_vario, lower_hoehe_vario, rotation)
-
+ as_es_methoden.vertausch_der_höhe(foerderer)
+ upper_hoehe_vario = foerderer.h1
+ lower_hoehe_vario = foerderer.h0
+ rotation = foerderer.drehung
# Hollen der Information der Nachbarn strukturen ob diese Kreisel sind
for nachbarn in strecken_nachbarn:
if teileid == nachbarn.get("Id"):