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"):