Compare commits
10
Commits
c8760fb024
...
2325316dd3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2325316dd3 | ||
|
|
5bfda0abf6 | ||
|
|
200145eea7 | ||
|
|
c29f13a4ae | ||
|
|
e8c6b17ea9 | ||
|
|
85132193dd | ||
|
|
4aae9e153d | ||
|
|
855a874e4a | ||
|
|
829fe76d0f | ||
|
|
ab415071c2 |
@@ -0,0 +1,78 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## What this project does
|
||||||
|
|
||||||
|
`plant2dxf` reads a RuleDesigner CSV export describing all components of a plant/conveyor system (Kreisel, Gefällestrecke, VarioFörderer, Omniflo, etc.) and generates a DXF drawing of the plant by placing and assembling DXF blocks from library files. Positions come from the CSV's `Planquadrat` column (`X:<val> Y:<val>`, in mm); component-specific properties come from the `Merkmale` JSON column; connections between components come from `NachbarIds`.
|
||||||
|
|
||||||
|
Full functional documentation (CSV format, config format, per-element-type handler behavior, ezdxf usage patterns) lives in [doc/plant2dxf_dokumentation.md](doc/plant2dxf_dokumentation.md) — read it before making non-trivial changes to handler logic, it is much more detailed than this file.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
All commands are run via the `.bat` scripts in `bin/` (Windows only; this project has no `.sh` counterparts). Scripts call `setenv.bat` first, which sets `PROJECT`, `PROJECT_BIN`, `PROJECT_CFG`, `PROJECT_DOC`, `PROJECT_LIB`, `PROJECT_DATA`, `PROJECT_WORK`, `PROJECT_TEST`, `PROJECT_LOG` and creates missing `data/`, `work/`, `log/`, `testdata/` folders.
|
||||||
|
|
||||||
|
```bat
|
||||||
|
bin\install_py.bat REM create .venv and pip install lib/requirements.txt (no-op if .venv exists)
|
||||||
|
bin\manage_interpreter.bat activate REM activate .venv (used internally by other scripts)
|
||||||
|
bin\run_unittests.bat REM run all *_tests.py under lib\Elemente via unittest discover
|
||||||
|
bin\plant2dxf.bat -f <csv-datei> [-c shapes.cfg] [-l blocks.dxf] [-o output.dxf] [-v]
|
||||||
|
```
|
||||||
|
|
||||||
|
`run_unittests.bat` runs `python -m unittest discover -t "%PROJECT%" -s lib\Elemente -p "*_tests.py"` from the project root — tests must be discovered from repo root or the `lib.*` imports break. To run a single test file/case directly (with venv already active):
|
||||||
|
|
||||||
|
```bat
|
||||||
|
python -m unittest lib.Elemente.Kreisel_tests -v
|
||||||
|
python -m unittest lib.Elemente.Kreisel_tests.TestKreiselModell.test_from_merkmale -v
|
||||||
|
```
|
||||||
|
|
||||||
|
Dependencies (`lib/requirements.txt`): `ezdxf==1.4.1`, `svg.path==7.0`, `pydantic>=2.0.0`, plus `ruff` and `black` (line-length 88, target py312 — see `pyproject.toml`).
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Processing pipeline (`lib/plant2dxf.py`)
|
||||||
|
|
||||||
|
1. Load `cfg/shapes.cfg` and `cfg/allgemein.cfg`.
|
||||||
|
2. Create a new empty DXF document (R2018, millimeters).
|
||||||
|
3. `arbeiten_mit_csv.get_nachbar_information(csv_path)` does a full pre-pass over the CSV to build a neighbor map (`strecken_nachbarn`): for every Gefällestrecke/VarioFörderer it resolves which Kreisel/Eckrad/angetriebene Kurve/other VarioFörderer are attached via `NachbarIds`, and captures their rotation, height, position and drehrichtung. This neighbor map drives almost all the geometric decisions downstream.
|
||||||
|
4. Row-by-row over the CSV: parse `TeileArt`/`TeileId`/`Planquadrat`/`Merkmale`, resolve the block library file for that `TeileArt` from `allgemein.cfg` (cached per path so each library is read once), dispatch to a handler function named `handle_<teileart normalized>` (spaces/dots → underscores, lowercased — see `arbeiten_mit_csv.normalize_func_name`), and call it with a `HandlerContext` (`lib/handler_context.py`) bundling `msp`, `teileid`, `merkmale`, `x`, `y`, `doc`, `lib_doc`, `symbols`, `strecken_nachbarn`, `config`, `config_allgemein`.
|
||||||
|
5. Save the DXF to the output path.
|
||||||
|
|
||||||
|
Handler dispatch has special-cased overrides: any `TeileArt` starting with "Omniflo" or "TEF" is routed to `handle_omniflo` regardless of exact name, and "ILS 2.0 Kreisel*" variants all route to `handle_ils_2_0_kreisel`.
|
||||||
|
|
||||||
|
### Handler functions (`lib/plant2dxf.py`)
|
||||||
|
|
||||||
|
One `handle_*` function per `TeileArt`, e.g. `handle_ils_2_0_kreisel`, `handle_ils_2_0_gefaellestrecke`, `handle_ils_2_0_variofoerderer`, `handle_ils_2_0_eckrad`, `handle_ils_2_0_kurve_angetrieben`, `handle_bt___beladung`/`handle_bt___entladung`, `handle_omniflo`. Each handler:
|
||||||
|
- builds a Pydantic element object via `<ElementClass>.from_merkmale(...)` (in `lib/Elemente/`),
|
||||||
|
- inspects `ctx.strecken_nachbarn` to determine how many/which neighbors it's connected to (0, 1, or 2 Kreisel/Eckrad, direct-to-Kreisel vs. via-Stahlband, connected to an angetriebene Kurve needing a Motor-/Umlenkstation),
|
||||||
|
- builds a deterministic, parameter-encoded block name (e.g. `Ils_2.0_Gefaellestrecke_{laenge}_{hoehe}_{hat_umlenk}_{hat_motor}_{tefkurve}_...`) and only constructs the block geometry via `doc.blocks.new(...)` if that exact name doesn't already exist in `doc.blocks` — this is the project's block-deduplication/caching strategy, and reusing it correctly for any new variant is essential to avoid duplicate block definitions,
|
||||||
|
- inserts the resulting block (or raw lines for simple cases) into the modelspace with `msp.add_blockref(...)`.
|
||||||
|
|
||||||
|
This dispatch/handler structure — and especially the block-name-encodes-geometry-variant caching pattern — is the main thing to understand before adding a new `TeileArt` or modifying connection logic.
|
||||||
|
|
||||||
|
### Element classes (`lib/Elemente/`)
|
||||||
|
|
||||||
|
Pydantic models with geometry/business logic, one per component type: `Kreisel`, `Gefaellestrecke`, `VarioFoerderer`, `Angetriebene_Kurve`, `Eckrad`, `Bt_element`, `Omniflo`. Each has a `<Name>_tests.py` sibling with unittest cases (discovered by `run_unittests.bat`). `VarioFoerderer.py` and `Gefaellestrecke.py` contain the most complex logic (motor/Umlenkstation placement, AS/ES element insertion, rotation across one/two connected Kreisel) — see [doc/VarioFoerderer_Refactoring.md](doc/VarioFoerderer_Refactoring.md) for the design rationale (constants, enums, dataclasses extracted from what was previously an 820-line method).
|
||||||
|
|
||||||
|
### Helper modules (`lib/`)
|
||||||
|
|
||||||
|
- `arbeiten_mit_csv.py` — CSV parsing, `Planquadrat` coordinate extraction, `Merkmale` JSON parsing, the neighbor-analysis pass (`get_nachbar_information`), `TeileArt` → handler-name normalization.
|
||||||
|
- `block_methoden.py` — importing blocks from a library doc into the target doc, block rotation/mirroring (`turn_two_blocks_left`), reading insert color/layer from a library block, attribute helpers.
|
||||||
|
- `as_es_methoden.py` — AS-/ES-Element placement logic for Gefällestrecke (these depend on CSV height-comparison fields and Kreisel connection topology — flagged in the docs as needing review if RuleDesigner's CSV format changes).
|
||||||
|
- `handler_context.py` — the `HandlerContext` dataclass-like container passed to every handler, plus a `create_context_from_row` factory and rich `__str__`/`to_dict` for debugging.
|
||||||
|
- `utils.py` — `check_environment_var` (reads a required env var or exits) and `setup_logger` (timestamped file + console logger under `PROJECT_LOG`).
|
||||||
|
|
||||||
|
### Configuration (`cfg/`)
|
||||||
|
|
||||||
|
- `allgemein.cfg` — maps a `TeileArt`'s family (`[ILS]`, `[Omniflo]`, `[BT]`, `[TEF]`) to a block library filename under `data/block_libraries/`; also holds `[LOG]` format settings. Unmapped `TeileArt` falls back to `data/blocks.dxf`.
|
||||||
|
- `shapes.cfg` — one section per exact `TeileArt` string (must match the CSV column value exactly), listing library block names to place (`items`), their offsets/rotations relative to the computed position, and type-specific parameters (e.g. `asoffset`/`esoffset` for Gefällestrecke, `vario_abstand` for VarioFörderer, `winkel_*` angle constants, Omniflo Sivas-number overrides).
|
||||||
|
|
||||||
|
Both are read fresh per run; there's no schema validation beyond what `configparser` and the handler code assume, so a `TeileArt` typo silently falls through to "no handler found" (logged as a warning, row skipped) rather than erroring.
|
||||||
|
|
||||||
|
### Known fragility (documented in doc/plant2dxf_dokumentation.md)
|
||||||
|
|
||||||
|
The AS/ES-element and height-comparison logic in `as_es_methoden.py`, `lib/plant2dxf.py` (`handle_ils_2_0_gefaellestrecke`, `handle_ils_2_0_variofoerderer`), `lib/Elemente/Gefaellestrecke.py` (`rotation_mit_zwei_verbunden`), and `lib/Elemente/VarioFoerderer.py` (`vario_verbuden_am_kreisel`) all assume the current RuleDesigner CSV column set and semantics. If the CSV export format changes, these are the first places to check.
|
||||||
|
|
||||||
|
## Directories not under version control
|
||||||
|
|
||||||
|
`data/`, `work/`, `log/` are gitignored (generated/input working data, log files with timestamped names). `testdata/` holds committed CSV+DXF fixture pairs used for manual/reference comparison, separate from the `lib/Elemente/*_tests.py` unittest suite.
|
||||||
+16
-4
@@ -7,13 +7,17 @@ echo Output directory: %PROJECT_WORK%
|
|||||||
echo ========================================
|
echo ========================================
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
|
REM Track if any errors occurred
|
||||||
|
set ERRORS_OCCURRED=0
|
||||||
|
|
||||||
REM Iterate through all CSV files in testdata directory
|
REM Iterate through all CSV files in testdata directory
|
||||||
for %%f in (%PROJECT_TEST%\*.csv) do (
|
for %%f in ("%PROJECT_TEST%\*.csv") do (
|
||||||
echo Processing: %%~nxf
|
echo Processing: %%~nxf
|
||||||
CALL plant2dxf.bat -f "%%f" -o "%PROJECT_WORK%\%%~nf.dxf"
|
CALL plant2dxf.bat -f "%%f" -o "%PROJECT_WORK%\%%~nf.dxf"
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo ERROR: Failed to process %%~nxf
|
echo ERROR: Failed to process %%~nxf
|
||||||
echo.
|
echo.
|
||||||
|
set ERRORS_OCCURRED=1
|
||||||
) else (
|
) else (
|
||||||
echo SUCCESS: Created %%~nf.dxf in work folder
|
echo SUCCESS: Created %%~nf.dxf in work folder
|
||||||
echo.
|
echo.
|
||||||
@@ -21,6 +25,14 @@ for %%f in (%PROJECT_TEST%\*.csv) do (
|
|||||||
)
|
)
|
||||||
|
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo All test DXF files processed!
|
if %ERRORS_OCCURRED%==1 (
|
||||||
echo ========================================
|
echo Processing completed with ERRORS!
|
||||||
pause
|
echo ========================================
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
) else (
|
||||||
|
echo All test DXF files processed successfully!
|
||||||
|
echo ========================================
|
||||||
|
pause
|
||||||
|
exit /b 0
|
||||||
|
)
|
||||||
|
|||||||
+1
-1
@@ -7,4 +7,4 @@ REM Beispielaufruf mit Parametern:
|
|||||||
REM --in: Ordner mit .dxf-Dateien (optional. Standard: work/converted_dxfs)
|
REM --in: Ordner mit .dxf-Dateien (optional. Standard: work/converted_dxfs)
|
||||||
REM --name: Name der zu erzeugenden Bibliothek
|
REM --name: Name der zu erzeugenden Bibliothek
|
||||||
|
|
||||||
python %PROJECT_LIB%\dxf2lib.py %*
|
"%VIRTUAL_ENV%\Scripts\python.exe" %PROJECT_LIB%\dxf2lib.py %*
|
||||||
|
|||||||
+25
-21
@@ -1,30 +1,34 @@
|
|||||||
@echo off
|
@echo off
|
||||||
CALL setenv.bat
|
CALL setenv.bat
|
||||||
IF DEFINED NETWORK_INTERPRETER_PATH (
|
|
||||||
goto %~1_network
|
|
||||||
|
|
||||||
|
IF /I "%1"=="activate" GOTO activate
|
||||||
|
IF /I "%1"=="deactivate" GOTO deactivate
|
||||||
|
GOTO :eof
|
||||||
|
|
||||||
|
|
||||||
|
:activate
|
||||||
|
REM Interpreter wählen
|
||||||
|
IF DEFINED NETWORK_PYTHON (
|
||||||
|
SET PYTHON_EXE=%NETWORK_PYTHON%\python.exe
|
||||||
) ELSE (
|
) ELSE (
|
||||||
goto %~1_local
|
SET PYTHON_EXE=python
|
||||||
)
|
)
|
||||||
|
|
||||||
:activate_interpreter_local
|
REM venv sicherstellen
|
||||||
IF NOT EXIST %PROJECT%\.venv CALL %PROJECT_BIN%\install_py.bat
|
IF NOT EXIST "%PROJECT%\.venv" (
|
||||||
CALL %PROJECT%\.venv\Scripts\activate.bat
|
"%PYTHON_EXE%" -m venv "%PROJECT%\.venv"
|
||||||
goto :eof
|
)
|
||||||
|
|
||||||
:deactivate_interpreter_local
|
REM venv aktivieren
|
||||||
deactivate
|
CALL "%PROJECT%\.venv\Scripts\activate.bat"
|
||||||
goto :eof
|
|
||||||
|
|
||||||
:activate_interpreter_network
|
REM Interpreter festnageln
|
||||||
SET OLD_PATH=%PATH%
|
SET VIRTUAL_ENV_PYTHON=%PYTHON_EXE%
|
||||||
SET PATH=%NETWORK_INTERPRETER_PATH%;%PATH%
|
GOTO :eof
|
||||||
CALL %PROJECT%\.venv\Scripts\activate.bat
|
|
||||||
goto :eof
|
|
||||||
|
|
||||||
:deactivate_interpreter_network
|
|
||||||
SET PATH=%OLD_PATH%
|
:deactivate
|
||||||
SET OLD_PATH=
|
CALL "%PROJECT%\.venv\Scripts\deactivate.bat"
|
||||||
SET NETWORK_INTERPRETER_PATH=
|
SET VIRTUAL_ENV_PYTHON=
|
||||||
CALL %PROJECT%\.venv\Scripts\deactivate.bat
|
GOTO :eof
|
||||||
goto :eof
|
echo off
|
||||||
|
|||||||
+17
-3
@@ -1,4 +1,18 @@
|
|||||||
@echo off
|
@echo off
|
||||||
CALL manage_interpreter.bat activate_interpreter
|
CALL manage_interpreter.bat activate
|
||||||
python %PROJECT_LIB%\plant2dxf.py %*
|
if errorlevel 1 (
|
||||||
CALL manage_interpreter.bat deactivate_interpreter
|
echo ERROR: Failed to activate Python environment
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
cd /d "%PROJECT%"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: Failed to change to project directory: %PROJECT%
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
"%VIRTUAL_ENV%\Scripts\python.exe" -m lib.plant2dxf %*
|
||||||
|
set PYTHON_EXIT=%ERRORLEVEL%
|
||||||
|
|
||||||
|
CALL manage_interpreter.bat deactivate
|
||||||
|
exit /b %PYTHON_EXIT%
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
@echo off
|
||||||
|
CALL setenv.bat
|
||||||
|
|
||||||
|
echo ========================================
|
||||||
|
echo running all unittests in lib\Elemente
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
CALL manage_interpreter.bat activate
|
||||||
|
if %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo.
|
||||||
|
echo Failed to activate the Python interpreter!
|
||||||
|
pause
|
||||||
|
exit /B 1
|
||||||
|
)
|
||||||
|
echo Gefundene Testdateien:
|
||||||
|
for /f "delims=" %%f in ('dir /b "%PROJECT_LIB%\Elemente\*_tests.py" 2^>nul') do echo %%f
|
||||||
|
echo.
|
||||||
|
cd /d "%PROJECT%"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: Failed to change to project directory: %PROJECT%
|
||||||
|
CALL manage_interpreter.bat deactivate
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
"%VIRTUAL_ENV%\Scripts\python.exe" -m unittest discover -t "%PROJECT%" -s lib\Elemente -p "*_tests.py" -v
|
||||||
|
if %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo.
|
||||||
|
echo Tests failed!
|
||||||
|
CALL manage_interpreter.bat deactivate
|
||||||
|
exit /B 1
|
||||||
|
) else (
|
||||||
|
echo.
|
||||||
|
echo All tests passed!
|
||||||
|
CALL manage_interpreter.bat deactivate
|
||||||
|
exit /B 0
|
||||||
|
)
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ if not exist %PROJECT%\log mkdir %PROJECT%\log
|
|||||||
if not exist %PROJECT%\testdata mkdir %PROJECT%\testdata
|
if not exist %PROJECT%\testdata mkdir %PROJECT%\testdata
|
||||||
|
|
||||||
set PATH=%PROJECT_BIN%;%PATH%
|
set PATH=%PROJECT_BIN%;%PATH%
|
||||||
|
REM set NETWORK_PYTHON=TO_BE_SET
|
||||||
|
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|||||||
@@ -25,16 +25,19 @@ esoffset = 479.95
|
|||||||
Umlenkstation = 500.0, 0.0, 0.0
|
Umlenkstation = 500.0, 0.0, 0.0
|
||||||
Motorstation = 500.0, 0.0, 0.0
|
Motorstation = 500.0, 0.0, 0.0
|
||||||
vario_abstand = 66.5
|
vario_abstand = 66.5
|
||||||
|
|
||||||
[Ils 2.0 core winkel]
|
[Ils 2.0 core winkel]
|
||||||
winkel_boegen = 3
|
winkel_boegen = 3
|
||||||
winkel_motor = 3.0
|
winkel_motor = 3.0
|
||||||
winkel_umlenk = 3.0
|
winkel_umlenk = 3.0
|
||||||
winkel_as = 3.0
|
winkel_as = 3.0
|
||||||
winkel_es = 3.0
|
winkel_es = 3.0
|
||||||
|
|
||||||
[Omniflo]
|
[Omniflo]
|
||||||
OFgeradesivas = 821106002
|
OFgeradesivas = 821106002
|
||||||
Tefgeradesivas = 0_B10030
|
Tefgeradesivas = 0_B10030
|
||||||
OFfoerderer = 2
|
OFfoerderer = 2
|
||||||
|
|
||||||
[Scanner_Stoper_namen]
|
[Scanner_Stoper_namen]
|
||||||
Omniflo_stopper = 827062022
|
Omniflo_stopper = 827062022
|
||||||
Omniflo_scanner = _DUMMY_SCANNER
|
Omniflo_scanner = _DUMMY_SCANNER
|
||||||
|
|||||||
@@ -825,19 +825,19 @@ dy = halbe_laenge * math.cos(0) # dy = halbe_laenge (da cos(0) = 1)
|
|||||||
**Rechts-Version:**
|
**Rechts-Version:**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}
|
Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_rechts_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Links-Version:**
|
**Links-Version:**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}
|
Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_links_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameter:**
|
**Parameter:**
|
||||||
|
|
||||||
- `winkel`: Winkel des Förderers (z.B. 3, 6, 9)
|
- `winkel`: Winkel des Förderers (z.B. 3, 6, 9)
|
||||||
- `voerder_richtung`: Auf, Ab oder Horizontal
|
- `foerder_richtung`: Auf, Ab oder Horizontal
|
||||||
- `laenge`: Länge in mm
|
- `laenge`: Länge in mm
|
||||||
- `hoehe_vario`: Mittlere Höhe ((h0 + h1) / 2)
|
- `hoehe_vario`: Mittlere Höhe ((h0 + h1) / 2)
|
||||||
- `motor_vorhanden`: True/False
|
- `motor_vorhanden`: True/False
|
||||||
@@ -874,7 +874,7 @@ Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{motor_
|
|||||||
foerderer, doc, lib_doc, config,
|
foerderer, doc, lib_doc, config,
|
||||||
block, block_name_links,
|
block, block_name_links,
|
||||||
start, ende,
|
start, ende,
|
||||||
voerder_richtung,
|
foerder_richtung,
|
||||||
winkel_VP_offset_vorne, # None, da keine Verbindung
|
winkel_VP_offset_vorne, # None, da keine Verbindung
|
||||||
winkel_VP_offset_hinten # None, da keine Verbindung
|
winkel_VP_offset_hinten # None, da keine Verbindung
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Angetriebene_Kurve — Modell für angetriebene Kurven.
|
||||||
|
|
||||||
|
Refactoring-Änderungen:
|
||||||
|
- antrieb-Property Side-Effect entfernt: Normalisierung "Aussen"→"außen" etc.
|
||||||
|
wird nun beim Erstellen durch @field_validator erledigt
|
||||||
|
- antrieb-Property ist jetzt ein reiner read-only Getter
|
||||||
|
"""
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, field_validator
|
||||||
|
|
||||||
|
|
||||||
class Angetriebene_Kurve(BaseModel):
|
class Angetriebene_Kurve(BaseModel):
|
||||||
|
"""Modell für eine angetriebene Kurve."""
|
||||||
|
|
||||||
teileid: str
|
teileid: str
|
||||||
x: float
|
x: float
|
||||||
y: float
|
y: float
|
||||||
@@ -15,22 +25,47 @@ class Angetriebene_Kurve(BaseModel):
|
|||||||
)
|
)
|
||||||
winkel: int = Field(description="Der Winkel der Kurve")
|
winkel: int = Field(description="Der Winkel der Kurve")
|
||||||
|
|
||||||
|
@field_validator("antriebNebenStrecke", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def normalize_antrieb(cls, v):
|
||||||
|
"""
|
||||||
|
Normalisiert die Antrieb-Bezeichnung beim Erstellen.
|
||||||
|
|
||||||
|
Konvertiert englische/gemischte Schreibvarianten in
|
||||||
|
die konsistente deutsche Kleinbuchstaben-Form.
|
||||||
|
|
||||||
|
"Aussen" → "außen", "Innen" → "innen"
|
||||||
|
"""
|
||||||
|
if isinstance(v, str):
|
||||||
|
mapping = {"Aussen": "außen", "Innen": "innen"}
|
||||||
|
return mapping.get(v, v)
|
||||||
|
return v
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def antrieb(self):
|
def antrieb(self) -> str:
|
||||||
if self.antriebNebenStrecke == "Aussen":
|
"""Antrieb-Seite (read-only, normalisiert beim Erstellen)."""
|
||||||
self.antriebNebenStrecke = "außen"
|
|
||||||
elif self.antriebNebenStrecke == "Innen":
|
|
||||||
self.antriebNebenStrecke = "innen"
|
|
||||||
return self.antriebNebenStrecke
|
return self.antriebNebenStrecke
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hight_zwischen(self):
|
def hight_zwischen(self):
|
||||||
|
"""Mittlere Höhe zwischen hoehe0 und hoehe1."""
|
||||||
return (self.hoehe0 + self.hoehe1) / 2
|
return (self.hoehe0 + self.hoehe1) / 2
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_merkmale(
|
def from_merkmale(
|
||||||
cls, teileid: str, x: float, y: float, merkmale: dict
|
cls, teileid: str, x: float, y: float, merkmale: dict
|
||||||
) -> "Angetriebene_Kurve":
|
) -> "Angetriebene_Kurve":
|
||||||
|
"""
|
||||||
|
Erstellt Angetriebene_Kurve aus einem Merkmale-Dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
teileid: Teile-Identifikator
|
||||||
|
x, y: Koordinaten in mm
|
||||||
|
merkmale: Dictionary mit Eigenschaftswerten
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Angetriebene_Kurve-Instanz
|
||||||
|
"""
|
||||||
hoehe0 = float(merkmale.get("Höhe Anfang")) * 1000
|
hoehe0 = float(merkmale.get("Höhe Anfang")) * 1000
|
||||||
hoehe1 = float(merkmale.get("Höhe Ende")) * 1000
|
hoehe1 = float(merkmale.get("Höhe Ende")) * 1000
|
||||||
winkel = int(merkmale.get("Kurvenwinkel"))
|
winkel = int(merkmale.get("Kurvenwinkel"))
|
||||||
@@ -38,7 +73,7 @@ class Angetriebene_Kurve(BaseModel):
|
|||||||
antriebNebenstrecke = merkmale.get("AntriebNebenStrecke")
|
antriebNebenstrecke = merkmale.get("AntriebNebenStrecke")
|
||||||
try:
|
try:
|
||||||
drehung = float(merkmale.get("Drehung"))
|
drehung = float(merkmale.get("Drehung"))
|
||||||
except Exception as e:
|
except (TypeError, ValueError):
|
||||||
drehung = 0.0
|
drehung = 0.0
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Unit-Tests für Angetriebene_Kurve (Refactored)
|
||||||
|
"""
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from .Angetriebene_Kurve import Angetriebene_Kurve
|
||||||
|
|
||||||
|
|
||||||
|
class TestAngetriebeneKurveModell(unittest.TestCase):
|
||||||
|
"""Tests für das Angetriebene_Kurve Modell."""
|
||||||
|
|
||||||
|
def test_from_merkmale(self):
|
||||||
|
merkmale = {
|
||||||
|
"Höhe Anfang": "1.5",
|
||||||
|
"Höhe Ende": "2.0",
|
||||||
|
"Kurvenwinkel": "90",
|
||||||
|
"Kurvenrichtung": "links",
|
||||||
|
"AntriebNebenStrecke": "außen",
|
||||||
|
"Drehung": "-270",
|
||||||
|
}
|
||||||
|
obj = Angetriebene_Kurve.from_merkmale("AK-001", 100.0, 200.0, merkmale)
|
||||||
|
self.assertEqual(obj.teileid, "AK-001")
|
||||||
|
self.assertEqual(obj.hoehe0, 1500.0)
|
||||||
|
self.assertEqual(obj.hoehe1, 2000.0)
|
||||||
|
self.assertEqual(obj.winkel, 90)
|
||||||
|
self.assertEqual(obj.kurvenrichtung, "links")
|
||||||
|
self.assertEqual(obj.drehung, -270.0)
|
||||||
|
|
||||||
|
def test_from_merkmale_drehung_fehlend(self):
|
||||||
|
merkmale = {
|
||||||
|
"Höhe Anfang": "1.0",
|
||||||
|
"Höhe Ende": "1.0",
|
||||||
|
"Kurvenwinkel": "45",
|
||||||
|
"Kurvenrichtung": "rechts",
|
||||||
|
"AntriebNebenStrecke": "innen",
|
||||||
|
}
|
||||||
|
obj = Angetriebene_Kurve.from_merkmale("AK-002", 0.0, 0.0, merkmale)
|
||||||
|
self.assertEqual(obj.drehung, 0.0) # None → default
|
||||||
|
|
||||||
|
|
||||||
|
class TestAntriebNormalisierung(unittest.TestCase):
|
||||||
|
"""Tests für die field_validator Normalisierung von antriebNebenStrecke."""
|
||||||
|
|
||||||
|
def test_aussen_normalisiert(self):
|
||||||
|
"""'Aussen' wird beim Erstellen zu 'außen' normalisiert."""
|
||||||
|
obj = Angetriebene_Kurve(
|
||||||
|
teileid="T", x=0, y=0, kurvenrichtung="links",
|
||||||
|
antriebNebenStrecke="Aussen", winkel=90,
|
||||||
|
)
|
||||||
|
self.assertEqual(obj.antriebNebenStrecke, "außen")
|
||||||
|
self.assertEqual(obj.antrieb, "außen")
|
||||||
|
|
||||||
|
def test_innen_normalisiert(self):
|
||||||
|
"""'Innen' wird beim Erstellen zu 'innen' normalisiert."""
|
||||||
|
obj = Angetriebene_Kurve(
|
||||||
|
teileid="T", x=0, y=0, kurvenrichtung="rechts",
|
||||||
|
antriebNebenStrecke="Innen", winkel=45,
|
||||||
|
)
|
||||||
|
self.assertEqual(obj.antriebNebenStrecke, "innen")
|
||||||
|
self.assertEqual(obj.antrieb, "innen")
|
||||||
|
|
||||||
|
def test_bereits_normalisiert_bleibt(self):
|
||||||
|
"""Bereits normalisierte Werte bleiben unverändert."""
|
||||||
|
obj = Angetriebene_Kurve(
|
||||||
|
teileid="T", x=0, y=0, kurvenrichtung="links",
|
||||||
|
antriebNebenStrecke="außen", winkel=90,
|
||||||
|
)
|
||||||
|
self.assertEqual(obj.antriebNebenStrecke, "außen")
|
||||||
|
|
||||||
|
def test_unbekannter_wert_bleibt(self):
|
||||||
|
"""Unbekannte Werte werden nicht verändert."""
|
||||||
|
obj = Angetriebene_Kurve(
|
||||||
|
teileid="T", x=0, y=0, kurvenrichtung="links",
|
||||||
|
antriebNebenStrecke="custom_wert", winkel=90,
|
||||||
|
)
|
||||||
|
self.assertEqual(obj.antriebNebenStrecke, "custom_wert")
|
||||||
|
|
||||||
|
def test_antrieb_property_read_only(self):
|
||||||
|
"""antrieb Property gibt den normierten Wert zurück ohne Side-Effect."""
|
||||||
|
obj = Angetriebene_Kurve(
|
||||||
|
teileid="T", x=0, y=0, kurvenrichtung="links",
|
||||||
|
antriebNebenStrecke="Aussen", winkel=90,
|
||||||
|
)
|
||||||
|
# Mehrfacher Zugriff ändert nichts
|
||||||
|
_ = obj.antrieb
|
||||||
|
_ = obj.antrieb
|
||||||
|
self.assertEqual(obj.antrieb, "außen")
|
||||||
|
|
||||||
|
|
||||||
|
class TestHightZwischen(unittest.TestCase):
|
||||||
|
"""Tests für hight_zwischen Property."""
|
||||||
|
|
||||||
|
def test_mittlere_hoehe(self):
|
||||||
|
obj = Angetriebene_Kurve(
|
||||||
|
teileid="T", x=0, y=0, kurvenrichtung="links",
|
||||||
|
antriebNebenStrecke="außen", winkel=90,
|
||||||
|
hoehe0=1000, hoehe1=3000,
|
||||||
|
)
|
||||||
|
self.assertEqual(obj.hight_zwischen, 2000.0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
+125
-32
@@ -1,12 +1,64 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from pydantic import BaseModel, Field, field_validator
|
"""
|
||||||
from typing import Optional
|
Eckrad — Modell und Erstellungsfunktionen für Eckrad-Komponenten.
|
||||||
import block_methoden
|
|
||||||
|
|
||||||
RADIUS = 400
|
Refactoring-Änderungen:
|
||||||
|
- Hartkodierte Block-Namen durch Konstanten (BLOCK_AN8, BLOCK_RICHTUNGSPFEIL, etc.) ersetzt
|
||||||
|
- @staticmethod auf erstellung_eckrad_richtung
|
||||||
|
- 3x try/except in from_merkmale vereinfacht: drehrichtung braucht kein try/except (dict.get wirft nie)
|
||||||
|
- drehung wird nun korrekt an den Konstruktor übergeben (war vorher gelesen aber nicht weitergegeben)
|
||||||
|
- Hinweis: Modell hat keine x/y-Felder, diese werden von Pydantic ignoriert
|
||||||
|
"""
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from lib import block_methoden
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# KONSTANTEN
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
RADIUS = 400 # Radius der Eckrad-Kreise (mm)
|
||||||
|
BLOCK_AN8 = "AN8" # Grundform-Block für Eckrad
|
||||||
|
BLOCK_RICHTUNGSPFEIL = "Richtungspfeil" # Richtungsanzeiger-Block
|
||||||
|
BLOCK_ECKRAD_UZS = "eckrad_UZS" # Eckrad-Block im Uhrzeigersinn
|
||||||
|
BLOCK_ECKRAD_GUZS = "eckrad_GUZS" # Eckrad-Block gegen Uhrzeigersinn
|
||||||
|
RICHTUNGSPFEIL_OFFSET = 200 # Versatz der Richtungspfeile vom Zentrum (mm)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HILFSFUNKTIONEN
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_float(value, default=0.0):
|
||||||
|
"""
|
||||||
|
Konvertiert einen Wert zu float mit Fallback.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Zu konvertierender Wert (kann None sein)
|
||||||
|
default: Rückgabewert bei Fehler
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Float-Wert oder default
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
return float(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# ECKRAD KLASSE
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
class Eckrad(BaseModel):
|
class Eckrad(BaseModel):
|
||||||
|
"""Modell für Eckrad-Komponenten."""
|
||||||
|
|
||||||
teileid: str
|
teileid: str
|
||||||
drehung: Optional[float] = Field(default=0.0, description="Rotation des Elements")
|
drehung: Optional[float] = Field(default=0.0, description="Rotation des Elements")
|
||||||
hoehe: Optional[float] = Field(default=0.0, description="Hoehe des Elements")
|
hoehe: Optional[float] = Field(default=0.0, description="Hoehe des Elements")
|
||||||
@@ -18,38 +70,79 @@ class Eckrad(BaseModel):
|
|||||||
def from_merkmale(
|
def from_merkmale(
|
||||||
cls, teileid: str, x: float, y: float, merkmale: dict
|
cls, teileid: str, x: float, y: float, merkmale: dict
|
||||||
) -> "Eckrad":
|
) -> "Eckrad":
|
||||||
try:
|
"""
|
||||||
hoehe = float(merkmale.get("Höhe in Meter")) * 1000
|
Erstellt Eckrad aus einem Merkmale-Dictionary.
|
||||||
except Exception as e:
|
|
||||||
hoehe = 0.0
|
|
||||||
try:
|
|
||||||
drehung = float(merkmale.get("Drehung"))
|
|
||||||
except Exception as e:
|
|
||||||
drehung = 0.0
|
|
||||||
try:
|
|
||||||
drehrichtung = merkmale.get("Drehrichtung")
|
|
||||||
except Exception as e:
|
|
||||||
drehrichtung = None
|
|
||||||
return cls(teileid=teileid, x=x, y=y, hoehe=hoehe, drehrichtung=drehrichtung)
|
|
||||||
|
|
||||||
|
Args:
|
||||||
|
teileid: Teile-Identifikator
|
||||||
|
x, y: Koordinaten (werden vom Modell nicht gespeichert)
|
||||||
|
merkmale: Dictionary mit Eigenschaftswerten
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Eckrad-Instanz
|
||||||
|
"""
|
||||||
|
hoehe = _safe_float(merkmale.get("Höhe in Meter"), 0.0) * 1000
|
||||||
|
drehung = _safe_float(merkmale.get("Drehung"), 0.0)
|
||||||
|
# dict.get() wirft keine Ausnahme — kein try/except nötig
|
||||||
|
drehrichtung = merkmale.get("Drehrichtung")
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
teileid=teileid,
|
||||||
|
hoehe=hoehe,
|
||||||
|
drehung=drehung,
|
||||||
|
drehrichtung=drehrichtung,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def erstellung_eckrad_richtung(merkmale, doc, lib_doc):
|
def erstellung_eckrad_richtung(merkmale, doc, lib_doc):
|
||||||
block_methoden.import_block("AN8", lib_doc, doc)
|
"""
|
||||||
block_methoden.import_block("Richtungspfeil", lib_doc, doc)
|
Erstellt Eckrad-Blöcke mit Richtungspfeilen (UZS und GUZS).
|
||||||
eckrad_rechts = "eckrad_UZS"
|
|
||||||
eckrad_links = "eckrad_GUZS"
|
Erstellt bei Bedarf neue Blöcke für beide Drehrichtungen,
|
||||||
|
jeder mit dem AN8-Grundform-Block und zwei Richtungspfeilen.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
merkmale: Dictionary mit "Höhe in m"
|
||||||
|
doc: DXF-Dokument
|
||||||
|
lib_doc: Bibliotheks-Dokument
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(eckrad_rechts, eckrad_links, hight): Blocknamen und Höhe in mm
|
||||||
|
"""
|
||||||
|
block_methoden.import_block(BLOCK_AN8, lib_doc, doc)
|
||||||
|
block_methoden.import_block(BLOCK_RICHTUNGSPFEIL, lib_doc, doc)
|
||||||
|
|
||||||
hight = float(merkmale.get("Höhe in m")) * 1000
|
hight = float(merkmale.get("Höhe in m")) * 1000
|
||||||
# Erstellung der Richtungung Blöcke der Eckrads
|
|
||||||
if eckrad_rechts not in doc.blocks:
|
# Blöcke nur erstellen wenn noch nicht vorhanden
|
||||||
block_rechts = doc.blocks.new(name=eckrad_rechts, base_point=(0, 0, 0))
|
if BLOCK_ECKRAD_UZS not in doc.blocks:
|
||||||
block_links = doc.blocks.new(name=eckrad_links, base_point=(0, 0, 0))
|
block_rechts = doc.blocks.new(name=BLOCK_ECKRAD_UZS, base_point=(0, 0, 0))
|
||||||
block_rechts.add_blockref("AN8", (0, 0, 0))
|
block_links = doc.blocks.new(name=BLOCK_ECKRAD_GUZS, base_point=(0, 0, 0))
|
||||||
block_links.add_blockref("AN8", (0, 0, 0))
|
|
||||||
block_rechts.add_blockref("Richtungspfeil", (0 + 200, 0 + RADIUS, 0))
|
# Grundform für beide
|
||||||
|
block_rechts.add_blockref(BLOCK_AN8, (0, 0, 0))
|
||||||
|
block_links.add_blockref(BLOCK_AN8, (0, 0, 0))
|
||||||
|
|
||||||
|
# UZS: Pfeile bei (+offset, +RADIUS) und (-offset, -RADIUS)
|
||||||
block_rechts.add_blockref(
|
block_rechts.add_blockref(
|
||||||
"Richtungspfeil", (0 - 200, 0 - RADIUS, 0), dxfattribs={"rotation": 180}
|
BLOCK_RICHTUNGSPFEIL,
|
||||||
|
(RICHTUNGSPFEIL_OFFSET, RADIUS, 0),
|
||||||
)
|
)
|
||||||
block_links.add_blockref("Richtungspfeil", (0 + 200, 0 - RADIUS, 0))
|
block_rechts.add_blockref(
|
||||||
|
BLOCK_RICHTUNGSPFEIL,
|
||||||
|
(-RICHTUNGSPFEIL_OFFSET, -RADIUS, 0),
|
||||||
|
dxfattribs={"rotation": 180},
|
||||||
|
)
|
||||||
|
|
||||||
|
# GUZS: Pfeile bei (+offset, -RADIUS) und (-offset, +RADIUS)
|
||||||
block_links.add_blockref(
|
block_links.add_blockref(
|
||||||
"Richtungspfeil", (0 - 200, 0 + RADIUS, 0), dxfattribs={"rotation": 180}
|
BLOCK_RICHTUNGSPFEIL,
|
||||||
|
(RICHTUNGSPFEIL_OFFSET, -RADIUS, 0),
|
||||||
)
|
)
|
||||||
return eckrad_rechts, eckrad_links, hight
|
block_links.add_blockref(
|
||||||
|
BLOCK_RICHTUNGSPFEIL,
|
||||||
|
(-RICHTUNGSPFEIL_OFFSET, RADIUS, 0),
|
||||||
|
dxfattribs={"rotation": 180},
|
||||||
|
)
|
||||||
|
|
||||||
|
return BLOCK_ECKRAD_UZS, BLOCK_ECKRAD_GUZS, hight
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Unit-Tests für Eckrad (Refactored)
|
||||||
|
"""
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from .Eckrad import Eckrad, _safe_float, BLOCK_AN8, BLOCK_ECKRAD_UZS, BLOCK_ECKRAD_GUZS, RADIUS
|
||||||
|
|
||||||
|
|
||||||
|
class TestSafeFloat(unittest.TestCase):
|
||||||
|
"""Tests für Eckrad _safe_float."""
|
||||||
|
|
||||||
|
def test_normaler_wert(self):
|
||||||
|
self.assertEqual(_safe_float("2.5"), 2.5)
|
||||||
|
|
||||||
|
def test_none_default(self):
|
||||||
|
self.assertEqual(_safe_float(None, 99.0), 99.0)
|
||||||
|
|
||||||
|
def test_ungueltig_default(self):
|
||||||
|
self.assertEqual(_safe_float("abc", 0.0), 0.0)
|
||||||
|
|
||||||
|
|
||||||
|
class TestEckradModell(unittest.TestCase):
|
||||||
|
"""Tests für das Eckrad Modell."""
|
||||||
|
|
||||||
|
def test_from_merkmale_vollstaendig(self):
|
||||||
|
merkmale = {
|
||||||
|
"Höhe in Meter": "3.5",
|
||||||
|
"Drehung": "45",
|
||||||
|
"Drehrichtung": "UZS",
|
||||||
|
}
|
||||||
|
obj = Eckrad.from_merkmale("E-001", 100.0, 200.0, merkmale)
|
||||||
|
self.assertEqual(obj.teileid, "E-001")
|
||||||
|
self.assertEqual(obj.hoehe, 3500.0) # 3.5 * 1000
|
||||||
|
self.assertEqual(obj.drehung, 45.0)
|
||||||
|
self.assertEqual(obj.drehrichtung, "UZS")
|
||||||
|
|
||||||
|
def test_from_merkmale_fehlende_hoehe(self):
|
||||||
|
merkmale = {"Drehung": "90", "Drehrichtung": None}
|
||||||
|
obj = Eckrad.from_merkmale("E-002", 0.0, 0.0, merkmale)
|
||||||
|
self.assertEqual(obj.hoehe, 0.0) # None → default 0
|
||||||
|
|
||||||
|
def test_from_merkmale_fehlende_drehung(self):
|
||||||
|
merkmale = {"Höhe in Meter": "1.0"}
|
||||||
|
obj = Eckrad.from_merkmale("E-003", 0.0, 0.0, merkmale)
|
||||||
|
self.assertEqual(obj.drehung, 0.0) # None → default 0
|
||||||
|
|
||||||
|
def test_drehrichtung_ohne_try_except(self):
|
||||||
|
# drehrichtung = merkmale.get("Drehrichtung") wirft nie
|
||||||
|
merkmale = {"Höhe in Meter": "1.0", "Drehung": "0"}
|
||||||
|
obj = Eckrad.from_merkmale("E-004", 0.0, 0.0, merkmale)
|
||||||
|
self.assertIsNone(obj.drehrichtung)
|
||||||
|
|
||||||
|
|
||||||
|
class TestEckradKonstanten(unittest.TestCase):
|
||||||
|
"""Tests für Eckrad-Konstanten."""
|
||||||
|
|
||||||
|
def test_block_namen(self):
|
||||||
|
self.assertEqual(BLOCK_AN8, "AN8")
|
||||||
|
self.assertEqual(BLOCK_ECKRAD_UZS, "eckrad_UZS")
|
||||||
|
self.assertEqual(BLOCK_ECKRAD_GUZS, "eckrad_GUZS")
|
||||||
|
|
||||||
|
def test_radius(self):
|
||||||
|
self.assertEqual(RADIUS, 400)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
+565
-248
@@ -1,215 +1,544 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from pydantic import BaseModel, Field, field_validator
|
"""
|
||||||
import re
|
Gefaellestrecke — Modell und Hilfsfunktionen für Gefällestrecken.
|
||||||
import plant2dxf
|
|
||||||
|
Enthält das Pydantic-Modell und statische Methoden zur Erstellung
|
||||||
|
von DXF-Geometrie für Gefällestrecken zwischen Fördererkomponenten.
|
||||||
|
|
||||||
|
Refactoring-Änderungen:
|
||||||
|
- @staticmethod auf alle Methoden, die self nicht nutzen
|
||||||
|
- Bug fix Zeile 112: am_kreisel == 1 → am_kreisel = 1
|
||||||
|
- Duplizierte Logik (Zeilen 69–90 vs 121–141) in _bestimme_position/_bestimme_gefaelle extrahiert
|
||||||
|
- rotation_mit_zwei_verbunden aufgeteilt in Hilfsfunktionen
|
||||||
|
- hat_motor_umlenk_station aufgeteilt: _pruefe_motor_umlenk_an_kurve, _bestimme_tefkurve
|
||||||
|
- ein_motor_oder_eine_umlenk aufgeteilt: _add_station_mit_bogen, _add_station_gerade
|
||||||
|
- Magic Numbers durch Konstanten ersetzt
|
||||||
|
- Dokumentation hinzugefügt
|
||||||
|
"""
|
||||||
import math
|
import math
|
||||||
from ezdxf.math import Matrix44
|
import re
|
||||||
import block_methoden
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from lib import block_methoden
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# KONSTANTEN
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
ROTATION_MAP = {"oben": 0, "unten": 180, "links": 90, "rechts": 270}
|
||||||
|
"""Zuordnung von Gefälle-Richtung zu Rotationswinkel in Grad."""
|
||||||
|
|
||||||
|
LAYER_SP = "6-SP"
|
||||||
|
"""DXF-Layer für Seitenprofile."""
|
||||||
|
|
||||||
|
WINKEL_3_GRAD = 3
|
||||||
|
"""Standard-Bogenwinkel in Grad."""
|
||||||
|
|
||||||
|
STATIONS_LAENGE = 500
|
||||||
|
"""Länge einer Motor-/Umlenkstation in mm."""
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HILFSFUNKTIONEN
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
def _bestimme_position(richtung, x0_kreisel, x1_kreisel, y0_kreisel, y1_kreisel, hight_position):
|
||||||
|
"""
|
||||||
|
Bestimmt die relative Position basierend auf Richtung und Kreiselkoordinaten.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
richtung: "Vertikal" oder "Horizontal"
|
||||||
|
x0_kreisel, y0_kreisel: Koordinaten erstes Kreisel
|
||||||
|
x1_kreisel, y1_kreisel: Koordinaten zweites Kreisel
|
||||||
|
hight_position: "higher" oder "lower"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Position-String z.B. "higher_links", "lower_rechts"
|
||||||
|
"""
|
||||||
|
if richtung == "Vertikal":
|
||||||
|
suffix = "_links" if x0_kreisel < x1_kreisel else "_rechts"
|
||||||
|
else:
|
||||||
|
suffix = "_higher" if y0_kreisel > y1_kreisel else "_lower"
|
||||||
|
return hight_position + suffix
|
||||||
|
|
||||||
|
|
||||||
|
def _bestimme_gefaelle(richtung, position):
|
||||||
|
"""
|
||||||
|
Bestimmt die Gefälle-Richtung basierend auf Richtung und Position.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
richtung: "Vertikal" oder "Horizontal"
|
||||||
|
position: Position-String (z.B. "lower_rechts")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Gefälle-Richtung: "links", "rechts", "oben" oder "unten"
|
||||||
|
"""
|
||||||
|
if richtung == "Vertikal":
|
||||||
|
return "links" if position in ("lower_rechts", "higher_links") else "rechts"
|
||||||
|
elif richtung == "Horizontal":
|
||||||
|
return "oben" if position in ("lower_lower", "higher_higher") else "unten"
|
||||||
|
return "links" # Fallback für unbekannte Richtung
|
||||||
|
|
||||||
|
|
||||||
|
def _tausche_drehungen_und_hoehe(drehung0, drehung1, hight_position):
|
||||||
|
"""
|
||||||
|
Tauscht Drehungen und invertiert die Höhe-Position.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(neue_drehung0, neue_drehung1, neue_hight_position)
|
||||||
|
"""
|
||||||
|
neue_hight = "lower" if hight_position == "higher" else "higher"
|
||||||
|
return drehung1, drehung0, neue_hight
|
||||||
|
|
||||||
|
|
||||||
|
def _bestimme_tefkurve(kurvenrichtung, tefkurve):
|
||||||
|
"""
|
||||||
|
Bestimmt die effektive Tefkurve-Richtung aus Kurvenrichtung und Tefkurve.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
kurvenrichtung: "links" oder "rechts"
|
||||||
|
tefkurve: "außen" oder "innen"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
"rechts" oder "links"
|
||||||
|
"""
|
||||||
|
if (kurvenrichtung == "links" and tefkurve == "außen") or (
|
||||||
|
kurvenrichtung == "rechts" and tefkurve == "innen"
|
||||||
|
):
|
||||||
|
return "rechts"
|
||||||
|
return "links"
|
||||||
|
|
||||||
|
|
||||||
|
def _ist_umlenk_position(rotation, x, y, x_angetrieben, y_angetrieben):
|
||||||
|
"""
|
||||||
|
Prüft ob die Position eine Umlenkposition ist (gleiche-Höhe-Fall).
|
||||||
|
|
||||||
|
Basiert auf Rotationswinkel und relativer Position zur angetriebenen Strecke.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rotation: Drehung in Grad
|
||||||
|
x, y: Position des Gefällestreckens
|
||||||
|
x_angetrieben, y_angetrieben: Position der angetriebenen Strecke
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True wenn Umlenkposition, sonst False (Motorposition)
|
||||||
|
"""
|
||||||
|
rotation_zwischen = rotation if rotation != 0.0 else -360.0
|
||||||
|
return (
|
||||||
|
((-360.0 <= rotation_zwischen < -270.0) and y > y_angetrieben)
|
||||||
|
or ((-90.0 < rotation < 0.0) and y > y_angetrieben)
|
||||||
|
or ((-270.0 < rotation_zwischen < -90.0) and y < y_angetrieben)
|
||||||
|
or (rotation == -90.0 and x < x_angetrieben)
|
||||||
|
or (rotation == -270.0 and x < x_angetrieben)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_deltas(*delta_lists):
|
||||||
|
"""Setzt negative Werte in Delta-Listen auf positiv."""
|
||||||
|
for delta_list in delta_lists:
|
||||||
|
for i, val in enumerate(delta_list):
|
||||||
|
if val < 0:
|
||||||
|
delta_list[i] = abs(val)
|
||||||
|
|
||||||
|
|
||||||
|
def _laedt_bogen_deltas(block_name, lib_doc, doc):
|
||||||
|
"""
|
||||||
|
Lädt Bogen-Delta-Attribute und normalisiert negative Werte.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block_name: Name des Bogen-Blocks
|
||||||
|
lib_doc: Bibliotheks-Dokument
|
||||||
|
doc: Ziel-Dokument
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict mit DELTA_SP_0 und DELTA_SP_1 als normalisierten Listen
|
||||||
|
"""
|
||||||
|
attrib = block_methoden.import_block(block_name, lib_doc, doc)
|
||||||
|
deltas = {}
|
||||||
|
for key in ("DELTA_SP_0", "DELTA_SP_1"):
|
||||||
|
values = [float(v) for v in re.split(r"[;,]", attrib[key])]
|
||||||
|
deltas[key] = [abs(v) if v < 0 else v for v in values]
|
||||||
|
return deltas
|
||||||
|
|
||||||
|
|
||||||
|
def _add_station_mit_bogen(
|
||||||
|
block, bogen_block, station_block, position, x, y, hoehe,
|
||||||
|
deltas_0, deltas_1, vorzeichen, rotation_bogen
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Fügt Station mit Bogen-Übergang hinzu.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block: DXF-Block zum Hinzufügen
|
||||||
|
bogen_block: Blockname des Bogens
|
||||||
|
station_block: Blockname der Station
|
||||||
|
position: [x, y, z] aktuelle Position
|
||||||
|
x, y: Offset-Koordinaten
|
||||||
|
hoehe: Höhe des Gefälles
|
||||||
|
deltas_0, deltas_1: Delta-Werte des Bogens [x, y, z]
|
||||||
|
vorzeichen: -1 für Motor (Anfang), +1 für Umlenk (Ende)
|
||||||
|
rotation_bogen: Rotation des Bogenblocks
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Neue Position nach Station-Platzierung
|
||||||
|
"""
|
||||||
|
v = vorzeichen
|
||||||
|
# Bogen hinzufügen
|
||||||
|
block.add_blockref(
|
||||||
|
bogen_block,
|
||||||
|
(position[0] - x, position[1] + v * deltas_0[0] - y, position[2] + v * deltas_0[2] - hoehe),
|
||||||
|
dxfattribs={"rotation": rotation_bogen},
|
||||||
|
)
|
||||||
|
# Position nach Bogen aktualisieren
|
||||||
|
new_pos = [
|
||||||
|
position[0],
|
||||||
|
position[1] + v * (deltas_0[0] + deltas_1[0]),
|
||||||
|
position[2] + v * (deltas_0[2] + deltas_1[2]),
|
||||||
|
]
|
||||||
|
# Station hinzufügen
|
||||||
|
winkel_rad = math.radians(WINKEL_3_GRAD)
|
||||||
|
block.add_blockref(
|
||||||
|
station_block,
|
||||||
|
(
|
||||||
|
new_pos[0] - x,
|
||||||
|
new_pos[1] + v * (STATIONS_LAENGE / 2) * math.cos(winkel_rad) - y,
|
||||||
|
new_pos[2] + v * (STATIONS_LAENGE / 2) * math.sin(winkel_rad) - hoehe,
|
||||||
|
),
|
||||||
|
dxfattribs={"rotation": 270},
|
||||||
|
)
|
||||||
|
new_pos[1] += v * STATIONS_LAENGE * math.cos(winkel_rad)
|
||||||
|
new_pos[2] += v * STATIONS_LAENGE * math.sin(winkel_rad)
|
||||||
|
return new_pos
|
||||||
|
|
||||||
|
|
||||||
|
def _add_station_gerade(block, station_block, position, x, y, hoehe, vorzeichen):
|
||||||
|
"""
|
||||||
|
Fügt Station ohne Bogen (gerade) hinzu.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block: DXF-Block zum Hinzufügen
|
||||||
|
station_block: Blockname der Station
|
||||||
|
position: [x, y, z] aktuelle Position
|
||||||
|
x, y: Offset-Koordinaten
|
||||||
|
hoehe: Höhe des Gefälles
|
||||||
|
vorzeichen: -1 für Motor, +1 für Umlenk
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Neue Position nach Station-Platzierung
|
||||||
|
"""
|
||||||
|
v = vorzeichen
|
||||||
|
block.add_blockref(
|
||||||
|
station_block,
|
||||||
|
(position[0] - x, position[1] + v * (STATIONS_LAENGE / 2) - y, position[2] - hoehe),
|
||||||
|
dxfattribs={"rotation": 270},
|
||||||
|
)
|
||||||
|
new_pos = list(position)
|
||||||
|
new_pos[1] += v * STATIONS_LAENGE
|
||||||
|
return new_pos
|
||||||
|
|
||||||
|
|
||||||
|
def _pruefe_motor_umlenk_an_kurve(
|
||||||
|
upper_hoehe, lower_hoehe, vario_hoehe_0, vario_hoehe_1,
|
||||||
|
rotation, x, y, x_angetrieben, y_angetrieben
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Prüft ob Motor oder Umlenk an einer Kurve benötigt wird.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
upper_hoehe: Obere Höhe des Gefällestreckens
|
||||||
|
lower_hoehe: Untere Höhe des Gefällestreckens
|
||||||
|
vario_hoehe_0, vario_hoehe_1: Höhen der Nachbarn
|
||||||
|
rotation: Drehung in Grad
|
||||||
|
x, y: Position des Gefällestreckens
|
||||||
|
x_angetrieben, y_angetrieben: Position der angetriebenen Strecke
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(hat_motor, hat_umlenk, ist_gerade)
|
||||||
|
"""
|
||||||
|
if upper_hoehe > lower_hoehe:
|
||||||
|
if vario_hoehe_0 == upper_hoehe or vario_hoehe_1 == upper_hoehe:
|
||||||
|
return True, False, False
|
||||||
|
return False, True, False
|
||||||
|
elif upper_hoehe < lower_hoehe:
|
||||||
|
if vario_hoehe_0 == lower_hoehe or vario_hoehe_1 == lower_hoehe:
|
||||||
|
return True, False, False
|
||||||
|
return False, True, False
|
||||||
|
else: # Gleiche Höhe → Positionsbasierte Bestimmung
|
||||||
|
ist_umlenk = _ist_umlenk_position(rotation, x, y, x_angetrieben, y_angetrieben)
|
||||||
|
if ist_umlenk:
|
||||||
|
return False, True, True
|
||||||
|
return True, False, True
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# GEFAELLESTRECKE KLASSE
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
class Gefaellestrecke(BaseModel):
|
class Gefaellestrecke(BaseModel):
|
||||||
|
"""Modell für Gefällestrecken zwischen Fördererkomponenten."""
|
||||||
|
|
||||||
teileid: str
|
teileid: str
|
||||||
x: float = Field(description="X-Koordinate des Foerder-Zentrums")
|
x: float = Field(description="X-Koordinate des Foerder-Zentrums")
|
||||||
y: float = Field(description="Y-Koordinate des Foerder-Zentrums")
|
y: float = Field(description="Y-Koordinate des Foerder-Zentrums")
|
||||||
laenge:float = Field(description = "Länge des Förderers")
|
laenge: float = Field(description="Länge des Förderers")
|
||||||
h0: float = Field(description="Höhe Unten in Merkmale")
|
h0: float = Field(description="Höhe Unten in Merkmale")
|
||||||
h1: float = Field(description="Höhe Oben in Merkmale")
|
h1: float = Field(description="Höhe Oben in Merkmale")
|
||||||
drehung: float = Field(default=0.0, description="Drehung an z achse")
|
drehung: float = Field(default=0.0, description="Drehung an z achse")
|
||||||
anzahl_scanner: int = Field(default=0, description="Anzahl der Scanner")
|
anzahl_scanner: int = Field(default=0, description="Anzahl der Scanner")
|
||||||
anzahl_separatoren: int = Field(default=0, description="Anzahl der Separatoren")
|
anzahl_separatoren: int = Field(default=0, description="Anzahl der Separatoren")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hight_zwischen(self):
|
def hight_zwischen(self):
|
||||||
return ((self.h0 + self.h1) /2)
|
"""Mittlere Höhe zwischen h0 und h1."""
|
||||||
|
return (self.h0 + self.h1) / 2
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_merkmale(cls, teileid: str, x: float, y: float, merkmale: dict) -> 'Gefaellestrecke':
|
def from_merkmale(
|
||||||
|
cls, teileid: str, x: float, y: float, merkmale: dict
|
||||||
|
) -> "Gefaellestrecke":
|
||||||
|
"""
|
||||||
|
Erstellt ein Gefaellestrecke-Objekt aus einem Merkmale-Dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
teileid: Teile-Identifikator
|
||||||
|
x, y: Koordinaten in mm
|
||||||
|
merkmale: Dictionary mit Eigenschaftswerten
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Gefaellestrecke-Instanz
|
||||||
|
"""
|
||||||
h0 = float(merkmale.get("Höhe unten")) * 1000
|
h0 = float(merkmale.get("Höhe unten")) * 1000
|
||||||
h1 = float(merkmale.get("Höhe oben")) * 1000
|
h1 = float(merkmale.get("Höhe oben")) * 1000
|
||||||
laenge = float(merkmale.get("Länge in Meter")) * 1000
|
laenge = float(merkmale.get("Länge in Meter")) * 1000
|
||||||
return cls(
|
return cls(
|
||||||
teileid = teileid,
|
teileid=teileid,
|
||||||
laenge = laenge,
|
laenge=laenge,
|
||||||
x = x,
|
x=x,
|
||||||
y = y,
|
y=y,
|
||||||
h0 = h0,
|
h0=h0,
|
||||||
h1 =h1,
|
h1=h1,
|
||||||
drehung = float(merkmale.get("Drehung")),
|
drehung=float(merkmale.get("Drehung")),
|
||||||
anzahl_scanner = int(merkmale.get("Anzahl der Scanner")),
|
anzahl_scanner=int(merkmale.get("Anzahl der Scanner")),
|
||||||
anzahl_separatoren = int(merkmale.get("Anzahl der Separatoren"))
|
anzahl_separatoren=int(merkmale.get("Anzahl der Separatoren")),
|
||||||
)
|
)
|
||||||
def erstehlung_von_gefalle_ohne_aussnahmen(msp, x, y, upper_hoehe_gefaelle, lower_hoehe_gefaelle, halbe_laenge, winkel):
|
|
||||||
dx = halbe_laenge *math.sin(winkel * -1)
|
@staticmethod
|
||||||
|
def erstehlung_von_gefalle_ohne_aussnahmen(
|
||||||
|
msp, x, y, upper_hoehe_gefaelle, lower_hoehe_gefaelle, halbe_laenge, winkel
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Zeichnet eine Gefällelinie ohne Motor-/Umlenk-Ausnahmen.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msp: DXF-Modelspace
|
||||||
|
x, y: Zentrum der Gefällelinie
|
||||||
|
upper_hoehe_gefaelle: Höhe am oberen Ende
|
||||||
|
lower_hoehe_gefaelle: Höhe am unteren Ende
|
||||||
|
halbe_laenge: Halbe Länge der Strecke
|
||||||
|
winkel: Rotationswinkel
|
||||||
|
"""
|
||||||
|
dx = halbe_laenge * math.sin(winkel * -1)
|
||||||
dy = halbe_laenge * math.cos(winkel)
|
dy = halbe_laenge * math.cos(winkel)
|
||||||
start = x +dx, y + dy,upper_hoehe_gefaelle
|
start = x + dx, y + dy, upper_hoehe_gefaelle
|
||||||
ende = x -dx, y - dy,lower_hoehe_gefaelle
|
ende = x - dx, y - dy, lower_hoehe_gefaelle
|
||||||
line =msp.add_line(start,ende)
|
line = msp.add_line(start, ende)
|
||||||
line.dxf.layer = "6-SP"
|
line.dxf.layer = LAYER_SP
|
||||||
def rotation_mit_zwei_verbunden(gefaellestrecke_nachbarn,richtung2, richtung0, am_kreisel, kreisel_verbunden, hight_position):
|
|
||||||
drehung0 =gefaellestrecke_nachbarn.get("Drehung0")
|
@staticmethod
|
||||||
|
def rotation_mit_zwei_verbunden(
|
||||||
|
gefaellestrecke_nachbarn,
|
||||||
|
richtung2,
|
||||||
|
richtung0,
|
||||||
|
am_kreisel,
|
||||||
|
kreisel_verbunden,
|
||||||
|
hight_position,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Berechnet Rotation wenn zwei Kreise verbunden sind.
|
||||||
|
|
||||||
|
Bestimmt die Gefälle-Richtung und passt Drehungen an,
|
||||||
|
damit der 1. Kreisel in der Liste konsistent am Kreisel verbunden ist.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gefaellestrecke_nachbarn: Dict mit Drehungen und Kreisel-Positionen
|
||||||
|
richtung2: Richtung des zweiten Kreisels ("DEFAULT", "Vertikal", "Horizontal")
|
||||||
|
richtung0: Richtung des ersten Kreisels
|
||||||
|
am_kreisel: Kreisel-Index (0, 1 oder 2)
|
||||||
|
kreisel_verbunden: Anzahl verbundener Kreisel
|
||||||
|
hight_position: "higher" oder "lower"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(rotation, drehung0, drehung1, hight_position)
|
||||||
|
"""
|
||||||
|
drehung0 = gefaellestrecke_nachbarn.get("Drehung0")
|
||||||
drehung1 = gefaellestrecke_nachbarn.get("Drehung1")
|
drehung1 = gefaellestrecke_nachbarn.get("Drehung1")
|
||||||
x0_kreisel = float(gefaellestrecke_nachbarn.get("x0"))
|
x0_kreisel = float(gefaellestrecke_nachbarn.get("x0"))
|
||||||
y0_kreisel = float(gefaellestrecke_nachbarn.get("y0"))
|
y0_kreisel = float(gefaellestrecke_nachbarn.get("y0"))
|
||||||
x1_kreisel = float(gefaellestrecke_nachbarn.get("x1"))
|
x1_kreisel = float(gefaellestrecke_nachbarn.get("x1"))
|
||||||
y1_kreisel = float(gefaellestrecke_nachbarn.get("y1"))
|
y1_kreisel = float(gefaellestrecke_nachbarn.get("y1"))
|
||||||
|
|
||||||
|
# DEFAULT → richtung0 verwenden, sonst richtung2
|
||||||
|
effektive_richtung = richtung0 if richtung2 == "DEFAULT" else richtung2
|
||||||
|
|
||||||
|
# Einheitliche Position- und Gefälle-Bestimmung (eliminiert Duplikation)
|
||||||
|
position = _bestimme_position(
|
||||||
|
effektive_richtung, x0_kreisel, x1_kreisel, y0_kreisel, y1_kreisel, hight_position
|
||||||
|
)
|
||||||
|
gefaelle = _bestimme_gefaelle(effektive_richtung, position)
|
||||||
|
|
||||||
if richtung2 == "DEFAULT":
|
if richtung2 == "DEFAULT":
|
||||||
if richtung0 == "Vertikal":
|
# Vertausch wenn Position rechts/lower und Drehungen verschieden
|
||||||
if x0_kreisel < x1_kreisel:
|
if (
|
||||||
position = hight_position + "_links"
|
position in ("higher_rechts", "lower_rechts", "higher_lower", "lower_lower")
|
||||||
else:
|
and drehung0 != drehung1
|
||||||
position = hight_position + "_rechts"
|
and am_kreisel == 0
|
||||||
else:
|
):
|
||||||
if y0_kreisel > y1_kreisel:
|
drehung0, drehung1, hight_position = _tausche_drehungen_und_hoehe(
|
||||||
position = hight_position + "_higher"
|
drehung0, drehung1, hight_position
|
||||||
else:
|
)
|
||||||
position = hight_position + "_lower"
|
|
||||||
|
|
||||||
if richtung0 == "Vertikal":
|
# Austausch damit der 1. Kreisel in der Liste am Kreisel verbunden ist
|
||||||
if position == "lower_rechts" or position == "higher_links":
|
if kreisel_verbunden == 1 and am_kreisel == 2:
|
||||||
gefaelle = "links"
|
am_kreisel = 1 # BUG FIX: war "am_kreisel == 1" (Vergleich statt Zuweisung)
|
||||||
else:
|
drehung0, drehung1, hight_position = _tausche_drehungen_und_hoehe(
|
||||||
gefaelle = "rechts"
|
drehung0, drehung1, hight_position
|
||||||
elif richtung0 == "Horizontal":
|
)
|
||||||
if position == "lower_lower" or position == "higher_higher":
|
|
||||||
gefaelle = "oben"
|
|
||||||
else:
|
|
||||||
gefaelle = "unten"
|
|
||||||
# vertausch der drehung und der höhe für die namens gebung des blockes
|
|
||||||
if (position == "higher_rechts" or position == "lower_rechts" or position=="higher_lower" or position== "lower_lower") and drehung0 != drehung1 and am_kreisel == 0:
|
|
||||||
drehung_2 = drehung0
|
|
||||||
drehung0 = drehung1
|
|
||||||
drehung1= drehung_2
|
|
||||||
if hight_position == "higher":
|
|
||||||
hight_position = "lower"
|
|
||||||
else:
|
|
||||||
hight_position = "higher"
|
|
||||||
|
|
||||||
# austausch der werte damit immer davon ausgehen dass der 1 kreisel in unserer Liste am Kreisel verbuden ist
|
|
||||||
if kreisel_verbunden == 1 and am_kreisel ==2:
|
|
||||||
am_kreisel == 1
|
|
||||||
drehung_2 = drehung0
|
|
||||||
drehung0 = drehung1
|
|
||||||
drehung1= drehung_2
|
|
||||||
if hight_position == "higher":
|
|
||||||
hight_position = "lower"
|
|
||||||
else:
|
|
||||||
hight_position = "higher"
|
|
||||||
else:
|
else:
|
||||||
if richtung2 == "Vertikal":
|
# Austausch wenn am_kreisel == 2
|
||||||
if x0_kreisel < x1_kreisel:
|
|
||||||
position = hight_position + "_links"
|
|
||||||
else:
|
|
||||||
position = hight_position + "_rechts"
|
|
||||||
else:
|
|
||||||
if y0_kreisel > y1_kreisel:
|
|
||||||
position = hight_position + "_higher"
|
|
||||||
else:
|
|
||||||
position = hight_position + "_lower"
|
|
||||||
|
|
||||||
if richtung2 == "Vertikal":
|
|
||||||
if position == "lower_rechts" or position == "higher_links":
|
|
||||||
gefaelle = "links"
|
|
||||||
else:
|
|
||||||
gefaelle = "rechts"
|
|
||||||
elif richtung2 == "Horizontal":
|
|
||||||
if position == "lower_lower" or position == "higher_higher":
|
|
||||||
gefaelle = "oben"
|
|
||||||
else:
|
|
||||||
gefaelle = "unten"
|
|
||||||
# austausch der werte damit immer davon ausgehen dass der 1 kreisel in unserer Liste am Kreisel verbuden ist
|
|
||||||
if am_kreisel == 2:
|
if am_kreisel == 2:
|
||||||
am_kreisel = 1
|
am_kreisel = 1
|
||||||
drehung_2 = drehung0
|
drehung0, drehung1, hight_position = _tausche_drehungen_und_hoehe(
|
||||||
drehung0 = drehung1
|
drehung0, drehung1, hight_position
|
||||||
drehung1= drehung_2
|
)
|
||||||
if hight_position == "higher":
|
|
||||||
hight_position = "lower"
|
|
||||||
else:
|
|
||||||
hight_position = "higher"
|
|
||||||
# Erstellung der Rotation
|
|
||||||
if gefaelle == "oben":
|
|
||||||
rotation = 0
|
|
||||||
elif gefaelle == "unten" :
|
|
||||||
rotation = 180
|
|
||||||
elif gefaelle == "links" :
|
|
||||||
rotation = 90
|
|
||||||
elif gefaelle == "rechts" :
|
|
||||||
rotation = 270
|
|
||||||
return rotation,drehung0,drehung1,hight_position
|
|
||||||
def ein_motor_oder_eine_umlenk(x, y,start,ende, doc, lib_doc, hoehe_gefaelle, block_Vario_Umlenkstation_500mm, block_Vario_Motorstation_500mm, blockname_motor_links, blockname_umlenk_links, hat_motor_0, hat_umlenk_0, tefkurve_0, block,umlenk_gerade,motor_gerade):
|
|
||||||
|
|
||||||
block_Vario_Bogen_auf = (f"Vario_Bogen_auf_3°")
|
rotation = ROTATION_MAP[gefaelle]
|
||||||
block_Vario_Bogen_ab = (f"Vario_Bogen_ab_3°")
|
return rotation, drehung0, drehung1, hight_position
|
||||||
block_Vario_Bogen_auf_links = (f"Vario_Bogen_auf_3°") + "_links"
|
|
||||||
block_Vario_Bogen_ab_links = (f"Vario_Bogen_ab_3°") + "_links"
|
|
||||||
auf_attrib = block_methoden.import_block(block_Vario_Bogen_auf,lib_doc,doc)
|
|
||||||
ab_attrib = block_methoden.import_block(block_Vario_Bogen_ab,lib_doc,doc)
|
|
||||||
Vario_Bogen_auf_Delta_SP_0 = list(float(att)for att in re.split(r"[;,]", auf_attrib["DELTA_SP_0"]))
|
|
||||||
Vario_Bogen_auf_Delta_SP_1 = list(float(att) for att in re.split(r"[;,]", auf_attrib["DELTA_SP_1"]))
|
|
||||||
Vario_Bogen_ab_Delta_SP_0 = list(float(att) for att in re.split(r"[;,]", ab_attrib["DELTA_SP_0"]))
|
|
||||||
Vario_Bogen_ab_Delta_SP_1 = list(float(att) for att in re.split(r"[;,]", ab_attrib["DELTA_SP_1"]))
|
|
||||||
for i, wert in enumerate(Vario_Bogen_auf_Delta_SP_0):
|
|
||||||
if wert < 0:
|
|
||||||
Vario_Bogen_auf_Delta_SP_0[i] = abs(wert)
|
|
||||||
for i, wert in enumerate(Vario_Bogen_auf_Delta_SP_1):
|
|
||||||
if wert< 0:
|
|
||||||
Vario_Bogen_auf_Delta_SP_1[i] = abs(wert)
|
|
||||||
for i, wert in enumerate(Vario_Bogen_ab_Delta_SP_0):
|
|
||||||
if wert< 0:
|
|
||||||
Vario_Bogen_ab_Delta_SP_0[i] = abs(wert)
|
|
||||||
for i, wert in enumerate(Vario_Bogen_ab_Delta_SP_1):
|
|
||||||
if wert< 0:
|
|
||||||
Vario_Bogen_ab_Delta_SP_1[i] = abs(wert)
|
|
||||||
block_methoden.turn_two_blocks_left(doc, block_Vario_Bogen_auf, block_Vario_Bogen_ab, block_Vario_Bogen_ab_links, block_Vario_Bogen_auf_links)
|
|
||||||
if hat_motor_0 == True:
|
|
||||||
if tefkurve_0 == "links":
|
|
||||||
if motor_gerade == False:
|
|
||||||
block.add_blockref(block_Vario_Bogen_ab_links,(start[0]-x,start[1]-Vario_Bogen_ab_Delta_SP_0[0]-y,start[2]- Vario_Bogen_ab_Delta_SP_0[2]-hoehe_gefaelle),dxfattribs={"rotation": 270})
|
|
||||||
start = [start[0],start[1]-Vario_Bogen_ab_Delta_SP_0[0]- Vario_Bogen_ab_Delta_SP_1[0],start[2]-Vario_Bogen_ab_Delta_SP_0[2]- Vario_Bogen_ab_Delta_SP_1[2]]
|
|
||||||
block.add_blockref(blockname_motor_links, (start[0]-x,start[1] - 250* math.cos(math.radians(3))-y,start[2] - 250* math.sin(math.radians(3))-hoehe_gefaelle),dxfattribs={"rotation": 270})
|
|
||||||
start[1]= start[1] - 500* math.cos(math.radians(3))
|
|
||||||
start[2] = start[2] - 500* math.sin(math.radians(3))
|
|
||||||
else:
|
|
||||||
block.add_blockref("Vario_Motorstation_500mm_links", (start[0]-x,start[1] -250 -y,start[2] -hoehe_gefaelle),dxfattribs={"rotation": 270})
|
|
||||||
start[1]= start[1] - 500
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ein_motor_oder_eine_umlenk(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
start,
|
||||||
|
ende,
|
||||||
|
doc,
|
||||||
|
lib_doc,
|
||||||
|
hoehe_gefaelle,
|
||||||
|
block_Vario_Umlenkstation_500mm,
|
||||||
|
block_Vario_Motorstation_500mm,
|
||||||
|
blockname_motor_links,
|
||||||
|
blockname_umlenk_links,
|
||||||
|
hat_motor_0,
|
||||||
|
hat_umlenk_0,
|
||||||
|
tefkurve_0,
|
||||||
|
block,
|
||||||
|
umlenk_gerade,
|
||||||
|
motor_gerade,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Platziert Motor- und/oder Umlenkstation mit optionalen Bögen.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x, y: Zentrum des Förderers
|
||||||
|
start, ende: Start-/End-Punkt der Gefällelinie
|
||||||
|
doc, lib_doc: DXF-Dokumente
|
||||||
|
hoehe_gefaelle: Höhe der Gefällelinie
|
||||||
|
block_Vario_Umlenkstation_500mm: Block-Name Umlenkstation rechts
|
||||||
|
block_Vario_Motorstation_500mm: Block-Name Motorstation rechts
|
||||||
|
blockname_motor_links: Block-Name Motorstation links
|
||||||
|
blockname_umlenk_links: Block-Name Umlenkstation links
|
||||||
|
hat_motor_0, hat_umlenk_0: Ob Motor/Umlenk vorhanden
|
||||||
|
tefkurve_0: "links" oder "rechts"
|
||||||
|
block: DXF-Block zum Hinzufügen
|
||||||
|
umlenk_gerade, motor_gerade: Ob Station gerade (keine Bögen)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(start, ende) nach Platzierung
|
||||||
|
"""
|
||||||
|
# Bogen-Blöcke laden und Deltas extrahieren
|
||||||
|
block_Vario_Bogen_auf = "Vario_Bogen_auf_3°"
|
||||||
|
block_Vario_Bogen_ab = "Vario_Bogen_ab_3°"
|
||||||
|
block_Vario_Bogen_auf_links = block_Vario_Bogen_auf + "_links"
|
||||||
|
block_Vario_Bogen_ab_links = block_Vario_Bogen_ab + "_links"
|
||||||
|
|
||||||
|
ab_deltas = _laedt_bogen_deltas(block_Vario_Bogen_ab, lib_doc, doc)
|
||||||
|
auf_deltas = _laedt_bogen_deltas(block_Vario_Bogen_auf, lib_doc, doc)
|
||||||
|
|
||||||
|
block_methoden.turn_two_blocks_left(
|
||||||
|
doc,
|
||||||
|
block_Vario_Bogen_auf,
|
||||||
|
block_Vario_Bogen_ab,
|
||||||
|
block_Vario_Bogen_ab_links,
|
||||||
|
block_Vario_Bogen_auf_links,
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Motor-Station ---
|
||||||
|
if hat_motor_0:
|
||||||
|
ist_links = (tefkurve_0 == "links")
|
||||||
|
# Motor: tefkurve "links" → bogen_links + station_links
|
||||||
|
bogen_motor = block_Vario_Bogen_ab_links if ist_links else block_Vario_Bogen_ab
|
||||||
|
station_motor = blockname_motor_links if ist_links else block_Vario_Motorstation_500mm
|
||||||
|
station_motor_gerade = (
|
||||||
|
"Vario_Motorstation_500mm_links" if ist_links else "Vario_Motorstation_500mm"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not motor_gerade:
|
||||||
|
start = _add_station_mit_bogen(
|
||||||
|
block, bogen_motor, station_motor, start,
|
||||||
|
x, y, hoehe_gefaelle,
|
||||||
|
ab_deltas["DELTA_SP_0"], ab_deltas["DELTA_SP_1"],
|
||||||
|
vorzeichen=-1, rotation_bogen=270,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if motor_gerade == False:
|
start = _add_station_gerade(
|
||||||
block.add_blockref(block_Vario_Bogen_ab,(start[0]-x,start[1]-Vario_Bogen_ab_Delta_SP_0[0]-y,start[2]- Vario_Bogen_ab_Delta_SP_0[2]-hoehe_gefaelle),dxfattribs={"rotation": 270})
|
block, station_motor_gerade, start,
|
||||||
start = [start[0],start[1]-Vario_Bogen_ab_Delta_SP_0[0]- Vario_Bogen_ab_Delta_SP_1[0],start[2]-Vario_Bogen_ab_Delta_SP_0[2]- Vario_Bogen_ab_Delta_SP_1[2]]
|
x, y, hoehe_gefaelle, vorzeichen=-1,
|
||||||
block.add_blockref(block_Vario_Motorstation_500mm, (start[0]-x,start[1] - 250* math.cos(math.radians(3))-y,start[2] - 250* math.sin(math.radians(3))-hoehe_gefaelle),dxfattribs={"rotation": 270})
|
)
|
||||||
start[1]= start[1] - 500* math.cos(math.radians(3))
|
|
||||||
start[2] = start[2] - 500* math.sin(math.radians(3))
|
|
||||||
else:
|
|
||||||
block.add_blockref("Vario_Motorstation_500mm", (start[0]-x,start[1]- 250 -y,start[2] -hoehe_gefaelle),dxfattribs={"rotation": 270})
|
|
||||||
start[1]= start[1] - 500
|
|
||||||
|
|
||||||
if hat_umlenk_0 == True:
|
# --- Umlenk-Station ---
|
||||||
if tefkurve_0 == "links":
|
if hat_umlenk_0:
|
||||||
if umlenk_gerade == False:
|
ist_links = (tefkurve_0 == "links")
|
||||||
block.add_blockref(block_Vario_Bogen_auf,(ende[0]-x,ende[1]+Vario_Bogen_auf_Delta_SP_0[0]-y,ende[2] + Vario_Bogen_auf_Delta_SP_0[2]-hoehe_gefaelle),dxfattribs={"rotation": 90})
|
# Umlenk: Bogen-Blockname ist INVERTIERT relativ zu tefkurve
|
||||||
ende = [ende[0],ende[1]+ Vario_Bogen_auf_Delta_SP_0[0]+ Vario_Bogen_auf_Delta_SP_1[0],ende[2]+Vario_Bogen_auf_Delta_SP_0[2]+ Vario_Bogen_auf_Delta_SP_1[2]]
|
bogen_umlenk = block_Vario_Bogen_auf if ist_links else block_Vario_Bogen_auf_links
|
||||||
block.add_blockref(blockname_umlenk_links, (ende[0]-x,ende[1] + 250* math.cos(math.radians(3))-y,ende[2] + 250* math.sin(math.radians(3))-hoehe_gefaelle),dxfattribs={"rotation": 270})
|
station_umlenk = blockname_umlenk_links if ist_links else block_Vario_Umlenkstation_500mm
|
||||||
|
station_umlenk_gerade = (
|
||||||
ende [1]= ende[1] + 500* math.cos(math.radians(3))
|
"Vario_Umlenkstation_500mm_links" if ist_links else "Vario_Umlenkstation_500mm"
|
||||||
ende[2] = ende[2] + 500* math.sin(math.radians(3))
|
)
|
||||||
else:
|
|
||||||
block.add_blockref("Vario_Umlenkstation_500mm_links", (ende[0]-x,ende[1] + 250-y,ende[2] -hoehe_gefaelle),dxfattribs={"rotation": 270})
|
|
||||||
ende [1]= ende[1] + 500
|
|
||||||
|
|
||||||
|
if not umlenk_gerade:
|
||||||
|
ende = _add_station_mit_bogen(
|
||||||
|
block, bogen_umlenk, station_umlenk, ende,
|
||||||
|
x, y, hoehe_gefaelle,
|
||||||
|
auf_deltas["DELTA_SP_0"], auf_deltas["DELTA_SP_1"],
|
||||||
|
vorzeichen=+1, rotation_bogen=90,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if umlenk_gerade == False:
|
ende = _add_station_gerade(
|
||||||
block.add_blockref(block_Vario_Bogen_auf_links,(ende[0]-x,ende[1]+Vario_Bogen_auf_Delta_SP_0[0]-y,ende[2] + Vario_Bogen_auf_Delta_SP_0[2]-hoehe_gefaelle),dxfattribs={"rotation": 90})
|
block, station_umlenk_gerade, ende,
|
||||||
ende = [ende[0],ende[1]+ Vario_Bogen_auf_Delta_SP_0[0]+ Vario_Bogen_auf_Delta_SP_1[0],ende[2]+Vario_Bogen_auf_Delta_SP_0[2]+ Vario_Bogen_auf_Delta_SP_1[2]]
|
x, y, hoehe_gefaelle, vorzeichen=+1,
|
||||||
block.add_blockref(block_Vario_Umlenkstation_500mm, (ende[0]-x,ende[1] + 250* math.cos(math.radians(3))-y,ende[2] + 250* math.sin(math.radians(3))-hoehe_gefaelle),dxfattribs={"rotation": 270})
|
)
|
||||||
ende [1]= ende[1] + 500* math.cos(math.radians(3))
|
|
||||||
ende[2] = ende[2] + 500* math.sin(math.radians(3))
|
return start, ende
|
||||||
else:
|
|
||||||
block.add_blockref("Vario_Umlenkstation_500mm", (ende[0]-x,ende[1] + 250-y,ende[2] -hoehe_gefaelle),dxfattribs={"rotation": 270})
|
@staticmethod
|
||||||
ende [1]= ende[1] + 500
|
def hat_motor_umlenk_station(gefaelle_objekt, gefaellestrecke_nachbarn):
|
||||||
return start,ende
|
"""
|
||||||
def hat_motor_umlenk_station (gefaelle_objekt, gefaellestrecke_nachbarn):
|
Analysiert Nachbar-Daten und bestimmt Motor-/Umlenkstation-Zuordnung.
|
||||||
|
|
||||||
|
Untersucht ob an jedem Ende der Gefällelinie eine Motorstation
|
||||||
|
oder eine Umlenkstation angeschlossen ist.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gefaelle_objekt: Gefaellestrecke-Instanz mit h0, h1, drehung, x, y
|
||||||
|
gefaellestrecke_nachbarn: Dict mit Kurven- und Höhen-Informationen
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict mit hat_motor_0/1, hat_umlenk_0/1, tefkurve_0/1,
|
||||||
|
umlenk_gerade, motor_gerade
|
||||||
|
"""
|
||||||
hat_motor_0 = None
|
hat_motor_0 = None
|
||||||
hat_motor_1 = None
|
hat_motor_1 = None
|
||||||
hat_umlenk_0 = None
|
hat_umlenk_0 = None
|
||||||
@@ -223,82 +552,70 @@ class Gefaellestrecke(BaseModel):
|
|||||||
rotation = gefaelle_objekt.drehung
|
rotation = gefaelle_objekt.drehung
|
||||||
x = gefaelle_objekt.x
|
x = gefaelle_objekt.x
|
||||||
y = gefaelle_objekt.y
|
y = gefaelle_objekt.y
|
||||||
|
|
||||||
if "Kurvenrichtung" in gefaellestrecke_nachbarn:
|
if "Kurvenrichtung" not in gefaellestrecke_nachbarn:
|
||||||
vario_hoehe_0 = float(gefaellestrecke_nachbarn.get("vario_hoehe_0"))
|
return {
|
||||||
vario_hoehe_1 = float(gefaellestrecke_nachbarn.get("vario_hoehe_1"))
|
"hat_motor_0": hat_motor_0,
|
||||||
kurvenrichtung = gefaellestrecke_nachbarn.get("Kurvenrichtung")
|
"hat_motor_1": hat_motor_1,
|
||||||
tefkurve_0 = gefaellestrecke_nachbarn.get("Tefkurve")
|
"hat_umlenk_0": hat_umlenk_0,
|
||||||
x_angetrieben = gefaellestrecke_nachbarn.get("X_angetrieben")
|
"hat_umlenk_1": hat_umlenk_1,
|
||||||
y_angetrieben = gefaellestrecke_nachbarn.get("Y_angetrieben")
|
"tefkurve_0": tefkurve_0,
|
||||||
|
"tefkurve_1": tefkurve_1,
|
||||||
|
"umlenk_gerade": umlenk_gerade,
|
||||||
|
"motor_gerade": motor_gerade,
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Erster Nachbar ---
|
||||||
|
vario_hoehe_0 = float(gefaellestrecke_nachbarn.get("vario_hoehe_0"))
|
||||||
|
vario_hoehe_1 = float(gefaellestrecke_nachbarn.get("vario_hoehe_1"))
|
||||||
|
kurvenrichtung = gefaellestrecke_nachbarn.get("Kurvenrichtung")
|
||||||
|
tefkurve_0 = gefaellestrecke_nachbarn.get("Tefkurve")
|
||||||
|
x_angetrieben = gefaellestrecke_nachbarn.get("X_angetrieben")
|
||||||
|
y_angetrieben = gefaellestrecke_nachbarn.get("Y_angetrieben")
|
||||||
|
|
||||||
|
tefkurve_0 = _bestimme_tefkurve(kurvenrichtung, tefkurve_0)
|
||||||
|
|
||||||
|
hat_motor_0, hat_umlenk_0, ist_gerade_0 = _pruefe_motor_umlenk_an_kurve(
|
||||||
|
upper_hoehe_gefaelle, lower_hoehe_gefaelle,
|
||||||
|
vario_hoehe_0, vario_hoehe_1,
|
||||||
|
rotation, x, y, x_angetrieben, y_angetrieben,
|
||||||
|
)
|
||||||
|
# Gerade-Flag nur setzen im gleiche-Höhe-Fall
|
||||||
|
if ist_gerade_0:
|
||||||
|
if hat_umlenk_0:
|
||||||
|
umlenk_gerade = True
|
||||||
|
if hat_motor_0:
|
||||||
|
motor_gerade = True
|
||||||
|
|
||||||
|
# --- Zweiter Nachbar (falls vorhanden) ---
|
||||||
|
if "Kurvenrichtung_1" in gefaellestrecke_nachbarn:
|
||||||
|
vario_hoehe_0_1 = float(gefaellestrecke_nachbarn.get("vario_hoehe_0_1"))
|
||||||
|
vario_hoehe_1_1 = float(gefaellestrecke_nachbarn.get("vario_hoehe_1_1"))
|
||||||
|
kurvenrichtung_1 = gefaellestrecke_nachbarn.get("Kurvenrichtung_1")
|
||||||
|
tefkurve_1 = gefaellestrecke_nachbarn.get("Tefkurve_1")
|
||||||
x_angetrieben_1 = gefaellestrecke_nachbarn.get("X_angetrieben_1")
|
x_angetrieben_1 = gefaellestrecke_nachbarn.get("X_angetrieben_1")
|
||||||
y_angetrieben_1 = gefaellestrecke_nachbarn.get("Y_angetrieben_1")
|
y_angetrieben_1 = gefaellestrecke_nachbarn.get("Y_angetrieben_1")
|
||||||
if (kurvenrichtung == "links" and tefkurve_0 == "außen") or kurvenrichtung == "rechts" and tefkurve_0 == "innen":
|
|
||||||
tefkurve_0 = "rechts"
|
tefkurve_1 = _bestimme_tefkurve(kurvenrichtung_1, tefkurve_1)
|
||||||
else:
|
|
||||||
tefkurve_0 = "links"
|
hat_motor_1, hat_umlenk_1, ist_gerade_1 = _pruefe_motor_umlenk_an_kurve(
|
||||||
|
upper_hoehe_gefaelle, lower_hoehe_gefaelle,
|
||||||
if upper_hoehe_gefaelle > lower_hoehe_gefaelle:
|
vario_hoehe_0_1, vario_hoehe_1_1,
|
||||||
if vario_hoehe_0 == upper_hoehe_gefaelle or vario_hoehe_1 == upper_hoehe_gefaelle:
|
rotation, x, y, x_angetrieben_1, y_angetrieben_1,
|
||||||
hat_motor_0 = True
|
)
|
||||||
else:
|
if ist_gerade_1:
|
||||||
hat_umlenk_0 = True
|
if hat_umlenk_1:
|
||||||
elif upper_hoehe_gefaelle < lower_hoehe_gefaelle:
|
|
||||||
if vario_hoehe_0 == lower_hoehe_gefaelle or vario_hoehe_1 == lower_hoehe_gefaelle:
|
|
||||||
hat_motor_0 = True
|
|
||||||
else:
|
|
||||||
hat_umlenk_0 = True
|
|
||||||
else:
|
|
||||||
rotation_zwischen = rotation
|
|
||||||
if rotation_zwischen == 0.0:
|
|
||||||
rotation_zwischen = -360.0
|
|
||||||
if (((-360.0<= rotation_zwischen< -270.0)and y > y_angetrieben) or ((-90.0< rotation< 0.0)and y > y_angetrieben) or
|
|
||||||
((-270.0< rotation_zwischen< -90.0)and y < y_angetrieben) or
|
|
||||||
(rotation == -90.0 and x < x_angetrieben) or ((rotation == -270.0)and x<x_angetrieben)):
|
|
||||||
hat_umlenk_0 = True
|
|
||||||
umlenk_gerade = True
|
umlenk_gerade = True
|
||||||
else:
|
if hat_motor_1:
|
||||||
hat_motor_0 = True
|
|
||||||
motor_gerade = True
|
motor_gerade = True
|
||||||
|
|
||||||
if "Kurvenrichtung_1" in gefaellestrecke_nachbarn:
|
return {
|
||||||
vario_hoehe_0_1 = float(gefaellestrecke_nachbarn.get("vario_hoehe_0_1"))
|
"hat_motor_0": hat_motor_0,
|
||||||
vario_hoehe_1_1 = float(gefaellestrecke_nachbarn.get("vario_hoehe_1_1"))
|
"hat_motor_1": hat_motor_1,
|
||||||
kurvenrichtung_1 = gefaellestrecke_nachbarn.get("Kurvenrichtung_1")
|
"hat_umlenk_0": hat_umlenk_0,
|
||||||
tefkurve_1 = gefaellestrecke_nachbarn.get("Tefkurve_1")
|
"hat_umlenk_1": hat_umlenk_1,
|
||||||
if (kurvenrichtung_1 == "links" and tefkurve_1 == "außen") or kurvenrichtung_1 == "rechts" and tefkurve_1 == "innen":
|
"tefkurve_0": tefkurve_0,
|
||||||
tefkurve_1 = "rechts"
|
"tefkurve_1": tefkurve_1,
|
||||||
else:
|
"umlenk_gerade": umlenk_gerade,
|
||||||
tefkurve_1 = "links"
|
"motor_gerade": motor_gerade,
|
||||||
if upper_hoehe_gefaelle > lower_hoehe_gefaelle:
|
}
|
||||||
if vario_hoehe_0_1 == upper_hoehe_gefaelle or vario_hoehe_1_1 == upper_hoehe_gefaelle:
|
|
||||||
hat_motor_1 = True
|
|
||||||
else:
|
|
||||||
hat_umlenk_1 = True
|
|
||||||
elif upper_hoehe_gefaelle < lower_hoehe_gefaelle:
|
|
||||||
if vario_hoehe_0_1 == lower_hoehe_gefaelle or vario_hoehe_1_1 == lower_hoehe_gefaelle:
|
|
||||||
hat_motor_1 = True
|
|
||||||
else:
|
|
||||||
hat_umlenk_1 = True
|
|
||||||
else:
|
|
||||||
rotation_zwischen = rotation
|
|
||||||
if rotation_zwischen == 0.0:
|
|
||||||
rotation_zwischen = -360.0
|
|
||||||
if (((-360.0<= rotation_zwischen< -270.0)and y > y_angetrieben_1) or ((-90.0< rotation< 0.0)and y > y_angetrieben_1) or
|
|
||||||
((-270.0< rotation_zwischen< -90.0)and y < y_angetrieben_1) or
|
|
||||||
(rotation == -90.0 and x < x_angetrieben_1) or ((rotation == -270.0)and x<x_angetrieben_1)):
|
|
||||||
hat_umlenk_1 = True
|
|
||||||
umlenk_gerade = True
|
|
||||||
else:
|
|
||||||
hat_motor_1 = True
|
|
||||||
motor_gerade = True
|
|
||||||
hat_zusatz ={}
|
|
||||||
hat_zusatz["hat_motor_0"] = hat_motor_0
|
|
||||||
hat_zusatz["hat_motor_1"] = hat_motor_1
|
|
||||||
hat_zusatz["hat_umlenk_0"] = hat_umlenk_0
|
|
||||||
hat_zusatz["hat_umlenk_1"] = hat_umlenk_1
|
|
||||||
hat_zusatz["tefkurve_0"] = tefkurve_0
|
|
||||||
hat_zusatz["tefkurve_1"] = tefkurve_1
|
|
||||||
hat_zusatz["umlenk_gerade"] = umlenk_gerade
|
|
||||||
hat_zusatz["motor_gerade"] = motor_gerade
|
|
||||||
return hat_zusatz
|
|
||||||
|
|||||||
@@ -0,0 +1,238 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Unit-Tests für Gefaellestrecke (Refactored)
|
||||||
|
"""
|
||||||
|
import unittest
|
||||||
|
import math
|
||||||
|
|
||||||
|
from .Gefaellestrecke import (
|
||||||
|
Gefaellestrecke,
|
||||||
|
ROTATION_MAP,
|
||||||
|
_bestimme_position,
|
||||||
|
_bestimme_gefaelle,
|
||||||
|
_tausche_drehungen_und_hoehe,
|
||||||
|
_bestimme_tefkurve,
|
||||||
|
_ist_umlenk_position,
|
||||||
|
_pruefe_motor_umlenk_an_kurve,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGefaellestreckeModell(unittest.TestCase):
|
||||||
|
"""Tests für das Gefaellestrecke Pydantic-Modell."""
|
||||||
|
|
||||||
|
def test_from_merkmale(self):
|
||||||
|
merkmale = {
|
||||||
|
"Höhe unten": "1.5",
|
||||||
|
"Höhe oben": "2.0",
|
||||||
|
"Länge in Meter": "5.0",
|
||||||
|
"Drehung": "-90",
|
||||||
|
"Anzahl der Scanner": "2",
|
||||||
|
"Anzahl der Separatoren": "1",
|
||||||
|
}
|
||||||
|
obj = Gefaellestrecke.from_merkmale("TEST-001", 100.0, 200.0, merkmale)
|
||||||
|
self.assertEqual(obj.teileid, "TEST-001")
|
||||||
|
self.assertEqual(obj.h0, 1500.0)
|
||||||
|
self.assertEqual(obj.h1, 2000.0)
|
||||||
|
self.assertEqual(obj.laenge, 5000.0)
|
||||||
|
self.assertEqual(obj.drehung, -90.0)
|
||||||
|
self.assertEqual(obj.anzahl_scanner, 2)
|
||||||
|
self.assertEqual(obj.anzahl_separatoren, 1)
|
||||||
|
|
||||||
|
def test_hight_zwischen(self):
|
||||||
|
obj = Gefaellestrecke(teileid="T", x=0, y=0, laenge=1000, h0=1000, h1=3000)
|
||||||
|
self.assertEqual(obj.hight_zwischen, 2000.0)
|
||||||
|
|
||||||
|
|
||||||
|
class TestBestimmePosition(unittest.TestCase):
|
||||||
|
"""Tests für _bestimme_position Hilfsfunktion."""
|
||||||
|
|
||||||
|
def test_vertikal_links(self):
|
||||||
|
# x0 < x1 → "_links"
|
||||||
|
result = _bestimme_position("Vertikal", 100, 200, 0, 0, "higher")
|
||||||
|
self.assertEqual(result, "higher_links")
|
||||||
|
|
||||||
|
def test_vertikal_rechts(self):
|
||||||
|
# x0 > x1 → "_rechts"
|
||||||
|
result = _bestimme_position("Vertikal", 300, 200, 0, 0, "lower")
|
||||||
|
self.assertEqual(result, "lower_rechts")
|
||||||
|
|
||||||
|
def test_horizontal_higher(self):
|
||||||
|
# y0 > y1 → "_higher"
|
||||||
|
result = _bestimme_position("Horizontal", 0, 0, 500, 100, "higher")
|
||||||
|
self.assertEqual(result, "higher_higher")
|
||||||
|
|
||||||
|
def test_horizontal_lower(self):
|
||||||
|
# y0 < y1 → "_lower"
|
||||||
|
result = _bestimme_position("Horizontal", 0, 0, 50, 500, "lower")
|
||||||
|
self.assertEqual(result, "lower_lower")
|
||||||
|
|
||||||
|
|
||||||
|
class TestBestimmeGefaelle(unittest.TestCase):
|
||||||
|
"""Tests für _bestimme_gefaelle Hilfsfunktion."""
|
||||||
|
|
||||||
|
def test_vertikal_links(self):
|
||||||
|
self.assertEqual(_bestimme_gefaelle("Vertikal", "lower_rechts"), "links")
|
||||||
|
self.assertEqual(_bestimme_gefaelle("Vertikal", "higher_links"), "links")
|
||||||
|
|
||||||
|
def test_vertikal_rechts(self):
|
||||||
|
self.assertEqual(_bestimme_gefaelle("Vertikal", "higher_rechts"), "rechts")
|
||||||
|
self.assertEqual(_bestimme_gefaelle("Vertikal", "lower_links"), "rechts")
|
||||||
|
|
||||||
|
def test_horizontal_oben(self):
|
||||||
|
self.assertEqual(_bestimme_gefaelle("Horizontal", "lower_lower"), "oben")
|
||||||
|
self.assertEqual(_bestimme_gefaelle("Horizontal", "higher_higher"), "oben")
|
||||||
|
|
||||||
|
def test_horizontal_unten(self):
|
||||||
|
self.assertEqual(_bestimme_gefaelle("Horizontal", "higher_lower"), "unten")
|
||||||
|
self.assertEqual(_bestimme_gefaelle("Horizontal", "lower_higher"), "unten")
|
||||||
|
|
||||||
|
|
||||||
|
class TestTauscheDrehungen(unittest.TestCase):
|
||||||
|
"""Tests für _tausche_drehungen_und_hoehe."""
|
||||||
|
|
||||||
|
def test_tausch_drehungen(self):
|
||||||
|
d0, d1, h = _tausche_drehungen_und_hoehe(10, 20, "higher")
|
||||||
|
self.assertEqual(d0, 20)
|
||||||
|
self.assertEqual(d1, 10)
|
||||||
|
self.assertEqual(h, "lower")
|
||||||
|
|
||||||
|
def test_tausch_invert_hoehe(self):
|
||||||
|
_, _, h = _tausche_drehungen_und_hoehe(0, 0, "lower")
|
||||||
|
self.assertEqual(h, "higher")
|
||||||
|
|
||||||
|
|
||||||
|
class TestBestimmeeTefkurve(unittest.TestCase):
|
||||||
|
"""Tests für _bestimme_tefkurve."""
|
||||||
|
|
||||||
|
def test_links_aussen_ist_rechts(self):
|
||||||
|
self.assertEqual(_bestimme_tefkurve("links", "außen"), "rechts")
|
||||||
|
|
||||||
|
def test_rechts_innen_ist_rechts(self):
|
||||||
|
self.assertEqual(_bestimme_tefkurve("rechts", "innen"), "rechts")
|
||||||
|
|
||||||
|
def test_links_innen_ist_links(self):
|
||||||
|
self.assertEqual(_bestimme_tefkurve("links", "innen"), "links")
|
||||||
|
|
||||||
|
def test_rechts_aussen_ist_links(self):
|
||||||
|
self.assertEqual(_bestimme_tefkurve("rechts", "außen"), "links")
|
||||||
|
|
||||||
|
|
||||||
|
class TestIstUmlenkPosition(unittest.TestCase):
|
||||||
|
"""Tests für _ist_umlenk_position."""
|
||||||
|
|
||||||
|
def test_rotation_minus90_x_kleiner(self):
|
||||||
|
# rotation == -90 und x < x_angetrieben → True (Umlenk)
|
||||||
|
self.assertTrue(_ist_umlenk_position(-90.0, 50, 100, 200, 100))
|
||||||
|
|
||||||
|
def test_rotation_minus90_x_groesser(self):
|
||||||
|
# rotation == -90 und x >= x_angetrieben → False (Motor)
|
||||||
|
self.assertFalse(_ist_umlenk_position(-90.0, 300, 100, 200, 100))
|
||||||
|
|
||||||
|
def test_rotation_0_wird_minus360(self):
|
||||||
|
# rotation == 0 → rotation_zwischen = -360
|
||||||
|
# -360 <= -360 < -270 und y > y_angetrieben → True
|
||||||
|
self.assertTrue(_ist_umlenk_position(0.0, 100, 500, 100, 100))
|
||||||
|
|
||||||
|
|
||||||
|
class TestPruefeMotorUmlenkAnKurve(unittest.TestCase):
|
||||||
|
"""Tests für _pruefe_motor_umlenk_an_kurve."""
|
||||||
|
|
||||||
|
def test_upper_hoeher_motor_passend(self):
|
||||||
|
# upper > lower, vario_hoehe_0 == upper → Motor
|
||||||
|
hat_motor, hat_umlenk, ist_gerade = _pruefe_motor_umlenk_an_kurve(
|
||||||
|
2000, 1000, 2000, 1500, 0, 0, 0, 0, 0
|
||||||
|
)
|
||||||
|
self.assertTrue(hat_motor)
|
||||||
|
self.assertFalse(hat_umlenk)
|
||||||
|
self.assertFalse(ist_gerade)
|
||||||
|
|
||||||
|
def test_upper_hoeher_umlenk(self):
|
||||||
|
# upper > lower, keine Höhe passt → Umlenk
|
||||||
|
hat_motor, hat_umlenk, ist_gerade = _pruefe_motor_umlenk_an_kurve(
|
||||||
|
2000, 1000, 1500, 1200, 0, 0, 0, 0, 0
|
||||||
|
)
|
||||||
|
self.assertFalse(hat_motor)
|
||||||
|
self.assertTrue(hat_umlenk)
|
||||||
|
self.assertFalse(ist_gerade)
|
||||||
|
|
||||||
|
def test_lower_hoeher_motor(self):
|
||||||
|
# upper < lower, vario_hoehe_1 == lower → Motor
|
||||||
|
hat_motor, hat_umlenk, ist_gerade = _pruefe_motor_umlenk_an_kurve(
|
||||||
|
1000, 2000, 1500, 2000, 0, 0, 0, 0, 0
|
||||||
|
)
|
||||||
|
self.assertTrue(hat_motor)
|
||||||
|
self.assertFalse(hat_umlenk)
|
||||||
|
|
||||||
|
def test_gleiche_hoehe_umlenk_position(self):
|
||||||
|
# gleiche Höhe, Umlenkposition → Umlenk gerade
|
||||||
|
hat_motor, hat_umlenk, ist_gerade = _pruefe_motor_umlenk_an_kurve(
|
||||||
|
1000, 1000, 800, 900, -90.0, 50, 100, 200, 100
|
||||||
|
)
|
||||||
|
self.assertFalse(hat_motor)
|
||||||
|
self.assertTrue(hat_umlenk)
|
||||||
|
self.assertTrue(ist_gerade)
|
||||||
|
|
||||||
|
def test_gleiche_hoehe_motor_position(self):
|
||||||
|
# gleiche Höhe, keine Umlenkposition → Motor gerade
|
||||||
|
hat_motor, hat_umlenk, ist_gerade = _pruefe_motor_umlenk_an_kurve(
|
||||||
|
1000, 1000, 800, 900, -90.0, 300, 100, 200, 100
|
||||||
|
)
|
||||||
|
self.assertTrue(hat_motor)
|
||||||
|
self.assertFalse(hat_umlenk)
|
||||||
|
self.assertTrue(ist_gerade)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRotationMap(unittest.TestCase):
|
||||||
|
"""Tests für ROTATION_MAP Konstante."""
|
||||||
|
|
||||||
|
def test_alle_richtungen(self):
|
||||||
|
self.assertEqual(ROTATION_MAP["oben"], 0)
|
||||||
|
self.assertEqual(ROTATION_MAP["unten"], 180)
|
||||||
|
self.assertEqual(ROTATION_MAP["links"], 90)
|
||||||
|
self.assertEqual(ROTATION_MAP["rechts"], 270)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRotationMitZweiVerbunden(unittest.TestCase):
|
||||||
|
"""Tests für rotation_mit_zwei_verbunden (Bug-Fix Zeile 112 getestet)."""
|
||||||
|
|
||||||
|
def _nachbarn(self):
|
||||||
|
return {
|
||||||
|
"Drehung0": 90, "Drehung1": 180,
|
||||||
|
"x0": "100", "y0": "200", "x1": "300", "y1": "400",
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_default_richtung_vertikal(self):
|
||||||
|
# richtung2 == "DEFAULT", richtung0 == "Vertikal"
|
||||||
|
# x0(100) < x1(300) → "_links", position="higher_links"
|
||||||
|
# gefaelle="links" → rotation=90
|
||||||
|
rotation, d0, d1, hp = Gefaellestrecke.rotation_mit_zwei_verbunden(
|
||||||
|
self._nachbarn(), "DEFAULT", "Vertikal", 0, 0, "higher"
|
||||||
|
)
|
||||||
|
self.assertEqual(rotation, 90)
|
||||||
|
|
||||||
|
def test_nicht_default_richtung_horizontal(self):
|
||||||
|
# richtung2 != "DEFAULT", richtung2 == "Horizontal"
|
||||||
|
# y0(200) < y1(400) → "_lower", position="higher_lower"
|
||||||
|
# gefaelle="unten" → rotation=180
|
||||||
|
rotation, _, _, _ = Gefaellestrecke.rotation_mit_zwei_verbunden(
|
||||||
|
self._nachbarn(), "Horizontal", "Vertikal", 0, 0, "higher"
|
||||||
|
)
|
||||||
|
self.assertEqual(rotation, 180)
|
||||||
|
|
||||||
|
def test_bug_fix_am_kreisel_zuweisung(self):
|
||||||
|
# kreisel_verbunden=1, am_kreisel=2 → muss tauschen (Bug fix: war ==)
|
||||||
|
nachbarn = self._nachbarn()
|
||||||
|
# Drehungen verschieden damit Tausch sichtbar
|
||||||
|
nachbarn["Drehung0"] = 45
|
||||||
|
nachbarn["Drehung1"] = 135
|
||||||
|
rotation, d0, d1, hp = Gefaellestrecke.rotation_mit_zwei_verbunden(
|
||||||
|
nachbarn, "DEFAULT", "Vertikal", 2, 1, "higher"
|
||||||
|
)
|
||||||
|
# Drehungen sollten getauscht sein
|
||||||
|
self.assertEqual(d0, 135)
|
||||||
|
self.assertEqual(d1, 45)
|
||||||
|
self.assertEqual(hp, "lower")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
+142
-101
@@ -1,14 +1,64 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from ezdxf.entities import Line
|
"""
|
||||||
|
Kreisel — Modell und Zeichenfunktionen für Kreisel-Komponenten.
|
||||||
|
|
||||||
|
Refactoring-Änderungen:
|
||||||
|
- Pin-Zeichenlogik (4x copy-paste für 0°/90°/180°/270°) durch PIN_OFFSETS-Lookup-Tabelle ersetzt
|
||||||
|
- @staticmethod auf draw_kreisel_lines und draw_kreisel_drehrichtung_markierung
|
||||||
|
- Magic Number 50 durch benanntes Constant PIN_OFFSET ersetzt
|
||||||
|
- Richtungspfeil-Import aus Loop herausgezogen (war 6x idempotent aufgerufen)
|
||||||
|
- Unused variable bref entfernt
|
||||||
|
"""
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
from ezdxf.entities import Line
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, field_validator
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import plant2dxf
|
|
||||||
import block_methoden
|
|
||||||
|
|
||||||
|
from lib import block_methoden
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# KONSTANTEN
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
ATTR_TAG = "TeileId" # Attributtag im Block
|
ATTR_TAG = "TeileId" # Attributtag im Block
|
||||||
RADIUS = 400 # Radius der Kreiselkreise (mm)
|
RADIUS = 400 # Radius der Kreiselkreise (mm)
|
||||||
|
PIN_OFFSET = 50 # Offset für Pin-Bereichsmarkierung (mm)
|
||||||
|
|
||||||
|
# Pin-Bereichs-Offsets pro Drehung: (p1a_dx, p1a_dy, p1b_dx, p1b_dy, p2a_dx, p2a_dy, p2b_dx, p2b_dy)
|
||||||
|
# Berechnet aus dem manuell kodierten Original für 0°/90°/180°/270°
|
||||||
|
PIN_OFFSETS = {
|
||||||
|
0.0: (
|
||||||
|
-(RADIUS + PIN_OFFSET), +PIN_OFFSET,
|
||||||
|
-(RADIUS + PIN_OFFSET), -PIN_OFFSET,
|
||||||
|
+(RADIUS + PIN_OFFSET), +PIN_OFFSET,
|
||||||
|
+(RADIUS + PIN_OFFSET), -PIN_OFFSET,
|
||||||
|
),
|
||||||
|
180.0: (
|
||||||
|
+(RADIUS + PIN_OFFSET), -PIN_OFFSET,
|
||||||
|
+(RADIUS + PIN_OFFSET), +PIN_OFFSET,
|
||||||
|
-(RADIUS + PIN_OFFSET), -PIN_OFFSET,
|
||||||
|
-(RADIUS + PIN_OFFSET), +PIN_OFFSET,
|
||||||
|
),
|
||||||
|
90.0: (
|
||||||
|
+PIN_OFFSET, +(RADIUS - PIN_OFFSET),
|
||||||
|
-PIN_OFFSET, +(RADIUS - PIN_OFFSET),
|
||||||
|
+PIN_OFFSET, -(RADIUS - PIN_OFFSET),
|
||||||
|
-PIN_OFFSET, -(RADIUS - PIN_OFFSET),
|
||||||
|
),
|
||||||
|
270.0: (
|
||||||
|
-PIN_OFFSET, -(RADIUS - PIN_OFFSET),
|
||||||
|
+PIN_OFFSET, -(RADIUS - PIN_OFFSET),
|
||||||
|
-PIN_OFFSET, +(RADIUS - PIN_OFFSET),
|
||||||
|
+PIN_OFFSET, +(RADIUS - PIN_OFFSET),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# KREISEL KLASSE
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
class Kreisel(BaseModel):
|
class Kreisel(BaseModel):
|
||||||
@@ -71,7 +121,6 @@ class Kreisel(BaseModel):
|
|||||||
@property
|
@property
|
||||||
def richtung_rad(self) -> float:
|
def richtung_rad(self) -> float:
|
||||||
"""Richtung in Radianten (für am_kreisel_direct_verbunden)."""
|
"""Richtung in Radianten (für am_kreisel_direct_verbunden)."""
|
||||||
# Wird aus drehung abgeleitet oder separat gesetzt
|
|
||||||
return math.radians(self.drehung)
|
return math.radians(self.drehung)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -140,11 +189,22 @@ class Kreisel(BaseModel):
|
|||||||
anzahl_separatoren=anzahl_separatoren,
|
anzahl_separatoren=anzahl_separatoren,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def draw_kreisel_lines(msp, pos1, pos2, kreisel):
|
def draw_kreisel_lines(msp, pos1, pos2, kreisel):
|
||||||
"""Zeichnet tangentiale Linien zwischen zwei Kreiselblöcken, unabhängig vom Winkel."""
|
"""
|
||||||
|
Zeichnet tangentiale Linien zwischen zwei Kreiselblöcken, unabhängig vom Winkel.
|
||||||
|
|
||||||
|
Für Pin-Kreisel werden zusätzlich Pinbereichs-Begrenzungslinien gezeichnet,
|
||||||
|
die sich basierend auf der Drehung (0°/90°/180°/270°) verschieben.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msp: DXF-Modelspace
|
||||||
|
pos1, pos2: Positionen der beiden Kreisel-Blöcke (x, y, z)
|
||||||
|
kreisel: Kreisel-Instanz
|
||||||
|
"""
|
||||||
rotation = kreisel.drehung
|
rotation = kreisel.drehung
|
||||||
x1, y1, z1 = pos1
|
x1, y1, z1 = pos1
|
||||||
x2, y2, z1 = pos2
|
x2, y2, _ = pos2 # z identisch für beide Blöcke
|
||||||
# Verbindungsvektor
|
# Verbindungsvektor
|
||||||
dx = x2 - x1
|
dx = x2 - x1
|
||||||
dy = y2 - y1
|
dy = y2 - y1
|
||||||
@@ -153,7 +213,6 @@ class Kreisel(BaseModel):
|
|||||||
if length == 0:
|
if length == 0:
|
||||||
return # keine Linie bei identischen Punkten
|
return # keine Linie bei identischen Punkten
|
||||||
# Normalenvektor (senkrecht, normiert, Länge = RADIUS)
|
# Normalenvektor (senkrecht, normiert, Länge = RADIUS)
|
||||||
|
|
||||||
nx = -dy / length * RADIUS
|
nx = -dy / length * RADIUS
|
||||||
ny = dx / length * RADIUS
|
ny = dx / length * RADIUS
|
||||||
# Tangentialpunkte
|
# Tangentialpunkte
|
||||||
@@ -161,72 +220,50 @@ class Kreisel(BaseModel):
|
|||||||
p1b = (x1 - nx, y1 - ny, z1)
|
p1b = (x1 - nx, y1 - ny, z1)
|
||||||
p2a = (x2 + nx, y2 + ny, z1)
|
p2a = (x2 + nx, y2 + ny, z1)
|
||||||
p2b = (x2 - nx, y2 - ny, z1)
|
p2b = (x2 - nx, y2 - ny, z1)
|
||||||
if kreisel.kreiselart == "Pin":
|
|
||||||
if rotation == 0.0:
|
|
||||||
p1a2 = p1a[0] - RADIUS - 50, p1a[1] + 50, z1
|
|
||||||
p1b2 = p1b[0] - RADIUS - 50, p1b[1] - 50, z1
|
|
||||||
p2a2 = p2a[0] + RADIUS + 50, p2a[1] + 50, z1
|
|
||||||
p2b2 = p2b[0] + RADIUS + 50, p2b[1] - 50, z1
|
|
||||||
Line1 = Line.new(
|
|
||||||
dxfattribs={"start": p1a2, "end": p2a2, "layer": "Pinbereich"}
|
|
||||||
)
|
|
||||||
Line2 = Line.new(
|
|
||||||
dxfattribs={"start": p1b2, "end": p2b2, "layer": "Pinbereich"}
|
|
||||||
)
|
|
||||||
msp.add_entity(Line1)
|
|
||||||
msp.add_entity(Line2)
|
|
||||||
elif rotation == 180.0:
|
|
||||||
p1a2 = p1a[0] + RADIUS + 50, p1a[1] - 50, z1
|
|
||||||
p1b2 = p1b[0] + RADIUS + 50, p1b[1] + 50, z1
|
|
||||||
p2a2 = p2a[0] - RADIUS - 50, p2a[1] - 50, z1
|
|
||||||
p2b2 = p2b[0] - RADIUS - 50, p2b[1] + 50, z1
|
|
||||||
Line1 = Line.new(
|
|
||||||
dxfattribs={"start": p1a2, "end": p2a2, "layer": "Pinbereich"}
|
|
||||||
)
|
|
||||||
Line2 = Line.new(
|
|
||||||
dxfattribs={"start": p1b2, "end": p2b2, "layer": "Pinbereich"}
|
|
||||||
)
|
|
||||||
msp.add_entity(Line1)
|
|
||||||
msp.add_entity(Line2)
|
|
||||||
elif rotation == 90.0:
|
|
||||||
p1a2 = p1a[0] + 50, p1a[1] - 50 + RADIUS, z1
|
|
||||||
p1b2 = p1b[0] - 50, p1b[1] - 50 + RADIUS, z1
|
|
||||||
p2a2 = p2a[0] + 50, p2a[1] + 50 - RADIUS, z1
|
|
||||||
p2b2 = p2b[0] - 50, p2b[1] + 50 - RADIUS, z1
|
|
||||||
Line1 = Line.new(
|
|
||||||
dxfattribs={"start": p1a2, "end": p2a2, "layer": "Pinbereich"}
|
|
||||||
)
|
|
||||||
Line2 = Line.new(
|
|
||||||
dxfattribs={"start": p1b2, "end": p2b2, "layer": "Pinbereich"}
|
|
||||||
)
|
|
||||||
msp.add_entity(Line1)
|
|
||||||
msp.add_entity(Line2)
|
|
||||||
elif rotation == 270.0:
|
|
||||||
p1a2 = p1a[0] - 50, p1a[1] + 50 - RADIUS, z1
|
|
||||||
p1b2 = p1b[0] + 50, p1b[1] + 50 - RADIUS, z1
|
|
||||||
p2a2 = p2a[0] - 50, p2a[1] - 50 + RADIUS, z1
|
|
||||||
p2b2 = p2b[0] + 50, p2b[1] - 50 + RADIUS, z1
|
|
||||||
Line1 = Line.new(
|
|
||||||
dxfattribs={"start": p1a2, "end": p2a2, "layer": "Pinbereich"}
|
|
||||||
)
|
|
||||||
Line2 = Line.new(
|
|
||||||
dxfattribs={"start": p1b2, "end": p2b2, "layer": "Pinbereich"}
|
|
||||||
)
|
|
||||||
msp.add_entity(Line1)
|
|
||||||
msp.add_entity(Line2)
|
|
||||||
|
|
||||||
# Linien zeichnen
|
# Pin-Bereichsmarkierung (Lookup-Tabelle statt 4x copy-paste)
|
||||||
|
if kreisel.kreiselart == "Pin" and rotation in PIN_OFFSETS:
|
||||||
|
offsets = PIN_OFFSETS[rotation]
|
||||||
|
points = [p1a, p1b, p2a, p2b]
|
||||||
|
adjusted = [
|
||||||
|
(p[0] + offsets[i * 2], p[1] + offsets[i * 2 + 1], z1)
|
||||||
|
for i, p in enumerate(points)
|
||||||
|
]
|
||||||
|
p1a2, p1b2, p2a2, p2b2 = adjusted
|
||||||
|
msp.add_entity(Line.new(
|
||||||
|
dxfattribs={"start": p1a2, "end": p2a2, "layer": "Pinbereich"}
|
||||||
|
))
|
||||||
|
msp.add_entity(Line.new(
|
||||||
|
dxfattribs={"start": p1b2, "end": p2b2, "layer": "Pinbereich"}
|
||||||
|
))
|
||||||
|
|
||||||
|
# Hauptlinien zeichnen
|
||||||
msp.add_line(p1a, p2a)
|
msp.add_line(p1a, p2a)
|
||||||
msp.add_line(p1b, p2b)
|
msp.add_line(p1b, p2b)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def draw_kreisel_drehrichtung_markierung(
|
def draw_kreisel_drehrichtung_markierung(
|
||||||
msp, pos1, pos2, kreisel, lib_doc, doc, verbose
|
msp, pos1, pos2, kreisel, lib_doc, doc, verbose
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Zeichnet Richtungspfeile für die Drehrichtung eines Kreisels.
|
||||||
|
|
||||||
|
Platziert 3 Pfeile auf der oberen und 3 auf der unteren Tangentiallinie,
|
||||||
|
mit invertierter Richtung zwischen oben und unten.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msp: DXF-Modelspace
|
||||||
|
pos1, pos2: Positionen der beiden Kreisel-Blöcke
|
||||||
|
kreisel: Kreisel-Instanz
|
||||||
|
lib_doc: Bibliotheks-Dokument
|
||||||
|
doc: DXF-Dokument
|
||||||
|
verbose: Ob Debug-Ausgabe aktiviert ist
|
||||||
|
"""
|
||||||
drehrichtung = (kreisel.drehrichtung or "").upper()
|
drehrichtung = (kreisel.drehrichtung or "").upper()
|
||||||
if drehrichtung not in ("UZS", "GUZS"):
|
if drehrichtung not in ("UZS", "GUZS"):
|
||||||
return
|
return
|
||||||
x1, y1, z1 = pos1
|
x1, y1, z1 = pos1
|
||||||
x2, y2, z2 = pos2
|
x2, y2, _ = pos2
|
||||||
dx = x2 - x1
|
dx = x2 - x1
|
||||||
dy = y2 - y1
|
dy = y2 - y1
|
||||||
length = math.hypot(dx, dy)
|
length = math.hypot(dx, dy)
|
||||||
@@ -235,55 +272,59 @@ class Kreisel(BaseModel):
|
|||||||
# Normalenvektor (senkrecht, normiert, Länge = RADIUS)
|
# Normalenvektor (senkrecht, normiert, Länge = RADIUS)
|
||||||
nx = -dy / length * RADIUS
|
nx = -dy / length * RADIUS
|
||||||
ny = dx / length * RADIUS
|
ny = dx / length * RADIUS
|
||||||
# Obere Linie
|
# Obere und untere Tangentiallinien
|
||||||
p1_oben = (x1 + nx, y1 + ny)
|
p1_oben = (x1 + nx, y1 + ny)
|
||||||
p2_oben = (x2 + nx, y2 + ny)
|
p2_oben = (x2 + nx, y2 + ny)
|
||||||
# Untere Linie
|
|
||||||
p1_unten = (x1 - nx, y1 - ny)
|
p1_unten = (x1 - nx, y1 - ny)
|
||||||
p2_unten = (x2 - nx, y2 - ny)
|
p2_unten = (x2 - nx, y2 - ny)
|
||||||
# S-LP auf oberer Linie (Drehrichtung wie angegeben)
|
|
||||||
|
# Richtungspfeil einmal importieren (nicht 6x in der Schleife)
|
||||||
|
block_methoden.import_block("Richtungspfeil", lib_doc, doc)
|
||||||
|
blockref_layer, color = block_methoden.get_insert_color_layer(lib_doc, "Richtungspfeil")
|
||||||
|
|
||||||
|
# Obere Linie: Drehrichtung wie angegeben
|
||||||
|
Kreisel._place_richtungspfeile(
|
||||||
|
msp, p1_oben, p2_oben, z1, drehrichtung,
|
||||||
|
invert=False, blockref_layer=blockref_layer,
|
||||||
|
verbose=verbose, label="oben",
|
||||||
|
)
|
||||||
|
# Untere Linie: Drehrichtung invertiert
|
||||||
|
Kreisel._place_richtungspfeile(
|
||||||
|
msp, p1_unten, p2_unten, z1, drehrichtung,
|
||||||
|
invert=True, blockref_layer=blockref_layer,
|
||||||
|
verbose=verbose, label="unten",
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _place_richtungspfeile(msp, p1, p2, z, drehrichtung, invert, blockref_layer, verbose, label):
|
||||||
|
"""
|
||||||
|
Platziert 3 Richtungspfeile entlang einer Tangentiallinie.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msp: DXF-Modelspace
|
||||||
|
p1, p2: Start-/End-Punkt der Linie (x, y)
|
||||||
|
z: Z-Koordinate
|
||||||
|
drehrichtung: "UZS" oder "GUZS"
|
||||||
|
invert: Ob die Richtung invertiert werden soll
|
||||||
|
blockref_layer: Layer für den Block-Reference
|
||||||
|
verbose: Ob Debug-Ausgabe aktiviert ist
|
||||||
|
label: "oben" oder "unten" für Logging
|
||||||
|
"""
|
||||||
for i in range(1, 4):
|
for i in range(1, 4):
|
||||||
t = i / 4 # 1/4, 2/4, 3/4
|
t = i / 4 # 1/4, 2/4, 3/4
|
||||||
px = p1_oben[0] + t * (p2_oben[0] - p1_oben[0])
|
px = p1[0] + t * (p2[0] - p1[0])
|
||||||
py = p1_oben[1] + t * (p2_oben[1] - p1_oben[1])
|
py = p1[1] + t * (p2[1] - p1[1])
|
||||||
rotation = math.degrees(
|
rotation = math.degrees(math.atan2(p2[1] - p1[1], p2[0] - p1[0]))
|
||||||
math.atan2(p2_oben[1] - p1_oben[1], p2_oben[0] - p1_oben[0])
|
# Obere Linie: GUZS invertiert; Untere Linie: UZS invertiert
|
||||||
)
|
should_invert = (drehrichtung == "GUZS" and not invert) or (drehrichtung == "UZS" and invert)
|
||||||
if drehrichtung == "GUZS":
|
if should_invert:
|
||||||
rotation += 180
|
rotation += 180
|
||||||
block_methoden.import_block("Richtungspfeil", lib_doc, doc)
|
msp.add_blockref(
|
||||||
blockref_layer, color = block_methoden.get_insert_color_layer(
|
|
||||||
lib_doc, "Richtungspfeil"
|
|
||||||
)
|
|
||||||
bref = msp.add_blockref(
|
|
||||||
"Richtungspfeil",
|
"Richtungspfeil",
|
||||||
(px, py, z1),
|
(px, py, z),
|
||||||
dxfattribs={"rotation": rotation, "layer": blockref_layer},
|
dxfattribs={"rotation": rotation, "layer": blockref_layer},
|
||||||
)
|
)
|
||||||
if verbose:
|
if verbose:
|
||||||
print(
|
print(
|
||||||
f"[INFO] Drehrichtung '{drehrichtung}': Richtungspfeil oben bei ({px:.1f}, {py:.1f}), rot={rotation:.1f}"
|
f"[INFO] Drehrichtung '{drehrichtung}': Richtungspfeil {label} bei ({px:.1f}, {py:.1f}), rot={rotation:.1f}"
|
||||||
)
|
|
||||||
# S-LP auf unterer Linie (Drehrichtung invertiert)
|
|
||||||
for i in range(1, 4):
|
|
||||||
t = i / 4
|
|
||||||
px = p1_unten[0] + t * (p2_unten[0] - p1_unten[0])
|
|
||||||
py = p1_unten[1] + t * (p2_unten[1] - p1_unten[1])
|
|
||||||
rotation = math.degrees(
|
|
||||||
math.atan2(p2_unten[1] - p1_unten[1], p2_unten[0] - p1_unten[0])
|
|
||||||
)
|
|
||||||
if drehrichtung == "UZS":
|
|
||||||
rotation += 180
|
|
||||||
block_methoden.import_block("Richtungspfeil", lib_doc, doc)
|
|
||||||
blockref_layer, color = block_methoden.get_insert_color_layer(
|
|
||||||
lib_doc, "Richtungspfeil"
|
|
||||||
)
|
|
||||||
bref = msp.add_blockref(
|
|
||||||
"Richtungspfeil",
|
|
||||||
(px, py, z1),
|
|
||||||
dxfattribs={"rotation": rotation, "layer": blockref_layer},
|
|
||||||
)
|
|
||||||
if verbose:
|
|
||||||
print(
|
|
||||||
f"[INFO] Drehrichtung '{drehrichtung}':Richtungspfeil unten bei ({px:.1f}, {py:.1f}), rot={rotation:.1f}"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Unit-Tests für Kreisel (Refactored)
|
||||||
|
"""
|
||||||
|
import unittest
|
||||||
|
import math
|
||||||
|
|
||||||
|
from .Kreisel import Kreisel, RADIUS, PIN_OFFSET, PIN_OFFSETS
|
||||||
|
|
||||||
|
|
||||||
|
class TestKreiselModell(unittest.TestCase):
|
||||||
|
"""Tests für das Kreisel Pydantic-Modell."""
|
||||||
|
|
||||||
|
def test_from_merkmale(self):
|
||||||
|
merkmale = {
|
||||||
|
"Höhe in m": "2,5", # Komma-Format
|
||||||
|
"Abstand (Kreiselachse A - Kreiselachse) in Meter": "15",
|
||||||
|
"Drehung": "90",
|
||||||
|
"Drehrichtung": "UZS",
|
||||||
|
"Kreiselart": "Pin",
|
||||||
|
"Anzahl der Scanner": "1",
|
||||||
|
"Anzahl der Separatoren": "0",
|
||||||
|
}
|
||||||
|
obj = Kreisel.from_merkmale("K-001", 1000.0, 2000.0, merkmale)
|
||||||
|
self.assertEqual(obj.teileid, "K-001")
|
||||||
|
self.assertEqual(obj.hoehe, 2500.0) # 2.5 * 1000
|
||||||
|
self.assertEqual(obj.abstand, 15000.0) # 15 * 1000
|
||||||
|
self.assertEqual(obj.drehung, 90.0)
|
||||||
|
self.assertEqual(obj.drehrichtung, "UZS")
|
||||||
|
self.assertEqual(obj.kreiselart, "Pin")
|
||||||
|
|
||||||
|
def test_halbabstand(self):
|
||||||
|
obj = Kreisel(teileid="K", x=0, y=0, hoehe=0, abstand=10000)
|
||||||
|
self.assertEqual(obj.halbabstand, 5000.0)
|
||||||
|
|
||||||
|
def test_pos1_pos2_bei_90_grad(self):
|
||||||
|
obj = Kreisel(teileid="K", x=1000, y=1000, hoehe=500, abstand=2000, drehung=90)
|
||||||
|
# Bei 90° → winkel_rad = radians(90) → cos=0, sin=1
|
||||||
|
# pos1 = (x - halbabstand*cos, y - halbabstand*sin, z) = (1000-0, 1000-1000, 500)
|
||||||
|
self.assertAlmostEqual(obj.pos1[0], 1000.0, places=3)
|
||||||
|
self.assertAlmostEqual(obj.pos1[1], 0.0, places=3)
|
||||||
|
# pos2 = (x + halbabstand*cos, y + halbabstand*sin, z) = (1000+0, 1000+1000, 500)
|
||||||
|
self.assertAlmostEqual(obj.pos2[0], 1000.0, places=3)
|
||||||
|
self.assertAlmostEqual(obj.pos2[1], 2000.0, places=3)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPinOffsets(unittest.TestCase):
|
||||||
|
"""Tests für die PIN_OFFSETS Lookup-Tabelle."""
|
||||||
|
|
||||||
|
def test_alle_rotationen_vorhanden(self):
|
||||||
|
for rotation in (0.0, 90.0, 180.0, 270.0):
|
||||||
|
self.assertIn(rotation, PIN_OFFSETS)
|
||||||
|
|
||||||
|
def test_offset_laenge(self):
|
||||||
|
# Jeder Eintrag muss 8 Werte haben (4 Punkte × 2 Koordinaten)
|
||||||
|
for rotation, offsets in PIN_OFFSETS.items():
|
||||||
|
self.assertEqual(len(offsets), 8, f"Rotation {rotation}: erwartet 8 Offsets")
|
||||||
|
|
||||||
|
def test_rotation_0_p1a_offset(self):
|
||||||
|
# p1a bei 0°: (-(RADIUS+PIN_OFFSET), +PIN_OFFSET)
|
||||||
|
offsets = PIN_OFFSETS[0.0]
|
||||||
|
self.assertEqual(offsets[0], -(RADIUS + PIN_OFFSET))
|
||||||
|
self.assertEqual(offsets[1], +PIN_OFFSET)
|
||||||
|
|
||||||
|
def test_rotation_90_p2b_offset(self):
|
||||||
|
# p2b bei 90°: (-PIN_OFFSET, -(RADIUS-PIN_OFFSET))
|
||||||
|
offsets = PIN_OFFSETS[90.0]
|
||||||
|
self.assertEqual(offsets[6], -PIN_OFFSET)
|
||||||
|
self.assertEqual(offsets[7], -(RADIUS - PIN_OFFSET))
|
||||||
|
|
||||||
|
def test_rotation_180_symmetrie_zu_0(self):
|
||||||
|
# 180° ist X-Spiegelung von 0° mit Y-Inversion
|
||||||
|
off_0 = PIN_OFFSETS[0.0]
|
||||||
|
off_180 = PIN_OFFSETS[180.0]
|
||||||
|
# p1a bei 0°: (-(R+O), +O) → p1a bei 180°: (+(R+O), -O)
|
||||||
|
self.assertEqual(off_180[0], -off_0[0])
|
||||||
|
self.assertEqual(off_180[1], -off_0[1])
|
||||||
|
|
||||||
|
|
||||||
|
class TestKreiselKonstanten(unittest.TestCase):
|
||||||
|
"""Tests für Kreisel-Konstanten."""
|
||||||
|
|
||||||
|
def test_radius_wert(self):
|
||||||
|
self.assertEqual(RADIUS, 400)
|
||||||
|
|
||||||
|
def test_pin_offset_wert(self):
|
||||||
|
self.assertEqual(PIN_OFFSET, 50)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
+119
-49
@@ -1,13 +1,77 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from ezdxf.entities import Line
|
"""
|
||||||
|
Omniflo — Modell und Erstellungsfunktionen für Omniflo-Kettenförderer.
|
||||||
|
|
||||||
|
Refactoring-Änderungen:
|
||||||
|
- 8x try/except-Boilerplate durch _safe_float/_safe_int Hilfsfunktionen ersetzt
|
||||||
|
- @staticmethod auf Omniflo_geraden_erstellung und omniflo_foerdererstellung
|
||||||
|
- Bug fix: omniflo_foerdererstellung nutzte rotation (Grad) statt winkel_rad in sin/cos
|
||||||
|
- Duplizierte dx/dy-Berechnung vereinheitlicht
|
||||||
|
"""
|
||||||
import math
|
import math
|
||||||
from pydantic import BaseModel, Field, field_validator
|
|
||||||
|
from ezdxf.entities import Line
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import plant2dxf
|
|
||||||
import block_methoden
|
from lib import block_methoden
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HILFSFUNKTIONEN
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_float(value, default=0.0):
|
||||||
|
"""
|
||||||
|
Konvertiert einen Wert zu float mit Fallback.
|
||||||
|
|
||||||
|
Behandelt automatisch Komma-als-Dezimaltrennzeichen (z.B. "3,5" → 3.5).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Zu konvertierender Wert (str, int, float oder None)
|
||||||
|
default: Rückgabewert bei Konvertierungsfehler
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Float-Wert oder default
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = value.replace(",", ".")
|
||||||
|
return float(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_int(value, default=0):
|
||||||
|
"""
|
||||||
|
Konvertiert einen Wert zu int mit Fallback (über float-Zwischenschritt).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Zu konvertierender Wert
|
||||||
|
default: Rückgabewert bei Konvertierungsfehler
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Int-Wert oder default
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
return int(float(value))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# OMNIFLO KLASSE
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
class Omniflo(BaseModel):
|
class Omniflo(BaseModel):
|
||||||
|
"""Modell für Omniflo-Kettenförderer."""
|
||||||
|
|
||||||
teileid: str
|
teileid: str
|
||||||
x: float
|
x: float
|
||||||
y: float
|
y: float
|
||||||
@@ -24,62 +88,58 @@ class Omniflo(BaseModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def hight_zwischen(self):
|
def hight_zwischen(self):
|
||||||
|
"""Mittlere Höhe zwischen h0 und h1."""
|
||||||
return (self.h0 + self.h1) / 2
|
return (self.h0 + self.h1) / 2
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_merkmale(cls, teileid, x, y, merkmale):
|
def from_merkmale(cls, teileid, x, y, merkmale):
|
||||||
sivasnummer = merkmale.get("SivasNummer")
|
"""
|
||||||
try:
|
Erstellt Omniflo aus einem Merkmale-Dictionary.
|
||||||
laenge = (
|
|
||||||
float(merkmale.get("Länge in Meter", "0").replace(",", ".")) * 1000
|
Verwendet _safe_float/_safe_int für robuste Konvertierung
|
||||||
) # Meter → mm
|
statt 8x wiederholtem try/except-Muster.
|
||||||
except Exception:
|
|
||||||
laenge = 0
|
Args:
|
||||||
try:
|
teileid: Teile-Identifikator
|
||||||
winkel = float(merkmale.get("Drehung"))
|
x, y: Koordinaten in mm
|
||||||
except Exception:
|
merkmale: Dictionary mit Eigenschaftswerten
|
||||||
winkel = 0.0
|
|
||||||
try:
|
Returns:
|
||||||
hoehe = float(merkmale.get("Höhe"))
|
Omniflo-Instanz
|
||||||
except Exception:
|
"""
|
||||||
hoehe = 0.0
|
|
||||||
try:
|
|
||||||
h0 = float(merkmale.get("Höhe unten"))
|
|
||||||
except Exception:
|
|
||||||
h0 = 0.0
|
|
||||||
try:
|
|
||||||
h1 = float(merkmale.get("Höhe oben"))
|
|
||||||
except Exception:
|
|
||||||
h1 = 0.0
|
|
||||||
try:
|
|
||||||
anzahl_scanner = float(merkmale.get("Anzahl der Scanner", "0"))
|
|
||||||
except Exception:
|
|
||||||
anzahl_scanner = 0
|
|
||||||
try:
|
|
||||||
anzahl_separatoren = float(merkmale.get("Anzahl der Separatoren", "0"))
|
|
||||||
except Exception:
|
|
||||||
anzahl_separatoren = 0
|
|
||||||
return cls(
|
return cls(
|
||||||
teileid=teileid,
|
teileid=teileid,
|
||||||
x=x,
|
x=x,
|
||||||
y=y,
|
y=y,
|
||||||
sivasnummer=sivasnummer,
|
sivasnummer=merkmale.get("SivasNummer"),
|
||||||
laenge=laenge,
|
laenge=_safe_float(merkmale.get("Länge in Meter"), 0.0) * 1000, # Meter → mm
|
||||||
drehung=winkel,
|
drehung=_safe_float(merkmale.get("Drehung"), 0.0),
|
||||||
hoehe=hoehe,
|
hoehe=_safe_float(merkmale.get("Höhe"), 0.0),
|
||||||
h0=h0,
|
h0=_safe_float(merkmale.get("Höhe unten"), 0.0),
|
||||||
h1=h1,
|
h1=_safe_float(merkmale.get("Höhe oben"), 0.0),
|
||||||
anzahl_scanner=anzahl_scanner,
|
anzahl_scanner=_safe_int(merkmale.get("Anzahl der Scanner"), 0),
|
||||||
anzahl_stopper=anzahl_separatoren,
|
anzahl_stopper=_safe_int(merkmale.get("Anzahl der Separatoren"), 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def Omniflo_geraden_erstellung(msp, doc, tefsivas, omniflo_objekt):
|
def Omniflo_geraden_erstellung(msp, doc, tefsivas, omniflo_objekt):
|
||||||
"""Erstellung der Tef gerade und Omniflo gerade"""
|
"""
|
||||||
|
Erstellung der Tef gerade und Omniflo gerade.
|
||||||
|
|
||||||
|
Zeichnet eine Linie zwischen Start- und Endpunkt basierend auf
|
||||||
|
Länge und Drehung des Omniflo-Objekts.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msp: DXF-Modelspace
|
||||||
|
doc: DXF-Dokument
|
||||||
|
tefsivas: Sivas-Nummer der Tef-Strecke
|
||||||
|
omniflo_objekt: Omniflo-Instanz
|
||||||
|
"""
|
||||||
winkel_rad = math.radians(omniflo_objekt.drehung)
|
winkel_rad = math.radians(omniflo_objekt.drehung)
|
||||||
halbe_laenge = omniflo_objekt.laenge / 2
|
halbe_laenge = omniflo_objekt.laenge / 2
|
||||||
x = omniflo_objekt.x
|
x = omniflo_objekt.x
|
||||||
y = omniflo_objekt.y
|
y = omniflo_objekt.y
|
||||||
# Man muss bei sin -1 machen wegen des links koordinaten system
|
# Koordinatensystem: sin mit -1 wegen links-Koordinatensystem
|
||||||
dx = halbe_laenge * math.sin(winkel_rad * -1)
|
dx = halbe_laenge * math.sin(winkel_rad * -1)
|
||||||
dy = halbe_laenge * math.cos(winkel_rad)
|
dy = halbe_laenge * math.cos(winkel_rad)
|
||||||
start = (x + dx, y + dy, omniflo_objekt.h1)
|
start = (x + dx, y + dy, omniflo_objekt.h1)
|
||||||
@@ -94,8 +154,17 @@ class Omniflo(BaseModel):
|
|||||||
else:
|
else:
|
||||||
linie.dxf.layer = "A-2"
|
linie.dxf.layer = "A-2"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def omniflo_foerdererstellung(msp, doc, lib_doc, omniflo_objekt):
|
def omniflo_foerdererstellung(msp, doc, lib_doc, omniflo_objekt):
|
||||||
""" "Erstellung des Kettenförderers aktuell nur grundriss"""
|
"""
|
||||||
|
Erstellung des Kettenförderers (aktuell nur Grundriss).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msp: DXF-Modelspace
|
||||||
|
doc: DXF-Dokument
|
||||||
|
lib_doc: Bibliotheks-Dokument
|
||||||
|
omniflo_objekt: Omniflo-Instanz
|
||||||
|
"""
|
||||||
block_methoden.import_block("bogen1", lib_doc, doc)
|
block_methoden.import_block("bogen1", lib_doc, doc)
|
||||||
block_methoden.import_block("bogen2", lib_doc, doc)
|
block_methoden.import_block("bogen2", lib_doc, doc)
|
||||||
x = omniflo_objekt.x
|
x = omniflo_objekt.x
|
||||||
@@ -108,9 +177,10 @@ class Omniflo(BaseModel):
|
|||||||
blockname = f"OF_Förderer_{laenge}_{h_zwischen}"
|
blockname = f"OF_Förderer_{laenge}_{h_zwischen}"
|
||||||
winkel_rad = math.radians(rotation)
|
winkel_rad = math.radians(rotation)
|
||||||
halbe_laenge = laenge / 2
|
halbe_laenge = laenge / 2
|
||||||
# Man muss bei sin -1 machen wegen des links koordinaten system
|
# BUG FIX: war `math.sin(rotation * -1)` — rotation ist in Grad!
|
||||||
dx = halbe_laenge * math.sin(rotation * -1)
|
# Muss winkel_rad (Radianten) verwenden wie in Omniflo_geraden_erstellung
|
||||||
dy = halbe_laenge * math.cos(rotation)
|
dx = halbe_laenge * math.sin(winkel_rad * -1)
|
||||||
|
dy = halbe_laenge * math.cos(winkel_rad)
|
||||||
start = (x + dx, y + dy, h1)
|
start = (x + dx, y + dy, h1)
|
||||||
ende = (x - dx, y - dy, h0)
|
ende = (x - dx, y - dy, h0)
|
||||||
if blockname not in doc.blocks:
|
if blockname not in doc.blocks:
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Unit-Tests für Omniflo (Refactored)
|
||||||
|
"""
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from .Omniflo import Omniflo, _safe_float, _safe_int
|
||||||
|
|
||||||
|
|
||||||
|
class TestSafeFloat(unittest.TestCase):
|
||||||
|
"""Tests für _safe_float Hilfsfunktion."""
|
||||||
|
|
||||||
|
def test_normaler_string(self):
|
||||||
|
self.assertEqual(_safe_float("3.14"), 3.14)
|
||||||
|
|
||||||
|
def test_komma_als_dezimaltrenner(self):
|
||||||
|
self.assertEqual(_safe_float("3,14"), 3.14)
|
||||||
|
|
||||||
|
def test_none_gibt_default(self):
|
||||||
|
self.assertEqual(_safe_float(None, 42.0), 42.0)
|
||||||
|
|
||||||
|
def test_ungueltiger_string_gibt_default(self):
|
||||||
|
self.assertEqual(_safe_float("abc", 1.0), 1.0)
|
||||||
|
|
||||||
|
def test_int_input(self):
|
||||||
|
self.assertEqual(_safe_float(5), 5.0)
|
||||||
|
|
||||||
|
def test_float_input(self):
|
||||||
|
self.assertEqual(_safe_float(2.5), 2.5)
|
||||||
|
|
||||||
|
|
||||||
|
class TestSafeInt(unittest.TestCase):
|
||||||
|
"""Tests für _safe_int Hilfsfunktion."""
|
||||||
|
|
||||||
|
def test_normaler_string(self):
|
||||||
|
self.assertEqual(_safe_int("3"), 3)
|
||||||
|
|
||||||
|
def test_float_string_wird_getruncated(self):
|
||||||
|
self.assertEqual(_safe_int("3.7"), 3)
|
||||||
|
|
||||||
|
def test_none_gibt_default(self):
|
||||||
|
self.assertEqual(_safe_int(None, 7), 7)
|
||||||
|
|
||||||
|
def test_ungueltiger_string(self):
|
||||||
|
self.assertEqual(_safe_int("xyz", 0), 0)
|
||||||
|
|
||||||
|
|
||||||
|
class TestOmnifloModell(unittest.TestCase):
|
||||||
|
"""Tests für das Omniflo Modell."""
|
||||||
|
|
||||||
|
def test_from_merkmale_vollstaendig(self):
|
||||||
|
merkmale = {
|
||||||
|
"SivasNummer": "SV-123",
|
||||||
|
"Länge in Meter": "10,5", # Komma-Format
|
||||||
|
"Drehung": "-180",
|
||||||
|
"Höhe": "3.0",
|
||||||
|
"Höhe unten": "1.0",
|
||||||
|
"Höhe oben": "2.0",
|
||||||
|
"Anzahl der Scanner": "2",
|
||||||
|
"Anzahl der Separatoren": "1",
|
||||||
|
}
|
||||||
|
obj = Omniflo.from_merkmale("TEST-001", 50.0, 60.0, merkmale)
|
||||||
|
self.assertEqual(obj.teileid, "TEST-001")
|
||||||
|
self.assertEqual(obj.sivasnummer, "SV-123")
|
||||||
|
self.assertAlmostEqual(obj.laenge, 10500.0) # 10.5 * 1000
|
||||||
|
self.assertEqual(obj.drehung, -180.0)
|
||||||
|
self.assertEqual(obj.hoehe, 3.0)
|
||||||
|
self.assertEqual(obj.h0, 1.0)
|
||||||
|
self.assertEqual(obj.h1, 2.0)
|
||||||
|
self.assertEqual(obj.anzahl_scanner, 2)
|
||||||
|
self.assertEqual(obj.anzahl_stopper, 1)
|
||||||
|
|
||||||
|
def test_from_merkmale_fehlende_werte(self):
|
||||||
|
# Fehlende optionale Felder → defaults
|
||||||
|
merkmale = {
|
||||||
|
"SivasNummer": "SV-001",
|
||||||
|
}
|
||||||
|
obj = Omniflo.from_merkmale("TEST-002", 0.0, 0.0, merkmale)
|
||||||
|
self.assertEqual(obj.laenge, 0.0) # None * 1000 → default 0 * 1000
|
||||||
|
self.assertEqual(obj.drehung, 0.0)
|
||||||
|
self.assertEqual(obj.hoehe, 0.0)
|
||||||
|
|
||||||
|
def test_hight_zwischen(self):
|
||||||
|
merkmale = {
|
||||||
|
"SivasNummer": "SV-001",
|
||||||
|
"Höhe unten": "1.0",
|
||||||
|
"Höhe oben": "3.0",
|
||||||
|
}
|
||||||
|
obj = Omniflo.from_merkmale("TEST-003", 0.0, 0.0, merkmale)
|
||||||
|
self.assertEqual(obj.hight_zwischen, 2.0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
+1137
-1543
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Unit-Tests für VarioFoerderer (Refactored)
|
||||||
|
|
||||||
|
Diese Tests können direkt in der Library-Methode verwendet werden
|
||||||
|
oder als separate Test-Datei ausgeführt werden.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import math
|
||||||
|
from unittest.mock import Mock, MagicMock
|
||||||
|
from .VarioFoerderer import (
|
||||||
|
VarioFoerderer,
|
||||||
|
Foerderrichtung,
|
||||||
|
MotorUmlenkConfig,
|
||||||
|
BogenDeltas,
|
||||||
|
OffsetResult,
|
||||||
|
_parse_delta_attribute,
|
||||||
|
_calculate_offset_with_rotation,
|
||||||
|
_is_es_as_element,
|
||||||
|
horizontale_ausrichtung,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMotorUmlenkConfig(unittest.TestCase):
|
||||||
|
"""Tests für MotorUmlenkConfig Dataclass"""
|
||||||
|
|
||||||
|
def test_motor_offset_berechnung(self):
|
||||||
|
"""Test: Motor-Offset-Berechnung"""
|
||||||
|
config = MotorUmlenkConfig(
|
||||||
|
winkel_motor=45.0,
|
||||||
|
winkel_umlenk=30.0,
|
||||||
|
umlenk_laenge=(100.0, 50.0),
|
||||||
|
motor_laenge=(80.0, 40.0),
|
||||||
|
vario_abstand=25.0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Motor-Offsets
|
||||||
|
expected_motor_x = 100.0 * math.cos(math.radians(45.0))
|
||||||
|
expected_motor_z = 100.0 * math.sin(math.radians(45.0))
|
||||||
|
|
||||||
|
self.assertAlmostEqual(config.motor_offset_x, expected_motor_x, places=5)
|
||||||
|
self.assertAlmostEqual(config.motor_offset_z, expected_motor_z, places=5)
|
||||||
|
|
||||||
|
def test_umlenk_offset_berechnung(self):
|
||||||
|
"""Test: Umlenk-Offset-Berechnung"""
|
||||||
|
config = MotorUmlenkConfig(
|
||||||
|
winkel_motor=45.0,
|
||||||
|
winkel_umlenk=30.0,
|
||||||
|
umlenk_laenge=(100.0, 50.0),
|
||||||
|
motor_laenge=(80.0, 40.0),
|
||||||
|
vario_abstand=25.0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Umlenk-Offsets
|
||||||
|
expected_umlenk_x = 80.0 * math.cos(math.radians(30.0))
|
||||||
|
expected_umlenk_z = 80.0 * math.sin(math.radians(30.0))
|
||||||
|
|
||||||
|
self.assertAlmostEqual(config.umlenk_offset_x, expected_umlenk_x, places=5)
|
||||||
|
self.assertAlmostEqual(config.umlenk_offset_z, expected_umlenk_z, places=5)
|
||||||
|
|
||||||
|
|
||||||
|
class TestBogenDeltas(unittest.TestCase):
|
||||||
|
"""Tests für BogenDeltas Dataclass"""
|
||||||
|
|
||||||
|
def test_normalize_negative_values(self):
|
||||||
|
"""Test: Normalisierung negativer Werte"""
|
||||||
|
deltas = BogenDeltas(
|
||||||
|
SP_0=[-10.0, -20.0, 5.0],
|
||||||
|
SP_1=[15.0, -25.0, 10.0],
|
||||||
|
VP_0=[-5.0, 8.0, -3.0],
|
||||||
|
VP_1=[12.0, -15.0, 7.0]
|
||||||
|
)
|
||||||
|
|
||||||
|
deltas.normalize_negative_values()
|
||||||
|
|
||||||
|
# X und Y (Index 0 und 1) sollen positiv sein, Z (Index 2) unverändert
|
||||||
|
self.assertEqual(deltas.SP_0, [10.0, 20.0, 5.0])
|
||||||
|
self.assertEqual(deltas.SP_1, [15.0, 25.0, 10.0])
|
||||||
|
self.assertEqual(deltas.VP_0, [5.0, 8.0, -3.0])
|
||||||
|
self.assertEqual(deltas.VP_1, [12.0, 15.0, 7.0])
|
||||||
|
|
||||||
|
|
||||||
|
class TestVarioFoerderer(unittest.TestCase):
|
||||||
|
"""Tests für VarioFoerderer Modell"""
|
||||||
|
|
||||||
|
def test_from_merkmale(self):
|
||||||
|
"""Test: VarioFoerderer aus Merkmale-Dict erstellen"""
|
||||||
|
merkmale = {
|
||||||
|
"Höhe Anfang": "1.5",
|
||||||
|
"Höhe Ende": "2.0",
|
||||||
|
"Länge in Meter": "5.0",
|
||||||
|
"Winkel": "6",
|
||||||
|
"Motorstation_hinten": True,
|
||||||
|
"Spannstation_vorn": False,
|
||||||
|
"Drehung": "-90",
|
||||||
|
"Förderrichtung": "Auf",
|
||||||
|
"Anzahl der Scanner": "2",
|
||||||
|
"Anzahl der Separatoren": "3",
|
||||||
|
}
|
||||||
|
|
||||||
|
foerderer = VarioFoerderer.from_merkmale("TEST-001", 100.0, 200.0, merkmale)
|
||||||
|
|
||||||
|
self.assertEqual(foerderer.teileid, "TEST-001")
|
||||||
|
self.assertEqual(foerderer.x, 100.0)
|
||||||
|
self.assertEqual(foerderer.y, 200.0)
|
||||||
|
self.assertEqual(foerderer.h0, 1500.0) # 1.5 * 1000
|
||||||
|
self.assertEqual(foerderer.h1, 2000.0) # 2.0 * 1000
|
||||||
|
self.assertEqual(foerderer.laenge, 5000.0) # 5.0 * 1000
|
||||||
|
self.assertEqual(foerderer.winkel, 6.0)
|
||||||
|
self.assertTrue(foerderer.hat_motor)
|
||||||
|
self.assertFalse(foerderer.hat_umlenk)
|
||||||
|
self.assertEqual(foerderer.drehung, -90.0)
|
||||||
|
self.assertEqual(foerderer.foerderer_richtung, "Auf")
|
||||||
|
self.assertEqual(foerderer.anzahl_scanner, 2)
|
||||||
|
self.assertEqual(foerderer.anzahl_separatoren, 3)
|
||||||
|
|
||||||
|
def test_hight_zwischen(self):
|
||||||
|
"""Test: Berechnung der mittleren Höhe"""
|
||||||
|
merkmale = {
|
||||||
|
"Höhe Anfang": "1.0",
|
||||||
|
"Höhe Ende": "3.0",
|
||||||
|
"Länge in Meter": "1.0",
|
||||||
|
"Winkel": "3",
|
||||||
|
"Motorstation_hinten": False,
|
||||||
|
"Spannstation_vorn": False,
|
||||||
|
"Drehung": "0",
|
||||||
|
"Förderrichtung": "Auf",
|
||||||
|
"Anzahl der Scanner": "0",
|
||||||
|
"Anzahl der Separatoren": "0",
|
||||||
|
}
|
||||||
|
|
||||||
|
foerderer = VarioFoerderer.from_merkmale("TEST-002", 0, 0, merkmale)
|
||||||
|
expected_height = (1000.0 + 3000.0) / 2 # (h0 + h1) / 2
|
||||||
|
|
||||||
|
self.assertEqual(foerderer.hight_zwischen, expected_height)
|
||||||
|
|
||||||
|
|
||||||
|
class TestHilfsfunktionen(unittest.TestCase):
|
||||||
|
"""Tests für generische Hilfsfunktionen"""
|
||||||
|
|
||||||
|
def test_parse_delta_attribute(self):
|
||||||
|
"""Test: Delta-Attribut parsen"""
|
||||||
|
attrib_dict = {
|
||||||
|
"DELTA_SP_0": "10.5;20.3;-5.7",
|
||||||
|
"DELTA_VP_1": "15.2,25.8,-8.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
result_sp = _parse_delta_attribute(attrib_dict, "DELTA_SP_0")
|
||||||
|
result_vp = _parse_delta_attribute(attrib_dict, "DELTA_VP_1")
|
||||||
|
|
||||||
|
self.assertEqual(result_sp, [10.5, 20.3, -5.7])
|
||||||
|
self.assertEqual(result_vp, [15.2, 25.8, -8.1])
|
||||||
|
|
||||||
|
def test_calculate_offset_with_rotation(self):
|
||||||
|
"""Test: Offset-Berechnung mit Rotation"""
|
||||||
|
SP = [10.0, 5.0, 8.0]
|
||||||
|
VP = [2.0, 5.0, 3.0]
|
||||||
|
winkel = 45.0
|
||||||
|
|
||||||
|
result = _calculate_offset_with_rotation(SP, VP, winkel)
|
||||||
|
|
||||||
|
# Erwartete Werte berechnen
|
||||||
|
rad = math.radians(45.0)
|
||||||
|
expected_x = (SP[0] - VP[0]) * math.cos(rad) + (SP[2] - VP[2]) * math.sin(rad)
|
||||||
|
expected_y = VP[1]
|
||||||
|
expected_z = -(SP[0] - VP[0]) * math.sin(rad) + (SP[2] - VP[2]) * math.cos(rad)
|
||||||
|
|
||||||
|
self.assertAlmostEqual(result[0], expected_x, places=5)
|
||||||
|
self.assertAlmostEqual(result[1], expected_y, places=5)
|
||||||
|
self.assertAlmostEqual(result[2], expected_z, places=5)
|
||||||
|
|
||||||
|
def test_is_es_as_element_true(self):
|
||||||
|
"""Test: ES/AS-Element erkennen"""
|
||||||
|
entity = Mock()
|
||||||
|
entity.dxftype.return_value = "INSERT"
|
||||||
|
entity.dxf.name = "200000146_ES-Element_90_rechts_modified"
|
||||||
|
|
||||||
|
self.assertTrue(_is_es_as_element(entity))
|
||||||
|
|
||||||
|
def test_is_es_as_element_false(self):
|
||||||
|
"""Test: Kein ES/AS-Element"""
|
||||||
|
entity = Mock()
|
||||||
|
entity.dxftype.return_value = "INSERT"
|
||||||
|
entity.dxf.name = "Vario_Bogen_auf_12°"
|
||||||
|
|
||||||
|
self.assertFalse(_is_es_as_element(entity))
|
||||||
|
|
||||||
|
def test_is_es_as_element_not_insert(self):
|
||||||
|
"""Test: Kein INSERT-Entity"""
|
||||||
|
entity = Mock()
|
||||||
|
entity.dxftype.return_value = "LINE"
|
||||||
|
|
||||||
|
self.assertFalse(_is_es_as_element(entity))
|
||||||
|
|
||||||
|
|
||||||
|
class TestHorizontaleAusrichtung(unittest.TestCase):
|
||||||
|
"""Tests für horizontale_ausrichtung Funktion"""
|
||||||
|
|
||||||
|
def test_horizontal_rotation_0(self):
|
||||||
|
"""Test: Horizontale Ausrichtung bei Rotation 0°"""
|
||||||
|
merkmale = {
|
||||||
|
"Höhe Anfang": "1.0",
|
||||||
|
"Höhe Ende": "1.0",
|
||||||
|
"Länge in Meter": "1.0",
|
||||||
|
"Winkel": "3",
|
||||||
|
"Motorstation_hinten": False,
|
||||||
|
"Spannstation_vorn": False,
|
||||||
|
"Drehung": "0",
|
||||||
|
"Förderrichtung": "Horizontal",
|
||||||
|
"Anzahl der Scanner": "0",
|
||||||
|
"Anzahl der Separatoren": "0",
|
||||||
|
}
|
||||||
|
|
||||||
|
foerderer = VarioFoerderer.from_merkmale("TEST", 100.0, 200.0, merkmale)
|
||||||
|
|
||||||
|
# Förderer ist über Kreisel (y > y_kreisel)
|
||||||
|
result = horizontale_ausrichtung(foerderer, 100.0, 100.0)
|
||||||
|
self.assertEqual(result, "unten_drehung_0_or_-90")
|
||||||
|
|
||||||
|
# Förderer ist unter Kreisel (y < y_kreisel)
|
||||||
|
result = horizontale_ausrichtung(foerderer, 100.0, 300.0)
|
||||||
|
self.assertEqual(result, "oben_drehung_0_or_-90")
|
||||||
|
|
||||||
|
def test_nicht_horizontal(self):
|
||||||
|
"""Test: Nicht-horizontale Förderer geben 'None' zurück"""
|
||||||
|
merkmale = {
|
||||||
|
"Höhe Anfang": "1.0",
|
||||||
|
"Höhe Ende": "2.0",
|
||||||
|
"Länge in Meter": "1.0",
|
||||||
|
"Winkel": "6",
|
||||||
|
"Motorstation_hinten": False,
|
||||||
|
"Spannstation_vorn": False,
|
||||||
|
"Drehung": "0",
|
||||||
|
"Förderrichtung": "Auf",
|
||||||
|
"Anzahl der Scanner": "0",
|
||||||
|
"Anzahl der Separatoren": "0",
|
||||||
|
}
|
||||||
|
|
||||||
|
foerderer = VarioFoerderer.from_merkmale("TEST", 100.0, 200.0, merkmale)
|
||||||
|
result = horizontale_ausrichtung(foerderer, 100.0, 100.0)
|
||||||
|
|
||||||
|
self.assertEqual(result, "None")
|
||||||
|
|
||||||
|
|
||||||
|
class TestFoerderrichtungEnum(unittest.TestCase):
|
||||||
|
"""Tests für Foerderrichtung Enum"""
|
||||||
|
|
||||||
|
def test_enum_werte(self):
|
||||||
|
"""Test: Enum-Werte sind korrekt"""
|
||||||
|
self.assertEqual(Foerderrichtung.AUF.value, "Auf")
|
||||||
|
self.assertEqual(Foerderrichtung.AB.value, "Ab")
|
||||||
|
self.assertEqual(Foerderrichtung.HORIZONTAL.value, "Horizontal")
|
||||||
|
|
||||||
|
def test_enum_vergleich(self):
|
||||||
|
"""Test: Enum-Vergleich funktioniert"""
|
||||||
|
richtung = "Auf"
|
||||||
|
self.assertEqual(richtung, Foerderrichtung.AUF.value)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Test-Runner
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
def run_tests():
|
||||||
|
"""Führt alle Unit-Tests aus"""
|
||||||
|
loader = unittest.TestLoader()
|
||||||
|
suite = loader.loadTestsFromModule(__import__(__name__))
|
||||||
|
runner = unittest.TextTestRunner(verbosity=2)
|
||||||
|
result = runner.run(suite)
|
||||||
|
|
||||||
|
print("\n" + "="*70)
|
||||||
|
print(f"Tests durchgeführt: {result.testsRun}")
|
||||||
|
print(f"Erfolgreich: {result.testsRun - len(result.failures) - len(result.errors)}")
|
||||||
|
print(f"Fehlgeschlagen: {len(result.failures)}")
|
||||||
|
print(f"Errors: {len(result.errors)}")
|
||||||
|
print("="*70)
|
||||||
|
|
||||||
|
return result.wasSuccessful()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
success = run_tests()
|
||||||
|
exit(0 if success else 1)
|
||||||
@@ -6,8 +6,8 @@ import json
|
|||||||
import re
|
import re
|
||||||
import configparser
|
import configparser
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from utils import check_environment_var, setup_logger
|
from lib.utils import check_environment_var, setup_logger
|
||||||
from Elemente import Kreisel, VarioFoerderer,Gefaellestrecke,Angetriebene_Kurve,Bt_element,Omniflo, Eckrad
|
from lib.Elemente import Kreisel, VarioFoerderer,Gefaellestrecke,Angetriebene_Kurve,Bt_element,Omniflo, Eckrad
|
||||||
# --------------------------------------------------------- CFG-Leser für shapes.cfg
|
# --------------------------------------------------------- CFG-Leser für shapes.cfg
|
||||||
def get_shape_cfg(teileart, cfg_path, logger=None):
|
def get_shape_cfg(teileart, cfg_path, logger=None):
|
||||||
parser = configparser.ConfigParser()
|
parser = configparser.ConfigParser()
|
||||||
@@ -219,4 +219,4 @@ def normalize_func_name(name):
|
|||||||
.replace('.', '_')
|
.replace('.', '_')
|
||||||
.replace('-', '_')
|
.replace('-', '_')
|
||||||
.lower()
|
.lower()
|
||||||
)
|
)
|
||||||
|
|||||||
+28
-28
@@ -1,8 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from ezdxf.entities import Line
|
from ezdxf.entities import Line
|
||||||
import math
|
import math
|
||||||
import plant2dxf
|
from lib import plant2dxf
|
||||||
import block_methoden
|
from lib import block_methoden
|
||||||
|
|
||||||
RADIUS = 400
|
RADIUS = 400
|
||||||
|
|
||||||
@@ -260,9 +260,9 @@ def gefaellegerade_erstellung(
|
|||||||
):
|
):
|
||||||
"""erstellung des blockes falls die gefaelle strecke mit einem kreisel verbunden ist, mehrere variation von selben weil diese nötig sind sobald man die richtigen inserts bekommen"""
|
"""erstellung des blockes falls die gefaelle strecke mit einem kreisel verbunden ist, mehrere variation von selben weil diese nötig sind sobald man die richtigen inserts bekommen"""
|
||||||
if hasattr(objekt, "foerderer_richtung"):
|
if hasattr(objekt, "foerderer_richtung"):
|
||||||
voerder_richtung = objekt.foerderer_richtung
|
foerder_richtung = objekt.foerderer_richtung
|
||||||
else:
|
else:
|
||||||
voerder_richtung = None
|
foerder_richtung = None
|
||||||
|
|
||||||
laenge = objekt.laenge
|
laenge = objekt.laenge
|
||||||
upper_hoehe_gefaelle = objekt.h1
|
upper_hoehe_gefaelle = objekt.h1
|
||||||
@@ -293,7 +293,7 @@ def gefaellegerade_erstellung(
|
|||||||
if (
|
if (
|
||||||
(drehung0 == "GUZS" and drehung1 == "GUZS" and hight_position == "higher")
|
(drehung0 == "GUZS" and drehung1 == "GUZS" and hight_position == "higher")
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Auf"
|
foerder_richtung == "Auf"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == False)
|
(am_kreisel == 1 and erster_kreisel_höher == False)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
||||||
@@ -302,7 +302,7 @@ def gefaellegerade_erstellung(
|
|||||||
and drehung1 == "GUZS"
|
and drehung1 == "GUZS"
|
||||||
)
|
)
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Ab"
|
foerder_richtung == "Ab"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == True)
|
(am_kreisel == 1 and erster_kreisel_höher == True)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
||||||
@@ -314,7 +314,7 @@ def gefaellegerade_erstellung(
|
|||||||
# Schaut bei ungleicher Orentierung
|
# Schaut bei ungleicher Orentierung
|
||||||
((drehung0 == "UZS" and drehung1 == "GUZS" and hight_position == "higher"))
|
((drehung0 == "UZS" and drehung1 == "GUZS" and hight_position == "higher"))
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Auf"
|
foerder_richtung == "Auf"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == False)
|
(am_kreisel == 1 and erster_kreisel_höher == False)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
||||||
@@ -322,7 +322,7 @@ def gefaellegerade_erstellung(
|
|||||||
and (drehung0 == "GUZS" and drehung1 == "UZS")
|
and (drehung0 == "GUZS" and drehung1 == "UZS")
|
||||||
)
|
)
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Ab"
|
foerder_richtung == "Ab"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == True)
|
(am_kreisel == 1 and erster_kreisel_höher == True)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
||||||
@@ -339,7 +339,7 @@ def gefaellegerade_erstellung(
|
|||||||
or (am_kreisel != None and drehung0 == "GUZS" and drehung1 == "GUZS")
|
or (am_kreisel != None and drehung0 == "GUZS" and drehung1 == "GUZS")
|
||||||
or (drehung0 == "UZS" and drehung1 == "GUZS" and hight_position == "lower")
|
or (drehung0 == "UZS" and drehung1 == "GUZS" and hight_position == "lower")
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Auf"
|
foerder_richtung == "Auf"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == True)
|
(am_kreisel == 1 and erster_kreisel_höher == True)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
||||||
@@ -347,7 +347,7 @@ def gefaellegerade_erstellung(
|
|||||||
and (drehung0 == "UZS" and drehung1 == "GUZS")
|
and (drehung0 == "UZS" and drehung1 == "GUZS")
|
||||||
)
|
)
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Ab"
|
foerder_richtung == "Ab"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == False)
|
(am_kreisel == 1 and erster_kreisel_höher == False)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
||||||
@@ -362,7 +362,7 @@ def gefaellegerade_erstellung(
|
|||||||
elif (
|
elif (
|
||||||
(drehung0 == "UZS" and drehung1 == "UZS" and hight_position == "higher")
|
(drehung0 == "UZS" and drehung1 == "UZS" and hight_position == "higher")
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Auf"
|
foerder_richtung == "Auf"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == False)
|
(am_kreisel == 1 and erster_kreisel_höher == False)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
||||||
@@ -371,7 +371,7 @@ def gefaellegerade_erstellung(
|
|||||||
and drehung1 == "UZS"
|
and drehung1 == "UZS"
|
||||||
)
|
)
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Ab"
|
foerder_richtung == "Ab"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == True)
|
(am_kreisel == 1 and erster_kreisel_höher == True)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
||||||
@@ -381,7 +381,7 @@ def gefaellegerade_erstellung(
|
|||||||
)
|
)
|
||||||
or (drehung0 == "GUZS" and drehung1 == "UZS" and hight_position == "higher")
|
or (drehung0 == "GUZS" and drehung1 == "UZS" and hight_position == "higher")
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Auf"
|
foerder_richtung == "Auf"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == False)
|
(am_kreisel == 1 and erster_kreisel_höher == False)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
||||||
@@ -389,7 +389,7 @@ def gefaellegerade_erstellung(
|
|||||||
and (drehung0 == "UZS" and drehung1 == "GUZS")
|
and (drehung0 == "UZS" and drehung1 == "GUZS")
|
||||||
)
|
)
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Ab"
|
foerder_richtung == "Ab"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == True)
|
(am_kreisel == 1 and erster_kreisel_höher == True)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
||||||
@@ -409,7 +409,7 @@ def gefaellegerade_erstellung(
|
|||||||
)
|
)
|
||||||
or (drehung0 == "GUZS" and drehung1 == "UZS" and hight_position == "lower")
|
or (drehung0 == "GUZS" and drehung1 == "UZS" and hight_position == "lower")
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Auf"
|
foerder_richtung == "Auf"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == True)
|
(am_kreisel == 1 and erster_kreisel_höher == True)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
or (am_kreisel == 2 and erster_kreisel_höher == False)
|
||||||
@@ -417,7 +417,7 @@ def gefaellegerade_erstellung(
|
|||||||
and (drehung0 == "GUZS" and drehung1 == "UZS")
|
and (drehung0 == "GUZS" and drehung1 == "UZS")
|
||||||
)
|
)
|
||||||
or (
|
or (
|
||||||
voerder_richtung == "Ab"
|
foerder_richtung == "Ab"
|
||||||
and (
|
and (
|
||||||
(am_kreisel == 1 and erster_kreisel_höher == False)
|
(am_kreisel == 1 and erster_kreisel_höher == False)
|
||||||
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
or (am_kreisel == 2 and erster_kreisel_höher == True)
|
||||||
@@ -434,7 +434,7 @@ def gefaellegerade_erstellung(
|
|||||||
block_es = block_methoden.dreh_block(
|
block_es = block_methoden.dreh_block(
|
||||||
block_es, doc, lib_doc, math.radians(winkel_es)
|
block_es, doc, lib_doc, math.radians(winkel_es)
|
||||||
)
|
)
|
||||||
if voerder_richtung == "Auf":
|
if foerder_richtung == "Auf":
|
||||||
start = (x, y + dy - esoffset_y, upper_hoehe_gefaelle + esoffset_z)
|
start = (x, y + dy - esoffset_y, upper_hoehe_gefaelle + esoffset_z)
|
||||||
ende = (x, y1, z1)
|
ende = (x, y1, z1)
|
||||||
block_vario.add_blockref(
|
block_vario.add_blockref(
|
||||||
@@ -447,7 +447,7 @@ def gefaellegerade_erstellung(
|
|||||||
dxfattribs={"rotation": 180, "layer": layer, "color": color},
|
dxfattribs={"rotation": 180, "layer": layer, "color": color},
|
||||||
)
|
)
|
||||||
return start, ende
|
return start, ende
|
||||||
elif voerder_richtung == "Ab":
|
elif foerder_richtung == "Ab":
|
||||||
start = (x, y1, z1)
|
start = (x, y1, z1)
|
||||||
ende = (x, y - dy + esoffset_y, lower_hoehe_gefaelle + esoffset_z)
|
ende = (x, y - dy + esoffset_y, lower_hoehe_gefaelle + esoffset_z)
|
||||||
block_vario.add_blockref(
|
block_vario.add_blockref(
|
||||||
@@ -478,7 +478,7 @@ def gefaellegerade_erstellung(
|
|||||||
block_as = block_methoden.dreh_block(
|
block_as = block_methoden.dreh_block(
|
||||||
block_as, doc, lib_doc, math.radians(winkel_as)
|
block_as, doc, lib_doc, math.radians(winkel_as)
|
||||||
)
|
)
|
||||||
if voerder_richtung == "Auf":
|
if foerder_richtung == "Auf":
|
||||||
start = (x, y1, z1)
|
start = (x, y1, z1)
|
||||||
ende = (x, y - dy + asoffset_y, lower_hoehe_gefaelle)
|
ende = (x, y - dy + asoffset_y, lower_hoehe_gefaelle)
|
||||||
block_vario.add_blockref(
|
block_vario.add_blockref(
|
||||||
@@ -491,7 +491,7 @@ def gefaellegerade_erstellung(
|
|||||||
dxfattribs={"rotation": 180, "layer": layer, "color": color},
|
dxfattribs={"rotation": 180, "layer": layer, "color": color},
|
||||||
)
|
)
|
||||||
return start, ende
|
return start, ende
|
||||||
elif voerder_richtung == "Ab":
|
elif foerder_richtung == "Ab":
|
||||||
start = (x, y + dy - asoffset_y, upper_hoehe_gefaelle - asoffset_z)
|
start = (x, y + dy - asoffset_y, upper_hoehe_gefaelle - asoffset_z)
|
||||||
ende = (x, y1, z1)
|
ende = (x, y1, z1)
|
||||||
block_vario.add_blockref(
|
block_vario.add_blockref(
|
||||||
@@ -525,7 +525,7 @@ def gefaellegerade_erstellung(
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if (drehung0 == "GUZS" and drehung1 == "GUZS") or (
|
if (drehung0 == "GUZS" and drehung1 == "GUZS") or (
|
||||||
voerder_richtung != None and drehung0 == "GUZS" and drehung1 == "GUZS"
|
foerder_richtung != None and drehung0 == "GUZS" and drehung1 == "GUZS"
|
||||||
):
|
):
|
||||||
block_as = "200000241_AS-Element_90_rechts"
|
block_as = "200000241_AS-Element_90_rechts"
|
||||||
block_methoden.import_block(block_as, lib_doc, doc)
|
block_methoden.import_block(block_as, lib_doc, doc)
|
||||||
@@ -550,8 +550,8 @@ def gefaellegerade_erstellung(
|
|||||||
and hight_position == "lower"
|
and hight_position == "lower"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
or (voerder_richtung == "Auf" and drehung0 == "UZS" and drehung1 == "GUZS")
|
or (foerder_richtung == "Auf" and drehung0 == "UZS" and drehung1 == "GUZS")
|
||||||
or (voerder_richtung == "Ab" and drehung0 == "GUZS" and drehung1 == "UZS")
|
or (foerder_richtung == "Ab" and drehung0 == "GUZS" and drehung1 == "UZS")
|
||||||
):
|
):
|
||||||
block_as = "200000241_AS-Element_90_rechts"
|
block_as = "200000241_AS-Element_90_rechts"
|
||||||
block_methoden.import_block(block_as, lib_doc, doc)
|
block_methoden.import_block(block_as, lib_doc, doc)
|
||||||
@@ -564,7 +564,7 @@ def gefaellegerade_erstellung(
|
|||||||
lib_doc, block_es
|
lib_doc, block_es
|
||||||
)
|
)
|
||||||
elif (drehung0 == "UZS" and drehung1 == "UZS") or (
|
elif (drehung0 == "UZS" and drehung1 == "UZS") or (
|
||||||
voerder_richtung != None and drehung0 == "UZS" and drehung1 == "UZS"
|
foerder_richtung != None and drehung0 == "UZS" and drehung1 == "UZS"
|
||||||
):
|
):
|
||||||
block_as = "200000217_AS-Element_90_links"
|
block_as = "200000217_AS-Element_90_links"
|
||||||
block_methoden.import_block(block_as, lib_doc, doc)
|
block_methoden.import_block(block_as, lib_doc, doc)
|
||||||
@@ -591,8 +591,8 @@ def gefaellegerade_erstellung(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
or (voerder_richtung == "Auf" and drehung0 == "GUZS" and drehung1 == "UZS")
|
or (foerder_richtung == "Auf" and drehung0 == "GUZS" and drehung1 == "UZS")
|
||||||
or (voerder_richtung == "Ab" and drehung0 == "UZS" and drehung1 == "GUZS")
|
or (foerder_richtung == "Ab" and drehung0 == "UZS" and drehung1 == "GUZS")
|
||||||
):
|
):
|
||||||
|
|
||||||
block_as = "200000217_AS-Element_90_links"
|
block_as = "200000217_AS-Element_90_links"
|
||||||
@@ -644,7 +644,7 @@ def gefaellegerade_erstellung(
|
|||||||
dxfattribs={"layer": es_layer, "color": es_color},
|
dxfattribs={"layer": es_layer, "color": es_color},
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
if voerder_richtung == "Auf":
|
if foerder_richtung == "Auf":
|
||||||
start = (x, y + dy - esoffset_y, upper_hoehe_gefaelle + esoffset_z)
|
start = (x, y + dy - esoffset_y, upper_hoehe_gefaelle + esoffset_z)
|
||||||
ende = (x, y - dy + asoffset, lower_hoehe_gefaelle - asoffset_z)
|
ende = (x, y - dy + asoffset, lower_hoehe_gefaelle - asoffset_z)
|
||||||
|
|
||||||
@@ -667,7 +667,7 @@ def gefaellegerade_erstellung(
|
|||||||
dxfattribs={"rotation": 180, "layer": es_layer, "color": es_color},
|
dxfattribs={"rotation": 180, "layer": es_layer, "color": es_color},
|
||||||
)
|
)
|
||||||
return start, ende
|
return start, ende
|
||||||
elif voerder_richtung == "Ab":
|
elif foerder_richtung == "Ab":
|
||||||
start = (x, y + dy - asoffset_y, upper_hoehe_gefaelle - asoffset_z)
|
start = (x, y + dy - asoffset_y, upper_hoehe_gefaelle - asoffset_z)
|
||||||
ende = (x, y - dy + esoffset_y, lower_hoehe_gefaelle + esoffset_z)
|
ende = (x, y - dy + esoffset_y, lower_hoehe_gefaelle + esoffset_z)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import math
|
import math
|
||||||
import plant2dxf
|
from lib import plant2dxf
|
||||||
from ezdxf import units
|
from ezdxf import units
|
||||||
from ezdxf.entities import Line
|
from ezdxf.entities import Line
|
||||||
from ezdxf.addons import importer
|
from ezdxf.addons import importer
|
||||||
|
|||||||
+35
-35
@@ -14,9 +14,9 @@ from ezdxf import units
|
|||||||
from ezdxf.entities import Line
|
from ezdxf.entities import Line
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import math
|
import math
|
||||||
from utils import check_environment_var, setup_logger
|
from lib.utils import check_environment_var, setup_logger
|
||||||
from handler_context import HandlerContext
|
from lib.handler_context import HandlerContext
|
||||||
from Elemente import (
|
from lib.Elemente import (
|
||||||
Kreisel,
|
Kreisel,
|
||||||
VarioFoerderer,
|
VarioFoerderer,
|
||||||
Gefaellestrecke,
|
Gefaellestrecke,
|
||||||
@@ -25,9 +25,9 @@ from Elemente import (
|
|||||||
Omniflo,
|
Omniflo,
|
||||||
Eckrad,
|
Eckrad,
|
||||||
)
|
)
|
||||||
import as_es_methoden
|
from lib import as_es_methoden
|
||||||
import block_methoden
|
from lib import block_methoden
|
||||||
import arbeiten_mit_csv
|
from lib import arbeiten_mit_csv
|
||||||
|
|
||||||
# --------------------------------------------------------- Konstante Parameter
|
# --------------------------------------------------------- Konstante Parameter
|
||||||
ATTR_TAG = "TeileId" # Attributtag im Block
|
ATTR_TAG = "TeileId" # Attributtag im Block
|
||||||
@@ -596,7 +596,7 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
if "6-SP" not in ctx.doc.layers:
|
if "6-SP" not in ctx.doc.layers:
|
||||||
ctx.doc.layers.add(name="6-SP", color=7)
|
ctx.doc.layers.add(name="6-SP", color=7)
|
||||||
# Vorbereitung der Werte
|
# Vorbereitung der Werte
|
||||||
voerder_richtung = foerderer.foerderer_richtung
|
foerder_richtung = foerderer.foerderer_richtung
|
||||||
winkel = int(foerderer.winkel)
|
winkel = int(foerderer.winkel)
|
||||||
erster_kreisel_höher = False
|
erster_kreisel_höher = False
|
||||||
ein_kreisel_höher = False
|
ein_kreisel_höher = False
|
||||||
@@ -644,8 +644,8 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
foerderer, x0_eckrad, y0_eckrad
|
foerderer, x0_eckrad, y0_eckrad
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
blockname = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{ein_kreisel_höher}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
blockname = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_rechts_{ein_kreisel_höher}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
||||||
block_name_links = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{ein_kreisel_höher}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
block_name_links = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_links_{ein_kreisel_höher}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
||||||
if blockname not in ctx.doc.blocks:
|
if blockname not in ctx.doc.blocks:
|
||||||
block_vario = ctx.doc.blocks.new(blockname, base_point=(0, 0, 0))
|
block_vario = ctx.doc.blocks.new(blockname, base_point=(0, 0, 0))
|
||||||
|
|
||||||
@@ -671,7 +671,7 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
block_name_links,
|
block_name_links,
|
||||||
start,
|
start,
|
||||||
ende,
|
ende,
|
||||||
voerder_richtung,
|
foerder_richtung,
|
||||||
winkel_VP_offset_vorne,
|
winkel_VP_offset_vorne,
|
||||||
winkel_VP_offset_hinten,
|
winkel_VP_offset_hinten,
|
||||||
)
|
)
|
||||||
@@ -693,8 +693,8 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
# Falls der Förderer mit zwei Eckrads verbunden ist
|
# Falls der Förderer mit zwei Eckrads verbunden ist
|
||||||
if "Eckrad_x" in gefaellestrecke_vario and "Eckrad_x_1" in gefaellestrecke_vario:
|
if "Eckrad_x" in gefaellestrecke_vario and "Eckrad_x_1" in gefaellestrecke_vario:
|
||||||
|
|
||||||
blockname = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_2_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
blockname = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_rechts_2_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
||||||
block_name_links = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_2_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
block_name_links = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_links_2_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
||||||
if blockname not in ctx.doc.blocks:
|
if blockname not in ctx.doc.blocks:
|
||||||
block_vario = ctx.doc.blocks.new(blockname, base_point=(0, 0, 0))
|
block_vario = ctx.doc.blocks.new(blockname, base_point=(0, 0, 0))
|
||||||
|
|
||||||
@@ -718,7 +718,7 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
block_name_links,
|
block_name_links,
|
||||||
start,
|
start,
|
||||||
ende,
|
ende,
|
||||||
voerder_richtung,
|
foerder_richtung,
|
||||||
winkel_VP_offset_vorne,
|
winkel_VP_offset_vorne,
|
||||||
winkel_VP_offset_hinten,
|
winkel_VP_offset_hinten,
|
||||||
)
|
)
|
||||||
@@ -776,8 +776,8 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
)
|
)
|
||||||
if am_kreisel == 1:
|
if am_kreisel == 1:
|
||||||
# Erstellung der blockname für wenn die Motorstation rechts und links
|
# Erstellung der blockname für wenn die Motorstation rechts und links
|
||||||
blockname = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{ein_kreisel_höher}_{drehung0}_True_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
blockname = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_rechts_{ein_kreisel_höher}_{drehung0}_True_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
||||||
block_name_links = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{ein_kreisel_höher}_{drehung0}_True_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
block_name_links = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_links_{ein_kreisel_höher}_{drehung0}_True_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
||||||
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
||||||
if blockname in ctx.doc.blocks:
|
if blockname in ctx.doc.blocks:
|
||||||
if ctx.merkmale.get("Motorseite") == "links":
|
if ctx.merkmale.get("Motorseite") == "links":
|
||||||
@@ -821,7 +821,7 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
block_name_links,
|
block_name_links,
|
||||||
start,
|
start,
|
||||||
ende,
|
ende,
|
||||||
voerder_richtung,
|
foerder_richtung,
|
||||||
winkel_VP_offset_vorne,
|
winkel_VP_offset_vorne,
|
||||||
winkel_VP_offset_hinten,
|
winkel_VP_offset_hinten,
|
||||||
)
|
)
|
||||||
@@ -843,8 +843,8 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
# Abschnitt falls der Vario nur mit einem Kreisel verbunden ist und nicht an dem Kreisel direkt verbunden ist
|
# Abschnitt falls der Vario nur mit einem Kreisel verbunden ist und nicht an dem Kreisel direkt verbunden ist
|
||||||
|
|
||||||
# Erstellung der blockname für wenn die Motorstation rechts und links
|
# Erstellung der blockname für wenn die Motorstation rechts und links
|
||||||
blockname = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{ein_kreisel_höher}_{drehung0}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
blockname = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_rechts_{ein_kreisel_höher}_{drehung0}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
||||||
block_name_links = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{ein_kreisel_höher}_{drehung0}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
block_name_links = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_links_{ein_kreisel_höher}_{drehung0}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}_{mit_horizontal_verbunden}"
|
||||||
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
||||||
if blockname in ctx.doc.blocks:
|
if blockname in ctx.doc.blocks:
|
||||||
if ctx.merkmale.get("Motorseite") == "links":
|
if ctx.merkmale.get("Motorseite") == "links":
|
||||||
@@ -882,7 +882,7 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
ctx.config,
|
ctx.config,
|
||||||
None,
|
None,
|
||||||
block_vario,
|
block_vario,
|
||||||
voerder_richtung,
|
foerder_richtung,
|
||||||
ein_kreisel_höher,
|
ein_kreisel_höher,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
@@ -899,7 +899,7 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
block_name_links,
|
block_name_links,
|
||||||
start,
|
start,
|
||||||
ende,
|
ende,
|
||||||
voerder_richtung,
|
foerder_richtung,
|
||||||
winkel_VP_offset_vorne,
|
winkel_VP_offset_vorne,
|
||||||
winkel_VP_offset_hinten,
|
winkel_VP_offset_hinten,
|
||||||
)
|
)
|
||||||
@@ -966,8 +966,8 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
# Falls der Förder mit beiden Kreisel verbunden ist
|
# Falls der Förder mit beiden Kreisel verbunden ist
|
||||||
if kreisel_verbunden == 2:
|
if kreisel_verbunden == 2:
|
||||||
# Erstellung der blockname für wenn die Motorstation rechts und links
|
# Erstellung der blockname für wenn die Motorstation rechts und links
|
||||||
blockname = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{kreisel_verbunden}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
blockname = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_rechts_{kreisel_verbunden}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
||||||
block_name_links = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{kreisel_verbunden}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
block_name_links = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_links_{kreisel_verbunden}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
||||||
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
||||||
if blockname in ctx.doc.blocks:
|
if blockname in ctx.doc.blocks:
|
||||||
if ctx.merkmale.get("Motorseite") == "links":
|
if ctx.merkmale.get("Motorseite") == "links":
|
||||||
@@ -1002,7 +1002,7 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
block_name_links,
|
block_name_links,
|
||||||
start,
|
start,
|
||||||
ende,
|
ende,
|
||||||
voerder_richtung,
|
foerder_richtung,
|
||||||
winkel_VP_offset_vorne,
|
winkel_VP_offset_vorne,
|
||||||
winkel_VP_offset_hinten,
|
winkel_VP_offset_hinten,
|
||||||
)
|
)
|
||||||
@@ -1029,8 +1029,8 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
else:
|
else:
|
||||||
erster_kreisel_höher = False
|
erster_kreisel_höher = False
|
||||||
# Erstellung der blockname für wenn die Motorstation rechts und links
|
# Erstellung der blockname für wenn die Motorstation rechts und links
|
||||||
blockname = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{am_kreisel}_{erster_kreisel_höher}_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
blockname = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_rechts_{am_kreisel}_{erster_kreisel_höher}_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
||||||
block_name_links = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{am_kreisel}_{erster_kreisel_höher}_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
block_name_links = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_links_{am_kreisel}_{erster_kreisel_höher}_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
||||||
|
|
||||||
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
||||||
if blockname in ctx.doc.blocks:
|
if blockname in ctx.doc.blocks:
|
||||||
@@ -1093,7 +1093,7 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
block_name_links,
|
block_name_links,
|
||||||
start,
|
start,
|
||||||
ende,
|
ende,
|
||||||
voerder_richtung,
|
foerder_richtung,
|
||||||
winkel_VP_offset_vorne,
|
winkel_VP_offset_vorne,
|
||||||
winkel_VP_offset_hinten,
|
winkel_VP_offset_hinten,
|
||||||
)
|
)
|
||||||
@@ -1114,8 +1114,8 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
# Falls es nicht mit dem Kreisel direkt verbunden ist
|
# Falls es nicht mit dem Kreisel direkt verbunden ist
|
||||||
else:
|
else:
|
||||||
# Erstellung der blockname für wenn die Motorstation rechts und links
|
# Erstellung der blockname für wenn die Motorstation rechts und links
|
||||||
blockname = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
blockname = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_rechts_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
||||||
block_name_links = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
block_name_links = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_links_{drehung0}_{drehung1}_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
||||||
|
|
||||||
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
||||||
if blockname in ctx.doc.blocks:
|
if blockname in ctx.doc.blocks:
|
||||||
@@ -1160,7 +1160,7 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
block_name_links,
|
block_name_links,
|
||||||
start,
|
start,
|
||||||
ende,
|
ende,
|
||||||
voerder_richtung,
|
foerder_richtung,
|
||||||
winkel_VP_offset_vorne,
|
winkel_VP_offset_vorne,
|
||||||
winkel_VP_offset_hinten,
|
winkel_VP_offset_hinten,
|
||||||
)
|
)
|
||||||
@@ -1183,8 +1183,8 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
halbe_laenge = laenge / 2
|
halbe_laenge = laenge / 2
|
||||||
dy = halbe_laenge * math.cos(0)
|
dy = halbe_laenge * math.cos(0)
|
||||||
# Erstellung der blockname für wenn die Motorstation rechts und links
|
# Erstellung der blockname für wenn die Motorstation rechts und links
|
||||||
blockname = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_rechts_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
blockname = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_rechts_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
||||||
block_name_links = f"Vario_Foerderer_{winkel}_{voerder_richtung}_{laenge}_{hoehe_vario}_links_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
block_name_links = f"Vario_Foerderer_{winkel}_{foerder_richtung}_{laenge}_{hoehe_vario}_links_{motor_vorhanden}_{umlenk_vorhanden}_{gefaelle}_{gefahellewinkel}"
|
||||||
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
# Falls der Block bereits in dem ctx.doc ist platziere diesen einfach
|
||||||
if blockname in ctx.doc.blocks:
|
if blockname in ctx.doc.blocks:
|
||||||
if ctx.merkmale.get("Motorseite") == "links":
|
if ctx.merkmale.get("Motorseite") == "links":
|
||||||
@@ -1219,7 +1219,7 @@ def handle_ils_2_0_variofoerderer(ctx):
|
|||||||
block_name_links,
|
block_name_links,
|
||||||
start,
|
start,
|
||||||
ende,
|
ende,
|
||||||
voerder_richtung,
|
foerder_richtung,
|
||||||
winkel_VP_offset_vorne,
|
winkel_VP_offset_vorne,
|
||||||
winkel_VP_offset_hinten,
|
winkel_VP_offset_hinten,
|
||||||
)
|
)
|
||||||
@@ -1604,6 +1604,7 @@ def main(
|
|||||||
logger.info(f"[DONE] DXF gespeichert unter: {output_path}")
|
logger.info(f"[DONE] DXF gespeichert unter: {output_path}")
|
||||||
else:
|
else:
|
||||||
print(f"[DONE] DXF gespeichert unter: {output_path}")
|
print(f"[DONE] DXF gespeichert unter: {output_path}")
|
||||||
|
logger.info("=== plant2dxf Verarbeitung abgeschlossen ===")
|
||||||
|
|
||||||
|
|
||||||
def check_dxflibrary_path(lib_path, verbose, logger):
|
def check_dxflibrary_path(lib_path, verbose, logger):
|
||||||
@@ -1686,7 +1687,7 @@ if __name__ == "__main__":
|
|||||||
output_path_json = (
|
output_path_json = (
|
||||||
Path(args.output) if args.output else (work_dir / f"{csv_path.stem}.json")
|
Path(args.output) if args.output else (work_dir / f"{csv_path.stem}.json")
|
||||||
)
|
)
|
||||||
main(
|
raise SystemExit(main(
|
||||||
csv_path,
|
csv_path,
|
||||||
default_lib_path,
|
default_lib_path,
|
||||||
cfg_path,
|
cfg_path,
|
||||||
@@ -1695,5 +1696,4 @@ if __name__ == "__main__":
|
|||||||
output_path_json,
|
output_path_json,
|
||||||
verbose=args.verbose,
|
verbose=args.verbose,
|
||||||
logger=logger,
|
logger=logger,
|
||||||
)
|
))
|
||||||
logger.info("=== plant2dxf Verarbeitung abgeschlossen ===")
|
|
||||||
|
|||||||
+2
-1
@@ -4,4 +4,5 @@ target-version = ["py312"]
|
|||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
line-length = 88
|
line-length = 88
|
||||||
select = ["E", "F", "I"]
|
select = ["E", "F", "I"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user