refactor: Sivas-CSV-Export auf Merkmalnummern umgestellt, lesbare Zweitdatei
- ILS 2.0 Kreisel/Strecke(VF_N)/Gefaellestrecke/Automation: details-JSON nutzt jetzt Sivas-Merkmalnummern (z.B. "2952") statt beschreibender Klartext-Keys, abgeglichen mit dem Merkmalstamm in cfg/Merkmalsstamm_in_Sivas.xlsx. - ILS 2.0 Zusatzmodul: BT-Beladung und Abwurfstationen laufen jetzt in einer gemeinsamen Zeile (Merkmale 3501-3504) statt zwei separaten Zeilen ohne details. - Gefaellestrecke-Gruppe: Anzahl-Spalte bleibt fest 1, die reale Stueckzahl der Gruppe steht im Merkmal 4311 (anders als bei Kreisel, wo die Anzahl-Spalte die Stueckzahl traegt und 3511 fix "1" ist). - Automation-Summenzeile: 4407 zaehlt jetzt echte Streckengruppen (VF_N-Bloecke) statt eines 0/1-Flags, 4408 (Motoren Vario-Foerderer) wird erstmals tatsaechlich berechnet (vorher nie inkrementiert), 4426 steht fest auf "0" (Konzept "einzelner Variofoerderer" entfaellt). - Neues Modul lib/merkmalstamm.py: liest Merkmalnummer->Bezeichnung aus der xlsx rein per Standard-Library (zipfile/xml.etree, keine neue pip-Abhaengigkeit fuer den ueber export.lsp aufgeloesten Interpreter). - export_sivas.py schreibt bei jedem Lauf zusaetzlich eine zweite Datei "<name>_mit_bezeichnung.csv" (gleicher Inhalt, aber jeder Merkmalnummer-Key um "<Nummer> | <Bezeichnung>" erweitert) - reine Lesehilfe zur Pruefung, wird von Sivas nicht eingelesen. - cfg/sivas_merkmal_zuordnung.md: Nachschlagetabelle Block-Attribut <-> Merkmalnummer je Teileart, inkl. offener Punkte (Separator-/ Scanner-Aufteilung Gefaelle-/Vario-Anteil noch Platzhalter). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
18771d53ff
commit
6f12cab86f
@@ -0,0 +1,154 @@
|
||||
# Sivas-Merkmal-Zuordnung (export_sivas.py)
|
||||
|
||||
Diese Datei dokumentiert, welches Block-Attribut in `export_raw.json` welche
|
||||
Sivas-Merkmalnummer im `details`-JSON von `export_sivas.py` befuellt (Spalte
|
||||
`Nr;TeileArt;SivasNummer;Bezeichnung;Dispogruppe;Anzahl;Laenge[mm];IDs;details`).
|
||||
Bezeichnungen stammen aus `cfg/Merkmalsstamm_in_Sivas.xlsx`. Einzige Quelle
|
||||
der Wahrheit ist der Code in `lib/export_sivas.py` - diese Datei ist ein
|
||||
Backup/Nachschlagewerk, bei Aenderungen am Code bitte hier nachziehen.
|
||||
|
||||
Stand: abgeglichen mit `lib/export_sivas.py` nach der Umstellung von
|
||||
beschreibenden JSON-Keys auf reine Merkmalnummern (Sept. 2026).
|
||||
|
||||
## ILS 2.0 Kreisel (SivasNr 6200, Dispogruppe 20)
|
||||
|
||||
Gruppierung: gleiche Kreiselart+Abstand+Separatoren/Scanner/Rampen+Geruest
|
||||
werden zu EINER Zeile zusammengefasst (`kreisel_signature`). Die Anzahl-Spalte
|
||||
traegt die reale Stueckzahl der Gruppe.
|
||||
|
||||
| Merkmalnr | Bezeichnung (Merkmalstamm) | Block-Attribut | Berechnung |
|
||||
|---|---|---|---|
|
||||
| 2952 | Kreiselhoehe in Meter | `MONTAGEHOEHE_m` | direkt |
|
||||
| 3510 | Kreisel oder Kreisel mit PINBAND oder Verbindungskreisel | `KREISELART` | `"PIN"` -> `"Kreisel mit PINBAND"`, sonst `"Kreisel"` |
|
||||
| 3511 | Kreisel - zu fertigende Menge | - | fest `"1"` (reale Stueckzahl steht in der Anzahl-Spalte der Zeile) |
|
||||
| 3512 | Kreisel - Abstand (Kreiselachse A - Kreiselachse B) in Meter | `ABSTAND` | `round(ABSTAND/1000, 2)` |
|
||||
| 3515 | Kreisel - Anzahl der Separatoren | `ANZAHL_SEPARATOR` (Fallback `N_SEPARATOREN`) | direkt |
|
||||
| 3517 | Kreisel - Anzahl der Scanner | `ANZAHL_SCANNER` (Fallback `N_SCANNER`) | direkt |
|
||||
| 3519 | Kreisel - Anzahl der Rampen | `ANZAHL_RAMPEN` (Fallback `N_RAMPEN`) | direkt |
|
||||
| 3992 | Geruest fuer Einzelmodul | `GERUEST_EINZELMODUL` | `"1"` -> `"Ja"`, sonst `"Nein"` |
|
||||
| 3993 | Geruestoption fuer Einzelmodul | `GERUEST_TYP` | Anzeige-Uebersetzung, siehe `GERUEST_TYP_ANZEIGE` (`"Schoenenberger Geruest"` -> `"Schoenenberger-Geruest"` mit Umlauten) |
|
||||
|
||||
## ILS 2.0 Strecke - Streckengruppe VF_N (SivasNr 6220, Dispogruppe 20)
|
||||
|
||||
Je VF_N-Block IMMER eine eigene Zeile (keine Gruppierung), Anzahl-Spalte
|
||||
immer 1. Sivas kennt je Streckengruppe nur EIN Gefaelle-Segment-Paar
|
||||
(001/002) und EIN Vario-Segment (001).
|
||||
|
||||
| Merkmalnr | Bezeichnung (Merkmalstamm) | Block-Attribut | Berechnung |
|
||||
|---|---|---|---|
|
||||
| 2950 | Anlagenhoehe in Meter | `MONTAGEHOEHE_m` | direkt |
|
||||
| 4300 | Streckengruppe | - | fest `"mehrere Objekte"` |
|
||||
| 4311 | Strecken- zu fertigende Menge | - | fest `"1"` |
|
||||
| 4312 | Streckengruppe - Gefaellestrecke (Segment 001 vorhanden) | `L_GF_m` (1. Wert vorhanden?) | `"Ja"`/`"Nein"` |
|
||||
| 4318 | Streckengruppe - Gefaellestrecke 001-Laenge in Meter | `L_GF_m` (1. Wert) | nur wenn Segment 1 existiert |
|
||||
| 4391 | Streckengruppe - Gefaellestrecke 001 -Winkel | `GF_WINKEL` (1. Wert) | `int(...)`, nur wenn Segment 1 existiert |
|
||||
| 4314 | Streckengruppe - Gefaellestrecke 002 (Segment vorhanden) | `L_GF_m` (2. Wert vorhanden?) | `"Ja"`/`"Nein"` |
|
||||
| 4319 | Streckengruppe - Gefaellestrecke 002-Laenge in Meter | `L_GF_m` (2. Wert) | nur wenn Segment 2 existiert |
|
||||
| 4392 | Streckengruppe - Gefaellestrecke 002- Winkel | `GF_WINKEL` (2. Wert) | `int(...)`, nur wenn Segment 2 existiert |
|
||||
| 4329-4334 | ...Kurven Gefaellestrecke links/rechts 90/60/30 Grad | `GF_Bogen_L_90/60/30`, `GF_Bogen_R_90/60/30` | direkt |
|
||||
| 4337 | Streckengruppe - Anzahl der Separatoren - Gefaellestrecke | - | **Platzhalter, fest `"0"`** (Aufteilung Gefaelle-/Vario-Anteil noch nicht umgesetzt, siehe unten) |
|
||||
| 4338 | Streckengruppe - Bearbeitung Gefaellestreckenteilstueck fuer Separator (829516050) | - | fest `SEPARATOR_BEARBEITUNG_ANZEIGE` = `"ohne HARTCOAT HRC 50 +/- 5"` |
|
||||
| 4339 | Streckengruppe - Anzahl der Scanner (Gefaelle-Anteil) | - | **Platzhalter, fest `"0"`** |
|
||||
| 4340 | Streckengruppe - Variofoerderer (vorhanden) | `L_VF_m` (vorhanden?) | `"Ja"`/`"Nein"` |
|
||||
| 4341 | ...Anzahl der Motoren fuer Vario-Foerderer (links) | `MOTORSEITE` | Anzahl Eintraege == "links" |
|
||||
| 4342 | ...Anzahl der Motoren fuer Vario-Foerderer (rechts) | `MOTORSEITE` | Anzahl Eintraege == "rechts" |
|
||||
| 4353 | Streckengruppe - Variofoerderer 001-Laenge in Meter | `L_VF_m` (1. Wert) | nur wenn Vario-Segment existiert |
|
||||
| 4354 | Streckengruppe - Variofoerderer 001-Richtung | `ANTRIEBFAHRTRICHTUNG` (1. Wert) | kleingeschrieben (`"Auf"` -> `"auf"`) |
|
||||
| 4386 | Streckengruppe - Variofoerderer 001- Winkel | `VF_WINKEL` (1. Wert) | `int(...)` |
|
||||
| 4373-4378 | ...Kurven Variofoerderer Aussen-/Innenbogen 90/60/30 Grad | `VF_Bogen_A_90/60/30`, `VF_Bogen_I_90/60/30` | direkt |
|
||||
| 4381 | Streckengruppe - Anzahl der Separatoren - Variofoerderer | `ANZAHL_SEPARATOR` | **voller Blockwert** (Aufteilung noch offen, siehe 4337) |
|
||||
| 4382 | (dieselbe Bezeichnung wie 4338 im Merkmalstamm) | - | fest `SEPARATOR_BEARBEITUNG_ANZEIGE` |
|
||||
| 4383 | Streckengruppe - Anzahl der Scanner (Vario-Anteil) | `ANZAHL_SCANNER` | **voller Blockwert** |
|
||||
| 3992 | Geruest fuer Einzelmodul | `GERUEST_EINZELMODUL` | wie Kreisel |
|
||||
| 3993 | Geruestoption fuer Einzelmodul | `GERUEST_TYP` | wie Kreisel |
|
||||
|
||||
**Offener Punkt:** 4337/4339 (Gefaelle-Anteil) stehen fest auf `"0"`, 4381/4383
|
||||
(Vario-Anteil) tragen den KOMPLETTEN `ANZAHL_SEPARATOR`/`ANZAHL_SCANNER`-Wert
|
||||
des Blocks. Die korrekte Aufteilung auf beide Streckenabschnitte folgt erst,
|
||||
wenn die Separator-/Scanner-Zuordnung im Zeichnungsmodell selbst ueberarbeitet
|
||||
ist (siehe `build_variofoerderer_details` in `export_sivas.py`).
|
||||
|
||||
## ILS 2.0 Gefaellestrecke - eigenstaendige GF_N-Gruppe (SivasNr 6220, Dispogruppe 20)
|
||||
|
||||
Gruppierung: gleiche Masse (Hoehe/Laenge/Winkel/Separatoren/Scanner/Typ/
|
||||
Geruest) werden zu EINER Zeile zusammengefasst (`gefaellestrecke_signature`).
|
||||
ANDERS ALS BEI KREISEL bleibt die Anzahl-Spalte fest auf `1` - die reale
|
||||
Stueckzahl der Gruppe steht im Merkmal 4311.
|
||||
|
||||
| Merkmalnr | Bezeichnung (Merkmalstamm) | Block-Attribut | Berechnung |
|
||||
|---|---|---|---|
|
||||
| 2950 | Anlagenhoehe in Meter | `MONTAGEHOEHE_m` | direkt |
|
||||
| 4300 | Streckengruppe | `TYP` | `"Streckengruppe"` -> `"mehrere Objekte"`, sonst (`"Gefaellestrecke"`) -> `"ein Objekt"` |
|
||||
| 4384 | Gefaellestrecke oder Vario-Foerderer | - | fest `"Gefaellestrecke"` |
|
||||
| 4311 | Strecken- zu fertigende Menge | - | Anzahl der in dieser Zeile gruppierten, baugleichen GF_N-Bloecke (Gegenstueck zur Anzahl-Spalte, die hier fest 1 ist) |
|
||||
| 4318 | Streckengruppe - Gefaellestrecke 001-Laenge in Meter | `L_GF_m` | direkt |
|
||||
| 4391 | Streckengruppe - Gefaellestrecke 001 -Winkel | `GF_WINKEL` | direkt |
|
||||
| 4337 | Streckengruppe - Anzahl der Separatoren - Gefaellestrecke | `ANZAHL_SEPARATOR` | direkt (kein Split noetig, kein Vario-Anteil vorhanden) |
|
||||
| 4338 | Streckengruppe - Bearbeitung Gefaellestreckenteilstueck fuer Separator (829516050) | - | fest `SEPARATOR_BEARBEITUNG_ANZEIGE` |
|
||||
| 4339 | Streckengruppe - Anzahl der Scanner | `ANZAHL_SCANNER` | direkt |
|
||||
| 3992 | Geruest fuer Einzelmodul | `GERUEST_EINZELMODUL` | wie Kreisel |
|
||||
| 3993 | Geruestoption fuer Einzelmodul | `GERUEST_TYP` | wie Kreisel |
|
||||
|
||||
## ILS 2.0 Zusatzmodul (SivasNr 6215, Dispogruppe 20)
|
||||
|
||||
BTMT-Beladestationen UND Abwurfstationen (Klammerbuegel-Entladung) in EINER
|
||||
Zeile zusammengefasst. Anzahl-Spalte fest `1`, Bezeichnung leer, IDs beider
|
||||
Blockarten kombiniert.
|
||||
|
||||
| Merkmalnr | Bezeichnung (Merkmalstamm) | Quelle | Berechnung |
|
||||
|---|---|---|---|
|
||||
| 3501 | BT - Beladung (mit Vereinzeler, ohne Separator) | Vorhandensein von BTMT-Beladung-Bloecken (`pattern_btmt_beladung`) | `"Ja"`, wenn mind. 1 Block, sonst `"Nein"` |
|
||||
| 3503 | Anzahl der BT - Beladung (mit Vereinzeler, ohne Separator) | Anzahl BTMT-Beladung-Bloecke | direkt gezaehlt |
|
||||
| 3502 | Abwurf fuer Klammerbuegel Sitzbezuege / Kleiderbuegel | Vorhandensein von Abwurf-Bloecken (`pattern_btmt_entladung`) | `"Ja"`, wenn mind. 1 Block, sonst `"Nein"` |
|
||||
| 3504 | Anzahl der Abwuerfe | Anzahl Abwurf-Bloecke | direkt gezaehlt |
|
||||
|
||||
## ILS 2.0 Automation und Pneumatik - Gesamtanlage (SivasNr 6269, Dispogruppe 20)
|
||||
|
||||
Eine Summenzeile pro Zeichnung, nur wenn mindestens ein ILS-Element (Kreisel/
|
||||
Eckrad/Variofoerderer/Gefaellestrecke/BTMT) vorhanden ist.
|
||||
|
||||
| Merkmalnr | Bezeichnung (Merkmalstamm) | Quelle (counters/process_blocks) | Berechnung |
|
||||
|---|---|---|---|
|
||||
| 4000 | Automation | - | fest `"Ja"` (Zeile existiert nur bei `has_ils`) |
|
||||
| 4400 | Anzahl der Kreisel | `anzahl_kreisel` | Anzahl aller Kreisel-Bloecke (vor Gruppierung) |
|
||||
| 4401 | Anzahl der Kreisel mit PINBAND | `anzahl_kreisel_pinband` | |
|
||||
| 4402 | Anzahl der Verbindungskreisel | `anzahl_verbindungskreisel` | aktuell nie befuellt (kein Verbindungskreisel-Typ erkannt) |
|
||||
| 4403 | Anzahl der Weichen | `anzahl_weichen_ils` | aktuell nie befuellt (kein ILS-Weichen-Typ erkannt) |
|
||||
| 4404 | Anzahl der Eckraeder | `anzahl_eckraeder` | |
|
||||
| 4407 | Anzahl der Streckengruppen | `len(vario_list)` | Anzahl VF_N-Compound-Bloecke (TYP "Streckengruppe") - NICHT `anzahl_variofoerderer` (der zaehlt auch lose Strecke-Module mit) |
|
||||
| 4408 | Anzahl der Motoren in Vario-Foerderer | `anzahl_motoren_vario` | Summe `ANZAHL_VF` ueber alle VF_N-Bloecke |
|
||||
| 4409 | Anzahl der ETFs | `anzahl_etf` | aktuell nie befuellt |
|
||||
| 4410 | Anzahl der Separatoren in der Anlage | `anzahl_separatoren` | Summe ueber Kreisel+VF_N+GF_N |
|
||||
| 4411 | Anzahl der Scanner in der Anlage | `anzahl_scanner` | Summe ueber Kreisel+VF_N+GF_N |
|
||||
| 4413 | Anzahl der Separatoren - Kreisel ohne PINBAND | `sep_kreisel_ohne_pin` | |
|
||||
| 4414 | Anzahl der Separatoren - Kreisel mit PINBAND | `sep_kreisel_pin` | |
|
||||
| 4417 | Laengen aller ETFS und Zusatzstuecke (Gefaellestrecken) | `laengen_etf` | aktuell immer `0.0`, NICHT auf 2 Nachkommastellen gerundet |
|
||||
| 4418 | Laengen aller Gefaellestrecken | `laengen_gefaellestrecken` | auf 2 Nachkommastellen gerundet (`.2f`) |
|
||||
| 4419 | Laengen aller Kreisel | `laengen_kreisel` | auf 2 Nachkommastellen gerundet (`.2f`) |
|
||||
| 4420 | Laengen aller Vario-Foerderer | `laengen_variofoerderer` | NICHT gerundet (roher `str()`-Wert) |
|
||||
| 4421 | Zusatzmodul - Anzahl der BT-Beladung | `anzahl_btbeladung` | |
|
||||
| 4422 | Zusatzmodul - Anzahl der Abwurfstation | `anzahl_abwurfstationen` | |
|
||||
| 4425 | Anzahl der einzelnen Gefaellestrecken | `anzahl_gefaellestrecken` | Anzahl aller GF_N-Bloecke (vor Gruppierung) |
|
||||
| 4426 | Anzahl der einzelnen Vario-Foerderer | - | fest `"0"` (Konzept "einzelner Variofoerderer" getrennt von "Streckengruppe" gibt es nicht mehr) |
|
||||
| 4100 | Pneumatik | - | fest `"Ja"` |
|
||||
|
||||
## Nicht angefasst (ausserhalb dieser Umstellung)
|
||||
|
||||
- **ILS 2.0 Eckrad** (`build_eckrad_details`): bleibt beim alten,
|
||||
beschreibenden Key-Format (`"Geruest fuer Einzelmodul"`/`"Geruestoption
|
||||
fuer Einzelmodul"`) - war nicht Teil der angefragten 5 Teilearten.
|
||||
- **Geruest-Baugruppen** (`build_geruest_details`, `GR_*`-Bloecke): eigenes
|
||||
Format ueber `ARTINR`/`BEZEICHNUNG`/`TEILEART`/`LAENGE` in den festen
|
||||
CSV-Spalten, nur `FARBE` bleibt als details-Key - unveraendert.
|
||||
- **Omniflo Bogen/Weiche/Gerade**: `details` bleibt leer (`{}`), keine
|
||||
Merkmalnummern-Zuordnung noetig.
|
||||
|
||||
## Bekannte offene Punkte
|
||||
|
||||
1. **4337/4339 vs. 4381/4383** (Streckengruppe VF_N): Aufteilung der
|
||||
Separatoren/Scanner auf Gefaelle- und Vario-Anteil ist ein Platzhalter
|
||||
(siehe oben) - folgt nach der Neuzuordnung von Separator/Scanner zum
|
||||
jeweiligen Streckenabschnitt im Zeichnungsmodell.
|
||||
2. Zeichnungsseitige Datenpflege (kein Code-Thema): `GERUEST_EINZELMODUL`
|
||||
muss am jeweiligen Block korrekt gesetzt sein (Dialog-Haekchen beim
|
||||
Einfuegen) - der Export uebernimmt das Attribut unveraendert 1:1.
|
||||
+246
-119
@@ -18,15 +18,31 @@ Neues CSV-Format:
|
||||
- Omniflo Gerade: gruppiert nach ARTINR-Attribut, Laenge[mm] summiert
|
||||
- ILS 2.0 Kreisel: gruppiert nach Kreiselart+Abstand+Anzahl Separatoren/
|
||||
Scanner/Rampen, Anzahl summiert
|
||||
- ILS 2.0 Gefaellestrecke: gruppiert nach identischen Massen, Anzahl-Spalte
|
||||
bleibt 1 - die reale Stueckzahl steht im Merkmal 4311 (siehe
|
||||
build_gefaellestrecke_details)
|
||||
- ILS 2.0 Zusatzmodul: BTMT-Beladung UND Abwurfstationen in EINER Zeile,
|
||||
Anzahl bleibt 1, Stueckzahlen stehen in 3503/3504 (siehe process_blocks)
|
||||
- ILS 2.0 Automation: Summierungszeile (nur wenn ILS-Elemente vorhanden)
|
||||
|
||||
details-JSON: Schluessel sind durchgaengig Sivas-Merkmalnummern (z.B.
|
||||
"2952" fuer Kreiselhoehe) statt beschreibender Texte - siehe
|
||||
cfg/Merkmalsstamm_in_Sivas.xlsx fuer die Bezeichnung je Nummer.
|
||||
|
||||
Aufruf:
|
||||
python export_sivas.py <export_raw.json> <data_dir> <output.csv>
|
||||
|
||||
Argumente:
|
||||
export_raw.json - JSON-Datei mit allen INSERT-Entities aus der Zeichnung
|
||||
data_dir - Pfad zum data/-Verzeichnis (mit json/ Unterordner)
|
||||
output.csv - Ziel-CSV-Datei
|
||||
output.csv - Ziel-CSV-Datei (reine Merkmalnummern, fuer den
|
||||
Sivas-Import)
|
||||
|
||||
Zusaetzlich zu output.csv wird immer eine zweite Datei
|
||||
"<output>_mit_bezeichnung.csv" geschrieben: gleicher Inhalt, aber jeder
|
||||
Merkmalnummer-Key im details-JSON wird um die zugehoerige Bezeichnung aus
|
||||
cfg/Merkmalsstamm_in_Sivas.xlsx erweitert ("<Nummer> | <Bezeichnung>") -
|
||||
reine Lesehilfe zur Pruefung, wird von Sivas selbst nicht eingelesen.
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -34,6 +50,7 @@ import sys
|
||||
import os
|
||||
|
||||
from export_blockpatterns import load_patterns, matches_any, csv_quote, safe_float, safe_int
|
||||
from merkmalstamm import lade_merkmalstamm, bezeichnung_fuer
|
||||
|
||||
BLOCKPATTERNS = load_patterns()
|
||||
|
||||
@@ -94,19 +111,56 @@ def format_sivasnr(sivasnr):
|
||||
return ", ".join(p.strip() for p in str(sivasnr).split("+") if p.strip())
|
||||
|
||||
|
||||
def build_gefaellestrecke_details(block):
|
||||
"""Merkmale-Dict fuer eine einzelne ILS Gefaellestrecke (GF_N-Block)."""
|
||||
# --- Sivas-Merkmalnummern: Anzeigetexte, die nicht 1:1 aus dem Attribut
|
||||
# uebernommen werden koennen (Attribute bleiben ASCII, siehe CLAUDE.md-
|
||||
# Konvention "Alle Texte in Dateien ohne Umlaute" - Sivas erwartet aber die
|
||||
# deutsche Schreibweise mit Umlauten/Sonderzeichen). Nur hier zentral, nicht
|
||||
# ueber die uebliche ssg-text-Sprachdatei, da rein Sivas-seitig.
|
||||
GERUEST_TYP_ANZEIGE = {
|
||||
"Schoenenberger Geruest": "Schönenberger-Gerüst",
|
||||
}
|
||||
|
||||
SEPARATOR_BEARBEITUNG_ANZEIGE = "ohne HARTCOAT HRC 50 ± 5"
|
||||
|
||||
|
||||
def geruest_typ_anzeige(wert):
|
||||
"""Wandelt den ASCII-Attributwert GERUEST_TYP in die Sivas-Anzeige um."""
|
||||
return GERUEST_TYP_ANZEIGE.get(wert, wert)
|
||||
|
||||
|
||||
def ja_nein(flag_1_0):
|
||||
"""Wandelt ein "0"/"1"-Attribut in die Sivas-Anzeige "Ja"/"Nein" um."""
|
||||
return "Ja" if flag_1_0 == "1" else "Nein"
|
||||
|
||||
|
||||
def streckengruppe_objekt_text(attribs):
|
||||
"""Merkmal 4300 "Streckengruppe": "mehrere Objekte", wenn der Block ein
|
||||
kombinierter Streckengruppe-Block (Gefaelle+Vario, TYP-Attribut
|
||||
"Streckengruppe") ist, sonst "ein Objekt" (reine Gefaellestrecke)."""
|
||||
typ = attribs.get("TYP", "Gefaellestrecke")
|
||||
return "mehrere Objekte" if typ == "Streckengruppe" else "ein Objekt"
|
||||
|
||||
|
||||
def build_gefaellestrecke_details(block, anzahl=1):
|
||||
"""Merkmale-Dict (Sivas-Merkmalnummern) fuer eine Gruppe gleichartiger
|
||||
ILS Gefaellestrecken (GF_N-Bloecke). anzahl ist die Anzahl der in dieser
|
||||
Zeile zusammengefassten, baugleichen Bloecke (4311) - anders als bei
|
||||
Kreisel steht hier auf der Sivas-Hauptzeile selbst immer Anzahl=1 (siehe
|
||||
process_blocks), die reale Stueckzahl steckt nur in 4311."""
|
||||
attribs = block.get("attribs", {})
|
||||
|
||||
return {
|
||||
"Hoehe in m": attribs.get("MONTAGEHOEHE_m", "0"),
|
||||
"Typ": attribs.get("TYP", "Gefaellestrecke"),
|
||||
"Laenge in Meter": attribs.get("L_GF_m", ""),
|
||||
"Winkel": attribs.get("GF_WINKEL", ""),
|
||||
"Anzahl der Separatoren": attribs.get("ANZAHL_SEPARATOR", "0"),
|
||||
"Anzahl der Scanner": attribs.get("ANZAHL_SCANNER", "0"),
|
||||
"Geruest fuer Einzelmodul": attribs.get("GERUEST_EINZELMODUL", "0") == "1",
|
||||
"Geruestoption fuer Einzelmodul": attribs.get("GERUEST_TYP", "Schoenenberger Geruest"),
|
||||
"2950": attribs.get("MONTAGEHOEHE_m", "0"),
|
||||
"4300": streckengruppe_objekt_text(attribs),
|
||||
"4384": "Gefällestrecke",
|
||||
"4311": str(anzahl),
|
||||
"4318": attribs.get("L_GF_m", ""),
|
||||
"4391": attribs.get("GF_WINKEL", ""),
|
||||
"4337": attribs.get("ANZAHL_SEPARATOR", "0"),
|
||||
"4338": SEPARATOR_BEARBEITUNG_ANZEIGE,
|
||||
"4339": attribs.get("ANZAHL_SCANNER", "0"),
|
||||
"3992": ja_nein(attribs.get("GERUEST_EINZELMODUL", "0")),
|
||||
"3993": geruest_typ_anzeige(attribs.get("GERUEST_TYP", "Schoenenberger Geruest")),
|
||||
}
|
||||
|
||||
|
||||
@@ -220,62 +274,104 @@ def parse_str_list(csv_str):
|
||||
|
||||
def build_variofoerderer_details(block):
|
||||
"""
|
||||
Merkmale-Dict fuer eine VF_N-Streckengruppe (VarioFoerderer, ggf.
|
||||
mehrsegmentig aus Gefaelle- und angetriebenen Foerderer-Teilen).
|
||||
Merkmale-Dict (Sivas-Merkmalnummern) fuer eine VF_N-Streckengruppe
|
||||
(VarioFoerderer, ggf. mehrsegmentig aus Gefaelle- und angetriebenen
|
||||
Foerderer-Teilen). Sivas kennt je Streckengruppe nur EIN Gefaelle-
|
||||
Segment-Paar (001/002) und EIN Vario-Segment (001) - ANZAHL_GF/ANZAHL_VF
|
||||
ueber diese Grenze hinaus werden hier (noch) nicht abgebildet.
|
||||
|
||||
Die Aufteilung der Separatoren/Scanner auf Gefaelle-Anteil (4337/4339)
|
||||
vs. Vario-Anteil (4381/4383) ist NOCH NICHT umgesetzt (haengt an der noch
|
||||
ausstehenden Neuzuordnung von Separator/Scanner zum jeweiligen
|
||||
Streckenabschnitt) - bis dahin steht 4337/4339 fest auf "0" und der volle
|
||||
Attributwert steht auf 4381/4383.
|
||||
"""
|
||||
attribs = block.get("attribs", {})
|
||||
|
||||
def to_int(key):
|
||||
return safe_int(attribs.get(key, "0") or "0")
|
||||
|
||||
return {
|
||||
"Hoehe in m": attribs.get("MONTAGEHOEHE_m", "0"),
|
||||
"Anzahl Foerdergruppen": to_int("ANZAHL_VF"),
|
||||
"Anzahl Gefaellegruppen": to_int("ANZAHL_GF"),
|
||||
"GruppenteileGefaelle": {
|
||||
"AnzahlGeraden": to_int("ANZAHL_GF"),
|
||||
"Laenge in Meter": parse_float_list(attribs.get("L_GF_m", "")),
|
||||
"Winkel": parse_float_list(attribs.get("GF_WINKEL", "")),
|
||||
"AnzahlKurvenLinks90": to_int("GF_Bogen_L_90"),
|
||||
"AnzahlKurvenLinks60": to_int("GF_Bogen_L_60"),
|
||||
"AnzahlKurvenLinks30": to_int("GF_Bogen_L_30"),
|
||||
"AnzahlKurvenRechts90": to_int("GF_Bogen_R_90"),
|
||||
"AnzahlKurvenRechts60": to_int("GF_Bogen_R_60"),
|
||||
"AnzahlKurvenRechts30": to_int("GF_Bogen_R_30"),
|
||||
},
|
||||
"GruppenteileAngetrieben": {
|
||||
"MotorSeite": parse_str_list(attribs.get("MOTORSEITE", "")),
|
||||
"AnzahlGeraden": to_int("ANZAHL_VF"),
|
||||
"Laenge in Meter": parse_float_list(attribs.get("L_VF_m", "")),
|
||||
"Winkel": parse_float_list(attribs.get("VF_WINKEL", "")),
|
||||
"Foerderrichtung": parse_str_list(attribs.get("ANTRIEBFAHRTRICHTUNG", "")),
|
||||
"AnzahlKurvenAussen90": to_int("VF_Bogen_A_90"),
|
||||
"AnzahlKurvenAussen60": to_int("VF_Bogen_A_60"),
|
||||
"AnzahlKurvenAussen30": to_int("VF_Bogen_A_30"),
|
||||
"AnzahlKurvenInnen90": to_int("VF_Bogen_I_90"),
|
||||
"AnzahlKurvenInnen60": to_int("VF_Bogen_I_60"),
|
||||
"AnzahlKurvenInnen30": to_int("VF_Bogen_I_30"),
|
||||
},
|
||||
"Anzahl der Separatoren": to_int("ANZAHL_SEPARATOR"),
|
||||
"Anzahl der Scanner": to_int("ANZAHL_SCANNER"),
|
||||
"Geruest fuer Einzelmodul": attribs.get("GERUEST_EINZELMODUL", "0") == "1",
|
||||
"Geruestoption fuer Einzelmodul": attribs.get("GERUEST_TYP", "Schoenenberger Geruest"),
|
||||
gf_laengen = parse_float_list(attribs.get("L_GF_m", ""))
|
||||
gf_winkel = parse_float_list(attribs.get("GF_WINKEL", ""))
|
||||
vf_laengen = parse_float_list(attribs.get("L_VF_m", ""))
|
||||
vf_winkel = parse_float_list(attribs.get("VF_WINKEL", ""))
|
||||
vf_richtung = parse_str_list(attribs.get("ANTRIEBFAHRTRICHTUNG", ""))
|
||||
motorseiten = parse_str_list(attribs.get("MOTORSEITE", ""))
|
||||
|
||||
details = {
|
||||
"2950": attribs.get("MONTAGEHOEHE_m", "0"),
|
||||
"4300": "mehrere Objekte",
|
||||
"4311": "1",
|
||||
}
|
||||
|
||||
if len(gf_laengen) >= 1:
|
||||
details["4312"] = "Ja"
|
||||
details["4318"] = gf_laengen[0]
|
||||
details["4391"] = str(int(gf_winkel[0])) if gf_winkel else ""
|
||||
else:
|
||||
details["4312"] = "Nein"
|
||||
|
||||
if len(gf_laengen) >= 2:
|
||||
details["4314"] = "Ja"
|
||||
details["4319"] = gf_laengen[1]
|
||||
details["4392"] = str(int(gf_winkel[1])) if len(gf_winkel) >= 2 else ""
|
||||
else:
|
||||
details["4314"] = "Nein"
|
||||
|
||||
details.update({
|
||||
"4329": to_int("GF_Bogen_L_90"),
|
||||
"4330": to_int("GF_Bogen_L_60"),
|
||||
"4331": to_int("GF_Bogen_L_30"),
|
||||
"4332": to_int("GF_Bogen_R_90"),
|
||||
"4333": to_int("GF_Bogen_R_60"),
|
||||
"4334": to_int("GF_Bogen_R_30"),
|
||||
"4337": "0",
|
||||
"4338": SEPARATOR_BEARBEITUNG_ANZEIGE,
|
||||
"4339": "0",
|
||||
"4340": "Ja" if vf_laengen else "Nein",
|
||||
"4341": sum(1 for seite in motorseiten if seite.lower() == "links"),
|
||||
"4342": sum(1 for seite in motorseiten if seite.lower() == "rechts"),
|
||||
})
|
||||
|
||||
if vf_laengen:
|
||||
details["4353"] = vf_laengen[0]
|
||||
details["4354"] = vf_richtung[0].lower() if vf_richtung else ""
|
||||
details["4386"] = str(int(vf_winkel[0])) if vf_winkel else ""
|
||||
|
||||
details.update({
|
||||
"4373": to_int("VF_Bogen_A_90"),
|
||||
"4374": to_int("VF_Bogen_A_60"),
|
||||
"4375": to_int("VF_Bogen_A_30"),
|
||||
"4376": to_int("VF_Bogen_I_90"),
|
||||
"4377": to_int("VF_Bogen_I_60"),
|
||||
"4378": to_int("VF_Bogen_I_30"),
|
||||
"4381": attribs.get("ANZAHL_SEPARATOR", "0"),
|
||||
"4382": SEPARATOR_BEARBEITUNG_ANZEIGE,
|
||||
"4383": attribs.get("ANZAHL_SCANNER", "0"),
|
||||
"3992": ja_nein(attribs.get("GERUEST_EINZELMODUL", "0")),
|
||||
"3993": geruest_typ_anzeige(attribs.get("GERUEST_TYP", "Schoenenberger Geruest")),
|
||||
})
|
||||
return {k: str(v) for k, v in details.items()}
|
||||
|
||||
|
||||
def build_kreisel_details(block):
|
||||
"""Merkmale-Dict fuer einen ILS Kreisel."""
|
||||
"""Merkmale-Dict (Sivas-Merkmalnummern) fuer eine Gruppe gleichartiger
|
||||
ILS Kreisel. 3511 ("Kreisel - zu fertigende Menge") steht bewusst fest
|
||||
auf "1" - die reale Stueckzahl der Gruppe steht auf der Sivas-Hauptzeile
|
||||
selbst (Anzahl-Spalte, siehe kreisel_groups in process_blocks)."""
|
||||
attribs = block.get("attribs", {})
|
||||
abstand_m = str(round(safe_float(attribs.get("ABSTAND", "2300"), 2300.0) / 1000.0, 2))
|
||||
kreiselart = "Kreisel mit PINBAND" if attribs.get("KREISELART", "STANDARD") == "PIN" else "Kreisel"
|
||||
return {
|
||||
"Hoehe in m": attribs.get("MONTAGEHOEHE_m", "0"),
|
||||
"Kreiselart": attribs.get("KREISELART", "STANDARD"),
|
||||
"Abstand (Kreiselachse A - Kreiselachse) in Meter": abstand_m,
|
||||
"Anzahl der Separatoren": attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")),
|
||||
"Anzahl der Scanner": attribs.get("ANZAHL_SCANNER", attribs.get("N_SCANNER", "0")),
|
||||
"Anzahl der Rampen": attribs.get("ANZAHL_RAMPEN", attribs.get("N_RAMPEN", "0")),
|
||||
"Geruest fuer Einzelmodul": attribs.get("GERUEST_EINZELMODUL", "0") == "1",
|
||||
"Geruestoption fuer Einzelmodul": attribs.get("GERUEST_TYP", "Schoenenberger Geruest"),
|
||||
"2952": attribs.get("MONTAGEHOEHE_m", "0"),
|
||||
"3510": kreiselart,
|
||||
"3511": "1",
|
||||
"3512": abstand_m,
|
||||
"3515": attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")),
|
||||
"3517": attribs.get("ANZAHL_SCANNER", attribs.get("N_SCANNER", "0")),
|
||||
"3519": attribs.get("ANZAHL_RAMPEN", attribs.get("N_RAMPEN", "0")),
|
||||
"3992": ja_nein(attribs.get("GERUEST_EINZELMODUL", "0")),
|
||||
"3993": geruest_typ_anzeige(attribs.get("GERUEST_TYP", "Schoenenberger Geruest")),
|
||||
}
|
||||
|
||||
|
||||
@@ -325,8 +421,6 @@ ILS_SIVASNR = {
|
||||
"ils_weiche": ("6216", "ILS 2.0 Weiche"),
|
||||
"strecke": ("6220", "ILS 2.0 Strecke"),
|
||||
"gefaellestrecke": ("6220", "ILS 2.0 Gefaellestrecke"),
|
||||
"bt_beladung": ("6215", "BT - Beladung (mit Vereinzeler, ohne Separator)"),
|
||||
"bg_entladung": ("6215", "Abwurf fuer Klammerbuegel Sitzbezuege / Kleiderbuegel"),
|
||||
"automation": ("6269", "ILS 2.0 Automation und Pneumatik - Gesamtanlage"),
|
||||
}
|
||||
|
||||
@@ -351,9 +445,7 @@ def process_blocks(blocks, lookup):
|
||||
vario_list = [] # Liste von VarioFoerderer-Compound-Bloecken (VF_N), je einer Zeile
|
||||
gefaelle_list = [] # Liste von Gefaellestrecke-Bloecken (GF_*), je einer Zeile
|
||||
btmt_beladung_ids = [] # IDs aller BTMT-Beladung*-Bloecke (eine gruppierte Zeile)
|
||||
btmt_beladung_bezeichnungen = []
|
||||
btmt_entladung_ids = [] # IDs aller SC_Entladung*-Bloecke (eine gruppierte Zeile)
|
||||
btmt_entladung_bezeichnungen = []
|
||||
geruest_list = [] # Liste von GR_-Geruest-Baugruppenbloecken, je einer Zeile
|
||||
|
||||
counters = {
|
||||
@@ -375,7 +467,6 @@ def process_blocks(blocks, lookup):
|
||||
"anzahl_eckraeder": 0,
|
||||
"anzahl_gefaellestrecken": 0,
|
||||
"anzahl_variofoerderer": 0,
|
||||
"anzahl_streckengruppen": 0,
|
||||
"laengen_gefaellestrecken": 0.0,
|
||||
"anzahl_motoren_vario": 0,
|
||||
"laengen_variofoerderer": 0.0,
|
||||
@@ -404,14 +495,12 @@ def process_blocks(blocks, lookup):
|
||||
# der ILS-Automation-Summierungszeile (siehe unten).
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_btmt_beladung", [])):
|
||||
btmt_beladung_ids.append(get_id(block))
|
||||
btmt_beladung_bezeichnungen.append(block.get("attribs", {}).get("Bezeichnung", ""))
|
||||
counters["anzahl_btbeladung"] += 1
|
||||
continue
|
||||
|
||||
# --- BTMT-Entladestation (Abwurfstation) ---
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_btmt_entladung", [])):
|
||||
btmt_entladung_ids.append(get_id(block))
|
||||
btmt_entladung_bezeichnungen.append(block.get("attribs", {}).get("Bezeichnung", ""))
|
||||
counters["anzahl_abwurfstationen"] += 1
|
||||
continue
|
||||
|
||||
@@ -542,8 +631,6 @@ def process_blocks(blocks, lookup):
|
||||
counters["laengen_kreisel"] += abstand_mm / 1000.0
|
||||
counters["anzahl_separatoren"] += n_sep
|
||||
counters["anzahl_scanner"] += safe_int(attribs.get("ANZAHL_SCANNER", attribs.get("N_SCANNER", "0")) or "0")
|
||||
if counters["anzahl_streckengruppen"] == 0:
|
||||
counters["anzahl_streckengruppen"] = 1
|
||||
continue
|
||||
|
||||
# --- VarioFoerderer Compound-Block (VF_N) ---
|
||||
@@ -556,8 +643,7 @@ def process_blocks(blocks, lookup):
|
||||
counters["laengen_variofoerderer"] += safe_float(attribs.get("L_VF_m", "0") or "0")
|
||||
counters["anzahl_separatoren"] += safe_int(attribs.get("ANZAHL_SEPARATOR", "0") or "0")
|
||||
counters["anzahl_scanner"] += safe_int(attribs.get("ANZAHL_SCANNER", "0") or "0")
|
||||
if counters["anzahl_streckengruppen"] == 0:
|
||||
counters["anzahl_streckengruppen"] = 1
|
||||
counters["anzahl_motoren_vario"] += safe_int(attribs.get("ANZAHL_VF", "0") or "0")
|
||||
continue
|
||||
|
||||
# --- Gefaellestrecke Compound-Block (GF_N) ---
|
||||
@@ -569,8 +655,6 @@ def process_blocks(blocks, lookup):
|
||||
counters["laengen_gefaellestrecken"] += safe_float(attribs.get("L_GF_m", "0") or "0")
|
||||
counters["anzahl_separatoren"] += safe_int(attribs.get("ANZAHL_SEPARATOR", "0") or "0")
|
||||
counters["anzahl_scanner"] += safe_int(attribs.get("ANZAHL_SCANNER", "0") or "0")
|
||||
if counters["anzahl_streckengruppen"] == 0:
|
||||
counters["anzahl_streckengruppen"] = 1
|
||||
continue
|
||||
|
||||
# --- ILS Strecke-Module (Vario/Staustrecke, einzelne Komponenten in Modelspace) ---
|
||||
@@ -695,39 +779,29 @@ def process_blocks(blocks, lookup):
|
||||
})
|
||||
nr += 1
|
||||
|
||||
# BTMT-Beladestationen: eine gruppierte Zeile fuer alle Instanzen (wie die
|
||||
# losen Strecke-Elemente oben) - Stueckzahl zusaetzlich im Automation-
|
||||
# Zaehler "Anzahl aller BTBeladung" (siehe counters oben).
|
||||
if btmt_beladung_ids:
|
||||
sivasnr, teileart = ILS_SIVASNR["bt_beladung"]
|
||||
# ILS 2.0 Zusatzmodul: BTMT-Beladestationen UND BTMT-Entladestationen
|
||||
# (Abwurfstationen) zusammen in EINER Zeile (Merkmale 3501-3504) statt wie
|
||||
# bisher zwei separaten TeileArt-Zeilen ohne details - Anzahl-Spalte
|
||||
# bleibt 1 (wie bei der Gefaellestrecke-Gruppe), die realen Stueckzahlen
|
||||
# stehen in 3503/3504.
|
||||
if btmt_beladung_ids or btmt_entladung_ids:
|
||||
sivasnr, teileart = ILS_SIVASNR["zusatzmodul"]
|
||||
items.append({
|
||||
"nr": nr,
|
||||
"teileart": teileart,
|
||||
"sivasnr": sivasnr,
|
||||
"sivasnr_quoted": True,
|
||||
"bezeichnung": ", ".join(b for b in btmt_beladung_bezeichnungen if b),
|
||||
"bezeichnung": "",
|
||||
"dispgruppe": 20,
|
||||
"anzahl": len(btmt_beladung_ids),
|
||||
"anzahl": 1,
|
||||
"laenge_mm": "",
|
||||
"ids": btmt_beladung_ids,
|
||||
"details": {},
|
||||
})
|
||||
nr += 1
|
||||
|
||||
# BTMT-Entladestationen (Abwurfstationen): analog gruppiert.
|
||||
if btmt_entladung_ids:
|
||||
sivasnr, teileart = ILS_SIVASNR["bg_entladung"]
|
||||
items.append({
|
||||
"nr": nr,
|
||||
"teileart": teileart,
|
||||
"sivasnr": sivasnr,
|
||||
"sivasnr_quoted": True,
|
||||
"bezeichnung": ", ".join(b for b in btmt_entladung_bezeichnungen if b),
|
||||
"dispgruppe": 20,
|
||||
"anzahl": len(btmt_entladung_ids),
|
||||
"laenge_mm": "",
|
||||
"ids": btmt_entladung_ids,
|
||||
"details": {},
|
||||
"ids": btmt_beladung_ids + btmt_entladung_ids,
|
||||
"details": {
|
||||
"3501": ja_nein("1" if btmt_beladung_ids else "0"),
|
||||
"3503": str(len(btmt_beladung_ids)),
|
||||
"3502": ja_nein("1" if btmt_entladung_ids else "0"),
|
||||
"3504": str(len(btmt_entladung_ids)),
|
||||
},
|
||||
})
|
||||
nr += 1
|
||||
|
||||
@@ -796,7 +870,10 @@ def process_blocks(blocks, lookup):
|
||||
|
||||
for group in gefaelle_groups.values():
|
||||
sivasnr, teileart = ILS_SIVASNR["gefaellestrecke"]
|
||||
details = build_gefaellestrecke_details(group["block"])
|
||||
# Anders als bei Kreisel steht hier die reale Stueckzahl der Gruppe
|
||||
# NICHT in der Anzahl-Spalte (die bleibt 1), sondern im Merkmal 4311 -
|
||||
# siehe build_gefaellestrecke_details.
|
||||
details = build_gefaellestrecke_details(group["block"], anzahl=len(group["ids"]))
|
||||
items.append({
|
||||
"nr": nr,
|
||||
"teileart": teileart,
|
||||
@@ -804,7 +881,7 @@ def process_blocks(blocks, lookup):
|
||||
"sivasnr_quoted": True,
|
||||
"bezeichnung": ", ".join(b for b in group["bezeichnungen"] if b),
|
||||
"dispgruppe": 20,
|
||||
"anzahl": len(group["ids"]),
|
||||
"anzahl": 1,
|
||||
"laenge_mm": "",
|
||||
"ids": group["ids"],
|
||||
"details": details,
|
||||
@@ -819,27 +896,36 @@ def process_blocks(blocks, lookup):
|
||||
or counters["anzahl_btbeladung"] > 0
|
||||
or counters["anzahl_abwurfstationen"] > 0)
|
||||
if has_ils:
|
||||
# 4407 "Anzahl der Streckengruppen" zaehlt NUR die kombinierten
|
||||
# Gefaelle+Vario-Compound-Bloecke (VF_N, TYP "Streckengruppe") - das
|
||||
# ist len(vario_list), NICHT counters["anzahl_variofoerderer"] (der
|
||||
# zaehlt auch lose Strecke-Module ausserhalb eines VF_N mit).
|
||||
# 4426 "Anzahl der einzelnen Vario-Foerderer" gibt es als eigenen
|
||||
# Typ nicht mehr (nur noch Gefaellestrecke ODER Streckengruppe) und
|
||||
# steht daher fest auf "0".
|
||||
automation_details = {
|
||||
"Anzahl Kreisel": counters["anzahl_kreisel"],
|
||||
"Anzahl Kreisel mit Pinband": counters["anzahl_kreisel_pinband"],
|
||||
"Anzahl aller Separatoren in Kreisel mit PIN": counters["sep_kreisel_pin"],
|
||||
"Anzahl aller Separatoren in Kreisel ohne PIN": counters["sep_kreisel_ohne_pin"],
|
||||
"Anzahl Verbindungskreisel": counters["anzahl_verbindungskreisel"],
|
||||
"Laengen aller Kreisel": counters["laengen_kreisel"],
|
||||
"Anzahl der Weichen": counters["anzahl_weichen_ils"],
|
||||
"Anzahl der Eckraeder": counters["anzahl_eckraeder"],
|
||||
"Anzahl der einzelnen Gefaellestrecken": counters["anzahl_gefaellestrecken"],
|
||||
"Anzahl der einzelnen Variofoerderer": counters["anzahl_variofoerderer"],
|
||||
"Anzahl der Streckengruppen": counters["anzahl_streckengruppen"],
|
||||
"Laengen aller Gefaellestrecken": counters["laengen_gefaellestrecken"],
|
||||
"Anzahl der Motoren Variofoerderer": counters["anzahl_motoren_vario"],
|
||||
"Laengen aller Variofoerderer": counters["laengen_variofoerderer"],
|
||||
"Anzahl ETFs": counters["anzahl_etf"],
|
||||
"Laengen aller ETFs": counters["laengen_etf"],
|
||||
"Anzahl aller Separatoren": counters["anzahl_separatoren"],
|
||||
"Anzahl aller Scanner": counters["anzahl_scanner"],
|
||||
"Anzahl aller BTBeladung": counters["anzahl_btbeladung"],
|
||||
"Anzahl aller Abwurfstationen": counters["anzahl_abwurfstationen"],
|
||||
"4000": "Ja",
|
||||
"4400": str(counters["anzahl_kreisel"]),
|
||||
"4413": str(counters["sep_kreisel_ohne_pin"]),
|
||||
"4401": str(counters["anzahl_kreisel_pinband"]),
|
||||
"4414": str(counters["sep_kreisel_pin"]),
|
||||
"4402": str(counters["anzahl_verbindungskreisel"]),
|
||||
"4419": f"{counters['laengen_kreisel']:.2f}",
|
||||
"4403": str(counters["anzahl_weichen_ils"]),
|
||||
"4404": str(counters["anzahl_eckraeder"]),
|
||||
"4425": str(counters["anzahl_gefaellestrecken"]),
|
||||
"4426": "0",
|
||||
"4407": str(len(vario_list)),
|
||||
"4418": f"{counters['laengen_gefaellestrecken']:.2f}",
|
||||
"4408": str(counters["anzahl_motoren_vario"]),
|
||||
"4420": str(counters["laengen_variofoerderer"]),
|
||||
"4409": str(counters["anzahl_etf"]),
|
||||
"4417": str(counters["laengen_etf"]),
|
||||
"4410": str(counters["anzahl_separatoren"]),
|
||||
"4411": str(counters["anzahl_scanner"]),
|
||||
"4421": str(counters["anzahl_btbeladung"]),
|
||||
"4422": str(counters["anzahl_abwurfstationen"]),
|
||||
"4100": "Ja",
|
||||
}
|
||||
auto_sivasnr, auto_teileart = ILS_SIVASNR["automation"]
|
||||
items.append({
|
||||
@@ -857,9 +943,17 @@ def process_blocks(blocks, lookup):
|
||||
return items, counters
|
||||
|
||||
|
||||
def format_csv_line(item):
|
||||
"""Formatiert eine CSV-Zeile im neuen Sivas-Format."""
|
||||
details_json = json.dumps(item["details"], ensure_ascii=False)
|
||||
def format_csv_line(item, details=None):
|
||||
"""Formatiert eine CSV-Zeile im neuen Sivas-Format.
|
||||
|
||||
details ueberschreibt item["details"] fuer die Ausgabe - genutzt von
|
||||
details_mit_bezeichnung(), um dieselbe Zeile mit lesbaren Merkmal-Keys
|
||||
in die zweite ("..._mit_bezeichnung.csv") Datei zu schreiben, ohne
|
||||
item["details"] selbst zu veraendern.
|
||||
"""
|
||||
if details is None:
|
||||
details = item["details"]
|
||||
details_json = json.dumps(details, ensure_ascii=False)
|
||||
sivasnr = f'"{csv_quote(item["sivasnr"])}"' if item["sivasnr_quoted"] else item["sivasnr"]
|
||||
ids_str = ", ".join(str(i) for i in item.get("ids", []) if i)
|
||||
return (
|
||||
@@ -875,6 +969,28 @@ def format_csv_line(item):
|
||||
)
|
||||
|
||||
|
||||
def details_mit_bezeichnung(details, merkmalstamm):
|
||||
"""Kopie von details, bei der jeder Merkmalnummer-Key um die Bezeichnung
|
||||
aus dem Merkmalstamm erweitert wird ("<Nummer> | <Bezeichnung>"). Keys,
|
||||
die keine reine Zahl sind (aktuell nur bei Eckrad/Geruest, siehe
|
||||
build_eckrad_details/build_geruest_details - dort noch beschreibende
|
||||
Klartext-Keys statt Merkmalnummern), bleiben unveraendert."""
|
||||
neu = {}
|
||||
for key, value in details.items():
|
||||
if key.isdigit():
|
||||
neu[f"{key} | {bezeichnung_fuer(key, merkmalstamm)}"] = value
|
||||
else:
|
||||
neu[key] = value
|
||||
return neu
|
||||
|
||||
|
||||
def lesbarer_dateiname(output_csv):
|
||||
"""Leitet den Dateinamen der Lesbarkeits-Kopie aus output_csv ab, z.B.
|
||||
"500651_sivas.csv" -> "500651_sivas_mit_bezeichnung.csv"."""
|
||||
stamm, endung = os.path.splitext(output_csv)
|
||||
return f"{stamm}_mit_bezeichnung{endung}"
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 4:
|
||||
print("Aufruf: python export_sivas.py <export_raw.json> <data_dir> <output.csv>")
|
||||
@@ -903,9 +1019,20 @@ def main():
|
||||
f.write(header + "\n")
|
||||
for item in items:
|
||||
f.write(format_csv_line(item) + "\n")
|
||||
|
||||
print(f"[export_sivas] CSV geschrieben: {output_csv} ({len(items)} Zeilen)")
|
||||
|
||||
# Zweite, lesbare Kopie fuer die manuelle Pruefung (Merkmalnummer +
|
||||
# Bezeichnung aus cfg/Merkmalsstamm_in_Sivas.xlsx) - wird von Sivas
|
||||
# selbst NICHT eingelesen, nur output_csv ist der eigentliche Import.
|
||||
merkmalstamm = lade_merkmalstamm()
|
||||
output_csv_lesbar = lesbarer_dateiname(output_csv)
|
||||
with open(output_csv_lesbar, "w", encoding="utf-8") as f:
|
||||
f.write(header + "\n")
|
||||
for item in items:
|
||||
details = details_mit_bezeichnung(item["details"], merkmalstamm)
|
||||
f.write(format_csv_line(item, details=details) + "\n")
|
||||
print(f"[export_sivas] Lesbare Kopie geschrieben: {output_csv_lesbar}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
merkmalstamm.py - Liest Merkmalnummer -> Bezeichnung aus
|
||||
cfg/Merkmalsstamm_in_Sivas.xlsx.
|
||||
|
||||
Bewusst ohne openpyxl (reine Standard-Library: zipfile + xml.etree) -
|
||||
export.lsp/export:python-exe loest den Interpreter fuer EXPORTSIVAS ueber
|
||||
Config/DXFM_PYTHON/Projekt-venv/PATH auf und garantiert dabei keine
|
||||
zusaetzliche pip-Abhaengigkeit; ein .xlsx ist im Kern ein ZIP aus XML-
|
||||
Dateien, das reicht hier ohne Zusatzpaket.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import zipfile
|
||||
from functools import lru_cache
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
_NS = {"main": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
|
||||
|
||||
|
||||
def merkmalstamm_pfad():
|
||||
"""Pfad zu Merkmalsstamm_in_Sivas.xlsx: DXFM_CFG env var, sonst <repo>/cfg."""
|
||||
dxfm_cfg = os.environ.get("DXFM_CFG")
|
||||
if dxfm_cfg:
|
||||
return os.path.join(dxfm_cfg, "Merkmalsstamm_in_Sivas.xlsx")
|
||||
basis = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
return os.path.join(basis, "cfg", "Merkmalsstamm_in_Sivas.xlsx")
|
||||
|
||||
|
||||
def _spalte_zu_zahl(spalte):
|
||||
zahl = 0
|
||||
for zeichen in spalte:
|
||||
zahl = zahl * 26 + (ord(zeichen) - ord("A") + 1)
|
||||
return zahl
|
||||
|
||||
|
||||
def _spalte_aus_zellref(ref):
|
||||
treffer = re.match(r"([A-Z]+)\d+", ref)
|
||||
return treffer.group(1) if treffer else "A"
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def lade_merkmalstamm(pfad=None):
|
||||
"""Liest Sheet 1 (Spalte A=Merkmal-Nr., Spalte C=Bezeichnung) und liefert
|
||||
ein Dict Merkmalnummer (str) -> Bezeichnung (str, von Excel-
|
||||
Zeilenumbruch-Artefakten wie "_x000D_" bereinigt). Bei fehlender/kaputter
|
||||
Datei: leeres Dict - Aufrufer faellt dann auf die reine Merkmalnummer
|
||||
zurueck (siehe bezeichnung_fuer)."""
|
||||
if pfad is None:
|
||||
pfad = merkmalstamm_pfad()
|
||||
if not os.path.exists(pfad):
|
||||
return {}
|
||||
|
||||
try:
|
||||
with zipfile.ZipFile(pfad) as z:
|
||||
shared = []
|
||||
if "xl/sharedStrings.xml" in z.namelist():
|
||||
baum = ET.fromstring(z.read("xl/sharedStrings.xml"))
|
||||
for si in baum.findall("main:si", _NS):
|
||||
texte = si.findall(".//main:t", _NS)
|
||||
shared.append("".join(t.text or "" for t in texte))
|
||||
|
||||
sheet = ET.fromstring(z.read("xl/worksheets/sheet1.xml"))
|
||||
mapping = {}
|
||||
for row in sheet.findall(".//main:sheetData/main:row", _NS):
|
||||
zellen = {}
|
||||
for c in row.findall("main:c", _NS):
|
||||
spalte = _spalte_zu_zahl(_spalte_aus_zellref(c.get("r", "A1")))
|
||||
v = c.find("main:v", _NS)
|
||||
wert = v.text if v is not None else None
|
||||
if c.get("t") == "s" and wert is not None:
|
||||
wert = shared[int(wert)]
|
||||
zellen[spalte] = wert
|
||||
nummer = zellen.get(1)
|
||||
bezeichnung = zellen.get(3)
|
||||
if nummer and str(nummer).strip().isdigit() and bezeichnung:
|
||||
bezeichnung = bezeichnung.replace("_x000D_", " ")
|
||||
bezeichnung = re.sub(r"\s+", " ", bezeichnung).strip()
|
||||
mapping[str(nummer).strip()] = bezeichnung
|
||||
return mapping
|
||||
except (KeyError, zipfile.BadZipFile, ET.ParseError):
|
||||
return {}
|
||||
|
||||
|
||||
def bezeichnung_fuer(nummer, mapping=None):
|
||||
"""Bezeichnung zu einer Merkmalnummer, Fallback: die Nummer selbst
|
||||
(z.B. bei fehlendem Eintrag im Merkmalstamm)."""
|
||||
if mapping is None:
|
||||
mapping = lade_merkmalstamm()
|
||||
return mapping.get(str(nummer), str(nummer))
|
||||
Reference in New Issue
Block a user