Refactor und dokumentation

This commit is contained in:
2026-01-26 09:27:48 +01:00
parent 9e024869d4
commit 05f1adc1b4
4 changed files with 958 additions and 31 deletions
-25
View File
@@ -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
+941
View File
@@ -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:<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
+5 -2
View File
@@ -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
+12 -4
View File
@@ -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"):