Merge branch 'main' of http://gitea.schoenenberger.de/mistangl/kabellaengen
This commit is contained in:
@@ -0,0 +1,283 @@
|
|||||||
|
# Symbole und Benennungen im DXF-Layout
|
||||||
|
|
||||||
|
Diese Dokumentation beschreibt, wie Symbole und Texte in der DXF-Zeichnung benannt und strukturiert sein muessen, damit sie vom System erkannt werden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Sensoren / Aktoren / Motoren (Equipment)
|
||||||
|
|
||||||
|
Equipment wird ueber **INSERT-Bloecke mit Attributen** erkannt.
|
||||||
|
|
||||||
|
### Block-Attribute
|
||||||
|
|
||||||
|
| Attribut-Tag | Bedeutung | Beispiel | Pflicht |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `NAME` | Geraete-ID (moderner Block) | `MA0062`, `BG3240` | Ja (oder IO+B) |
|
||||||
|
| `IO` | Geraete-ID (I/O-Block, Legacy) | `MA0062` | Alternativ zu NAME |
|
||||||
|
| `B` | Geraete-ID (Technik-Block, Legacy) | `MA0062` | Alternativ zu NAME |
|
||||||
|
| `ARTINR` | SIVAS-Artikelnummer | `790902001` | Ja |
|
||||||
|
| `KENNZEICHNUNG` | Routing-Adresse | `=A01+UH01-KF1DQ04` | Ja |
|
||||||
|
| `SPS` | SPS-Praefix / Anlage | `1` | Ja (fuer Zuordnung) |
|
||||||
|
| `VERW` | Verwendung/Beschreibung | `CV-M0062_0,75` | Nein |
|
||||||
|
| `REALE_POSITION` | Marker fuer tatsaechliche Position | `x` | Nein |
|
||||||
|
|
||||||
|
### Geraete-Praefix (erste 2 Zeichen der ID)
|
||||||
|
|
||||||
|
Welche Geraete geroutet werden, bestimmt die `cfg/BMK.cfg`:
|
||||||
|
|
||||||
|
**Routing-Include** (werden geroutet):
|
||||||
|
|
||||||
|
| Praefix | Typ |
|
||||||
|
|---|---|
|
||||||
|
| `MA` | Motoren |
|
||||||
|
| `MB` | Ventile |
|
||||||
|
| `QM` | Ventile/Pumpen |
|
||||||
|
| `BG` | Sensoren/Naeherungsschalter |
|
||||||
|
| `BP` | Naeherungsschalter |
|
||||||
|
| `BX` | Mehrzweckgeraete |
|
||||||
|
| `PO` | Pneumatik-Elemente |
|
||||||
|
| `SF` | Sicherheitselemente |
|
||||||
|
| `PF` | Programmierbare Logik |
|
||||||
|
|
||||||
|
**Routing-Ignore** (Schaltschrankelemente, nicht geroutet):
|
||||||
|
|
||||||
|
| Praefix | Typ |
|
||||||
|
|---|---|
|
||||||
|
| `FC` | Steuerungselemente |
|
||||||
|
| `UH` | Unterverteiler |
|
||||||
|
| `UC` | Unterverteiler |
|
||||||
|
| `UZ` | Spezialverteiler |
|
||||||
|
| `DI` | Digitale Eingaenge |
|
||||||
|
| `DQ` | Digitale Ausgaenge |
|
||||||
|
| `QA` | Ausgabegeraete |
|
||||||
|
|
||||||
|
### KENNZEICHNUNG-Format
|
||||||
|
|
||||||
|
```
|
||||||
|
=ANLAGE+VERTEILER-KARTE
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel:** `=A01+UH01-KF1DQ04`
|
||||||
|
|
||||||
|
| Teil | Bedeutung | Beispiel |
|
||||||
|
|---|---|---|
|
||||||
|
| ANLAGE | Anlagengruppe | `A01` |
|
||||||
|
| VERTEILER | Unterverteiler-ID | `UH01` |
|
||||||
|
| KARTE | Karte/Kanal | `KF1DQ04` |
|
||||||
|
|
||||||
|
Trennzeichen: `=` (Start), `+` (zwischen Anlage und Verteiler), `-` (zwischen Verteiler und Karte)
|
||||||
|
|
||||||
|
### Block-Zusammenfuehrung (Legacy Dual-Block)
|
||||||
|
|
||||||
|
Bei der alten Dual-Block-Struktur werden zwei Bloecke mit gleicher ID (z.B. `MA0062`) zusammengefuehrt, wenn:
|
||||||
|
- Beide innerhalb von **1000mm** Abstand liegen
|
||||||
|
- Einer das `SPS`-Attribut hat, der andere nicht
|
||||||
|
- Ergebnis-ID: `{id}@{sps}` (z.B. `MA0062@1`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Unterverteiler (Distributoren)
|
||||||
|
|
||||||
|
Unterverteiler koennen auf **zwei Arten** definiert werden: als Text oder als Symbol.
|
||||||
|
|
||||||
|
### Variante A: Text (MTEXT)
|
||||||
|
|
||||||
|
Ein MTEXT-Element auf einem erlaubten Layer mit folgendem Muster:
|
||||||
|
|
||||||
|
```
|
||||||
|
-UH01 (Minus vor dem Namen)
|
||||||
|
+UH01 (Plus vor dem Namen)
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Text muss den Verteilernamen als Teilstring enthalten, mit `-` oder `+` davor.
|
||||||
|
|
||||||
|
**Erlaubte Layer** (konfiguriert in `cfg/allgemein.cfg`, Sektion `GetPos-Layer_Distributors`):
|
||||||
|
- `Busverteiler-Kennzeichnung`
|
||||||
|
- `0-0_ILS_Busverteiler-Kennzeichnung`
|
||||||
|
- `0-0_ILS_UNTERVERTEILER`
|
||||||
|
- `0-0_ILS_Unterverteiler`
|
||||||
|
- `0-0_Omniflo_UNTERVERTEILER`
|
||||||
|
- `0-0_Omniflo_Unterverteiler`
|
||||||
|
- `UNTERVERTEILER`
|
||||||
|
- `0-0_Omniflo_Busverteiler-Kennzeichnung`
|
||||||
|
- `Schaltschrank-ILS`
|
||||||
|
|
||||||
|
### Variante B: Symbol (INSERT-Block)
|
||||||
|
|
||||||
|
Ein INSERT-Block mit dem Attribut `NAME`, dessen Wert einem **Cabinet-Pattern** entspricht.
|
||||||
|
|
||||||
|
**Cabinet-Patterns** (Regex, konfiguriert in `cfg/BMK.cfg`, Sektion `Cabinet-Pattern`):
|
||||||
|
|
||||||
|
| Pattern | Beispiel-Match | Extrahierte ID |
|
||||||
|
|---|---|---|
|
||||||
|
| `A\d\d\+(UH0\d)` | `A01+UH01` | `UH01` |
|
||||||
|
| `A\d\d\+(UC\d\d\d)` | `A01+UC001` | `UC001` |
|
||||||
|
| `\+(UC\d\d\d\d)` | `+UC0101` | `UC0101` |
|
||||||
|
| `\+(UH\d\d)` | `+UH01` | `UH01` |
|
||||||
|
|
||||||
|
Die Klammer-Gruppe im Regex bestimmt die extrahierte Verteiler-ID.
|
||||||
|
|
||||||
|
**Optionale Attribute:**
|
||||||
|
- `REALE_POSITION` = `x` : Position wird aus Attribut-Position berechnet (Mittelpunkt des Markers)
|
||||||
|
- `SPS` : SPS-Zuordnung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Tunnel
|
||||||
|
|
||||||
|
Tunnel koennen ebenfalls als **Text** oder **Symbol** definiert werden.
|
||||||
|
|
||||||
|
### Variante A: Text (MTEXT)
|
||||||
|
|
||||||
|
Ein MTEXT-Element auf einem erlaubten Layer mit folgendem Muster:
|
||||||
|
|
||||||
|
```
|
||||||
|
TUNNEL1-5 (Tunnelname-Laenge)
|
||||||
|
TUNNEL2-10
|
||||||
|
```
|
||||||
|
|
||||||
|
**Regex:** `(TUNNEL\d+)-(\d+)`
|
||||||
|
|
||||||
|
| Teil | Bedeutung | Beispiel |
|
||||||
|
|---|---|---|
|
||||||
|
| Gruppe 1 | Tunnelname | `TUNNEL1` |
|
||||||
|
| Gruppe 2 | Laenge in Metern | `5` |
|
||||||
|
|
||||||
|
Pro Tunnel muessen genau **2 MTEXT-Elemente** vorhanden sein (Ein- und Ausgang).
|
||||||
|
|
||||||
|
**Erlaubte Layer** (konfiguriert in `cfg/allgemein.cfg`, Sektion `GetPos-Layer_Tunnel`):
|
||||||
|
- `Busverteiler-Kennzeichnung`
|
||||||
|
- `0-0_Tunnel`
|
||||||
|
|
||||||
|
### Variante B: Symbol (INSERT-Block)
|
||||||
|
|
||||||
|
Ein INSERT-Block mit dem Attribut `NAME`, dessen Wert einem **Tunnel-Pattern** entspricht.
|
||||||
|
|
||||||
|
**Tunnel-Patterns** (Regex, konfiguriert in `cfg/BMK.cfg`, Sektion `Tunnel-Pattern`):
|
||||||
|
|
||||||
|
| Pattern | Beispiel |
|
||||||
|
|---|---|
|
||||||
|
| `Tunnel\d+` | `Tunnel1`, `Tunnel2` |
|
||||||
|
| `Tunnel_\d+` | `Tunnel_1`, `Tunnel_2` |
|
||||||
|
| `Tunnel-\d+` | `Tunnel-1`, `Tunnel-2` |
|
||||||
|
| `TUNNEL\d+` | `TUNNEL1`, `TUNNEL2` |
|
||||||
|
|
||||||
|
**Attribute:**
|
||||||
|
|
||||||
|
| Attribut-Tag | Bedeutung | Beispiel | Pflicht |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `NAME` | Tunnelname | `Tunnel1` | Ja |
|
||||||
|
| `LAENGE` | Tunnellaenge in Metern | `5` | Nein (Default: 5) |
|
||||||
|
| `REALE_POSITION` | Positionsmarker | `x` | Nein |
|
||||||
|
|
||||||
|
Pro Tunnel muessen genau **2 Symbole** platziert werden (Ein- und Ausgang).
|
||||||
|
|
||||||
|
**Fehler bei Tunnel-Definition:**
|
||||||
|
- Weniger als 2 Positionen: Fehler `missing_tunnel`
|
||||||
|
- Mehr als 2 Positionen: Fehler `overdefined_tunnel`
|
||||||
|
- Fehlende Laenge: Warnung, Default 5m wird verwendet
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Kabelpritschen (Racks)
|
||||||
|
|
||||||
|
Kabelpritschen werden als **LWPOLYLINE** oder **POLYLINE** auf bestimmten Layern erkannt.
|
||||||
|
|
||||||
|
Es sind keine Attribute oder Texte noetig - nur der **Layer** entscheidet.
|
||||||
|
|
||||||
|
**Erlaubte Layer** (konfiguriert in `cfg/allgemein.cfg`, Sektion `GetPos-Layer_Racks`):
|
||||||
|
|
||||||
|
| Layer | Beschreibung |
|
||||||
|
|---|---|
|
||||||
|
| `PRITSCHE_100-60` | Standard 100x60 |
|
||||||
|
| `PRITSCHE_100-60-SCHRAFF` | Standard 100x60 schraffiert |
|
||||||
|
| `PRITSCHE_200-60` | Standard 200x60 |
|
||||||
|
| `PRITSCHE_200-60_ILS` | ILS 200x60 |
|
||||||
|
| `PRITSCHE_200-60_OMNIFLO` | Omniflo 200x60 |
|
||||||
|
| `0-0_ILS_PRITSCHE_200-60_storage_Level1` | ILS Storage Level 1 |
|
||||||
|
| `0-0_ILS_PRITSCHE_200-60_storage_Level2` | ILS Storage Level 2 |
|
||||||
|
| `0-0_ILS_PRITSCHE_200-60_Workstation` | ILS Workstation |
|
||||||
|
| `0-0_ILS_Pritsche_200-60_AMR` | ILS AMR |
|
||||||
|
| `0-0_ILS_PRITSCHE_200-60_Highway` | ILS Highway |
|
||||||
|
| `0-0_ILS_Pritsche_200-60_Inbound` | ILS Inbound |
|
||||||
|
| `0-0_Omniflo_Pritsche_200-60_Workstation-Outbound` | Omniflo Workstation Outbound |
|
||||||
|
| `0-0_Omniflo_Pritsche_200-60_outbound` | Omniflo Outbound |
|
||||||
|
| `0-0_Omniflo_Pritsche_200-60_AMR` | Omniflo AMR |
|
||||||
|
| `0-0_Omniflo_Pritsche_200-60_Highway` | Omniflo Highway |
|
||||||
|
|
||||||
|
**Validierung:** Wenn die Z-Koordinaten aller Racks mehr als 2000mm auseinanderliegen, wird eine Warnung ausgegeben (Schwellwert konfigurierbar in `cfg/allgemein.cfg`, Sektion `Racks`, Schluessel `MaximalTotalHeightDifferences`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Zusammenfassung: Was muss im Layout vorhanden sein?
|
||||||
|
|
||||||
|
| Element | Erkennungsart | Mindestangaben |
|
||||||
|
|---|---|---|
|
||||||
|
| **Sensor/Motor** | INSERT-Block | `NAME` (oder `IO`+`B`), `ARTINR`, `KENNZEICHNUNG` |
|
||||||
|
| **Unterverteiler** | MTEXT **oder** INSERT-Block | Text: `-UH01` auf richtigem Layer / Block: `NAME` = `A01+UH01` |
|
||||||
|
| **Tunnel** | MTEXT **oder** INSERT-Block | Text: `TUNNEL1-5` (2x) / Block: `NAME` = `Tunnel1`, `LAENGE` = `5` (2x) |
|
||||||
|
| **Kabelpritsche** | LWPOLYLINE/POLYLINE | Auf richtigem Layer gezeichnet |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Funktionsreferenz (getpositions.py)
|
||||||
|
|
||||||
|
Uebersicht der zentralen Funktionen, die die Erkennung durchfuehren.
|
||||||
|
|
||||||
|
### Sensoren / Equipment
|
||||||
|
|
||||||
|
| Funktion | Zeile | Aufgabe |
|
||||||
|
|---|---|---|
|
||||||
|
| `get_attributes_of_insert(d_insert, d_pos)` | ~76 | Extrahiert ID, Typ und Position aus einem einzelnen INSERT-Block. Prueft zuerst `IO`, dann `NAME`, dann `B`. Bei `REALE_POSITION='x'` wird der Mittelpunkt aus Marker-Geometrie berechnet (Breite/Hoehe aus Config). Gibt `(dict, id, typ)` zurueck. |
|
||||||
|
| `extract_input_positions(all_inserts, all_positions, error_collector)` | ~221 | Hauptfunktion: Iteriert ueber alle Bloecke, ruft `get_attributes_of_insert()` auf, trennt Sensoren von Schaltschrankelementen anhand des Praefixes. Fuehrt Block-Zusammenfuehrung via `CompareBuffer` durch. |
|
||||||
|
| `allocate_blocks_together(...)` | ~329 | Fuehrt die Zusammenfuehrung von Dual-Bloecken durch (Legacy). Nutzt `CompareBuffer.positions_are_close()` mit 1000mm Toleranz. |
|
||||||
|
|
||||||
|
### Unterverteiler
|
||||||
|
|
||||||
|
| Funktion | Zeile | Aufgabe |
|
||||||
|
|---|---|---|
|
||||||
|
| `get_subdistributor_positions_from_entities(entities, dist2sensors)` | ~547 | **Text-Erkennung:** Iteriert ueber MTEXT-Entities auf erlaubten Layern. Sucht nach `-{name}` oder `+{name}` im Text. Gibt `{distname: (x, y)}` zurueck. |
|
||||||
|
| `get_subdistributor_positions_from_symbols(all_inserts, all_positions, dist2sensors)` | ~472 | **Symbol-Erkennung:** Iteriert ueber alle INSERT-Bloecke mit `NAME`-Attribut. Prueft NAME gegen Cabinet-Patterns aus BMK.cfg. Ruft `get_subdistributor_position_of_symbol()` fuer die Positionsextraktion auf. |
|
||||||
|
| `get_subdistributor_position_of_symbol(d_insert, d_pos)` | ~413 | Hilfsfunktion: Extrahiert Position aus einem einzelnen Unterverteiler-Block. Beruecksichtigt `REALE_POSITION` fuer Mittelpunktberechnung. Gibt `(dict, id)` zurueck. |
|
||||||
|
|
||||||
|
### Tunnel
|
||||||
|
|
||||||
|
| Funktion | Zeile | Aufgabe |
|
||||||
|
|---|---|---|
|
||||||
|
| `get_tunnel_positions_from_entities(entities)` | ~574 | **Text-Erkennung:** Iteriert ueber MTEXT-Entities auf erlaubten Layern. Regex `(TUNNEL\d+)-(\d+)` extrahiert Name und Laenge. Sammelt alle Positionen pro Tunnelname. |
|
||||||
|
| `get_tunnel_positions_from_symbols(all_inserts, all_positions, error_collector)` | ~493 | **Symbol-Erkennung:** Iteriert ueber alle INSERT-Bloecke mit `NAME`-Attribut. Prueft NAME gegen Tunnel-Patterns aus BMK.cfg. Liest `LAENGE`-Attribut (Default: 5m). Warnung bei fehlender Laenge. |
|
||||||
|
| `get_tunnel_position_of_symbol(d_insert, d_pos)` | ~441 | Hilfsfunktion: Extrahiert Position und Laenge aus einem einzelnen Tunnel-Block. Beruecksichtigt `REALE_POSITION`. Gibt `(dict, id)` zurueck. |
|
||||||
|
|
||||||
|
### Kabelpritschen
|
||||||
|
|
||||||
|
| Funktion | Zeile | Aufgabe |
|
||||||
|
|---|---|---|
|
||||||
|
| `get_rack_positions(msp)` | ~614 | Sucht LWPOLYLINE und POLYLINE auf erlaubten Layern. Nummeriert Racks automatisch (`Rack_1`, `Rack_2`, ...). Delegiert an `handle_lwpolyline()` bzw. `handle_polyline()`. |
|
||||||
|
| `handle_lwpolyline(entity, rack_key, ret)` | ~636 | Verarbeitet 2D-Polylinien. Z-Wert kommt global aus `elevation`. |
|
||||||
|
| `handle_polyline(entity, rack_key, ret)` | ~644 | Verarbeitet 3D-Polylinien. Jeder Vertex hat individuelle x/y/z-Koordinaten. |
|
||||||
|
| `check_rack_z_coordinates(res_racks, error_collector, config)` | ~654 | Validierung: Prueft ob Z-Koordinaten-Differenz den Schwellwert uebersteigt. |
|
||||||
|
|
||||||
|
### Erkennungsablauf im Hauptprogramm
|
||||||
|
|
||||||
|
```
|
||||||
|
1. DXF laden: get_dxf_file() -> msp
|
||||||
|
2. Alle Bloecke extrahieren: attribs_to_dicts(msp) -> all_inserts, all_positions
|
||||||
|
3. Sensoren: extract_input_positions(all_inserts, all_positions)
|
||||||
|
4. Mappings: create_mappings(res_sens) -> sensor2unterverteiler
|
||||||
|
5. Unterverteiler (Text): get_subdistributor_positions_from_entities(msp.query('MTEXT'))
|
||||||
|
6. Unterverteiler (Symbol): get_subdistributor_positions_from_symbols(all_inserts, all_positions)
|
||||||
|
7. Tunnel (Text): get_tunnel_positions_from_entities(msp.query('MTEXT'))
|
||||||
|
8. Tunnel (Symbol): get_tunnel_positions_from_symbols(all_inserts, all_positions)
|
||||||
|
9. Racks: get_rack_positions(msp)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Konfigurationsdateien
|
||||||
|
|
||||||
|
| Datei | Inhalt |
|
||||||
|
|---|---|
|
||||||
|
| `cfg/allgemein.cfg` | Layer-Zuordnungen, Geometrie-Parameter, Toleranzen |
|
||||||
|
| `cfg/BMK.cfg` | Geraete-Praefixe, Cabinet-Patterns, Tunnel-Patterns, Kabelzuordnungen |
|
||||||
|
| `cfg/kabel.cfg` | SIVAS-Artikelnummern nach Kabeltyp und Laenge |
|
||||||
|
| `cfg/bezeichner.cfg` | Artikelnummer-Beschreibungen |
|
||||||
+15
-83
@@ -6,16 +6,12 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import ezdxf
|
|
||||||
from ezdxf.addons import iterdxf
|
|
||||||
from shapely.geometry import Point
|
from shapely.geometry import Point
|
||||||
from ezdxf.lldxf.const import DXFStructureError
|
|
||||||
|
|
||||||
from error_collector import ErrorCollector, write_json_file
|
from error_collector import ErrorCollector
|
||||||
from utils import (
|
from utils import (
|
||||||
check_environment_var,
|
check_environment_var,
|
||||||
check_file_in_work,
|
check_file_in_work,
|
||||||
dxf_is_binary,
|
|
||||||
get_dxf_file,
|
get_dxf_file,
|
||||||
merge_two_dicts,
|
merge_two_dicts,
|
||||||
to_json,
|
to_json,
|
||||||
@@ -551,10 +547,10 @@ def get_tunnel_positions_from_symbols(all_inserts: list, all_positions: list, er
|
|||||||
def get_subdistributor_positions_from_entities(entities, dist2sensors: dict) -> dict:
|
def get_subdistributor_positions_from_entities(entities, dist2sensors: dict) -> dict:
|
||||||
"""Ermittelt Unterverteiler-Positionen aus einer beliebigen Entity-Iterable.
|
"""Ermittelt Unterverteiler-Positionen aus einer beliebigen Entity-Iterable.
|
||||||
|
|
||||||
Erwartet eine Iterable von DXF-Entities (z. B. aus `msp.query("MTEXT")` oder
|
Erwartet eine Iterable von DXF-Entities (z. B. aus `msp.query("MTEXT")`)
|
||||||
`iterdxf.modelspace(dxf_path)`) und filtert nach den in der Config erlaubten
|
und filtert nach den in der Config erlaubten Layern. Es werden beide bisher
|
||||||
Layern. Es werden beide bisher verwendeten Suchmuster in der MTEXT-Zeile
|
verwendeten Suchmuster in der MTEXT-Zeile unterstützt ("-<distname>" und
|
||||||
unterstützt ("-<distname>" und "+<distname>").
|
"+<distname>").
|
||||||
"""
|
"""
|
||||||
ret = {}
|
ret = {}
|
||||||
all_distributors = dist2sensors.keys()
|
all_distributors = dist2sensors.keys()
|
||||||
@@ -578,10 +574,10 @@ def get_subdistributor_positions_from_entities(entities, dist2sensors: dict) ->
|
|||||||
def get_tunnel_positions_from_entities(entities) -> dict:
|
def get_tunnel_positions_from_entities(entities) -> dict:
|
||||||
"""Ermittelt Tunnel-Ein/Ausgangs-Positionen aus einer beliebigen Entity-Iterable.
|
"""Ermittelt Tunnel-Ein/Ausgangs-Positionen aus einer beliebigen Entity-Iterable.
|
||||||
|
|
||||||
Erwartet eine Iterable von DXF-Entities (z. B. aus `msp.query("MTEXT")` oder
|
Erwartet eine Iterable von DXF-Entities (z. B. aus `msp.query("MTEXT")`)
|
||||||
`iterdxf.modelspace(dxf_path)`) und filtert nach den in der Config erlaubten
|
und filtert nach den in der Config erlaubten Layern. Erkennt Tunnel anhand
|
||||||
Layern. Erkennt Tunnel anhand des Musters "TUNNEL<nr>-<laenge>" und sammelt
|
des Musters "TUNNEL<nr>-<laenge>" und sammelt pro Tunnelname die gefundenen
|
||||||
pro Tunnelname die gefundenen Positionen sowie die Länge.
|
Positionen sowie die Länge.
|
||||||
"""
|
"""
|
||||||
all_tunnels = dict()
|
all_tunnels = dict()
|
||||||
tunnel_length = dict()
|
tunnel_length = dict()
|
||||||
@@ -614,17 +610,6 @@ def get_tunnel_positions_from_entities(entities) -> dict:
|
|||||||
all_tunnels['length'] = tunnel_length
|
all_tunnels['length'] = tunnel_length
|
||||||
return all_tunnels
|
return all_tunnels
|
||||||
|
|
||||||
# helper function
|
|
||||||
def print_line(e) -> None:
|
|
||||||
print(f"LINE on layer: {e.dxf.layer}\n")
|
|
||||||
print(f"points: {repr(e.dxf)}\n")
|
|
||||||
|
|
||||||
def print_polyline(e) -> None:
|
|
||||||
print(f"POLYLINE on layer: {e.dxf.layer}\n")
|
|
||||||
for x, y, start_width, end_width, bulge in e.get_points():
|
|
||||||
print(f" Punkt: ({x}, {y}), Startbreite: ({start_width}, Endbreite: {end_width})")
|
|
||||||
if e.is_closed:
|
|
||||||
print("Diese Polyline ist geschlossen.")
|
|
||||||
|
|
||||||
def get_rack_positions(msp) -> dict:
|
def get_rack_positions(msp) -> dict:
|
||||||
"""Hole alle Positionen aller Kabelpritschen und nummeriere Racks."""
|
"""Hole alle Positionen aller Kabelpritschen und nummeriere Racks."""
|
||||||
@@ -647,30 +632,6 @@ def get_rack_positions(msp) -> dict:
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_rack_positions_iter(dxf_path) -> dict:
|
|
||||||
"""Hole alle Positionen aller Kabelpritschen (Racks) mithilfe von iterdxf."""
|
|
||||||
ret = dict()
|
|
||||||
rack_counter = 1
|
|
||||||
all_layers = config.items('GetPos-Layer_Racks')
|
|
||||||
|
|
||||||
for entity in iterdxf.modelspace(dxf_path):
|
|
||||||
layer = entity.dxf.layer
|
|
||||||
|
|
||||||
if not any(layer == cfg_layer for cfg_layer, _ in all_layers):
|
|
||||||
continue
|
|
||||||
|
|
||||||
rack_key = f"Rack_{rack_counter}"
|
|
||||||
|
|
||||||
if entity.dxftype() == "LWPOLYLINE":
|
|
||||||
handle_lwpolyline(entity, rack_key, ret)
|
|
||||||
elif entity.dxftype() == "POLYLINE":
|
|
||||||
handle_polyline(entity, rack_key, ret)
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
rack_counter += 1
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def handle_lwpolyline(entity, rack_key: str, ret: dict) -> None:
|
def handle_lwpolyline(entity, rack_key: str, ret: dict) -> None:
|
||||||
"""Verarbeitet eine 2D LWPOLYLINE mit globalem Z-Wert (elevation)."""
|
"""Verarbeitet eine 2D LWPOLYLINE mit globalem Z-Wert (elevation)."""
|
||||||
@@ -689,15 +650,6 @@ def handle_polyline(entity, rack_key: str, ret: dict) -> None:
|
|||||||
z = vertex.dxf.location.z
|
z = vertex.dxf.location.z
|
||||||
ret[rack_key].append([round(x, 1), round(y, 1), round(z, 1)])
|
ret[rack_key].append([round(x, 1), round(y, 1), round(z, 1)])
|
||||||
|
|
||||||
def scan(dxf_source) -> dict:
|
|
||||||
layer_names_inside = list(dxf_source.layers.names())
|
|
||||||
alle_block_defs = set(dxf_source.blocks.block_names())
|
|
||||||
used_block_names = set(insert.dxf.name for insert in dxf_source.modelspace().query("INSERT"))
|
|
||||||
ret = dict()
|
|
||||||
ret['all_layers'] = layer_names_inside
|
|
||||||
ret['used_blocks'] = used_block_names
|
|
||||||
ret['all_blocks'] = alle_block_defs
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def check_rack_z_coordinates(res_racks: dict, error_collector, config) -> None:
|
def check_rack_z_coordinates(res_racks: dict, error_collector, config) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -828,17 +780,10 @@ if __name__ == '__main__':
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
if dxf_is_binary(dxf_path): # Wenn dxf eine binary ist, dann komplett parsen und modelspace anlegen
|
doc = get_dxf_file(dxf_path)
|
||||||
print("Given .dxf-file is binary dxf. Proceeding to read file. Watch RAM-usage.")
|
msp = doc.modelspace()
|
||||||
doc = get_dxf_file(dxf_path)
|
|
||||||
msp = doc.modelspace()
|
|
||||||
use_iter = False
|
|
||||||
else:
|
|
||||||
print("Given .dxf-file is ASCII-dxf. Proceeding to use iterative functions. Process may take longer.")
|
|
||||||
use_iter = True
|
|
||||||
|
|
||||||
res_sens = dict()
|
res_sens = dict()
|
||||||
res_cables = dict()
|
|
||||||
res_dist = dict()
|
res_dist = dict()
|
||||||
res_rac = dict()
|
res_rac = dict()
|
||||||
res_mappings = dict()
|
res_mappings = dict()
|
||||||
@@ -870,15 +815,11 @@ if __name__ == '__main__':
|
|||||||
if args.sensors:
|
if args.sensors:
|
||||||
# Sensoren auslesen aus den Symbolen
|
# Sensoren auslesen aus den Symbolen
|
||||||
|
|
||||||
if use_iter:
|
all_inserts, all_positions = attribs_to_dicts(msp)
|
||||||
all_inserts, all_positions = attribs_to_dicts(iterdxf.modelspace(dxf_path))
|
|
||||||
else:
|
|
||||||
all_inserts, all_positions = attribs_to_dicts(msp)
|
|
||||||
res_sens, res_schaltschrank_elemente = extract_input_positions(all_inserts, all_positions, error_collector)
|
res_sens, res_schaltschrank_elemente = extract_input_positions(all_inserts, all_positions, error_collector)
|
||||||
|
|
||||||
output_results['sensors'] = res_sens
|
output_results['sensors'] = res_sens
|
||||||
output_results['schaltschrank_elemente'] = res_schaltschrank_elemente
|
output_results['schaltschrank_elemente'] = res_schaltschrank_elemente
|
||||||
#output_results['cables'] = res_cables
|
|
||||||
|
|
||||||
if args.console:
|
if args.console:
|
||||||
print(to_json(res_sens))
|
print(to_json(res_sens))
|
||||||
@@ -893,10 +834,7 @@ if __name__ == '__main__':
|
|||||||
print(to_json(res_mappings))
|
print(to_json(res_mappings))
|
||||||
|
|
||||||
# Distributoren auslesen (generisch über Entities)
|
# Distributoren auslesen (generisch über Entities)
|
||||||
if use_iter:
|
entities = msp.query('MTEXT')
|
||||||
entities = iterdxf.modelspace(dxf_path)
|
|
||||||
else:
|
|
||||||
entities = msp.query('MTEXT')
|
|
||||||
# die Infos aus den Texten (alter Stil)
|
# die Infos aus den Texten (alter Stil)
|
||||||
res_dist = get_subdistributor_positions_from_entities(entities, res_mappings)
|
res_dist = get_subdistributor_positions_from_entities(entities, res_mappings)
|
||||||
# die Infos aus den Blöcken
|
# die Infos aus den Blöcken
|
||||||
@@ -907,10 +845,7 @@ if __name__ == '__main__':
|
|||||||
print(to_json(res_dist))
|
print(to_json(res_dist))
|
||||||
|
|
||||||
# Tunnel auslesen (generisch über Entities)
|
# Tunnel auslesen (generisch über Entities)
|
||||||
if use_iter:
|
t_entities = msp.query('MTEXT')
|
||||||
t_entities = iterdxf.modelspace(dxf_path)
|
|
||||||
else:
|
|
||||||
t_entities = msp.query('MTEXT')
|
|
||||||
# die Infos aus den Texten (alter Stil)
|
# die Infos aus den Texten (alter Stil)
|
||||||
res_tunnel = get_tunnel_positions_from_entities(t_entities)
|
res_tunnel = get_tunnel_positions_from_entities(t_entities)
|
||||||
# die Infos aus den Blöcken (neuer Stil)
|
# die Infos aus den Blöcken (neuer Stil)
|
||||||
@@ -921,10 +856,7 @@ if __name__ == '__main__':
|
|||||||
print(to_json(res_tunnel))
|
print(to_json(res_tunnel))
|
||||||
|
|
||||||
if args.rack:
|
if args.rack:
|
||||||
if use_iter:
|
res_rac = get_rack_positions(msp)
|
||||||
res_rac = get_rack_positions_iter(dxf_path)
|
|
||||||
else:
|
|
||||||
res_rac = get_rack_positions(msp)
|
|
||||||
|
|
||||||
output_results['racks'] = res_rac
|
output_results['racks'] = res_rac
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
Test für die check_rack_z_coordinates Funktion
|
Test für die check_rack_z_coordinates Funktion
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import configparser
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -13,6 +14,14 @@ sys.path.insert(0, str(Path(__file__).parent))
|
|||||||
from getpositions import check_rack_z_coordinates
|
from getpositions import check_rack_z_coordinates
|
||||||
from error_collector import ErrorCollector
|
from error_collector import ErrorCollector
|
||||||
|
|
||||||
|
|
||||||
|
def _make_config(max_height_diff=2000.0):
|
||||||
|
"""Erzeugt ein minimales Config-Objekt für die Tests."""
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.add_section("Racks")
|
||||||
|
config.set("Racks", "MaximalTotalHeightDifferences", str(max_height_diff))
|
||||||
|
return config
|
||||||
|
|
||||||
def test_no_deviation():
|
def test_no_deviation():
|
||||||
"""Test mit Racks ohne starke Abweichung (< 2000mm)"""
|
"""Test mit Racks ohne starke Abweichung (< 2000mm)"""
|
||||||
print("Test 1: Keine starke Abweichung (< 2000mm)")
|
print("Test 1: Keine starke Abweichung (< 2000mm)")
|
||||||
@@ -24,7 +33,7 @@ def test_no_deviation():
|
|||||||
}
|
}
|
||||||
|
|
||||||
error_collector = ErrorCollector()
|
error_collector = ErrorCollector()
|
||||||
check_rack_z_coordinates(racks, error_collector)
|
check_rack_z_coordinates(racks, error_collector, _make_config())
|
||||||
|
|
||||||
warnings = error_collector.warnings
|
warnings = error_collector.warnings
|
||||||
if warnings:
|
if warnings:
|
||||||
@@ -44,7 +53,7 @@ def test_with_deviation():
|
|||||||
}
|
}
|
||||||
|
|
||||||
error_collector = ErrorCollector()
|
error_collector = ErrorCollector()
|
||||||
check_rack_z_coordinates(racks, error_collector)
|
check_rack_z_coordinates(racks, error_collector, _make_config())
|
||||||
|
|
||||||
warnings = error_collector.warnings
|
warnings = error_collector.warnings
|
||||||
if warnings:
|
if warnings:
|
||||||
@@ -63,7 +72,7 @@ def test_exactly_2000mm():
|
|||||||
}
|
}
|
||||||
|
|
||||||
error_collector = ErrorCollector()
|
error_collector = ErrorCollector()
|
||||||
check_rack_z_coordinates(racks, error_collector)
|
check_rack_z_coordinates(racks, error_collector, _make_config())
|
||||||
|
|
||||||
warnings = error_collector.warnings
|
warnings = error_collector.warnings
|
||||||
if warnings:
|
if warnings:
|
||||||
@@ -82,7 +91,7 @@ def test_dict_format():
|
|||||||
}
|
}
|
||||||
|
|
||||||
error_collector = ErrorCollector()
|
error_collector = ErrorCollector()
|
||||||
check_rack_z_coordinates(racks, error_collector)
|
check_rack_z_coordinates(racks, error_collector, _make_config())
|
||||||
|
|
||||||
warnings = error_collector.warnings
|
warnings = error_collector.warnings
|
||||||
if warnings:
|
if warnings:
|
||||||
|
|||||||
+75
-27
@@ -517,10 +517,10 @@ def load_ignore_patterns(cfg_path: Path) -> tuple[list[str], list[str]]:
|
|||||||
return wildcard_patterns, regex_patterns
|
return wildcard_patterns, regex_patterns
|
||||||
|
|
||||||
|
|
||||||
def translate_text(text: str, multi: dict[str, str], trigramme: dict[str, str], bigramme: dict[str, str], single: dict[str, str]) -> str:
|
def translate_text(text: str, multi: dict[str, str], trigramme: dict[str, str], bigramme: dict[str, str], single: dict[str, str], transfer_terms: set[str] | None = None) -> str:
|
||||||
"""
|
"""
|
||||||
Übersetzt einen Text durch Ersetzen von Multi-Phrasen, Trigrammen, Bigrammen und Einzelwörtern.
|
Übersetzt einen Text durch Ersetzen von Multi-Phrasen, Trigrammen, Bigrammen und Einzelwörtern.
|
||||||
Ersetzungen erfolgen in dieser Reihenfolge: Multi → Trigramme → Bigramme → Einzelwörter.
|
Ersetzungen erfolgen in dieser Reihenfolge: Transfer → Multi → Trigramme → Bigramme → Einzelwörter.
|
||||||
|
|
||||||
Bei Einzelwörtern werden auch Satzzeichen und eingebettete Wörter berücksichtigt:
|
Bei Einzelwörtern werden auch Satzzeichen und eingebettete Wörter berücksichtigt:
|
||||||
- "Weight:" wird zu "Hmotnost:" (Doppelpunkt bleibt erhalten)
|
- "Weight:" wird zu "Hmotnost:" (Doppelpunkt bleibt erhalten)
|
||||||
@@ -529,16 +529,23 @@ def translate_text(text: str, multi: dict[str, str], trigramme: dict[str, str],
|
|||||||
(alle Wörter in einem Token werden durchsucht, Trennzeichen bleiben erhalten)
|
(alle Wörter in einem Token werden durchsucht, Trennzeichen bleiben erhalten)
|
||||||
Bindestriche im Wort (wie "Sub-distributor") bleiben Teil des Kern-Worts.
|
Bindestriche im Wort (wie "Sub-distributor") bleiben Teil des Kern-Worts.
|
||||||
|
|
||||||
|
Transfer-Begriffe werden 1:1 übernommen (ohne Übersetzung) wenn der Text exakt übereinstimmt.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
text: Zu übersetzender Text
|
text: Zu übersetzender Text
|
||||||
multi: Dictionary mit Multi-Wort-Phrasen (4+ Wörter)
|
multi: Dictionary mit Multi-Wort-Phrasen (4+ Wörter)
|
||||||
trigramme: Dictionary mit Drei-Wort-Phrasen
|
trigramme: Dictionary mit Drei-Wort-Phrasen
|
||||||
bigramme: Dictionary mit Zwei-Wort-Phrasen
|
bigramme: Dictionary mit Zwei-Wort-Phrasen
|
||||||
single: Dictionary mit Einzelwörtern
|
single: Dictionary mit Einzelwörtern
|
||||||
|
transfer_terms: Set mit Begriffen die 1:1 übernommen werden (aus [transfer] Sektion)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Übersetzter Text (leer wenn keine Übersetzung gefunden wurde)
|
Übersetzter Text (leer wenn keine Übersetzung gefunden wurde)
|
||||||
"""
|
"""
|
||||||
|
# 0. Prüfe Transfer-Begriffe (exakte Übereinstimmung, 1:1 Übernahme)
|
||||||
|
if transfer_terms is not None and text in transfer_terms:
|
||||||
|
return text
|
||||||
|
|
||||||
translated_text = text
|
translated_text = text
|
||||||
has_translation = False
|
has_translation = False
|
||||||
|
|
||||||
@@ -1668,7 +1675,8 @@ def _retranslate_language_block(
|
|||||||
trigramme: dict[str, str],
|
trigramme: dict[str, str],
|
||||||
bigramme: dict[str, str],
|
bigramme: dict[str, str],
|
||||||
single: dict[str, str],
|
single: dict[str, str],
|
||||||
label: str) -> tuple[int, int, int]:
|
label: str,
|
||||||
|
transfer_terms: set[str] | None = None) -> tuple[int, int, int, int]:
|
||||||
"""
|
"""
|
||||||
Verarbeitet einen einzelnen Sprach-Block einer JSON-Übersetzungsdatei in-place.
|
Verarbeitet einen einzelnen Sprach-Block einer JSON-Übersetzungsdatei in-place.
|
||||||
|
|
||||||
@@ -1687,6 +1695,8 @@ def _retranslate_language_block(
|
|||||||
Einträge mit bereits gesetztem target (manuell befüllt) werden
|
Einträge mit bereits gesetztem target (manuell befüllt) werden
|
||||||
ebenfalls direkt nach translations_list übernommen.
|
ebenfalls direkt nach translations_list übernommen.
|
||||||
|
|
||||||
|
Schritt 3 – Transfer-Begriffe werden 1:1 übernommen (ohne Übersetzung).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
translations_list: Liste der bereits übersetzten Einträge (in-place verändert)
|
translations_list: Liste der bereits übersetzten Einträge (in-place verändert)
|
||||||
untranslated_list: Liste der noch nicht übersetzten Einträge (in-place verändert)
|
untranslated_list: Liste der noch nicht übersetzten Einträge (in-place verändert)
|
||||||
@@ -1695,13 +1705,15 @@ def _retranslate_language_block(
|
|||||||
bigramme: Bigramme-Dict
|
bigramme: Bigramme-Dict
|
||||||
single: Einzelwörter-Dict
|
single: Einzelwörter-Dict
|
||||||
label: Bezeichnung für Warnmeldungen, z.B. "[CS]"
|
label: Bezeichnung für Warnmeldungen, z.B. "[CS]"
|
||||||
|
transfer_terms: Set mit Begriffen die 1:1 übernommen werden (aus [transfer] Sektion)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple (aktualisiert, unveraendert, neu_aus_untranslated)
|
Tuple (aktualisiert, unveraendert, neu_aus_untranslated, transferred)
|
||||||
"""
|
"""
|
||||||
updated = 0
|
updated = 0
|
||||||
unchanged = 0
|
unchanged = 0
|
||||||
newly = 0
|
newly = 0
|
||||||
|
transferred = 0
|
||||||
|
|
||||||
# Schritt 0: Deduplizierung – erstes Vorkommen je source wird behalten
|
# Schritt 0: Deduplizierung – erstes Vorkommen je source wird behalten
|
||||||
deduped_t, seen_t = _dedup_source_list(
|
deduped_t, seen_t = _dedup_source_list(
|
||||||
@@ -1718,7 +1730,7 @@ def _retranslate_language_block(
|
|||||||
continue
|
continue
|
||||||
source = entry['source']
|
source = entry['source']
|
||||||
old_target = entry.get('target', '')
|
old_target = entry.get('target', '')
|
||||||
new_target = translate_text(source, multi, trigramme, bigramme, single)
|
new_target = translate_text(source, multi, trigramme, bigramme, single, transfer_terms)
|
||||||
if new_target:
|
if new_target:
|
||||||
if new_target != old_target:
|
if new_target != old_target:
|
||||||
entry['target'] = new_target
|
entry['target'] = new_target
|
||||||
@@ -1743,18 +1755,23 @@ def _retranslate_language_block(
|
|||||||
newly += 1
|
newly += 1
|
||||||
continue
|
continue
|
||||||
source = entry['source']
|
source = entry['source']
|
||||||
new_target = translate_text(source, multi, trigramme, bigramme, single)
|
new_target = translate_text(source, multi, trigramme, bigramme, single, transfer_terms)
|
||||||
if new_target:
|
if new_target:
|
||||||
translations_list.append({"source": source, "target": new_target})
|
translations_list.append({"source": source, "target": new_target})
|
||||||
untranslated_list.remove(entry)
|
untranslated_list.remove(entry)
|
||||||
newly += 1
|
# Prüfe ob es ein Transfer-Begriff war
|
||||||
|
if transfer_terms and source in transfer_terms:
|
||||||
|
transferred += 1
|
||||||
|
else:
|
||||||
|
newly += 1
|
||||||
|
|
||||||
return updated, unchanged, newly
|
return updated, unchanged, newly, transferred
|
||||||
|
|
||||||
|
|
||||||
def _retranslate_legacy(data: dict,
|
def _retranslate_legacy(data: dict,
|
||||||
translation_languages: list[str],
|
translation_languages: list[str],
|
||||||
translation_dicts_all: dict[str, tuple]) -> dict:
|
translation_dicts_all: dict[str, tuple],
|
||||||
|
transfer_terms_all: dict[str, set[str]] | None = None) -> dict:
|
||||||
"""
|
"""
|
||||||
Verarbeitet eine Legacy-Flat-JSON-Struktur und konvertiert sie ins Multilang-Format.
|
Verarbeitet eine Legacy-Flat-JSON-Struktur und konvertiert sie ins Multilang-Format.
|
||||||
|
|
||||||
@@ -1762,6 +1779,7 @@ def _retranslate_legacy(data: dict,
|
|||||||
data: Geladene JSON-Daten (flache Struktur mit 'translations'/'untranslated')
|
data: Geladene JSON-Daten (flache Struktur mit 'translations'/'untranslated')
|
||||||
translation_languages: Sprachen in der Reihenfolge der Verarbeitung
|
translation_languages: Sprachen in der Reihenfolge der Verarbeitung
|
||||||
translation_dicts_all: Dict mit {LANG: (multi, trigramme, bigramme, single), ...}
|
translation_dicts_all: Dict mit {LANG: (multi, trigramme, bigramme, single), ...}
|
||||||
|
transfer_terms_all: Dict mit {LANG: set[transfer_terms], ...} (aus [transfer] Sektion)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Konvertierte Daten im Multilang-Format
|
Konvertierte Daten im Multilang-Format
|
||||||
@@ -1778,11 +1796,13 @@ def _retranslate_legacy(data: dict,
|
|||||||
|
|
||||||
lang = translation_languages[0]
|
lang = translation_languages[0]
|
||||||
multi, trigramme, bigramme, single = translation_dicts_all[lang]
|
multi, trigramme, bigramme, single = translation_dicts_all[lang]
|
||||||
|
transfer_terms = transfer_terms_all.get(lang, set()) if transfer_terms_all else set()
|
||||||
|
|
||||||
updated, unchanged, newly = _retranslate_language_block(
|
updated, unchanged, newly, transferred = _retranslate_language_block(
|
||||||
data['translations'], data['untranslated'],
|
data['translations'], data['untranslated'],
|
||||||
multi, trigramme, bigramme, single,
|
multi, trigramme, bigramme, single,
|
||||||
f"[{lang}]"
|
f"[{lang}]",
|
||||||
|
transfer_terms
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"\nRe-Übersetzung abgeschlossen (alte Struktur → wird zu multilang konvertiert, Sprache: {lang}):")
|
print(f"\nRe-Übersetzung abgeschlossen (alte Struktur → wird zu multilang konvertiert, Sprache: {lang}):")
|
||||||
@@ -1790,6 +1810,8 @@ def _retranslate_legacy(data: dict,
|
|||||||
print(f" Unverändert: {unchanged}")
|
print(f" Unverändert: {unchanged}")
|
||||||
if newly:
|
if newly:
|
||||||
print(f" Neu aus 'untranslated' übersetzt/verschoben: {newly}")
|
print(f" Neu aus 'untranslated' übersetzt/verschoben: {newly}")
|
||||||
|
if transferred:
|
||||||
|
print(f" Transfer-Begriffe (1:1): {transferred}")
|
||||||
|
|
||||||
# Legacy → Multilang konvertieren: flache Struktur in [LANG]-Block einpacken
|
# Legacy → Multilang konvertieren: flache Struktur in [LANG]-Block einpacken
|
||||||
return {
|
return {
|
||||||
@@ -1804,7 +1826,8 @@ def _retranslate_legacy(data: dict,
|
|||||||
def _retranslate_multilang(data: dict,
|
def _retranslate_multilang(data: dict,
|
||||||
existing_languages: list[str],
|
existing_languages: list[str],
|
||||||
translation_languages: list[str],
|
translation_languages: list[str],
|
||||||
translation_dicts_all: dict[str, tuple]) -> None:
|
translation_dicts_all: dict[str, tuple],
|
||||||
|
transfer_terms_all: dict[str, set[str]] | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
Verarbeitet eine Multilang-JSON-Struktur in-place.
|
Verarbeitet eine Multilang-JSON-Struktur in-place.
|
||||||
|
|
||||||
@@ -1813,12 +1836,14 @@ def _retranslate_multilang(data: dict,
|
|||||||
existing_languages: Sprachen die bereits in der Datei vorhanden sind
|
existing_languages: Sprachen die bereits in der Datei vorhanden sind
|
||||||
translation_languages: Sprachen in der Reihenfolge der Verarbeitung
|
translation_languages: Sprachen in der Reihenfolge der Verarbeitung
|
||||||
translation_dicts_all: Dict mit {LANG: (multi, trigramme, bigramme, single), ...}
|
translation_dicts_all: Dict mit {LANG: (multi, trigramme, bigramme, single), ...}
|
||||||
|
transfer_terms_all: Dict mit {LANG: set[transfer_terms], ...} (aus [transfer] Sektion)
|
||||||
"""
|
"""
|
||||||
print(f"Erkannte mehrsprachige Struktur mit Sprachen: {', '.join(existing_languages)}")
|
print(f"Erkannte mehrsprachige Struktur mit Sprachen: {', '.join(existing_languages)}")
|
||||||
|
|
||||||
total_updated = 0
|
total_updated = 0
|
||||||
total_unchanged = 0
|
total_unchanged = 0
|
||||||
total_newly = 0
|
total_newly = 0
|
||||||
|
total_transferred = 0
|
||||||
new_block_count = 0
|
new_block_count = 0
|
||||||
|
|
||||||
for lang in translation_languages:
|
for lang in translation_languages:
|
||||||
@@ -1844,30 +1869,38 @@ def _retranslate_multilang(data: dict,
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
multi, trigramme, bigramme, single = translation_dicts_all[lang]
|
multi, trigramme, bigramme, single = translation_dicts_all[lang]
|
||||||
updated, unchanged, newly = _retranslate_language_block(
|
transfer_terms = transfer_terms_all.get(lang, set()) if transfer_terms_all else set()
|
||||||
|
updated, unchanged, newly, transferred = _retranslate_language_block(
|
||||||
lang_data['translations'], lang_data['untranslated'],
|
lang_data['translations'], lang_data['untranslated'],
|
||||||
multi, trigramme, bigramme, single,
|
multi, trigramme, bigramme, single,
|
||||||
f"[{lang}]"
|
f"[{lang}]",
|
||||||
|
transfer_terms
|
||||||
)
|
)
|
||||||
total_updated += updated + newly
|
total_updated += updated + newly
|
||||||
total_unchanged += unchanged
|
total_unchanged += unchanged
|
||||||
total_newly += newly
|
total_newly += newly
|
||||||
|
total_transferred += transferred
|
||||||
|
|
||||||
print(f" [{lang}] Aktualisiert: {updated}, Unverändert: {unchanged}", end="")
|
print(f" [{lang}] Aktualisiert: {updated}, Unverändert: {unchanged}", end="")
|
||||||
if newly:
|
if newly:
|
||||||
print(f", Neu aus 'untranslated': {newly}", end="")
|
print(f", Neu aus 'untranslated': {newly}", end="")
|
||||||
|
if transferred:
|
||||||
|
print(f", Transfer-Begriffe (1:1): {transferred}", end="")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
print(f"\nRe-Übersetzung abgeschlossen (mehrsprachige Struktur):")
|
print(f"\nRe-Übersetzung abgeschlossen (mehrsprachige Struktur):")
|
||||||
print(f" Gesamt aktualisiert: {total_updated}")
|
print(f" Gesamt aktualisiert: {total_updated}")
|
||||||
print(f" Gesamt unverändert: {total_unchanged}")
|
print(f" Gesamt unverändert: {total_unchanged}")
|
||||||
|
if total_transferred:
|
||||||
|
print(f" Gesamt Transfer-Begriffe (1:1): {total_transferred}")
|
||||||
if new_block_count:
|
if new_block_count:
|
||||||
print(f" Neue Sprach-Blöcke erstellt: {new_block_count}")
|
print(f" Neue Sprach-Blöcke erstellt: {new_block_count}")
|
||||||
|
|
||||||
|
|
||||||
def retranslate_json_file(json_path: Path,
|
def retranslate_json_file(json_path: Path,
|
||||||
translation_dicts_all: dict[str, tuple],
|
translation_dicts_all: dict[str, tuple],
|
||||||
translation_languages: list[str]):
|
translation_languages: list[str],
|
||||||
|
transfer_terms_all: dict[str, set[str]] | None = None):
|
||||||
"""
|
"""
|
||||||
Re-übersetzt eine JSON-Datei mit neuen Übersetzungs-Configs.
|
Re-übersetzt eine JSON-Datei mit neuen Übersetzungs-Configs.
|
||||||
Überschreibt die ursprüngliche JSON-Datei mit den aktualisierten Übersetzungen.
|
Überschreibt die ursprüngliche JSON-Datei mit den aktualisierten Übersetzungen.
|
||||||
@@ -1894,9 +1927,9 @@ def retranslate_json_file(json_path: Path,
|
|||||||
structure_type, existing_languages = detect_json_structure(data)
|
structure_type, existing_languages = detect_json_structure(data)
|
||||||
|
|
||||||
if structure_type == "legacy":
|
if structure_type == "legacy":
|
||||||
data = _retranslate_legacy(data, translation_languages, translation_dicts_all)
|
data = _retranslate_legacy(data, translation_languages, translation_dicts_all, transfer_terms_all)
|
||||||
elif structure_type == "multilang":
|
elif structure_type == "multilang":
|
||||||
_retranslate_multilang(data, existing_languages, translation_languages, translation_dicts_all)
|
_retranslate_multilang(data, existing_languages, translation_languages, translation_dicts_all, transfer_terms_all)
|
||||||
else:
|
else:
|
||||||
print(f"FEHLER: JSON-Datei hat unbekannte Struktur")
|
print(f"FEHLER: JSON-Datei hat unbekannte Struktur")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -2004,7 +2037,8 @@ def retranslate_txt_file(txt_path: Path,
|
|||||||
translation_dicts_all: dict[str, tuple],
|
translation_dicts_all: dict[str, tuple],
|
||||||
translation_languages: list[str],
|
translation_languages: list[str],
|
||||||
wildcard_patterns: list[str] | None = None,
|
wildcard_patterns: list[str] | None = None,
|
||||||
regex_patterns: list[str] | None = None) -> None:
|
regex_patterns: list[str] | None = None,
|
||||||
|
transfer_terms_all: dict[str, set[str]] | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
Re-übersetzt eine Textdatei im Format von write_texts_to_text.
|
Re-übersetzt eine Textdatei im Format von write_texts_to_text.
|
||||||
Überschreibt die ursprüngliche Datei mit aktualisierten Inhalten.
|
Überschreibt die ursprüngliche Datei mit aktualisierten Inhalten.
|
||||||
@@ -2015,6 +2049,7 @@ def retranslate_txt_file(txt_path: Path,
|
|||||||
2. Einträge aus untranslated werden gegen die Ignore-Pattern geprüft:
|
2. Einträge aus untranslated werden gegen die Ignore-Pattern geprüft:
|
||||||
- Treffer → ignored (neu erkannte Pattern seit dem letzten Extrakt)
|
- Treffer → ignored (neu erkannte Pattern seit dem letzten Extrakt)
|
||||||
- Kein Treffer → auto-übersetzen; Erfolg → translations, sonst bleibt in untranslated.
|
- Kein Treffer → auto-übersetzen; Erfolg → translations, sonst bleibt in untranslated.
|
||||||
|
3. Transfer-Begriffe werden 1:1 übernommen (ohne Übersetzung).
|
||||||
|
|
||||||
Arbeitet immer mit der ersten Sprache aus translation_languages.
|
Arbeitet immer mit der ersten Sprache aus translation_languages.
|
||||||
|
|
||||||
@@ -2024,6 +2059,7 @@ def retranslate_txt_file(txt_path: Path,
|
|||||||
translation_languages: Liste der Sprachen (nur die erste wird verwendet)
|
translation_languages: Liste der Sprachen (nur die erste wird verwendet)
|
||||||
wildcard_patterns: Wildcard-Muster zum Filtern (aus translator.cfg)
|
wildcard_patterns: Wildcard-Muster zum Filtern (aus translator.cfg)
|
||||||
regex_patterns: Regex-Muster zum Filtern (aus translator.cfg)
|
regex_patterns: Regex-Muster zum Filtern (aus translator.cfg)
|
||||||
|
transfer_terms_all: Dict mit {LANG: set[transfer_terms], ...} (aus [transfer] Sektion)
|
||||||
"""
|
"""
|
||||||
if not translation_languages or translation_languages[0] not in translation_dicts_all:
|
if not translation_languages or translation_languages[0] not in translation_dicts_all:
|
||||||
print(f"FEHLER: Keine Übersetzungs-Config für Re-Übersetzung verfügbar")
|
print(f"FEHLER: Keine Übersetzungs-Config für Re-Übersetzung verfügbar")
|
||||||
@@ -2034,6 +2070,7 @@ def retranslate_txt_file(txt_path: Path,
|
|||||||
|
|
||||||
lang = translation_languages[0]
|
lang = translation_languages[0]
|
||||||
multi, trigramme, bigramme, single = translation_dicts_all[lang]
|
multi, trigramme, bigramme, single = translation_dicts_all[lang]
|
||||||
|
transfer_terms = transfer_terms_all.get(lang, set()) if transfer_terms_all else set()
|
||||||
|
|
||||||
existing_translations, untranslated, ignored = _parse_txt_sections(txt_path)
|
existing_translations, untranslated, ignored = _parse_txt_sections(txt_path)
|
||||||
|
|
||||||
@@ -2048,7 +2085,7 @@ def retranslate_txt_file(txt_path: Path,
|
|||||||
|
|
||||||
# Schritt 1: Bestehende Übersetzungen neu übersetzen
|
# Schritt 1: Bestehende Übersetzungen neu übersetzen
|
||||||
for source, old_target in existing_translations:
|
for source, old_target in existing_translations:
|
||||||
new_target = translate_text(source, multi, trigramme, bigramme, single)
|
new_target = translate_text(source, multi, trigramme, bigramme, single, transfer_terms)
|
||||||
if new_target:
|
if new_target:
|
||||||
new_translations.append((source, new_target))
|
new_translations.append((source, new_target))
|
||||||
if new_target != old_target:
|
if new_target != old_target:
|
||||||
@@ -2064,16 +2101,21 @@ def retranslate_txt_file(txt_path: Path,
|
|||||||
still_untranslated: list[str] = list(moved_to_untranslated)
|
still_untranslated: list[str] = list(moved_to_untranslated)
|
||||||
newly = 0
|
newly = 0
|
||||||
newly_ignored = 0
|
newly_ignored = 0
|
||||||
|
transferred = 0
|
||||||
for text in untranslated:
|
for text in untranslated:
|
||||||
# Neu hinzugefügte Ignore-Pattern prüfen
|
# Neu hinzugefügte Ignore-Pattern prüfen
|
||||||
if should_ignore_text(text, wildcard_patterns, regex_patterns):
|
if should_ignore_text(text, wildcard_patterns, regex_patterns):
|
||||||
ignored.append(text)
|
ignored.append(text)
|
||||||
newly_ignored += 1
|
newly_ignored += 1
|
||||||
continue
|
continue
|
||||||
new_target = translate_text(text, multi, trigramme, bigramme, single)
|
new_target = translate_text(text, multi, trigramme, bigramme, single, transfer_terms)
|
||||||
if new_target:
|
if new_target:
|
||||||
new_translations.append((text, new_target))
|
new_translations.append((text, new_target))
|
||||||
newly += 1
|
# Prüfe ob es ein Transfer-Begriff war
|
||||||
|
if transfer_terms and text in transfer_terms:
|
||||||
|
transferred += 1
|
||||||
|
else:
|
||||||
|
newly += 1
|
||||||
else:
|
else:
|
||||||
still_untranslated.append(text)
|
still_untranslated.append(text)
|
||||||
|
|
||||||
@@ -2098,6 +2140,8 @@ def retranslate_txt_file(txt_path: Path,
|
|||||||
print(f" Unverändert: {unchanged}")
|
print(f" Unverändert: {unchanged}")
|
||||||
if newly:
|
if newly:
|
||||||
print(f" Neu übersetzt: {newly}")
|
print(f" Neu übersetzt: {newly}")
|
||||||
|
if transferred:
|
||||||
|
print(f" Transfer-Begriffe (1:1): {transferred}")
|
||||||
if newly_ignored:
|
if newly_ignored:
|
||||||
print(f" Neu ignoriert (Pattern): {newly_ignored}")
|
print(f" Neu ignoriert (Pattern): {newly_ignored}")
|
||||||
if moved_to_untranslated:
|
if moved_to_untranslated:
|
||||||
@@ -2112,7 +2156,8 @@ def _run_retranslate_txt(
|
|||||||
translation_dicts_all: dict,
|
translation_dicts_all: dict,
|
||||||
translation_languages: list,
|
translation_languages: list,
|
||||||
wildcard_patterns: list,
|
wildcard_patterns: list,
|
||||||
regex_patterns: list) -> None:
|
regex_patterns: list,
|
||||||
|
transfer_terms_all: dict) -> None:
|
||||||
"""Workflow: Re-Übersetzung einer bestehenden TXT-Datei (--retranslate-txt)."""
|
"""Workflow: Re-Übersetzung einer bestehenden TXT-Datei (--retranslate-txt)."""
|
||||||
print("=== Re-Übersetzung TXT-Datei ===\n")
|
print("=== Re-Übersetzung TXT-Datei ===\n")
|
||||||
|
|
||||||
@@ -2127,7 +2172,7 @@ def _run_retranslate_txt(
|
|||||||
print(f"Sprache: {translation_languages[0]}")
|
print(f"Sprache: {translation_languages[0]}")
|
||||||
|
|
||||||
retranslate_txt_file(txt_file, translation_dicts_all, translation_languages,
|
retranslate_txt_file(txt_file, translation_dicts_all, translation_languages,
|
||||||
wildcard_patterns, regex_patterns)
|
wildcard_patterns, regex_patterns, transfer_terms_all)
|
||||||
|
|
||||||
print("\nVerarbeitung erfolgreich abgeschlossen.")
|
print("\nVerarbeitung erfolgreich abgeschlossen.")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@@ -2227,7 +2272,8 @@ def _run_retranslate(
|
|||||||
args,
|
args,
|
||||||
work_dir: Path,
|
work_dir: Path,
|
||||||
translation_dicts_all: dict,
|
translation_dicts_all: dict,
|
||||||
translation_languages: list) -> None:
|
translation_languages: list,
|
||||||
|
transfer_terms_all: dict) -> None:
|
||||||
"""Workflow: Re-Übersetzung einer bestehenden JSON-Datei (--retranslate-json)."""
|
"""Workflow: Re-Übersetzung einer bestehenden JSON-Datei (--retranslate-json)."""
|
||||||
print("=== Re-Übersetzung JSON-Datei ===\n")
|
print("=== Re-Übersetzung JSON-Datei ===\n")
|
||||||
|
|
||||||
@@ -2241,7 +2287,7 @@ def _run_retranslate(
|
|||||||
print(f"JSON-Datei: {json_file}")
|
print(f"JSON-Datei: {json_file}")
|
||||||
print(f"Sprachen: {', '.join(translation_languages)}")
|
print(f"Sprachen: {', '.join(translation_languages)}")
|
||||||
|
|
||||||
retranslate_json_file(json_file, translation_dicts_all, translation_languages)
|
retranslate_json_file(json_file, translation_dicts_all, translation_languages, transfer_terms_all)
|
||||||
|
|
||||||
print("\nVerarbeitung erfolgreich abgeschlossen.")
|
print("\nVerarbeitung erfolgreich abgeschlossen.")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@@ -2451,10 +2497,12 @@ def main() -> None:
|
|||||||
|
|
||||||
# Übersetzungs-Configs laden: {LANG: (multi, trigramme, bigramme, single), ...}
|
# Übersetzungs-Configs laden: {LANG: (multi, trigramme, bigramme, single), ...}
|
||||||
translation_dicts_all = {}
|
translation_dicts_all = {}
|
||||||
|
transfer_terms_all = {}
|
||||||
if translation_languages:
|
if translation_languages:
|
||||||
translation_dir = check_environment_var('PROJECT_TRANSLATION')
|
translation_dir = check_environment_var('PROJECT_TRANSLATION')
|
||||||
for lang in translation_languages:
|
for lang in translation_languages:
|
||||||
translation_dicts_all[lang] = load_translation_config(lang, translation_dir)
|
translation_dicts_all[lang] = load_translation_config(lang, translation_dir)
|
||||||
|
transfer_terms_all[lang] = load_transfer_terms(lang, translation_dir)
|
||||||
|
|
||||||
# Für backwards compatibility: bei genau einer Sprache flaches Dict bereitstellen
|
# Für backwards compatibility: bei genau einer Sprache flaches Dict bereitstellen
|
||||||
translation_dicts = translation_dicts_all.get(translation_languages[0]) if len(translation_languages) == 1 else None
|
translation_dicts = translation_dicts_all.get(translation_languages[0]) if len(translation_languages) == 1 else None
|
||||||
@@ -2464,11 +2512,11 @@ def main() -> None:
|
|||||||
|
|
||||||
# Workflow delegieren
|
# Workflow delegieren
|
||||||
if args.retranslate_json:
|
if args.retranslate_json:
|
||||||
_run_retranslate(args, work_dir, translation_dicts_all, translation_languages)
|
_run_retranslate(args, work_dir, translation_dicts_all, translation_languages, transfer_terms_all)
|
||||||
|
|
||||||
if args.retranslate_txt:
|
if args.retranslate_txt:
|
||||||
_run_retranslate_txt(args, work_dir, translation_dicts_all, translation_languages,
|
_run_retranslate_txt(args, work_dir, translation_dicts_all, translation_languages,
|
||||||
wildcard_patterns, regex_patterns)
|
wildcard_patterns, regex_patterns, transfer_terms_all)
|
||||||
|
|
||||||
if args.todxf:
|
if args.todxf:
|
||||||
_run_todxf(args, work_dir, translation_languages, wildcard_patterns, regex_patterns,
|
_run_todxf(args, work_dir, translation_languages, wildcard_patterns, regex_patterns,
|
||||||
|
|||||||
Reference in New Issue
Block a user