# 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 |
