Files
plant2dxf/doc/plant2dxf_dokumentation.md
T

978 lines
32 KiB
Markdown

# plant2dxf - Dokumentation
## ⚠️ Wichtiger Hinweis
**Zukünftige Anpassungen erforderlich:**
Aufgrund möglicher Änderungen im CSV-Dateiformat von RuleDesigner müssen in Zukunft möglicherweise folgende Berechnungen und Logiken angepasst werden:
### AS/ES-Element-Berechnungen
Die Logik zur Bestimmung und Platzierung von AS- und ES-Elementen bei Gefällestrecken und Vario-Förderern basiert auf dem aktuellen CSV-Format. Bei Änderungen der Datenstruktur oder der verfügbaren Informationen müssen folgende Methoden in `as_es_methoden.py` überprüft und angepasst werden:
- `erstellung_gefaelle_block_verbunenden_am_einen()`: Erstellt AS/ES-Elemente für Gefällestrecken, die mit einem Kreisel verbunden sind
- `gefaellegerade_erstellung()`: Erstellt AS/ES-Elemente für Gefällestrecken, die mit zwei Kreiseln verbunden sind
- `am_kreisel_direct_verbunden()`: Prüft, ob eine Gefällestrecke/Vario-Förderer direkt mit einem Kreisel verbunden ist (ohne Stahlband)
### Höhenvergleiche für Kreisel-Logik
Die Höhenvergleiche zur Bestimmung der Verbindungsart zwischen Gefällestrecken/Vario-Förderern und Kreiseln (z.B. ob die Gefällestrecke mit dem höheren oder niedrigeren Teil des Kreisels verbunden ist) basieren auf den aktuellen CSV-Datenfeldern. Änderungen in der Struktur der Höheninformationen erfordern Anpassungen in:
**Handler-Funktionen (`lib/plant2dxf.py`):**
- `handle_ils_2_0_gefaellestrecke()`: Haupt-Handler für Gefällestrecken
- `handle_ils_2_0_variofoerderer()`: Haupt-Handler für Vario-Förderer
**Gefällestrecke-Methoden (`lib/Elemente/Gefaehllestrecke.py`):**
- `rotation_mit_zwei_verbunden()`: Berechnet Rotation bei Verbindung mit zwei Kreiseln (verwendet Höhenvergleiche)
**Vario-Förderer-Methoden (`lib/Elemente/VarioFoerderer.py`):**
- `vario_verbuden_am_kreisel()`: Erstellt Gefällestrecken-Komponenten bei direkter Kreisel-Verbindung (verwendet Höhenvergleiche mit Kreiseln)
**Betroffene Dateien:**
- `lib/as_es_methoden.py`: AS/ES-Element-Berechnungen
- `lib/plant2dxf.py`: Handler-Funktionen für Gefällestrecken und Vario-Förderer
- `lib/Elemente/Gefaehllestrecke.py`: Höhenvergleiche und Verbindungslogik
- `lib/Elemente/VarioFoerderer.py`: Höhenvergleiche und Verbindungslogik
## Ü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:<Wert> Y:<Wert>`
- **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: `<PROJECT>/data`
- Enthält Unterverzeichnis `block_libraries/` mit DXF-Bibliotheken
- **PROJECT_WORK**: Arbeitsverzeichnis für CSV-Eingabedateien und DXF-Ausgabedateien
- Standard: `<PROJECT>/work`
- **PROJECT_CFG**: Verzeichnis für Konfigurationsdateien
- Standard: `<PROJECT>/cfg`
- Enthält: `allgemein.cfg`, `shapes.cfg`
- **PROJECT_LOG**: Verzeichnis für Log-Dateien
- Standard: `<PROJECT>/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:<Wert> Y:<Wert>` (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 <csv-datei> [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: `<csv-name>.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 <csv-datei>
```
## 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:<Wert> Y:<Wert>` vorliegen
- Bibliotheksdateien müssen im DXF-Format vorliegen
- Blocknamen müssen exakt mit Bibliothek übereinstimmen