Merge e-planung-bruecke: stabile TRO-IDs, TRO-JSON v4, E-Planung-Bruecke
Uebernimmt Michael Stangls Zweig (tro_ids, Slots/jamAreas v4, e_planning --eplan, Konzept- und Datenquellen-Doku) in master, auf dem seitdem die SCL-Templates fuer alle 7 Typen mit Referenz-Aufrufstelle liegen. Konfliktloesung: - lib/scl_context.py: beide Seiten zusammengefuehrt - Michaels _separator (E-Planung-Namen) und Staubereiche (_entries/_exit_area, ein JamEntr je eigenem Staubereich) plus die Template-Felder (_scanner, _store, carriers/data, part_sensor). _jam() fuehrt jetzt auch jam_id. Die beim automatischen Zusammenfuehren verlorene Property SclJam.title wiederhergestellt. - Templates 1Sep, 1Sep2Swi, 1Sep_SSCC, Vario, Vario_workStation, PinStore_Auto auf v4 nachgezogen: Kommentar "Staubereich J.... von/vor ..." wie im Pilot; PinStore-TODO zaehlt Staubereiche statt Nachbarn (mubea: 2 Zulaeufe laufen in einen Eingang). - Golden-Files *_templates neu erzeugt; geaendert sind ausschliesslich Staubereichs-Zeilen (Kennung im Kommentar, ein Eingang statt einem je Vorgaenger). Generische Golden-Files = Michaels Stand. - Neuer Test: E-Planung-Namen kommen auch in der Template-Ausgabe an. - Doku: README Schema v4, SCL_Templates.md Staubereiche und TODO-Zahlen. Geprueft: 71 Tests gruen; tro_flow --json + scl_skeleton --templates auf allen 8 CSVs ohne Fehler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,13 +27,18 @@ Template" below). What exists today is:
|
||||
tool in `lib/` (see "Environment scripts" below)
|
||||
- `lib/*.py` — CLI tools and libraries that turn a CSV export into a material-flow graph,
|
||||
a derived TRO list/diagram, a TRO-JSON (`lib/model.py`), an `FB_Main` SCL skeleton, and
|
||||
(with the BricsCAD DXF) an annotated copy of the drawing for visual checking.
|
||||
(with the BricsCAD DXF) an annotated copy of the drawing for visual checking;
|
||||
`lib/e_planning.py` assigns the E-planning's sensors/valves (from the cable-length
|
||||
tool's `*_positionsdraw.json`) to the layout parts so the skeleton can carry real names.
|
||||
See **`doc/Python_Scripts.md`** for what each script does and which switches it takes.
|
||||
- `data/` — input CSV exports (gitignored, not committed)
|
||||
- `cad/tro_types.lsp` — generated type list for the BricsCAD `TROEDIT` dialog (written by
|
||||
`tro_annotate.py --emit-lisp`)
|
||||
- `cfg/dxf_registration.json` — persisted, verified CSV↔DXF coordinate transform;
|
||||
`cfg/tro_overrides.ini` — hand-maintained corrections to the derived TROs (see `tro_overrides.py`)
|
||||
`cfg/tro_overrides.ini` — hand-maintained corrections to the derived TROs (see `tro_overrides.py`);
|
||||
`cfg/e_planning.ini` — project-specific rules for assigning E-planning symbols to layout parts;
|
||||
`cfg/<csv>_tro_ids.ini` — per-plant TRO-ID file (versioned, IDs renamed by the SPS
|
||||
programmers), created/extended by `tro_flow.py` via `lib/tro_ids.py`
|
||||
- `doc/*.md` — analysis documents that reverse-engineer the SCL patterns and propose the
|
||||
fuller JSON layout schema for the parts of the pipeline still to be built out
|
||||
|
||||
@@ -90,7 +95,7 @@ this order for onboarding:
|
||||
|
||||
0. **`doc/Python_Scripts.md`** — the existing CLI tooling (`lib/material_flow.py`,
|
||||
`lib/tro_flow.py`, `lib/tro_annotate.py`, `lib/tro_extract.py`, `lib/scl_skeleton.py`,
|
||||
plus the `lib/tro_catalog.py`, `lib/model.py`, `lib/dxf_registration.py` and
|
||||
`lib/e_planning.py`, plus the `lib/tro_catalog.py`, `lib/model.py`, `lib/dxf_registration.py` and
|
||||
`lib/tro_overrides.py` libraries they share): what each script does, its switches, inputs/outputs. This is
|
||||
working code, not a proposal — read it before touching `lib/`.
|
||||
1. **`doc/HundM/Json_Layout-Konzept.md`** — the core proposal: a JSON file as single source
|
||||
@@ -135,6 +140,16 @@ this order for onboarding:
|
||||
its generated images come from `doc/TRO_Katalog/tro_graphs/gen_tro_graphs.py`
|
||||
(doc-tooling, not part of the generator — see `doc/Python_Scripts.md` §8) built from
|
||||
`doc/TRO_Katalog/connect.ini`.
|
||||
9. **`doc/Datenquellen_Layout_Hardwareplanung.md`** — which TRO/SCL data comes from the
|
||||
mechanical layout (CSV), which from the hardware planning (EA list, TIA import,
|
||||
WSCAD, `positionsdraw`) and which is a default; two reference call sites annotated
|
||||
line by line. The full raw field lists of both sources are in
|
||||
`doc/Klassenhierarchie.md`, chapter "Daten".
|
||||
10. **`doc/Konzept_Layout_Hardware_SCL.md`** — the target workflow (planned, not built):
|
||||
layout → `<csv>_tro.json` (with slots, `jamAreas`), hardware planning →
|
||||
`hw_import.py` → `<projekt>_hw.json`, `hw_assign.py` → `<csv>_anlage.json`,
|
||||
then `scl_skeleton.py`; what happens to `e_planning.py` (split),
|
||||
`tro_annotate.py` (viewer reading the JSON) and `tro_extract.py` (dropped).
|
||||
|
||||
### Used libraries (referenced but not vendored)
|
||||
|
||||
@@ -167,8 +182,12 @@ and status: `doc/SCL_Templates.md`. Golden files in `tests/fixtures/scl/` pin th
|
||||
byte — rewrite them (`SKEL_UPDATE_GOLDEN=1`) only for a reviewed, intended change.
|
||||
|
||||
1. **`lib/tro_flow.py --json`** (implemented) — writes `<csv>_tro.json` (schema
|
||||
`sps_skel-tro-v3`, `model.TroDocument`): TROs, system boundaries `SYSn`, connections with
|
||||
their kind, findings. See `doc/Python_Scripts.md` §4.
|
||||
`sps_skel-tro-v4`, `model.TroDocument`): TROs with their hardware `slots`, system
|
||||
boundaries `SYSn`, connections with their kind, `jamAreas` (one per TRO entry, key
|
||||
`J<Separator>`, all predecessors that feed it), findings. TRO-IDs come from `cfg/<csv>_tro_ids.ini` (`lib/tro_ids.py`): each
|
||||
TRO is found again by the position of its separators, so IDs survive a new layout export
|
||||
(TeileIds do not); `--renumber` renumbers along the material flow, `--ids` points another
|
||||
export of the same plant at the same file. See `doc/Python_Scripts.md` §4.
|
||||
2. **`lib/scl_skeleton.py`** (implemented) — reads that TRO-JSON through `TroDocument` (a
|
||||
wrong/renamed key or `items` not matching the catalog stops the run) and emits an `FB_Main` SCL
|
||||
skeleton (`<source>_FB_Main.scl`): one `REGION` per TRO with the instance call and every
|
||||
@@ -184,6 +203,13 @@ byte — rewrite them (`SKEL_UPDATE_GOLDEN=1`) only for a reviewed, intended cha
|
||||
`sps_skel-tro-extract-v1` JSON, which `scl_skeleton.py` no longer accepts. Not deleted, not
|
||||
extended; if drawing edits (`TRO_INSERT`/`TRO_EDIT`) are ever needed again, it should
|
||||
write `model.TroDocument`. See `doc/Python_Scripts.md` §5a.
|
||||
5. **`lib/e_planning.py`** (implemented, first version) — CSV + E-planning
|
||||
`*_positionsdraw.json` → `<csv>_eplan.json` (`EplanDocument`): registers the E drawing to
|
||||
the CSV (`dxf_registration.py`) and assigns BMKs to layout parts by rules in
|
||||
`cfg/e_planning.ini` (distance, target distance, numbering rule `MB = BG + 1`).
|
||||
`scl_skeleton.py --eplan` fills separator sensor and stopper valve from it; jam sensors
|
||||
and outfeed/switch valves are assigned but not yet placed in the SCL. See
|
||||
`doc/Python_Scripts.md` §5c and `doc/E-Planung_Bruecke.md`.
|
||||
|
||||
What is **not** built out yet, relative to the original two-tool design:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ einem CSV-Export der mechanischen Planung (ILS 2.0) entstehen automatisch:
|
||||
- der **Materialfluss-Graph** der mechanischen Objekte (Strecken,
|
||||
Gefällestrecken, Kreisel),
|
||||
- die daraus abgeleitete **TRO-Liste** samt eigenem Flussdiagramm,
|
||||
- die **TRO-JSON** `<name>_tro.json` (Schema `sps_skel-tro-v3`, Modell
|
||||
- die **TRO-JSON** `<name>_tro.json` (Schema `sps_skel-tro-v4`, Modell
|
||||
`lib/model.py`) — die Schnittstelle zwischen Ableitung und SCL-Erzeugung,
|
||||
- daraus ein **SCL-Gerüst für `FB_Main`** — ein `REGION` pro TRO mit
|
||||
Instanzaufruf und allen Parametern in der richtigen Reihenfolge, aber
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
@echo off
|
||||
REM ================================================================
|
||||
REM SPS_SKEL - E-Planung den Bauteilen des Layouts zuordnen
|
||||
REM ================================================================
|
||||
REM Aufruf:
|
||||
REM bin\e_planning.bat --file mubea.csv --eplan Mubea_positionsdraw.json
|
||||
REM bin\e_planning.bat --file mubea.csv --eplan C:\pfad\Mubea_positionsdraw.json --re-register
|
||||
REM
|
||||
REM CSV und *_positionsdraw.json (Kabellaengen-Tool) werden in %SKEL_DATA%
|
||||
REM gesucht (oder als Pfad angegeben). Regeln: %SKEL_CFG%\e_planning.ini.
|
||||
REM Ergebnis <csv>_eplan.json und Bericht <csv>_eplan.md in %SKEL_RESULTS%.
|
||||
REM
|
||||
REM Weiter: bin\scl_skeleton.bat --json <name>_tro.json --eplan <name>_eplan.json
|
||||
REM ================================================================
|
||||
|
||||
setlocal
|
||||
|
||||
REM Umgebung setzen (SKEL_DATA, SKEL_RESULTS, SKEL_LIB, PYTHONPATH ...)
|
||||
call "%~dp0setenv.bat" >nul
|
||||
|
||||
REM Virtuelle Umgebung nutzen, falls vorhanden (pydantic wird benoetigt)
|
||||
if exist "%SPS_SKEL%\.venv\Scripts\activate.bat" (
|
||||
call "%SPS_SKEL%\.venv\Scripts\activate.bat"
|
||||
)
|
||||
|
||||
REM Python-Interpreter bestimmen (py-Launcher bevorzugt, sonst python)
|
||||
set "PY=py"
|
||||
py --version >nul 2>&1 || set "PY=python"
|
||||
|
||||
"%PY%" "%SKEL_LIB%\e_planning.py" %*
|
||||
set "RC=%ERRORLEVEL%"
|
||||
|
||||
endlocal & exit /b %RC%
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# ================================================================
|
||||
# SPS_SKEL - E-Planung den Bauteilen des Layouts zuordnen
|
||||
# ================================================================
|
||||
# Aufruf:
|
||||
# bash bin/e_planning.sh --file mubea.csv --eplan Mubea_positionsdraw.json
|
||||
# bash bin/e_planning.sh --file mubea.csv --eplan /pfad/Mubea_positionsdraw.json --re-register
|
||||
#
|
||||
# CSV und *_positionsdraw.json (Kabellaengen-Tool) werden in $SKEL_DATA
|
||||
# gesucht (oder als Pfad angegeben). Regeln: $SKEL_CFG/e_planning.ini.
|
||||
# Ergebnis <csv>_eplan.json und Bericht <csv>_eplan.md in $SKEL_RESULTS.
|
||||
#
|
||||
# Weiter: bin/scl_skeleton.sh --json <name>_tro.json --eplan <name>_eplan.json
|
||||
# ================================================================
|
||||
|
||||
set -eu
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Umgebung setzen (SKEL_DATA, SKEL_RESULTS, SKEL_LIB, PYTHONPATH ...)
|
||||
# shellcheck source=/dev/null
|
||||
. "$SCRIPT_DIR/setenv.sh" >/dev/null
|
||||
|
||||
# Virtuelle Umgebung nutzen, falls vorhanden (pydantic wird benoetigt)
|
||||
if [ -f "$SPS_SKEL/.venv/bin/activate" ]; then
|
||||
# shellcheck source=/dev/null
|
||||
. "$SPS_SKEL/.venv/bin/activate"
|
||||
fi
|
||||
|
||||
# Python-Interpreter bestimmen
|
||||
PY="python3"
|
||||
command -v python3 >/dev/null 2>&1 || PY="python"
|
||||
|
||||
exec "$PY" "$SKEL_LIB/e_planning.py" "$@"
|
||||
@@ -0,0 +1,81 @@
|
||||
; ================================================================
|
||||
; e_planning.ini - Regeln fuer lib/e_planning.py
|
||||
; ================================================================
|
||||
; Ordnet die Sensoren/Ventile der E-Planung (*_positionsdraw.json des
|
||||
; Kabellaengen-Tools) den Bauteilen des Layouts (CSV) zu. Hintergrund und
|
||||
; Befunde: doc/E-Planung_Bruecke.md.
|
||||
;
|
||||
; [Registrierung]
|
||||
; anker VERW-Muster der E-Symbole, die auf einem Separator sitzen;
|
||||
; daraus wird der Lagebezug E-Zeichnung -> CSV ermittelt.
|
||||
;
|
||||
; Jeder Abschnitt "<Art>.<Rolle>" ist eine Regel; sie werden in dieser
|
||||
; Reihenfolge angewendet, ein E-Bauteil wird hoechstens einmal vergeben.
|
||||
; layout Muster fuer die Layout-Art (Separator, Ausschleus,
|
||||
; Einschleus, Weiche, Scanner, Beladung, Entladung).
|
||||
; Standard: der Teil vor dem Punkt im Abschnittsnamen.
|
||||
; verw Muster fuer das E-Attribut VERW (fnmatch, Komma-Liste,
|
||||
; Gross-/Kleinschreibung egal)
|
||||
; praefix BMK-Praefix (BG, MB ...), leer = egal
|
||||
; max_mm raeumlich: Hoechstabstand nach dem Lagebezug in mm
|
||||
; soll_mm raeumlich: Sollabstand - das Symbol liegt immer gleich weit
|
||||
; neben dem Bauteil; gewertet wird die Abweichung vom Soll
|
||||
; (nicht der kleinste Abstand), zulaessig bis toleranz_mm
|
||||
; toleranz_mm zulaessige Abweichung vom Sollabstand (Standard 150)
|
||||
; nummer_von Nummernregel: BMK-Nummer = Nummer der genannten Rolle
|
||||
; nummer_versatz ... plus dieser Versatz (Mubea: Ventil = Sensor + 1)
|
||||
; Mit nummer_von wird zuerst die Nummer versucht, danach (wenn max_mm oder
|
||||
; soll_mm gesetzt) der Abstand fuer die uebrigen Bauteile.
|
||||
;
|
||||
; Lieber eine Luecke als ein falscher Name: was keine Regel sicher trifft,
|
||||
; bleibt in der SCL TODO(E-Planung) und steht im Bericht <csv>_eplan.md.
|
||||
;
|
||||
; Die VERW-Texte und die Nummernregel sind projektabhaengig. Die Werte hier
|
||||
; stammen aus Mubea (500573); in den Referenz-SCL (UH01..UH05) haben Sensor
|
||||
; und Stopper dieselbe Nummer (BG3242/MB3242) -> dort nummer_versatz = 0.
|
||||
; ================================================================
|
||||
|
||||
[Registrierung]
|
||||
anker = IN SEP
|
||||
|
||||
; Sensor im Stopper -> stInSeparator<n>.stSenInSep
|
||||
; sitzt in der Zeichnung auf dem Separator (Mubea Speicher: 0-1 mm).
|
||||
; An den Kreiseln liegen Sensoren 200-550 mm daneben, teils naeher an
|
||||
; Aus-/Einschleuselementen - unsicher, deshalb bewusst eng.
|
||||
[Separator.sensor]
|
||||
verw = IN SEP
|
||||
praefix = BG
|
||||
max_mm = 300
|
||||
|
||||
; Stopperventil -> xOutStopper<n>
|
||||
; liegt ~270 mm neben dem Separator, im 800-mm-Raster der Speicherlinien
|
||||
; naeher am Nachbarn als ueber die Nummer -> Nummernregel zuerst
|
||||
[Separator.stopper]
|
||||
verw = SEP
|
||||
praefix = MB
|
||||
nummer_von = Separator.sensor
|
||||
nummer_versatz = 1
|
||||
max_mm = 600
|
||||
soll_mm = 270
|
||||
|
||||
; Stausensor hinter dem Ausschleuselement (Mubea: konstant ~1390 mm)
|
||||
[Ausschleus.stau]
|
||||
verw = STAU*
|
||||
praefix = BG
|
||||
max_mm = 1600
|
||||
soll_mm = 1390
|
||||
|
||||
; Ausschleusventil (Mubea: konstant ~1414 mm, Nummer = Stausensor + 1)
|
||||
[Ausschleus.ventil]
|
||||
verw = AE ABZW*
|
||||
praefix = MB
|
||||
nummer_von = Ausschleus.stau
|
||||
nummer_versatz = 1
|
||||
max_mm = 1600
|
||||
soll_mm = 1414
|
||||
|
||||
; Weichenventil an einer Kreisel-Weiche
|
||||
[Weiche.ventil]
|
||||
verw = ABZWEIG-*, WEICHE*
|
||||
praefix = MB
|
||||
max_mm = 1000
|
||||
@@ -0,0 +1,246 @@
|
||||
# Datenquellen für TROs und SCL: mechanisches Layout und Hardwareplanung
|
||||
|
||||
Stand 2026-10-11. Welche Angaben für die TRO-Liste und die erzeugten `.scl`-Bausteine
|
||||
aus dem **mechanischen Layout** kommen, welche aus der **Hardwareplanung** (EA-Liste
|
||||
und TIA-Import), und was als **Defaultvorbelegung** gesetzt wird. Die vollständige
|
||||
Liste der Rohangaben beider Quellen (Spalten, Merkmale, Attribute) steht in
|
||||
`doc/Klassenhierarchie.md`, Kapitel "Daten", Abschnitte "Angaben 1" und "Angaben 2";
|
||||
die Zuordnung jedes SCL-Parameters zu seiner Quelle in den Tabellen danach.
|
||||
Wie beide Quellen künftig über eine gemeinsame JSON zum SCL werden:
|
||||
`doc/Konzept_Layout_Hardware_SCL.md`.
|
||||
|
||||
## Die drei Quellen
|
||||
|
||||
| Quelle | Dateien | Liefert im Kern |
|
||||
|---|---|---|
|
||||
| **Mechanisches Layout** (Anlagen-Hardware) | CSV-Export ILS 2.0 (`data/*.csv`), aus der BricsCAD-Layoutzeichnung | **Struktur**: welche Bauteile, wer folgt auf wen, Drehrichtung, Längen und Höhen, Anzahl Separatoren/Scanner |
|
||||
| **Hardwareplanung** (E-Planung) | `<projekt>-<n>_EA.xlsx`, `<projekt>-<n>_TIA.xlsx` (PLC Tags + Constants), `<projekt>-<n>_WSCAD.xlsx`, `<zeichnung>_positionsdraw.json` | **Namen und Adressen**: jedes Signal mit BMK, Adresse, Index-Konstante, Rolle, Steuerung, Schaltschrank; mit `positionsdraw` auch die Lage |
|
||||
| **Defaultvorbelegung** | Typ-Katalog (`lib/tro_catalog.py`), Namensregeln (`scl_context.py`), Projektkonstanten | alles, was weder Layout noch E-Planung kennen: Zeiten, Konstanten-Namen nach Regel, Freigaben, WCS-Ziele |
|
||||
|
||||
Die Schlüssel zwischen den Quellen:
|
||||
|
||||
```text
|
||||
Layout-Bauteil (TeileId) --[Lage, e_planning.py]--> E-Symbol (BMK, positionsdraw)
|
||||
E-Symbol (BMK) --[Name]-----------------> TIA PLC Tag (Adresse, Kommentar)
|
||||
--[cIn<BMK>]--------------> TIA Constant (Index in DB_Inputs)
|
||||
E/A-Adresse --[Anschluss]-------------> WSCAD Bezug (Feldstation, Karte)
|
||||
```
|
||||
|
||||
EA-Liste und TIA-Import allein haben **keine Lage**, `positionsdraw` hat **keine
|
||||
Adresse**. Erst über das BMK als gemeinsamen Schlüssel wird aus "Sensor neben
|
||||
Separator 0008" ein vollständiger SCL-Eintrag mit Adresse und Indexkonstante.
|
||||
|
||||
## Was die Hardwareplanung liefert
|
||||
|
||||
### Für die TROs
|
||||
|
||||
| Angabe | aus | Regel |
|
||||
|---|---|---|
|
||||
| Signal gehört zu welchem Layout-Bauteil | `positionsdraw` `pos` | räumlich, `cfg/e_planning.ini` (heute umgesetzt) |
|
||||
| Rolle des Signals im TRO | `positionsdraw` `VERW`; ersatzweise Kommentar der EA/TIA-Liste | `IN SEP` → Sensor im Separator, `SEP` → Stopperventil, `STAU_REF-LT` → Stausensor, `AE ABZWEIG` → Ausschleusventil, `BT BELADEN` → Beladesensor, `MT in Position`/`ABFR.FINGER`/`conveyor full` → Vario |
|
||||
| Welche optionalen Sensoren verbaut sind | vorhanden / nicht vorhanden je Rolle | ergibt die `xPlugged*`-Schalter |
|
||||
| Fördereinheit (M-Nummer) | Kommentar `(ILS-CV M0108)`, `BEZEICHNUNG` `KR-K0102` | Instanzname des Antriebs (`#fbCarousel0102`), Name der Staubereiche (`DB_JamArea0102`) |
|
||||
| Steuerung, TRO-Nummernkreis | `KENNZEICHNUNG` `=A1+UH01-KF102`, Dateinummer `-<n>`, `mappings` | `UH01` → TRO 1xx |
|
||||
| Schaltschrank / Bedienfeld | `KENNZEICHNUNG`, WSCAD `Bezug`, `schaltschrank_elemente` | `stInOutMachineState`, Taster `SF…` |
|
||||
| Motor ↔ Motorschutz | `MA`/`FC` mit gleicher Nummer | `xOutVarioMotor`, `xInMotorProtection` |
|
||||
| Safety-Signale | WSCAD `Bezug` auf F-Karte (`KF1FDQ…`) | Not-Halt-Kreis, nur Hinweis |
|
||||
| Plausibilisierung des TRO-Typs | Anzahl "in Separator n", "AE Abzweig", "Pin Abfrage" je M-Nummer | Gegenprüfung des aus dem Layout abgeleiteten Typs |
|
||||
|
||||
### Für die SCL-Dateien
|
||||
|
||||
| Ziel | aus | Bemerkung |
|
||||
|---|---|---|
|
||||
| Namen in `FB_Main` (`=> "MB3306"`, `:= "FC0145"`, `"SF0002"`) | TIA PLC Tags `Name` | 1:1 |
|
||||
| Sensor-Indizes `"DB_Inputs".Sensors["cInBG3306"]` | TIA Constants `cIn<BMK>` | 1:1; jede Konstante, die der Generator schreibt, muss in diesem Blatt stehen → **Prüfung möglich** |
|
||||
| `FB_CallSensors`, Größe `DB_Inputs` | alle `cInBG…`/`cInFC…` mit `Value` | noch nicht generiert |
|
||||
| Variablen- und Konstantentabelle im TIA Portal | der TIA-Import selbst | wird nicht generiert, sondern importiert |
|
||||
| `stInOutMachineState`, `xInAutomatic`, `xInErrorQuit` | Schrankelemente `SF…`, `KENNZEICHNUNG` | heute `TODO(E-Planung)` |
|
||||
| `xInSftyOk` | Safety-Programm; WSCAD nur als Hinweis | heute `TODO(E-Planung)` |
|
||||
| Scanner `BX…`, Cognex-Variablen | **nicht** in EA/TIA-Tagliste (PROFINET) | TIA-Gerätekonfiguration oder `positionsdraw` mit `BX` |
|
||||
|
||||
## Was das mechanische Layout liefert
|
||||
|
||||
### Für die TROs
|
||||
|
||||
| Angabe | aus | Regel / Modul |
|
||||
|---|---|---|
|
||||
| Existenz und Grenzen der TROs | Separatoren (`ILS 2.0 Separator`) und ihr Host (`Zuordnung`, `TrackIds`) | `tro_flow.analyse_tros()` |
|
||||
| TRO-Typ und damit FB | Host Kreisel/Strecke/Gefälle, Weiche, Aus-/Einschleuselement, Scanner | `doc/500573_Mubea/TRO_Identifikation_500573.md`, `tro_catalog` |
|
||||
| Anzahl Separatoren, Weichen, Scanner je TRO | `Zuordnung`, `Naechster Separator`, Weichen-Merkmale | Parameterblöcke `stInSeparator<n>`, `stInSwitch<n>` |
|
||||
| Vorgänger/Nachfolger | `Nachbarn` (mit Bahn `-L`/`-R`), `TrackIds`, `Drehrichtung` | `material_flow` → Kanten, Ein-/Ausgangs-Staubereiche |
|
||||
| Systemgrenzen | `BTMT Beladung`/`Entladung`, `Eingang`/`Ausgang` | `SYSn`, `xIsBorner` |
|
||||
| Ziele je Weichenabgang | Erreichbarkeit im TRO-Graphen | `nTo1Destinations`, Zielliste |
|
||||
| Vorfahrt nötig | Einschleuselement / zwei Ströme in einen Staubereich | `xPrioDir`, `xReleaseOk` |
|
||||
| Tragende Fördertechnik | Host-Kreisel bzw. Gefälle | `xInCarouselRun1/2` |
|
||||
| angetriebene Strecke | `L_VF_m`, `Antriebfahrtrichtung` `Auf` | Vario |
|
||||
| Längen | `L_VF_m`, `L_GF_m`, Kreiselabstand | Grundlage für `tConvEmpty`, Staulängen |
|
||||
|
||||
### Für die SCL-Dateien
|
||||
|
||||
REGION-Reihenfolge, FB-Typ, Instanzname, Zahl und Reihenfolge der Parameterzeilen,
|
||||
Staubereichs-Verkettung (Exit von A = Entr von B), `FC_Direction`-Aufruf mit dem
|
||||
Carrier aus dem Eingangs-Staubereich, `nTo1Destinations`, Cognex-Dummy bei TROs ohne
|
||||
Scanner. Das ist heute der Teil, den `scl_skeleton.py` schon ohne E-Planung füllt.
|
||||
|
||||
## Überschneidungen: gegenseitige Prüfung
|
||||
|
||||
Beide Quellen beschreiben dieselbe Anlage, aber unabhängig voneinander. Wo sie sich
|
||||
überschneiden, prüft die eine die andere:
|
||||
|
||||
| Angabe | Layout | Hardwareplanung |
|
||||
|---|---|---|
|
||||
| Separatoren je Strecke/Kreisel | `Anzahl_Separator`, `Zuordnung` | Anzahl `IN SEP`/"in Separator n" je M-Nummer |
|
||||
| Weiche/Abzweig vorhanden | `ILS Weiche`, `ILS Ausschleuselement` | `AE ABZWEIG`, `ABZWEIG-K104`, `ES branch` |
|
||||
| Scanner | `ILS 2.0 Scanner` | `BX…` (nur in `positionsdraw` mancher Projekte) |
|
||||
| angetriebene Strecke | `L_VF_m`, `Auf` | `MA…`/`FC…` mit Bezeichnung der Strecke |
|
||||
| Lage | `Position` | `pos` (nach Lagebezug `dxf_registration`) |
|
||||
|
||||
Ein Widerspruch (z. B. Layout 6 Separatoren, E-Planung 5 `IN SEP`) heißt meist:
|
||||
unterschiedliche Planungsstände. Er gehört in den Bericht `<csv>_eplan.md`, nicht
|
||||
stillschweigend in die SCL.
|
||||
|
||||
## Beispielzeilen mit Herkunft
|
||||
|
||||
Zwei reale Aufrufstellen aus `doc/TRO_Katalog/scl_templates/` (UH01), Zeile für Zeile
|
||||
mit der Quelle als Kommentar:
|
||||
|
||||
- **Layout** = mechanisches Layout (CSV)
|
||||
- **HW-Planung** = Hardwareplanung (EA-Liste, TIA-Import, WSCAD, `positionsdraw`)
|
||||
- **Default** = Defaultvorbelegung (Typ-Default, Namensregel, Projektkonstante)
|
||||
|
||||
Bei zwei Quellen steht die führende zuerst: "HW-Planung + Layout" heißt, der Name
|
||||
kommt aus der Hardwareplanung, die Zuordnung zu diesem TRO bzw. Ausgang aus dem
|
||||
Layout. Der Status heute (Generator setzt / `TODO(E-Planung)`) steht in den
|
||||
Tabellen von `doc/Klassenhierarchie.md`.
|
||||
|
||||
Die Codeblöcke sind als `pascal` ausgezeichnet: Markdown-Renderer (VS Code,
|
||||
GitHub, Gitea) kennen kein `scl`, und SCL ist Pascal-basiert. So werden
|
||||
Schlüsselwörter, Zuweisungen, Zahlen und `//`-Kommentare hervorgehoben.
|
||||
|
||||
### `FB_ILS_MTRO_1Sep1Swi` (TRO 107, Separator mit Weiche am Kreisel)
|
||||
|
||||
```pascal
|
||||
REGION TRO 107(1Sep1Swi) // Layout: Typ aus Separator am Kreisel + Ausschleuselement; Nummer 107: Default (Nummernkreis UH01 aus HW-Planung)
|
||||
#TRO107.stInSeparator1.Settings.nSeparatorNo := "cSep107.1"; // Default: Namensregel cSep<TRO>.<n>; Anzahl Separatoren: Layout
|
||||
#TRO107.stInSeparator1.Settings.tDelayToNextItem := "TimeForSepWaiting_Short"; // Default; Short/Medium je Host (Kreisel/Strecke): Layout
|
||||
#TRO107.stInSeparator1.Settings.tTrailingTime := t#100ms; // Default: Typ-Default, bei IBN prüfen
|
||||
#TRO107.stInSeparator1.Settings.tHandlingTime := t#50ms; // Default: Typ-Default, bei IBN prüfen
|
||||
#TRO107.stInSeparator1.Settings.tJam := "TimeForSepJam"; // Default: Projektkonstante
|
||||
#TRO107.stInSeparator1.Settings.xPluggedJam := TRUE; // HW-Planung: Stausensor (VERW STAU_REF-LT) vorhanden
|
||||
#TRO107.stInSeparator1.Settings.xPluggedPart := FALSE; // HW-Planung: kein Beladesensor (VERW BT BELADEN)
|
||||
#TRO107.stInSeparator1.Settings.xPluggedScanner := FALSE; // Layout: kein ILS 2.0 Scanner mit "Naechster Separator" = dieser
|
||||
#TRO107.stInSeparator1.stSenInSep := "DB_Inputs".Sensors["cInBG3306"]; // HW-Planung + Layout: BG mit VERW IN SEP am Separator; cInBG3306 aus TIA Constants
|
||||
#TRO107.stInSenJam2 := "DB_Inputs".Sensors["cInBG3305"]; // HW-Planung + Layout: Stausensor; Ausgang 2 = Weiterfahrt auf dem Kreisel
|
||||
#TRO107.stInSenJam3 := "DB_Inputs".Sensors["cInBG3330"]; // HW-Planung + Layout: Stausensor; Ausgang 3 = Abzweig am Ausschleuselement
|
||||
|
||||
#TRO107.stInSwitch1.Settings.nSwitchNo := "cSwi107.1"; // Default: Namensregel cSwi<TRO>.<n>; Anzahl Weichen: Layout
|
||||
#TRO107.stInSwitch1.Settings.xPluggedExSen3 := FALSE; // HW-Planung: Abgangssensor Exit 3 verbaut?
|
||||
#TRO107.stInSwitch1.Settings.xPluggedExSen4 := FALSE; // HW-Planung: Abgangssensor Exit 4 verbaut?
|
||||
#TRO107.stInSwitch1.Settings.nTo1Destinations := 3; // Layout: über Abgang 1 erreichbare Ziele (reachable_via)
|
||||
|
||||
#TRO107.stInSettings.xPrioDir2 := TRUE; // Layout: Zusammenführung vor Ausgang 2 -> Vorfahrt nötig
|
||||
#TRO107(stInPriorityManagerDir2 := "DB_Prozessdaten".stPriorityManager.udt_TRO107.TroIn, // Default: Namensregel udt_TRO<n>; ob nötig: Layout
|
||||
stOutPriorityManagerDir2 => "DB_Prozessdaten".stPriorityManager.udt_TRO107.TroOut, // Default: Namensregel
|
||||
nInMainTroNo := "cMainTro107", // Default: Namensregel cMainTro<n>
|
||||
xInSftyOk := "DB_InterfaceSafety".stFromSafety.EStopMainOF1.Q, // HW-Planung: Not-Halt-Kreis OF1 (Safety-Programm, F-Karten im WSCAD)
|
||||
xInAllRdyToStart := TRUE, // Default
|
||||
xInCarouselRun1 := #fbCarousel0102.xOutConveyorFullSpeed, // Layout: Host-Kreisel; Name 0102: HW-Planung (KR-K0102, MA/FC-Bezeichnung)
|
||||
xInRelease := TRUE, // Default: Freigabe/Testlogik
|
||||
stInOutMachineState := #stInOutControlUnitCabinetOF1, // HW-Planung: Schaltschrank OF1 (KENNZEICHNUNG, WSCAD Bezug)
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy, // Layout: kein Scanner am TRO
|
||||
arInOutJamEntr1 := "DB_JamArea0102".stJam0102_1.arCarrier, // Layout: Vorgänger-Staubereich; DB-Name 0102: HW-Planung (Kreisel K0102) + Default (Index _1)
|
||||
stInOutJamEntr1 := "DB_JamArea0102".stJam0102_1.stData, // wie oben
|
||||
arInOutJamExit2 := "DB_JamArea0103".stJam0103_1.arCarrier, // Layout: Nachfolger an Ausgang 2; DB-Name: HW-Planung + Default
|
||||
stInOutJamExit2 := "DB_JamArea0103".stJam0103_1.stData, // wie oben
|
||||
arInOutJamExit3 := "DB_JamArea0102".stJam0102_2.arCarrier, // Layout: Nachfolger an Ausgang 3; DB-Name: HW-Planung + Default
|
||||
stInOutJamExit3 := "DB_JamArea0102".stJam0102_2.stData, // wie oben
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO107, // Default: Namensregel
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro107"], // Default: Namensregel
|
||||
xOutStopper1 => "MB3306", // HW-Planung: MB mit VERW SEP, Nummernregel ab BG3306 (TIA PLC Tag)
|
||||
xOutSw1ExTo3 => "MB3304"); // HW-Planung + Layout: MB AE ABZWEIG am Ausschleuselement von Ausgang 3
|
||||
|
||||
REGION Direction
|
||||
"FC_Direction"(nInSwitchNo := "cSwi107.1", // Default: Namensregel
|
||||
nOutDirection => #TRO107.nInDirection, // Default: fest
|
||||
InOutCarrier := "DB_JamArea0102".stJam0102_1.arCarrier[1]); // Layout: Carrier aus dem Eingangs-Staubereich
|
||||
|
||||
END_REGION
|
||||
END_REGION
|
||||
```
|
||||
|
||||
### `FB_ILS_MTRO_Vario` (TRO 115, angetriebene Strecke mit Separator)
|
||||
|
||||
```pascal
|
||||
REGION TRO 115 (Vario) // Layout: Strecke mit L_VF_m / Antriebfahrtrichtung "Auf"; HW-Planung bestätigt (MA, MT in Position)
|
||||
#TRO115.stInSeparator1.Settings.nSeparatorNo := "cSep115.1"; // Default: Namensregel; Anzahl Separatoren: Layout
|
||||
#TRO115.stInSeparator1.Settings.tDelayToNextItem := "TimeForSepWaiting_Short"; // Default; Wahl je Host: Layout
|
||||
#TRO115.stInSeparator1.Settings.tTrailingTime := t#800ms; // Default: Typ-Default Vario, bei IBN prüfen
|
||||
#TRO115.stInSeparator1.Settings.tHandlingTime := t#300ms; // Default: Typ-Default Vario, bei IBN prüfen
|
||||
#TRO115.stInSeparator1.Settings.tJam := "TimeForSepJam"; // Default: Projektkonstante
|
||||
#TRO115.stInSeparator1.Settings.xPluggedJam := FALSE; // HW-Planung: kein Stausensor am Separator
|
||||
#TRO115.stInSeparator1.Settings.xPluggedPart := FALSE; // HW-Planung: kein Beladesensor
|
||||
#TRO115.stInSeparator1.Settings.xPluggedScanner := FALSE; // Layout: kein Scanner
|
||||
#TRO115.stInSeparator1.stSenInSep := "DB_Inputs".Sensors["cInBG3025"]; // HW-Planung + Layout: BG IN SEP am Separator; Index aus TIA Constants
|
||||
|
||||
#TRO115.stInSeparator1.Settings.tSenFree := t#2000ms; // Default: Typ-Default
|
||||
#TRO115.stInSeparator1.Settings.tSenWait := T#5000ms; // Default: Typ-Default
|
||||
|
||||
#TRO115.stInVario.Settings.xContinousEmptying := FALSE; // Default: Prozessentscheidung
|
||||
#TRO115.stInVario.Settings.tWaitForJog := T#300ms; // Default: Typ-Default
|
||||
#TRO115.stInVario.Settings.tConvEmpty := T#45000ms; // Layout: Förderlänge L_VF_m / Geschwindigkeit (Default) -> heute fest
|
||||
#TRO115.stInVario.Settings.nCountCarrier := 1; // Default: Prozessentscheidung
|
||||
#TRO115.stInVario.Settings.tWaitForStop := t#3s; // Default: Typ-Default
|
||||
|
||||
#TRO115.stInVario.Settings.tCarrierInPosDelay := t#1200ms; // Default: Typ-Default
|
||||
#TRO115.stInVario.Settings.tCarrierInPosEdgeDelay := t#100ms; // Default: Typ-Default
|
||||
|
||||
#TRO115.stInVario.Settings.tRasterHigh := t#5s; // Default: Typ-Default
|
||||
#TRO115.stInVario.Settings.tRasterLow := t#5s; // Default: Typ-Default
|
||||
|
||||
#TRO115.stInVario.stSenCarrInPos := "DB_Inputs".Sensors["cInBG3026"]; // HW-Planung: VERW "MT in Position" an dieser Strecke
|
||||
#TRO115.stInVario.stSenFinger := "DB_Inputs".Sensors["cInBG3027"]; // HW-Planung: VERW "Finger in position" / ABFR.FINGER
|
||||
#TRO115.stInVario.stSenLastPos := "DB_Inputs".Sensors["cInBG3000"]; // HW-Planung: VERW "conveyor full"
|
||||
#TRO115.stInVario.stSenJam := "DB_Inputs".Sensors["cInBG3001"]; // HW-Planung: VERW "Jam detector" / STAU_REF-LT
|
||||
|
||||
#TRO115.stInPriorityManager.xReleaseOk := TRUE; // Layout: keine Zusammenführung -> keine Vorfahrt
|
||||
#TRO115.stInVario.Settings.xJamSenDelayActive := TRUE; // Default (nur sinnvoll, wenn HW-Planung einen Stausensor hat)
|
||||
#TRO115.stInVario.Settings.tJamSenDelay := T#2s; // Default: Typ-Default
|
||||
|
||||
#TRO115.stInSettings.xIsBorner := TRUE; // Layout: erster TRO nach Systemgrenze (Beladung/Eingang)
|
||||
|
||||
#TRO115(nInMainTroNo := "cMainTro115", // Default: Namensregel
|
||||
xInSftyOk := "DB_InterfaceSafety".stFromSafety.EStopMainOF1.Q, // HW-Planung: Not-Halt-Kreis
|
||||
xInMotorProtection := "FC0145", // HW-Planung: FC mit Nummer des Motors (schaltschrank_elemente, TIA "Meldekontakt Motorschutzschalter")
|
||||
xInAllRdyToStart := TRUE, // Default
|
||||
xInReset := "cTodoFalse", // Default
|
||||
xInErrorQuit := "SF0002", // HW-Planung: Quittiertaster (SF, Schrank)
|
||||
xInAutomatic := #t_Automatik_OF1 AND "SF0120", // HW-Planung: Schlüsselschalter SF0120; Verknüpfung: Default
|
||||
xInRelease := "DB_TRO_TEST".TRO115, // Default: Test-/IBN-Freigabe
|
||||
arInOutJamEntr := "DB_JamArea4104".stJam4104_1.arCarrier, // Layout: Vorgänger-Staubereich; Nummer: HW-Planung (M-Nummer) + Default
|
||||
stInOutJamEntr := "DB_JamArea4104".stJam4104_1.stData, // wie oben
|
||||
arInOutJamFinger := "DB_JamArea4103".stJam4103_1.arCarrier, // Layout: Staubereich auf der Vario-Strecke selbst; Nummer: HW-Planung + Default
|
||||
stInOutJamFinger := "DB_JamArea4103".stJam4103_1.stData, // wie oben
|
||||
stInOutMachineState := #stInOutControlUnitCabinetOF1, // HW-Planung: Schaltschrank OF1
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy, // Layout: kein Scanner
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO115, // Default: Namensregel
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro115"], // Default: Namensregel
|
||||
xOutStopper => "MB3025", // HW-Planung: MB VERW SEP, Nummernregel ab BG3025
|
||||
xOutVarioMotor => "MA0145"); // HW-Planung + Layout: MA an dieser angetriebenen Strecke (Lage bzw. Bezeichnung)
|
||||
END_REGION
|
||||
```
|
||||
|
||||
### Auszählung der beiden Beispiele
|
||||
|
||||
| Führende Quelle | 1Sep1Swi (39 Zeilen) | Vario (47 Zeilen) |
|
||||
|---|---|---|
|
||||
| Layout | 13 | 10 |
|
||||
| HW-Planung | 11 | 14 |
|
||||
| Default | 15 | 23 |
|
||||
|
||||
Gezählt sind alle kommentierten Zeilen (Zuweisungen und REGION-Kopf) nach der zuerst
|
||||
genannten Quelle; "wie oben" zählt wie die Zeile davor. Die Struktur (welche Zeilen es gibt, wie oft, in welcher Reihenfolge) kommt
|
||||
immer aus dem Layout; die Tabelle zählt, woher der **Wert** einer Zeile kommt. Der
|
||||
Großteil der Defaults sind Namensregeln und Zeiten, die nach dem Typ-Katalog
|
||||
vorbelegt und bei der Inbetriebnahme geprüft werden. Fast alle HW-Planung-Zeilen
|
||||
lassen sich aus TIA-Import und `positionsdraw` füllen; offen bleiben `xInSftyOk`
|
||||
(Safety-Programm) und die Scanner (PROFINET, nicht im Tag-Import).
|
||||
@@ -0,0 +1,161 @@
|
||||
# Brücke Layout ↔ E-Planung über `*_positionsdraw.json`
|
||||
|
||||
Stand 2026-10-10. Erst Machbarkeitsprüfung, inzwischen umgesetzt als
|
||||
`lib/e_planning.py` (Regeln `cfg/e_planning.ini`, Aufruf `bin/e_planning.bat`,
|
||||
`scl_skeleton --eplan`); Bedienung in `doc/Python_Scripts.md` §5c, Stand der
|
||||
Umsetzung unten.
|
||||
|
||||
## Problem
|
||||
|
||||
Das Kapitel "Daten" in `doc/Klassenhierarchie.md` zeigt: das Hardwarelayout (CSV)
|
||||
liefert die **Struktur** (welche TROs, welche Separatoren/Weichen, wer mit wem), die
|
||||
E-Planung die **Namen und Adressen** (`BG…`, `MB…`, `MA…`, `FC…`). Die EA-Liste allein
|
||||
kennt aber keine Topologie. Es fehlt die Zuordnung **Layout-Bauteil (CSV-`TeileId`) →
|
||||
Betriebsmittelkennzeichen (BMK)**.
|
||||
|
||||
## Idee
|
||||
|
||||
Die E-Planung wird als Layer über der Hardwareplanung gezeichnet: Sensor- und
|
||||
Aktorsymbole liegen in der Zeichnung neben dem Bauteil, zu dem sie gehören. Das
|
||||
Kabellängen-Tool (`C:\10-Develop\gitrepos\kabellaengen`, `lib/getpositions.py`)
|
||||
schreibt diese Symbole mit Position und Attributen nach `<zeichnung>_positionsdraw.json`.
|
||||
Damit ist die Brücke eine **räumliche Zuordnung**: E-Symbol → nächstes Layout-Bauteil
|
||||
passender Art.
|
||||
|
||||
## Datenquelle `*_positionsdraw.json`
|
||||
|
||||
| Schlüssel | Inhalt | Nutzen für sps_skel |
|
||||
|---|---|---|
|
||||
| `sensors` | je BMK (`BG0642@UNKNOWN`): `pos` [x, y], `VERW` (Verwendung), `BEZEICHNUNG` (Klartext), `BESCHR`, `ARTINR`, `KENNZEICHNUNG` (`=A1+UH01-KF107`) | Hauptquelle: Sensoren/Ventile/Motoren im Feld |
|
||||
| `schaltschrank_elemente` | Elemente im Schrank (Motorschutz `FC…`, Taster, Leuchten, `QA…`) mit `VERW`, `BEZEICHNUNG`, `KENNZEICHNUNG`, ohne Feldposition | Motorschutz ↔ Motor, Bedienelemente |
|
||||
| `mappings` | Steuerung → Liste ihrer BMK (`UH01: [...]`) | Steuerungsaufteilung, Nummernkreis |
|
||||
| `racks`, `distributors`, `tunnels` | Kabeltrassen, Verteiler | für sps_skel ohne Belang |
|
||||
| `double_ids`, `warnings` | doppelte BMK, fehlende Attribute | Qualitätsprüfung der E-Daten |
|
||||
|
||||
Die Erdungs-Beispiele (`PR500501_…_Erdung_…`, `ST500652_…`) enthalten nur
|
||||
Potentialausgleich (`POT…`) und taugen nicht für die Brücke. Brauchbar sind
|
||||
Dateien mit `BG`/`MB`/`MA`: `Mubea_positionsdraw.json` (157 Feld-BMK),
|
||||
`ST500592_ILS_positionsdraw.json` (~3100), `ST500592_10_5-10_Omniflo_…` (~900),
|
||||
`ST-Fortna_positions_.json` (mit 38 Scannern `BX`).
|
||||
|
||||
## Test an Mubea
|
||||
|
||||
Eingaben: `data/mubea.csv` (Layout, 38 Separatoren) und
|
||||
`kabellaengen/work/Mubea_positionsdraw.json` (E-Planung). Die Zahlen in diesem
|
||||
Abschnitt stammen aus der ersten Untersuchung (Umkreis 700 mm, ohne Regeln); das
|
||||
Ergebnis der Umsetzung steht unten.
|
||||
|
||||
### 1. Lagebezug
|
||||
|
||||
Die Ermittlung aus `lib/dxf_registration.py` (`derive_transform`) funktioniert
|
||||
unverändert, wenn man die E-Symbole mit `VERW` `SEP`/`IN SEP` als Rolle "Separator"
|
||||
einsetzt:
|
||||
|
||||
```text
|
||||
mubea.csv <- Mubea_positionsdraw.json: dx +50613.6 mm, dy +8555.8 mm, Drehung 0
|
||||
26/38 Separatoren im Toleranzband
|
||||
```
|
||||
|
||||
Das entspricht dem schon gespeicherten Bezug `mubea.csv|500573_60_1.dxf`
|
||||
(dx +50833.7, dy +8414.5) bis auf ~250 mm: die E-Zeichnung liegt im selben
|
||||
Koordinatensystem wie die Layoutzeichnung. Zum Prüfen genügt also der
|
||||
bestehende Mechanismus; als Schlüssel in `cfg/dxf_registration.json` kann
|
||||
`<csv>|<positionsdraw.json>` dienen.
|
||||
|
||||
### 2. Was sich zuordnen lässt
|
||||
|
||||
| E-Symbol (`VERW`) | Layout-Partner | Abstand | Ergebnis |
|
||||
|---|---|---|---|
|
||||
| `IN SEP` (BG, Sensor im Separator) | Separator | **0–1 mm** im Pin-Speicher (20/20), sonst 200–500 mm | Anker: das Symbol sitzt auf dem Separator |
|
||||
| `SEP` (MB, Stopperventil) | Separator | konstant **~270 mm** daneben | eindeutig über Nummernregel (unten), nicht über Abstand |
|
||||
| `AE ABZWEIG` (MB, Ausschleusventil) | Ausschleuselement | konstant **1414 mm** (20/20, Versatz (−1405, −159)) | eindeutig, wenn der Versatz je Symbolart bekannt ist |
|
||||
| `STAU_REF-LT` (BG, Stausensor) | Ausschleuselement | konstant **1391 mm** (20/20) | wie oben |
|
||||
| `ABZWEIG-K104` (MB, Weiche) | Weiche | 783 mm | eindeutig (nur eine Weiche) |
|
||||
| `BANDBRUCH`, `KETTENBRUCH`, `ÜBERGABE`/`ÜBERNAHME`, `SYNC P1..4`, `ZAPPLER` | Kreisel/Strecke/Station | uneinheitlich, teils > 50 m (Symbol an Konvejoren außerhalb des CSV-Ausschnitts) | nicht über Abstand, nur über `BEZEICHNUNG` |
|
||||
|
||||
Nebeneinander liegende Separatoren (Pin-Speicher, Raster 800 mm) machen den
|
||||
Abstand allein mehrdeutig: das MB von Strecke n liegt 270 mm von Separator n und
|
||||
530 mm von Separator n+1. Eindeutig wird es erst mit der Nummernregel.
|
||||
|
||||
### 3. Nummernregel der E-Planung
|
||||
|
||||
Ventil = Sensor + 1, gleiche Funktionsgruppe:
|
||||
|
||||
| Paar | Treffer (von 58 MB) |
|
||||
|---|---|
|
||||
| `MB(n)` `SEP` ↔ `BG(n−1)` `IN SEP` (z. B. BG0642 / MB0643) | 31 |
|
||||
| `MB(n)` `AE ABZWEIG` ↔ `BG(n−1)` `STAU_REF-LT` (z. B. BG0542 / MB0543) | 21 |
|
||||
| sonstige Paare (`VEREINZ(CP)`, `STOPPER (CP)`, `AE ABZW.`) | 3 |
|
||||
| ohne BG-Partner | 3 |
|
||||
|
||||
Damit reicht es, den **Sensor** räumlich zuzuordnen (der `IN SEP` liegt auf dem
|
||||
Separator); das Ventil folgt aus der Nummer. Die SCL-Referenzen zeigen dasselbe
|
||||
Muster (`stSenInSep := …["cInBG3242"]`, `xOutStopper1 => "MB3242"`) — dort sogar
|
||||
mit gleicher Nummer; die Regel ist also projektabhängig und gehört in eine
|
||||
Konfiguration, nicht in den Code.
|
||||
|
||||
Gleiches gilt für Motor ↔ Motorschutz: `MA0200` "PIN CONVEYOR#100" ↔
|
||||
`FC0200` "MOTORSCHUTZ CONVEYER #100" (gleiche Nummer, `schaltschrank_elemente`),
|
||||
wie in der SCL `xOutVarioMotor => "MA0145"`, `xInMotorProtection := "FC0145"`.
|
||||
|
||||
### 4. Was nicht passt
|
||||
|
||||
12 von 38 Layout-Separatoren (vor allem an den Kreiseln K0102/K0104/K0105) haben im
|
||||
Umkreis von 700 mm kein `IN SEP`/`SEP`; umgekehrt liegen mehrere `IN SEP` des
|
||||
Kreisels K0102 neben Aus-/Einschleuselementen statt neben einem Separator. Vermutlich
|
||||
ist die CSV (aus `500573_60_1.dxf`) ein anderer Planungsstand als die E-Zeichnung
|
||||
`Mubea.dxf` des Kabellängen-Tools, oder Kreisel-Separatoren sind im CSV anders
|
||||
modelliert. Das muss mit zueinander passenden Ständen geprüft werden, bevor man
|
||||
eine Quote angibt.
|
||||
|
||||
`Mubea_positionsdraw.json` enthält **keine Scanner** (`BX`). Für Scanner-Zuordnung
|
||||
ist ein anderes Projekt nötig (`ST-Fortna_positions_.json`, `ILS-Baugruppe`).
|
||||
|
||||
## Was die Brücke für das Kapitel "Daten" liefert
|
||||
|
||||
| SCL-Datum | aus `positionsdraw.json` | Regel |
|
||||
|---|---|---|
|
||||
| `stInSeparator<n>.stSenInSep` | BG mit `VERW` `IN SEP` | räumlich am Separator (Anker) |
|
||||
| `xOutStopper<n>` | MB mit `VERW` `SEP` | Nummernregel ab dem BG |
|
||||
| `stInSenJam<n>`, `Settings.xPluggedJam` | BG `STAU_REF-LT` | räumlich, Versatz je Symbolart; vorhanden ⇒ `xPluggedJam := TRUE` |
|
||||
| `xOutSw<n>ExTo3/4` | MB `AE ABZWEIG` / Weichen-MB | Nummernregel ab dem Stausensor bzw. räumlich an Weiche/Ausschleuselement |
|
||||
| `Settings.xPluggedPart`, `stSenPart` | BG `BT BELADEN` | räumlich; vorhanden ⇒ `TRUE` |
|
||||
| `xInMotorProtection`, `xOutVarioMotor` | MA im Feld + FC in `schaltschrank_elemente` | gleiche Nummer |
|
||||
| `xInCarouselRun` (welcher Antrieb) | FC/MA-`BEZEICHNUNG` "KREISEL-K0102", "CONVEYOR #100" | Textabgleich mit dem Host-Kreisel des TRO |
|
||||
| Steuerung, TRO-Nummernkreis, `stInOutMachineState` | `KENNZEICHNUNG` `=A1+UH01-KF107`, `mappings` | `UH01` = Steuerung, `KF107` = E/A-Station |
|
||||
| Plausibilisierung | `BEZEICHNUNG` "SEPARATOR STR. 14", "KR-K0102 VOR BT-BELADUNG" | Strecken-/Kreiselnummer gegen Layout prüfen |
|
||||
|
||||
Nicht aus dieser Quelle: Not-Halt-Kreis (`xInSftyOk`), Taster für Automatik/Quittieren
|
||||
(stehen nur als Schrankelemente ohne Bezug zum TRO), Scanner-Hardwarekennungen,
|
||||
Weichenbauart (`xNewSwitch`).
|
||||
|
||||
## Umsetzung (Stand 2026-10-10)
|
||||
|
||||
1. **`lib/e_planning.py`**: liest `*_positionsdraw.json` in `EDevice` (BMK, VERW,
|
||||
Bezeichnung, Kennzeichnung → Steuerung/E/A-Station, Position).
|
||||
2. **Lagebezug** über `dxf_registration.derive_transform` mit den `IN SEP`-Sensoren
|
||||
als Rolle "Separator"; abgelegt in `cfg/dxf_registration.json` unter
|
||||
`<csv>|<positionsdraw.json>`.
|
||||
3. **Regeln in `cfg/e_planning.ini`** (projektabhängig): je Rolle Layout-Art, VERW,
|
||||
Höchstabstand, **Sollabstand** (für die konstanten Versätze von Stausensor und
|
||||
Ausschleusventil — sonst gewinnt bei 0080/0084 ein näherer, fremder Stausensor
|
||||
des Kreisels K0102) und die Nummernregel (`MB = BG + 1`).
|
||||
4. **Ausgabe** `<csv>_eplan.json` (`EplanDocument`) und Bericht `<csv>_eplan.md`.
|
||||
`scl_skeleton --eplan` setzt Separator-Sensor und Stopperventil ein
|
||||
(`SclName.todo = False`); alles andere bleibt `TODO(E-Planung)`.
|
||||
|
||||
Ergebnis Mubea mit den ausgelieferten Regeln: Separator-Sensor 21/38,
|
||||
Stopper 22/38, Stausensor 20/23, Ausschleusventil 20/23, Weichenventil 1/1. Den
|
||||
Separator-Sensor nimmt die Regel nur bis 300 mm; die Kreisel-Fälle mit 470–540 mm
|
||||
(z. B. BG0532 an 0044) bleiben bewusst offen.
|
||||
|
||||
Offen:
|
||||
|
||||
- Stausensoren und Ausschleus-/Weichenventile stehen in JSON und Bericht, kommen
|
||||
aber noch nicht in die SCL: die TRO-JSON sagt nicht, welches Ausschleuselement
|
||||
bzw. welche Weiche zu welchem TRO-Ausgang gehört.
|
||||
- Die Lagerlinien eines `PinStore`-TRO (`arInSep[n]`) sind zugeordnet, der
|
||||
Generator erzeugt die Linienparameter aber noch nicht.
|
||||
- Scanner (`BX`) fehlen in den Mubea-Daten; Regel und Test mit
|
||||
`ST-Fortna_positions_.json` stehen aus.
|
||||
- Ein Abgleich mit passenden Ständen von CSV und E-Zeichnung für die Kreisel.
|
||||
+329
-4
@@ -31,6 +31,8 @@ scl_skeleton.py (Ausgabe: SclFile -> FB_Main.scl, generisch oder per Jin
|
||||
|
||||
material_flow.py dxf_registration.py (eigenständig, ohne Abhängigkeit auf obige)
|
||||
tro_overrides.py (importiert model.Connection / model.TroCandidate)
|
||||
e_planning.py (Brücke zur E-Planung; importiert dxf_registration, liest das
|
||||
CSV über material_flow; scl_context kennt nur EplanDocument)
|
||||
```
|
||||
|
||||
`tro_flow.py` und `tro_annotate.py` definieren keine eigenen Klassen (nur
|
||||
@@ -68,8 +70,11 @@ lib/model.py [Instanz-Ebene / TRO-JSON]
|
||||
├── TroBase(BaseModel)
|
||||
│ └─ definition() --> TroDefinition (gelesen aus tro_catalog, nicht gespeichert)
|
||||
│ └── DerivedTro(TroBase) [einzige echte Vererbung im Projekt]
|
||||
│ └─ slots: list[Slot] (Stellen fuer die Hardwareplanung)
|
||||
├── _Positioned(BaseModel) (Mixin: position {x,y,z}, describe_position)
|
||||
│ ├── TroBase, TroCandidate
|
||||
│ ├── TroBase, TroCandidate, JamArea
|
||||
├── Slot(BaseModel) (separator1/switch1/scanner1/drive + Rollen)
|
||||
├── JamArea(_Positioned) (Staubereich J<Sep>: Eingang eines TRO)
|
||||
├── Connection(BaseModel) (from/to/kind zwischen zwei TRO-IDs)
|
||||
├── TroCandidate(_Positioned) (gemeldete Stelle, an der ein TRO erwartet wird)
|
||||
├── Analysis(BaseModel)
|
||||
@@ -80,7 +85,8 @@ lib/model.py [Instanz-Ebene / TRO-JSON]
|
||||
├─ tros: list[DerivedTro]
|
||||
├─ boundaries: list[DerivedTro]
|
||||
├─ connections: list[Connection]
|
||||
└─ candidates: list[TroCandidate]
|
||||
├─ candidates: list[TroCandidate]
|
||||
└─ jam_areas: list[JamArea] (JSON: jamAreas)
|
||||
|
||||
lib/material_flow.py [CSV-Rohmodell -> Graph, @dataclass]
|
||||
├── Element (1 CSV-Zeile)
|
||||
@@ -132,6 +138,17 @@ lib/scl_context.py [Render-Kontext für die SCL, BaseM
|
||||
|
||||
lib/scl_skeleton.py [SCL-Ausgabe]
|
||||
└── SclTemplates (Ordner mit <fbType>.scl.j2, hält ein jinja2.Environment)
|
||||
|
||||
lib/e_planning.py [Brücke Layout <-> E-Planung]
|
||||
├── EDevice(BaseModel) (ein BMK aus *_positionsdraw.json: VERW, Bezeichnung, Position)
|
||||
├── LayoutPoint (@dataclass: punktartiges Layout-Bauteil in CSV-Koordinaten)
|
||||
├── Rule (@dataclass: eine Regel aus cfg/e_planning.ini)
|
||||
├── Assignment(BaseModel) (BMK in einer Rolle an einem Layout-Bauteil, mit Abstand)
|
||||
├── Drive(BaseModel) (Motor MA ↔ Motorschutz FC)
|
||||
└── EplanDocument(BaseModel) (= <csv>_eplan.json)
|
||||
├─ transform: Transform (aus dxf_registration)
|
||||
├─ assignments: list[Assignment]
|
||||
└─ drives: list[Drive]
|
||||
```
|
||||
|
||||
## Auffälligkeiten für ein mögliches Refactoring
|
||||
@@ -238,13 +255,14 @@ Kantenarten) → Prüfung gegen den Katalog.
|
||||
|
||||
### `TroDocument` — `lib/model.py`
|
||||
|
||||
**Wozu:** Die TRO-JSON `<csv>_tro.json` (Schema `sps_skel-tro-v3`) — die
|
||||
**Wozu:** Die TRO-JSON `<csv>_tro.json` (Schema `sps_skel-tro-v4`) — die
|
||||
Schnittstelle zwischen Ableitung (`tro_flow --json`) und SCL-Ausgabe
|
||||
(`scl_skeleton`). Weil beide Seiten dieselbe Klasse benutzen, kann das Format
|
||||
nicht unbemerkt auseinanderlaufen.
|
||||
|
||||
**Was passiert darin:** `from_analysis()` macht aus einer `Analysis` ein Dokument
|
||||
(Kanten + Kantenarten → `Connection`-Liste). `to_json()` schreibt mit den
|
||||
(Kanten + Kantenarten → `Connection`-Liste, je TRO die `slots` aus
|
||||
`derive_slots()`, die Staubereiche aus `derive_jam_areas()`). `to_json()` schreibt mit den
|
||||
JSON-Aliasen, `from_json()` liest und prüft. `extra="forbid"` sorgt dafür, dass ein
|
||||
umbenannter oder falscher Schlüssel als Fehler auffällt statt still einen
|
||||
Standardwert zu bekommen; `scl_skeleton.main` meldet bei falschem `schema`
|
||||
@@ -414,3 +432,310 @@ generischen Aufbau (`emit_region()`).
|
||||
**Nicht Produktion:** `doc/TRO_Katalog/scl_templates/*.scl` sind die realen
|
||||
Referenz-Aufrufstellen. Gegen sie prüft `tests/test_scl_templates.py` jedes
|
||||
Template im Round-Trip; in die erzeugte SCL fliessen sie nicht ein.
|
||||
|
||||
## Daten
|
||||
|
||||
Bestandsaufnahme aller Daten, die in den Referenz-Aufrufstellen und FB-Schnittstellen
|
||||
unter `doc/TRO_Katalog/scl_templates/*.scl` vorkommen (Stand 2026-10-10), und woher
|
||||
der Generator sie bekommen muss. Grundlage: die sieben Aufrufstellen
|
||||
(`FB_ILS_MTRO_1Sep`, `_1Sep1Swi`, `_1Sep2Swi`, `_1Sep_SSCC`, `_PinStore_Auto`,
|
||||
`_Vario`, `_Vario_workStation_CallSite`) und die `VAR_INPUT/OUTPUT/IN_OUT`-Abschnitte der
|
||||
vier vollständig vorliegenden FBs (`FB_ILS_MTRO_2Sep1Swi`, `FB_ILS_MTRO_Vario_workStation`,
|
||||
`FB_EmptyCarrBuffer`, `FB_LoadingBoom_INBOUND`). Die Beispielwerte stammen aus diesen
|
||||
Dateien.
|
||||
|
||||
**Quellen** (Spalte "Quelle"):
|
||||
|
||||
| Kürzel | Quelle | Was sie liefert |
|
||||
|---|---|---|
|
||||
| **L** | Anlagen-Hardware = mechanisches Layout (CSV-Export ILS 2.0, `data/*.csv`) | Bauteile (`TeileArt`: Separator, Scanner, Weiche, Kreisel, Strecke, Gefällestrecke, BTMT Be-/Entladung, Ein-/Ausschleuselement), `Nachbarn`/`TrackIds`, `Merkmale` (Drehrichtung, Antriebfahrtrichtung, Höhen, Längen, Anzahl Separatoren/Scanner, Zuordnung). Daraus abgeleitet: TRO-Typ, Topologie, Reihenfolge, Ziele. |
|
||||
| **E** | Hardwareplanung = Elektroplanung (EA-Liste, TIA-Import, WSCAD-Import, `*_positionsdraw.json`; siehe `doc/HundM/EA-Listen-Analyse.md`) | Betriebsmittelkennzeichen und Variablennamen (`BG…` Sensoren, `MB…` Ventile, `MA…` Motoren, `FC…` Motorschutz, `SF…` Taster, `BX…` Scanner), Hardware-Kennungen, welche optionalen Sensoren verbaut sind, Zuordnung zu Schaltschrank und Not-Halt-Kreis. |
|
||||
| **K** | Namenskonvention | Name folgt aus TRO-Nummer bzw. Staubereichs-/Weichennummer (`cMainTro<n>`, `cSep<n>.<i>`, `"DB_Interface_HMI".stTRO.TRO<n>` …). Keine externe Quelle nötig, sobald Nummernkreis und Regel feststehen. |
|
||||
| **P** | Parameter / Projektstandard | Weder Layout noch E-Planung: Zeiten, Schwellwerte, Projektkonstanten, WCS-/Inbetriebnahme-Logik. Kandidaten für Typ-Defaults (`doc/TRO_Typen.md`). |
|
||||
|
||||
`L + E` heisst: das Layout sagt, **dass/wie viele** es gibt und **wo**, die
|
||||
E-Planung liefert den **Namen/die Adresse**. Das ist der häufigste Fall.
|
||||
|
||||
Die zwei folgenden Abschnitte listen **alle Angaben**, die die beiden externen
|
||||
Quellen heute enthalten (Stand 2026-10-11, geprüft an `data/*.csv` und an den
|
||||
Exporten unter `kabellaengen/work/`: `easy_sps-1_EA/_TIA/_WSCAD.xlsx`,
|
||||
`Mubea_positionsdraw.json`). Was sich daraus für TROs und SCL gewinnen lässt und
|
||||
zwei kommentierte SCL-Beispiele: `doc/Datenquellen_Layout_Hardwareplanung.md`.
|
||||
|
||||
### Angaben 1: Anlagen-Hardware (mechanisches Layout, CSV)
|
||||
|
||||
Eine Zeile je Bauteil, Trennzeichen `;`, `utf-8-sig`. Gelesen von
|
||||
`material_flow.load_elements()`.
|
||||
|
||||
#### Spalten (alle Bauteilarten)
|
||||
|
||||
| Spalte | Beispiel | Bedeutung | Nutzen für TRO/SCL |
|
||||
|---|---|---|---|
|
||||
| `Elementnummer` | `1` | laufende Zeilennummer des Exports | keiner (nicht stabil) |
|
||||
| `TeileArt` | `ILS 2.0 Separator` | Bauteilart (Liste unten) | Knotenart, TRO-Typ |
|
||||
| `TeileId` | `0008` | stabile ID im Layout | Schlüssel für Topologie, Overrides, E-Brücke |
|
||||
| `Bezeichnung` | `VF_1`, `Kreisel :3` | Klartext aus der Zeichnung | Anzeige, REGION-Kommentar |
|
||||
| `Planquadrat` | `B/1-C/4` | Raster der Zeichnung | Bericht/Suche |
|
||||
| `Anzahl` | `1` | Stückzahl | keiner |
|
||||
| `Position` | `5878.63, 4330.96, 2128.70` | Einfügepunkt x, y, z in mm | Lagebezug zur E-Planung (`dxf_registration`), Abstände |
|
||||
| `Boundingbox` | `2837.82, 6404.92, 731.58` | Ausdehnung x, y, z in mm | Lage von Weichen/Bahnen |
|
||||
| `Insertpoint` | `TV0UEkxg…` | kodierter Einfügepunkt (Base64) | intern |
|
||||
| `K1`–`K4` | `AK41AC7XAFQk` | Anschluss-Koordinatensysteme (Base64, je x/y/z) | Anschlussseite, Lage der AS/ES (`decode_kpoint`) |
|
||||
| `Nachbarn` | `0006-L, 0014` | berührende Bauteile, bei Kreiseln mit Bahn `-L`/`-R` | Topologie (Kanten) |
|
||||
| `TrackIds` | `0044, 0027, 0026` | Anbauteile eines Containers in Baureihenfolge bzw. Umlaufrichtung | Reihenfolge Separator/AS/ES auf Strecke und Kreisel |
|
||||
| `Fehler`, `Warnungen` | `nur ein Partner`, `unverbunden` | Prüfmeldungen des Exports | Qualitätsprüfung |
|
||||
| `Merkmale` | JSON | artspezifische Eigenschaften (unten) | siehe unten |
|
||||
|
||||
#### Merkmale je `TeileArt`
|
||||
|
||||
| TeileArt | Merkmale | Nutzen für TRO/SCL |
|
||||
|---|---|---|
|
||||
| `ILS 2.0 Kreisel` | `Name`, `Kreiselart` (`STANDARD`), `Drehrichtung` (`UZS`/`GUZ`), `Drehung`, `Höhe in m`, `Abstand (Kreiselachse A - Kreiselachse) in Meter`, `Anzahl der Separatoren`, `Anzahl der Scanner`, `Anzahl der Rampen`, `Geruest fuer Einzelmodul` | Host eines TRO (`xInCarouselRun1/2`), Umlaufrichtung → Reihenfolge, Länge → Staulänge; Anzahlen zur Plausibilisierung |
|
||||
| `ILS 2.0 Strecke` | `Typ` (`Streckengruppe`), `Seite_AS`, `Seite_ES`, `Winkel`, `VF_Winkel`, `DeltaH_mm`, `DeltaL_mm`, `L_VF_m` (angetriebene Teilstücke), `L_GF_m` (Gefälleteilstücke), `Montagehoehe_m`, `Hoehe_Von_mm`, `Hoehe_Bis_mm`, `Antriebfahrtrichtung` (`horizontal`/`Auf`), `Anzahl_Separator`, `Anzahl_Scanner`, `Separatorlist` | angetrieben (`L_VF_m`, `Auf`) → Vario/Antrieb; Längen → `tConvEmpty`, Staulänge; Anzahlen zur Plausibilisierung |
|
||||
| `ILS 2.0 Gefaellestrecke` | wie Strecke, `L_VF_m` und `Antriebfahrtrichtung` leer | Host "Gefälle" → `1Sep`, `xInCarouselRun1 := TRUE` |
|
||||
| `ILS 2.0 Separator` | `Zuordnung` (Host, ggf. mit Bahn `-L`/`-R`), `ARTINR`, `Höhe`, `Drehung`, `Typ` | **TRO-Anker**: jeder Separator ist ein TRO oder Teil davon; Host → TRO-Typ |
|
||||
| `ILS 2.0 Scanner` | `Zuordnung`, `Naechster Separator`, `ARTINR`, `Höhe`, `Drehung`, `Typ` | Scanner am TRO (`xPluggedScanner`, `stInBarcodeReader`, REGION-Titel) |
|
||||
| `ILS Weiche` | `Kreisel A`, `Kreisel B`, `Achse von Kreisel`, `Beruehrung` (`Kreis/Kreis`), `Erzeugt aus` | Kreisel-Kreisel-Weiche → `1SepNSwi`, `xCar2NecRight/Left`, zweiter Kreisel `xInCarouselRun2` |
|
||||
| `ILS Ausschleuselement` | `Kreisel`, `Strecke`/`Förderer`, `Streckenende` (`Anfang`), `Element` (`AS_Element_90_links_3D`), `ARTINR`, `Erzeugt aus` | Abgang Kreisel → Strecke: Weichenausgang `Exit3/4`, Ziel je Abgang |
|
||||
| `ILS Einschleuselement` | wie Ausschleuselement, `Streckenende` `Ende` | Zusammenführung Strecke → Kreisel: Vorfahrt (`xPrioDir`, PriorityManager) |
|
||||
| `ILS 2.0 BTMT Beladung` / `Entladung` | `ARTINR`, `Höhe`, `Drehung` | Systemgrenze `SYSn`, `xIsBorner`, Ziele; Ladeschleife |
|
||||
| `Eingang` / `Ausgang` | `ARTINR`, `Höhe`, `Drehung` | Systemgrenze zu Nachbaranlage, Übergabe-Staubereich |
|
||||
|
||||
`ARTINR` (Artikelnummer) unterscheidet Bauarten, z. B. Separator `200000077`,
|
||||
Scanner `829534306`; daran ließe sich die Weichenbauart (`xNewSwitch`) prüfen.
|
||||
|
||||
Zusätzlich gibt es den **Sivas-Stücklisten-Export** (`data/*_sivas.csv`:
|
||||
`SivasNummer`, `Dispogruppe`, `Laenge[mm]`, `IDs`, `details` mit
|
||||
Förder-/Gefällegruppen, Kurven, `MotorSeite`). Er ist eine Stückliste, keine
|
||||
Topologie, und wird nicht gelesen; brauchbar wäre `MotorSeite` und die
|
||||
Gruppenlängen.
|
||||
|
||||
**Nicht im Layout:** Betriebsmittelkennzeichen, E/A-Adressen, welche optionalen
|
||||
Sensoren verbaut sind, Schaltschrank/Steuerung, Zeiten, Ziel-Nummern des WCS.
|
||||
|
||||
### Angaben 2: Hardwareplanung (EA-Liste, TIA-Import, WSCAD-Import, positionsdraw)
|
||||
|
||||
Vier Exporte derselben E-Planung. Gemeinsamer Schlüssel ist das
|
||||
**Betriebsmittelkennzeichen (BMK)** `BG3306`, `MB3306` …; nur `positionsdraw`
|
||||
kennt eine Lage, nur EA/TIA/WSCAD kennen die E/A-Adresse.
|
||||
|
||||
**`<projekt>-<n>_EA.xlsx`** — Blatt "Ein-, Ausgaenge", je SPS `n` eine Datei
|
||||
|
||||
| Spalte | Beispiel | Bedeutung |
|
||||
|---|---|---|
|
||||
| `Name` | `E324.0` | E/A-Adresse |
|
||||
| `Kommentar` | `Stausensor 1 (ILS-CV M0108)` | Funktionsrolle, lfd. Nr., Fördereinheit (M-Nummer), Bereich |
|
||||
| `Zeile` | `2` | Verweis Schaltplan |
|
||||
|
||||
**`<projekt>-<n>_TIA.xlsx`** — Importdatei für das TIA Portal
|
||||
|
||||
| Blatt / Spalte | Beispiel | Bedeutung | Nutzen für SCL |
|
||||
|---|---|---|---|
|
||||
| PLC Tags `Name` | `BG3240`, `MB3306`, `MA0062`, `FC0062` | BMK = Variablenname | direkt die Namen in `FB_Main` (`=> "MB3306"`, `:= "FC0145"`) |
|
||||
| PLC Tags `Path` | `Import` | Variablentabelle | Ordner der Variablentabelle |
|
||||
| PLC Tags `Data Type` | `Bool` | Datentyp (nur Bool) | – |
|
||||
| PLC Tags `Logical Address` | `%E324.0`, `%A006.2` | Absolutadresse | Prüfung, `FB_CallSensors` |
|
||||
| PLC Tags `Comment` | `Stausensor 1 (ILS-CV M0108)` | wie EA-Kommentar | Rolle, M-Nummer |
|
||||
| PLC Tags `Hmi Visible/Accessible/Writeable` | `True` | HMI-Flags | – |
|
||||
| PLC Tags `Typeobject ID`, `Version ID` | leer | – | – |
|
||||
| Constants `Name` | `cInBG3240`, `cInFC0062`, `cOutMA0062` | Index-Konstante je Signal | genau die Indizes in `"DB_Inputs".Sensors["cInBG3306"]` |
|
||||
| Constants `Data Type`, `Value` | `Int`, `1` | Array-Index | Dimensionierung `DB_Inputs` |
|
||||
|
||||
**`<projekt>-<n>_WSCAD.xlsx`** — Importdatei für das E-CAD
|
||||
|
||||
| Spalte | Beispiel | Bedeutung | Nutzen für SCL |
|
||||
|---|---|---|---|
|
||||
| `Anschluss` | `E324.0` | E/A-Adresse | Schlüssel zu EA/TIA |
|
||||
| `Kommentar` | wie EA | | |
|
||||
| `Bezug` | `=A01+UC0101-KF1DI1` | Anlage, Feldstation (`UC…`), Baugruppe (`KF1DI1`, `KF1FDQ2` = F-Karte) | Steuerungs-/Schaltschrankzuordnung, Safety-Signale |
|
||||
| `Symboltext`, `Sondertext 1..8` | meist leer | | |
|
||||
|
||||
**`<zeichnung>_positionsdraw.json`** — Kabellängen-Tool, E-Symbole aus der Zeichnung
|
||||
|
||||
| Schlüssel / Attribut | Beispiel | Bedeutung | Nutzen für TRO/SCL |
|
||||
|---|---|---|---|
|
||||
| `sensors{BMK@SPS}` | `BG0542@UNKNOWN` | Feldgeräte (BG, MB, MA …) | Hauptquelle der Brücke |
|
||||
| `.IO` / `.B` | `BG0542` | BMK | Schlüssel zu TIA |
|
||||
| `.pos` | `[-42369.8, -11437.2]` | Lage in mm | **Zuordnung zum Layout-Bauteil** (`e_planning.py`) |
|
||||
| `.VERW` | `IN SEP`, `SEP`, `STAU_REF-LT`, `AE ABZWEIG`, `ABZWEIG-K104`, `BT BELADEN`, `SYNC P1..4`, `ABFR.FINGER`, `ÜBERGABE`, `ÜBERNAHME`, `BANDBRUCH`, `KETTENBRUCH`, `ZAPPLER P1`, `MOT_0,37kW` | Verwendung (kontrolliertes Vokabular) | **Rolle** des Signals im TRO |
|
||||
| `.BEZEICHNUNG` | `STAUSENSOR STR. 01`, `PRÜFUNG BT. POS.1 (KR-K0102 EINFAHRT PUFFER)` | Klartext | Strecken-/Kreiselnummer, Plausibilisierung |
|
||||
| `.BESCHR` | `STAUSENSOR` | Gerätebeschreibung | – |
|
||||
| `.KENNZEICHNUNG` | `=A1+UH01-KF102` | Steuerung `UH01`, E/A-Station `KF102` | Steuerung, TRO-Nummernkreis, Schaltschrank |
|
||||
| `.ARTINR` | `829434300` | Artikelnummer Gerät | Gerätetyp |
|
||||
| `.SPS` | `''`, `A/B` | SPS-Nummer (oft leer) | Steuerung |
|
||||
| `.A`, `.C`, `.ID`, `.MENGE`, `.POSITION`, `.GRUPPE`, `.ETIKETTE`, `.AUFLOESE`, `.TEXT-D/E/ES/F` | | Blockattribute des Symbols | – |
|
||||
| `schaltschrank_elemente{…}` | `FC0200` `MOTORSCHUTZ CONVEYER #100`, `SF…` Taster, `PF…` Leuchten, `QA7000` Hauptschütze, `GF` Encoder, `BP` Druckschalter | Elemente im Schrank, ohne Feldlage | Motorschutz ↔ Motor, Bedienelemente, Encoder |
|
||||
| `mappings` | `UH01: [BMK …]` bzw. Verteiler → BMK | Zuordnung zur Steuerung/Verteiler aus der Verkabelung | Steuerungsaufteilung |
|
||||
| `distributors`, `racks`, `tunnels` | | Verteiler, Trassen | – |
|
||||
| `warnings`, `not_found`, `double_ids` | | Prüfprotokoll | Qualitätsprüfung |
|
||||
|
||||
**BMK-Präfixe:** `BG` Sensor, `SF` Taster/Schalter, `DI` Diagnose, `BP`
|
||||
Druckschalter, `GF` Encoder, `BX` Scanner (nur PROFINET, nicht in den Digitallisten),
|
||||
`MB` Ventil (Stopper, Weiche, Ausschleuser), `MA`/`QA` Motor/Schütz, `DQ`
|
||||
Sonderausgang, `FC` Motorschutz, `PF` Leuchtmelder.
|
||||
|
||||
**Nicht in der Hardwareplanung:** Topologie (wer auf wen folgt), TRO-Grenzen,
|
||||
Ziele/Weichenrichtungen, Zeiten, Analogwerte und PROFINET-Geräte (Cognex-Variablen
|
||||
stehen in der TIA-Gerätekonfiguration, nicht im Tag-Import).
|
||||
|
||||
### TRO-Kopf und Instanzaufruf
|
||||
|
||||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||||
|---|---|---|---|
|
||||
| Existenz, Anzahl, Reihenfolge der TROs | `REGION TRO 100 …` | L | `tro_flow` → `DerivedTro`; REGION-Reihenfolge `scl_context.flow_order()` |
|
||||
| TRO-Typ / FB-Typ | `FB_ILS_MTRO_1Sep1Swi` | L (→ Katalog) | Typ aus Separator-Host (`doc/500573_Mubea/TRO_Identifikation_500573.md`), FB aus `tro_catalog` |
|
||||
| TRO-Nummer, Instanzname | `#TRO107` | L + E | Anzahl aus L; Nummernkreis je Steuerung (100er = UH01, 200er = UH02 …) ist Projektvorgabe |
|
||||
| REGION-Titel | `TRO 267 (1Sep2Switch)+Scanner(BX2100)` | L + E | Typ aus L, Scanner-BMK aus E |
|
||||
| `nInMainTroNo` | `"cMainTro100"` | K | Wert der Konstante in der PLC-Konstantentabelle |
|
||||
| `nOutStateLast` | `"DB_GLOB_TroState".arTroState["cMainTro100"]` | K | |
|
||||
| `stInOutHMI` | `"DB_Interface_HMI".stTRO.TRO100` | K | PinStore/Puffer: eigener UDT `UDT_HMI_TRO_PinStore` |
|
||||
| `xInRelease` | `"DB_TRO_TEST".TRO100`, `TRUE`, WCS-Verbindung aktiv | P | Freigabe-/Testlogik, Inbetriebnahme |
|
||||
| `xInAllRdyToStart`, `xInCancel`, `xInReset` | `TRUE`, `"cTodoFalse"` | P | meist Konstante |
|
||||
| `stInSettings.xIsBorner` (`xIsBorner1/2`) | `TRUE` | L | erster TRO nach Einfahrt in die Anlage (Beladestation / Systemgrenze `SYSn`) |
|
||||
| `stInSettings.nBornerDest(1/2)` | – | P | Standardziel für neu angelegte Carrier (WCS) |
|
||||
| `stInSettings.bSepType` (`bSep1Type/2Type`) | `.%X2 := TRUE` | E + P | X0 erstes Bein halten, X1 Beladekontrolle, X2 Beladeerfassung — hängt an verbauter Sensorik und Prozess |
|
||||
| `stInSettings.nTroStopper1/2`, `nTroSwitchExit2/3` | – | L + K | Nummern der Nachbar-TROs |
|
||||
| `stInSettings.sJamSrc` / `sJamDest` | – | L + K | Namen der benachbarten Staubereiche |
|
||||
| `stInSettings.xCar2NecRight/Left` | – | L | zweiter Kreisel nötig für Richtung rechts/links (Topologie an einer Kreisel-Weiche) |
|
||||
| `tPartSenHigh`, `tInSenHigh`, `tDummyTimeNormal`, `tDummyTimeBorner` | FB-Defaults 500 ms / 3 s / 0 ms | P | Typ-Defaults |
|
||||
|
||||
### Separator (`stInSeparator<n>`, `xOutStopper<n>`)
|
||||
|
||||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||||
|---|---|---|---|
|
||||
| Anzahl Separatoren je TRO | `stInSeparator1`, `stInSeparator2` | L | `ILS 2.0 Separator` + `Zuordnung`, Typ-Katalog `items` |
|
||||
| `Settings.nSeparatorNo` | `"cSep107.1"` | K | Layout-Separator-ID (`TeileId`) ist **nicht** diese Nummer |
|
||||
| Sensor im Stopper `stSenInSep` | `"DB_Inputs".Sensors["cInBG3242"]` | E | |
|
||||
| Sensor nach Stopper `stSenSepLeft`, Beladesensor `stSenPart`, Scan-Start `stSenStartScan` | `["cInBG4465"]` | E | nur wenn verbaut |
|
||||
| Stopperventil `xOutStopper<n>` / `xOutStopper` | `=> "MB3242"` | E | BG- und MB-Nummer sind in der E-Planung meist gleich (BG3242/MB3242) |
|
||||
| `Settings.xPluggedJam` | `TRUE` | E | Stausensor verbaut? |
|
||||
| `Settings.xPluggedPart` | `FALSE` | E | Beladesensor verbaut? |
|
||||
| `Settings.xPluggedConfirm`, `xPluggedNullBT` | `FALSE` | E | |
|
||||
| `Settings.xPluggedScanner` | `TRUE` | L | Scanner am Separator (`DerivedTro.scanners`) |
|
||||
| `Settings.tDelayToNextItem` | `"TimeForSepWaiting_Short"` / `_Medium` | P (+ L) | Wahl Short/Medium hängt am Host (Kreisel/Strecke/Gefälle) |
|
||||
| `Settings.tTrailingTime`, `tHandlingTime` | `t#600ms`, `t#200ms`, `"cTimeSepTrailing"` | P | Typ-Default, IBN |
|
||||
| `Settings.tJam` | `"TimeForSepJam"` | P | Projektkonstante |
|
||||
| `Settings.tSenFree`, `tSenWait` | `T#1500ms`, `T#4000ms` | P | |
|
||||
|
||||
### Staubereiche (Jam)
|
||||
|
||||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||||
|---|---|---|---|
|
||||
| Anzahl Ein-/Ausgänge und Zuordnung Exit-Nr ↔ Nachbar | `arInOutJamEntr1`, `arInOutJamExit2..4` | L | ein Eingang je eigenem Staubereich (`TroDocument.jamAreas`, Kennung `J<Separator>`; mehrere Vorgänger fahren in denselben ein), je Nachfolger/Weichenabgang ein Ausgang (`SclJam`) |
|
||||
| Gleicher Staubereich auf beiden Seiten einer Kante | Exit von A = Entr von B | L | Identität aus der Topologie |
|
||||
| Staubereichs-DB und -Index | `"DB_JamArea0102".stJam0102_1.arCarrier` / `.stData` | K (+ E) | Nummerierung der Staubereiche ist Projektvorgabe |
|
||||
| Stausensor je Ausgang | `stInSenJam2 := "DB_Inputs".Sensors["cInBG3243"]` | E | |
|
||||
| Übergabe-Staubereich an Steuerungsgrenze | `#arCarrierHandoverJam5001`, `#stJamDataHandoverJam5001` | L + E | Steuerungsaufteilung aus der E-Planung |
|
||||
| `xInJamAtExit2/3` | – | P | NIU |
|
||||
|
||||
### Weiche (`stInSwitch<n>`, `FC_Direction`)
|
||||
|
||||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||||
|---|---|---|---|
|
||||
| Anzahl Weichen je TRO | `stInSwitch1`, `stInSwitch2` | L | `ILS Weiche`, Ausschleuselement, Typ-Katalog |
|
||||
| `Settings.nSwitchNo` | `"cSwi107.1"` | K | |
|
||||
| `Settings.nTo1Destinations` | `3` | L | Erreichbarkeit im TRO-Graphen (`reachable_via()`); bei mehreren Weichen Planungsentscheidung |
|
||||
| Zielliste je Abgang | `cDest…` | L + P | welche Ziele: L; Zielnummern: Projekt/WCS |
|
||||
| `Settings.xPluggedExSen3/4`, `stSenExit3/4` | `FALSE` | E | Abgangssensoren verbaut? |
|
||||
| `Settings.xNewSwitch` | `TRUE` | E | Weichenbauart (ggf. über `ARTINR` im CSV prüfbar) |
|
||||
| Weichenventile `xOutSw<n>ExTo3/4`, `xOutSw1EntrTo1/2` | `=> "MB3304"` | E | |
|
||||
| `FC_Direction`-Aufruf | `nInSwitchNo := "cSwi107.1"`, `InOutCarrier := …stJam0102_1.arCarrier[1]` | K + L | Carrier aus dem Eingangs-Staubereich |
|
||||
| Vorfahrt `stInSettings.xPrioDir<n>`, `stInPriorityManagerDir<n>` | `"DB_Prozessdaten".stPriorityManager.udt_TRO107.TroIn` | L + K | Vorfahrt nötig, wo zwei Ströme zusammenlaufen (Kreuzung/Zusammenführung) |
|
||||
| `stInPriorityManager.xReleaseOk` | `TRUE` ("braucht keine Vorfahrtsregelung") | L | TRUE, wenn keine Zusammenführung |
|
||||
|
||||
### Scanner / Barcode / SSCC
|
||||
|
||||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||||
|---|---|---|---|
|
||||
| Scanner vorhanden, Anzahl | `stInBarcodeReader`, `stInBarcodeReader1/2` | L | `ILS 2.0 Scanner`, `Anzahl_Scanner` |
|
||||
| Scanner-BMK und Index | `#arCognexInterface["cScannerBX2100"]` | E | |
|
||||
| Cognex-Variablen | `"bx2100-TriggerReady"`, `-TriggerAck`, `-Results Available`, `-General Fault`, `-TriggerEnable`, `-Trigger`, `-ResultsAck` | E | aus dem BMK nach Regel bildbar, wenn die TIA-Variablentabelle dem Muster folgt |
|
||||
| Hardware-Kennung Ergebnisse | `"=A01+UC021-BX2100~Ergebnisse_–_64_Bytes_1"` | E | |
|
||||
| Kein Scanner | `#stCognexInterfaceDummy` | L | |
|
||||
| `Settings.xActivateCheckASCI`, `tTimeout` | `TRUE`, `T#5s` | P | |
|
||||
| `Settings.nStation`, `nScannerPurpose` | `111`, `0` | P | WCS |
|
||||
| Zweck SSCC-Scanner, `xInPluggedSSCC` | `#stCognexIntfTro105` | E + P | Layout kennt den zweiten Scanner, nicht seinen Zweck |
|
||||
| `stSenEndMeasurement` | `["cInBG3281"]` | E | |
|
||||
| `FC_CARR_GLOB_CORRECTION` | `nInStation := 0`, Carrier aus Eingangs-Staubereich | P + L | |
|
||||
| WCS-Telegramm, HMI-SSCC-Ausgabe | `FC_Send_GOHDReqM`, `"DB_Interface_HMI".stSSCC.SSCC1107` | P | WCS-Schnittstelle |
|
||||
|
||||
### Vario (`stInVario`) und Arbeitsplatz
|
||||
|
||||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||||
|---|---|---|---|
|
||||
| Vario vorhanden | `FB_ILS_MTRO_Vario` | L | angetriebene Strecke / Variofoerderer |
|
||||
| Sensoren `stSenCarrInPos`, `stSenFinger`, `stSenLastPos`, `stSenJam`, `stSenNextSep`, `stSenSepVario` | `["cInBG3026"]` … | E | |
|
||||
| Motor `xOutVarioMotor`, Motorschutz `xInMotorProtection` | `"MA0145"`, `"FC0145"` | E | |
|
||||
| Finger-Staubereich `arInOutJamFinger` | `"DB_JamArea4103".stJam4103_1` | L + K | |
|
||||
| `Settings.tConvEmpty` | `T#45000ms` | L + P | aus Förderlänge (`L_VF_m`) und Geschwindigkeit ableitbar |
|
||||
| `Settings.nCountCarrier`, `tWaitForJog`, `tWaitForStop`, `tCarrierInPosDelay`, `tCarrierInPosEdgeDelay`, `tRasterHigh/Low`, `xJamSenDelayActive`, `tJamSenDelay`, `xContinousEmptying`, `SettingsVarioFingerMonitoring` | `1`, `T#300ms` … | P | |
|
||||
| Arbeitsplatz `stInStationSensor`, `xInReleaseStation`, `fbConvVario.xBeladenError` | `["cInBG3314"]`, `#"rTrig_Workstation 1".CLK` | E | Taster/Sensor am Arbeitsplatz |
|
||||
|
||||
### Antrieb, Sicherheit, Betriebsart
|
||||
|
||||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||||
|---|---|---|---|
|
||||
| Fördertechnik, die den TRO trägt | `xInCarouselRun1 := #fbCarousel0102.xOutConveyorFullSpeed`; Gefälle: `TRUE` | L | Host-Kreisel aus `DerivedTro.hosts`; Instanzname nach K |
|
||||
| `xInCarouselRun2` | zweiter Kreisel | L | TRO an der Berührung zweier Kreisel |
|
||||
| `xInConveyorRunIn/Out` (PinStore) | `#fbSortConv1101`, `#fbExitConv1103` | L + K | |
|
||||
| `xInSftyOk` | `"DB_InterfaceSafety".stFromSafety.EStopMainOF1.Q` | E | Not-Halt-Kreis |
|
||||
| `stInOutMachineState` | `#stInOutControlUnitCabinetOF1` | E | Schaltschrank (OF1/OF2) |
|
||||
| `xInAutomatic`, `xInErrorQuit`, `xInQuitLocal` | `#t_Automatik_OF1 AND "SF0120"`, `"SF0002"`, `"SF0121" OR "SF0117"` | E | Bedienelemente |
|
||||
| `nInTestTarget` | `"IBN".nTestTarget111` | P | |
|
||||
|
||||
### Pin-Lager (`FB_ILS_MTRO_PinStore_Auto`) und Leerträgerpuffer (`FB_EmptyCarrBuffer`)
|
||||
|
||||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||||
|---|---|---|---|
|
||||
| Anzahl Lagerlinien | `nNoOfLines := 19`, `arInSep[1..5]` | L | Separatoren auf Kreisel mit Pinband / `Separatorlist` |
|
||||
| Positionen je Linie, Staulänge | `nNoOfPositions := 70`, `nJamLength := 4` / `"cStorage111ExitJam2"` | L + P | aus Linienlänge und Teilung |
|
||||
| Liniensensoren `arInSep[n].stSenTrolley`, `arInJamSensor[n]`, `stInSenJamExit` | `["cInBG3270"]` | E | |
|
||||
| Linienventile `arOutSeparatorStore[n]` | `"MB3270"` | E | |
|
||||
| Pin-Separator `stInSeparatorPin`, `xOutSeparatorPin` | `["cInBG3000"]`, `"MB3000"` | E | |
|
||||
| Referenzsensor "Zappler" `stInSensorReference` | `["cInBG3001"]` | E | |
|
||||
| Encoder `stInEncoderParameter` | `#fbEncoderGF2200.nOutPosActDif` | E | |
|
||||
| `nDestGeneral`, `nConvStations`, `nStationsMax`, `nStorageThreshold` | `"cDest11100"`, `"cHooksSorterMaxPin111"` | P | WCS/Projekt |
|
||||
| `nInBufferNr`, `xInBufferReq/Release/ReleaseNext`, `xInPause` | `101`/`102` | P | Pufferlogik |
|
||||
| Lager-/Stations-DBs | `"DB_Storage1.1.1".arLine`, `"DB_ConveyorStations"`, `"DB_HandshakeStorage"` | K + P | |
|
||||
| Linien-Zeiten, Simulation | `"cTimeSepHandlingStorageLine"`, `stSimu.*` | P | |
|
||||
|
||||
### Ladeschleife (`FB_LoadingBoom_INBOUND`)
|
||||
|
||||
| Daten | SCL-Stelle (Beispiel) | Quelle | Bemerkung |
|
||||
|---|---|---|---|
|
||||
| Ladeschleife vorhanden, Lage | – | L | Beladestation (`ILS 2.0 BTMT Beladung`), prüfen |
|
||||
| Bedienelemente `stInControl.*` | Taster, Knebel-/Schlüsselschalter | E | |
|
||||
| Ausleger/Stützfuß `stInBoom.*`, `stInFoot.*` | Endlagen, Motorschutz, Bumper | E | |
|
||||
| Analogwerte `stInTilt.wTilt`, `stInDistance.wDistance` | Neigung, Abstand | E | |
|
||||
| Sensoren `stInSen2mThreshold`, `stInSenJamEntr/Exit1/Exit2`, `stInSenStopper1/2`, `stInSenGrid` | | E | |
|
||||
| Ausgänge `stOutStopper.*`, `stOutBoom.*`, `stOutFoot.*`, `xOutLedMovement`, `xOutLight`, `xOutLedBumper`, `xOutCarousel` | | E | |
|
||||
| Schwellwerte `nDiff`, `nZeroOk`, `nStartSlow/Fast`, `nDistanceZero`, `tDiff`, `tZero`, `nTiltComp` | | P | Inbetriebnahme |
|
||||
| `stInSettings.*` Zeiten | `tBoomInput := T#3500ms` … | P | |
|
||||
| Exit-Staubereich `arInOutCarrierExit`, `stInOutCarrierExit` | | L + K | |
|
||||
|
||||
### Zusammenfassung
|
||||
|
||||
- **Aus dem Hardwarelayout (L)** kommt die **Struktur**: welche TROs es gibt, ihr Typ
|
||||
und damit FB und Anzahl der Parameterblöcke (Separatoren, Weichen, Scanner, Vario,
|
||||
Lagerlinien), Vorgänger/Nachfolger und damit Ein-/Ausgangs-Staubereiche,
|
||||
REGION-Reihenfolge, Ziele je Weichenabgang (`nTo1Destinations`), Vorfahrt ja/nein,
|
||||
Systemgrenzen (`xIsBorner`), die tragende Fördertechnik (`xInCarouselRun`, Gefälle →
|
||||
`TRUE`), ob ein Scanner am Separator sitzt (`xPluggedScanner`) und Längen als
|
||||
Grundlage für Zeiten/Staulängen. Das meiste davon ist heute in `DerivedTro` →
|
||||
`SclTro` abgebildet; Vorfahrt, `xIsBorner` und Längen noch nicht.
|
||||
- **Aus der Elektroplanung (E)** kommen **alle Namen und Adressen**: jeder Sensor
|
||||
(`BG`), jedes Ventil (`MB`), Motor (`MA`), Motorschutz (`FC`), Taster (`SF`), Scanner
|
||||
(`BX`) samt Cognex-Variablen und Hardware-Kennung, die `xPlugged*`-Schalter (was ist
|
||||
tatsächlich verbaut), Weichenbauart, Not-Halt-Kreis, Schaltschrank und
|
||||
Steuerungsgrenzen. Im Render-Kontext sind das die `SclName` mit `todo=True`
|
||||
(`TODO(E-Planung)`). Offen ist die Brücke: die EA-Liste kennt keine Topologie; es
|
||||
braucht eine Zuordnung Layout-Bauteil (Separator-`TeileId`) ↔ BMK (`BG3242`/`MB3242`).
|
||||
Diese Brücke baut `lib/e_planning.py` über die Symbolpositionen in
|
||||
`*_positionsdraw.json` des Kabellängen-Tools (`doc/E-Planung_Bruecke.md`);
|
||||
heute kommen damit Separator-Sensor und Stopperventil in die SCL
|
||||
(`scl_skeleton --eplan`).
|
||||
- **Namenskonvention (K)**: `cMainTro`, `cSep`, `cSwi`, Staubereichs-DBs, HMI- und
|
||||
State-Pfade — sobald Nummernkreise feststehen, ohne weitere Quelle erzeugbar.
|
||||
- **Parameter (P)**: Zeiten, Projektkonstanten, WCS-Ziele/Stationen, Test-/IBN-Signale —
|
||||
weder L noch E; gehören als Typ-Defaults in den Katalog bzw. in eine
|
||||
Projektkonfiguration.
|
||||
|
||||
@@ -0,0 +1,379 @@
|
||||
# Konzept: Layout und Hardwareplanung → Anlagen-JSON → SCL
|
||||
|
||||
Stand 2026-10-11. Zielbild für den Ausbau der Pipeline, damit die beiden
|
||||
Fachabteilungen unabhängig voneinander und zeitversetzt liefern können. Welche
|
||||
Angaben aus welcher Quelle kommen, steht in `doc/Datenquellen_Layout_Hardwareplanung.md`;
|
||||
die Rohangaben beider Quellen in `doc/Klassenhierarchie.md`, Kapitel "Daten".
|
||||
|
||||
## Ausgangslage
|
||||
|
||||
Die Produktion läuft über zwei Abteilungen:
|
||||
|
||||
1. **Anlagenlayout** (aus dem Angebotswesen, detaillierter aus der Projektierung).
|
||||
Liefert alles für die TROs: Bauteile, Abfolge, Weichen, Staubereiche,
|
||||
Systemgrenzen.
|
||||
2. **Hardwareplanung** (Karten, Kabel, Sensoren, Aktoren). Liefert die globalen
|
||||
Variablen und die tatsächlichen Sensoren und Aktoren mit Namen und Adressen.
|
||||
|
||||
Die Hardwareplanung kommt **Tage bis Wochen später** als das Layout. Das System
|
||||
muss deshalb mit dem Layout allein ein brauchbares SCL-Skelett erzeugen und die
|
||||
Hardware später nachtragen können, ohne Layout-Arbeit zu wiederholen.
|
||||
|
||||
## Zielbild
|
||||
|
||||
```text
|
||||
Abteilung 1: Anlagenlayout Abteilung 2: Hardwareplanung (später)
|
||||
CSV ──tro_flow──> <csv>_tro.json EA/TIA/WSCAD.xlsx, positionsdraw.json
|
||||
(TROs, Abfolge, ──hw_import──> <projekt>_hw.json
|
||||
Staubereiche, Slots) (globale Variablen, Geräte)
|
||||
│ │
|
||||
└──────────── hw_assign ─────────────────┘
|
||||
│
|
||||
<csv>_anlage.json (Layout + Zuordnungen)
|
||||
│
|
||||
scl_skeleton ──> FB_Main.scl, FB_CallSensors.scl, …
|
||||
```
|
||||
|
||||
| Stufe | Skript | Eingabe | Ausgabe | Status |
|
||||
|---|---|---|---|---|
|
||||
| 1 Layout | `tro_flow.py --json` | CSV, `cfg/tro_overrides.ini` | `<csv>_tro.json` | vorhanden, wird erweitert |
|
||||
| 2 Hardware | `hw_import.py` | `*_TIA.xlsx`, `*_EA.xlsx`, `*_WSCAD.xlsx`, `*_positionsdraw.json` | `<projekt>_hw.json` | neu |
|
||||
| 3 Zuordnung | `hw_assign.py` | `<csv>_tro.json`, `<projekt>_hw.json`, Regeln, `cfg/hw_overrides.ini` | `<csv>_anlage.json`, Bericht `.md` | neu (Logik aus `e_planning.py`) |
|
||||
| 4 SCL | `scl_skeleton.py` | `<csv>_anlage.json` (oder nur `<csv>_tro.json`) | `.scl` | vorhanden, wird umgestellt |
|
||||
|
||||
Grundsätze:
|
||||
|
||||
- **Stufe 1 allein reicht für ein Skelett.** Ohne Hardware wird jeder offene Wert
|
||||
wie heute `TODO(E-Planung)`.
|
||||
- **Die Stufen verändern sich nicht gegenseitig.** Die Zuordnung legt ihre
|
||||
Ergebnisse neben das Layout, statt es zu überschreiben. Ein neuer Layoutstand
|
||||
oder ein neuer Hardwarestand heißt: die betroffene Stufe und alles danach neu
|
||||
ausführen.
|
||||
- **Lieber eine Lücke als ein falscher Name** (wie heute in `cfg/e_planning.ini`):
|
||||
was keine Regel sicher trifft, bleibt offen und steht im Bericht.
|
||||
- **Drei Quellen, jede sichtbar:** Layout, Hardwareplanung und Defaults
|
||||
(`cfg/defaults.ini`, Typ-Katalog). Jede SCL-Zeile lässt sich einer davon zuordnen.
|
||||
|
||||
## Umbauten
|
||||
|
||||
### 1. TRO-JSON zur Layout-JSON ausbauen (Stufe 1)
|
||||
|
||||
Heute fehlt der Zuordnung ein Ziel: die TRO-JSON sagt nicht, welche Stellen ein
|
||||
TRO hat, an die Hardware gehört. Daran scheitern heute Stausensoren und
|
||||
Weichenventile (`doc/E-Planung_Bruecke.md`, "Offen").
|
||||
|
||||
- **Slots je TRO:** je Separator, Weiche, Ausgang und Antrieb ein Eintrag mit der
|
||||
Layout-ID des Bauteils und den Rollen, die die Hardwareplanung dort füllen muss.
|
||||
|
||||
```json
|
||||
"slots": [
|
||||
{"slot": "separator1", "layoutId": "0008", "roles": ["sensor", "stopper", "jamSensor?", "partSensor?"]},
|
||||
{"slot": "switch1", "layoutId": "0037", "exit": 3, "roles": ["valve", "exitSensor?"]},
|
||||
{"slot": "drive", "layoutId": "0002", "roles": ["carouselRun"]}
|
||||
]
|
||||
```
|
||||
|
||||
Die Slots ergeben sich aus dem Katalogtyp (`items`) und der Topologie; `?` =
|
||||
optional, fehlt das Gerät, wird der zugehörige `xPlugged*`-Schalter `FALSE`.
|
||||
- **Staubereiche als eigene Liste `jamAreas[]`:** ein Staubereich je TRO-Eingang,
|
||||
mit allen Vorgängern, dem Förderer, auf dem er liegt, und seiner Lage auf dem
|
||||
Förderer (siehe "Staubereiche" unten). Heute nur abgeleitet beim Rendern
|
||||
(`scl_context.SclJam`). Dabei korrigieren: `scl_skeleton` erzeugt heute je
|
||||
Vorgänger einen eigenen Eingang (`arInOutJamEntr1`, `…Entr2`); in den Referenzen
|
||||
laufen mehrere Vorgänger in **einem** Staubereich zusammen, der TRO hat einen
|
||||
Eingang (Ausnahme `2Sep1Swi`, PinStore).
|
||||
- **Weitere Layout-Angaben:** `xIsBorner`, Vorfahrt ja/nein, tragender Förderer
|
||||
mit Rolle (heute `hosts` ohne Rolle), Längen für `tConvEmpty` und Staulängen.
|
||||
- **Ableitungen aus `scl_context` nach `tro_flow`/`model` verschieben,** damit die
|
||||
JSON sie trägt und nicht erst das Rendern sie kennt.
|
||||
- Schema `sps_skel-tro-v4`.
|
||||
|
||||
**Stand (2026-10-11), erster Teil umgesetzt:**
|
||||
|
||||
- `model.Slot` und `DerivedTro.slots`: `separator<n>`, `switch<n>`,
|
||||
`scanner<n>`, `drive` mit Rollen, Anzahl aus dem Katalog (`derive_slots`).
|
||||
Weichen-Slots haben noch keine `layoutId`: `tro_flow` hält das Weichen- bzw.
|
||||
Ausschleuselement nicht am TRO fest.
|
||||
- `model.JamArea` und `TroDocument.jamAreas`: je Separator laut Katalog ein
|
||||
Eingangs-Staubereich `J<Separator>` mit allen Vorgängern, dem Host und der
|
||||
Lage (`derive_jam_areas`). Bei 2Sep-Typen ist die Aufteilung der Vorgänger
|
||||
auf die zwei Eingänge offen (Hinweis im Feld `note`); beim PinStore heißt
|
||||
der Eingang vorläufig nach dem ersten Linien-Separator.
|
||||
- `scl_context` liest die Staubereiche aus der JSON: **ein** `JamEntr` je
|
||||
eigenem Staubereich statt je Vorgänger (Abweichung zu UH01–UH05 behoben),
|
||||
jeder Ausgang nennt im Kommentar den Staubereich des Nachfolgers. Golden
|
||||
Files neu erzeugt. Eine v3-Datei ohne `jamAreas` wird weiter gelesen.
|
||||
- **Noch offen aus Umbau 1:** Layout-ID der Weichen, `xIsBorner`, Vorfahrt,
|
||||
Host-Rolle, Längen (`tConvEmpty`, Staulängen), `<NNNN>`/`<k>` für den
|
||||
SCL-Namen der Staubereiche.
|
||||
|
||||
### 2. Neues Skript `hw_import.py` (Stufe 2)
|
||||
|
||||
- Liest TIA-Import (PLC Tags + Constants), EA-Liste, WSCAD-Import und optional
|
||||
`positionsdraw.json`.
|
||||
- Schreibt ein neues Modell `HardwareDocument`:
|
||||
- **Geräte** je BMK: Adresse, Index-Konstante (`cInBG3306`), Kommentar, VERW,
|
||||
Steuerung, Station/Schrank, Position (falls vorhanden);
|
||||
- **globale Variablen**: alle Tags und Konstanten, Motor↔Motorschutz-Paare,
|
||||
Taster, Not-Halt.
|
||||
- Prüft die Exporte gegeneinander: BMK fehlt in einer Liste, doppelte BMK,
|
||||
Adresse ohne Tag.
|
||||
- Übernimmt aus `e_planning.py`: `load_positionsdraw`, `EDevice`, `pair_drives`.
|
||||
- Erste Quelle: der TIA-Import, weil er Namen und Konstanten 1:1 liefert und die
|
||||
spätere Prüfung des SCL ermöglicht.
|
||||
|
||||
### 3. Neues Skript `hw_assign.py` (Stufe 3)
|
||||
|
||||
- Eingabe: `<csv>_tro.json` + `<projekt>_hw.json`; das CSV wird nicht mehr gelesen.
|
||||
- Zuordnungswege, als Regeln in einer INI (heute `cfg/e_planning.ini`):
|
||||
- **räumlich** über `positionsdraw`: die bestehende Logik aus `e_planning.py`
|
||||
(Lagebezug über `dxf_registration`, Höchst-/Sollabstand), jetzt gegen die Slots;
|
||||
- **Nummernregel**: `MB = BG + 1` bzw. gleiche Nummer, Motor = Motorschutz;
|
||||
- **nur EA-Liste, ohne Positionen**: über die Kommentare
|
||||
("in Separator 1 (ILS-CV M0108)") und eine von Hand gepflegte Tabelle
|
||||
M-Nummer → Layout-Förderer.
|
||||
- Handkorrekturen in `cfg/hw_overrides.ini` (analog `cfg/tro_overrides.ini`).
|
||||
- Ausgabe `<csv>_anlage.json`: Layoutteil unverändert, daneben
|
||||
|
||||
```json
|
||||
"bindings": [
|
||||
{"layoutId": "0008", "role": "sensor", "bmk": "BG3306", "constant": "cInBG3306",
|
||||
"address": "%E330.6", "source": "positionsdraw", "rule": "raeumlich",
|
||||
"distance": 12.0, "status": "zugeordnet"}
|
||||
]
|
||||
```
|
||||
|
||||
`status`: `zugeordnet` | `offen` | `widerspruch`.
|
||||
- **Stabile Schlüssel:** Zuordnungen hängen an TRO-ID + Slot + Rolle. Die TRO-IDs
|
||||
sind ab der Festlegung durch die SPS-Programmierer stabil (siehe "TRO-IDs"
|
||||
unten). Die Layout-`TeileId` taugt dafür **nicht**: sie ändert sich zwischen
|
||||
zwei CSV-Exporten (siehe dort). `layoutId` in `bindings` dient nur zur
|
||||
Nachverfolgung im aktuellen Stand.
|
||||
- **Gegenprüfung** beider Quellen (Anzahl Separatoren vs. `IN SEP`, Weichen vs.
|
||||
`AE ABZWEIG`, angetriebene Strecken vs. `MA`) in den Bericht.
|
||||
|
||||
### 4. `scl_skeleton.py` / `scl_context.py` (Stufe 4)
|
||||
|
||||
- Liest **eine** Eingabe: `<csv>_anlage.json`; ohne Hardwareteil wie heute
|
||||
`<csv>_tro.json`.
|
||||
- `--eplan` und die Zusammenführung in `scl_context._separator` entfallen; der
|
||||
Kontext liest Namen aus `bindings`, offene Slots werden `TODO(E-Planung)`.
|
||||
- **Prüfung gegen die globalen Variablen:** jeder Tag und jede `cIn…`-Konstante im
|
||||
SCL muss im TIA-Import stehen, sonst Abbruch mit Liste.
|
||||
- **Neue Bausteine** nach Stufe 2: `FB_CallSensors` aus der Sensorliste,
|
||||
Antriebsaufrufe (`fbCarousel0102`) aus den Motorpaaren, Staubereichs-DBs.
|
||||
|
||||
### 5. Defaults als dritte Quelle
|
||||
|
||||
Zeiten und Projektkonstanten, die heute fest in `scl_skeleton.py` stehen
|
||||
(`t#600ms`, `T#1500ms` …), wandern in den Typ-Katalog (`tro_catalog.py`) bzw. eine
|
||||
Projektdatei `cfg/defaults.ini`.
|
||||
|
||||
## Bestehende Programme
|
||||
|
||||
| Programm | Rolle im neuen Ablauf |
|
||||
|---|---|
|
||||
| `material_flow.py`, `tro_flow.py` | bleiben Stufe 1; Ausbau um Slots und `jamAreas` |
|
||||
| `tro_catalog.py`, `model.py`, `tro_overrides.py`, `dxf_registration.py` | bleiben Bibliotheken; `model.py` bekommt Slots, `jamAreas`, Anlagen-JSON |
|
||||
| `e_planning.py` | wird aufgeteilt (unten) |
|
||||
| `tro_annotate.py` | bleibt als Kontrollansicht, liest künftig die JSON (unten) |
|
||||
| `tro_extract.py` | entfällt bzw. wird umgebaut (unten) |
|
||||
| `scl_skeleton.py`, `scl_context.py` | Stufe 4, Umstellung auf `<csv>_anlage.json` |
|
||||
|
||||
### `e_planning.py` → `hw_import` + `hw_assign`
|
||||
|
||||
| Teil heute | Wohin |
|
||||
|---|---|
|
||||
| `load_positionsdraw`, `EDevice`, `pair_drives` | `hw_import.py` |
|
||||
| `resolve_transform`, `load_rules`, `_assign_spatial`, `_assign_by_number`, Bericht | `hw_assign.py`, Ziele = Slots statt CSV-Bauteile |
|
||||
| `cfg/e_planning.ini` | Regeldatei von `hw_assign` |
|
||||
| `EplanDocument` / `<csv>_eplan.json` | entfällt, ersetzt durch `bindings[]` |
|
||||
|
||||
Übergang: `e_planning.py` bleibt kurz als Hülle, die `hw_import` und `hw_assign`
|
||||
nacheinander aufruft, damit `bin/e_planning.bat` weiter funktioniert; danach
|
||||
löschen.
|
||||
|
||||
### `tro_annotate.py` → Kontrollansicht für Layout und Zuordnung
|
||||
|
||||
- **Aus der JSON zeichnen** statt `analyse_tros` selbst aufzurufen: die Zeichnung
|
||||
zeigt dann genau das, woraus das SCL entsteht.
|
||||
- **Zuordnungen einzeichnen:** neben jedem Slot das BMK (`BG3306`, `MB3306`),
|
||||
offene Slots und Widersprüche rot. Für die Hardwareplanung die schnellste
|
||||
Kontrolle der räumlichen Zuordnung.
|
||||
- `--emit-lisp` (`TROEDIT`) bleibt, solange Marker in BricsCAD bearbeitet werden;
|
||||
hängt an der Entscheidung zu `tro_extract`.
|
||||
|
||||
### `tro_extract.py` → entfällt im neuen Ablauf
|
||||
|
||||
Es wäre ein dritter Korrekturweg neben `cfg/tro_overrides.ini` (Layout) und
|
||||
`cfg/hw_overrides.ini` (Zuordnung) und schreibt noch das alte Schema
|
||||
`sps_skel-tro-extract-v1`, das niemand mehr liest.
|
||||
|
||||
- **Löschen** (Empfehlung), zusammen mit `TROEDIT`/`--emit-lisp`. Korrekturen nur
|
||||
in den INI-Dateien: nachvollziehbar und versioniert.
|
||||
- **Umbauen**, falls die Projektierung in BricsCAD korrigieren will: es schreibt
|
||||
dann Einträge in `tro_overrides.ini`/`hw_overrides.ini`, keine eigene JSON.
|
||||
|
||||
Bis zur Entscheidung bleibt es geparkt.
|
||||
|
||||
## Festlegungen (2026-10-11)
|
||||
|
||||
### TRO-IDs
|
||||
|
||||
**Festgelegt:** Die TRO-IDs vergeben die SPS-Programmierer, die zugleich die
|
||||
Anwender der Skripte sind. Eine ID muss nur **eindeutig** sein. Üblich ist eine
|
||||
Nummerierung entlang des Materialflusses und eine Benennung nach den Bauteilen
|
||||
(Kreisel usw.), vorgeschrieben ist das nicht. Sobald größere Teile des SPS-Codes
|
||||
stehen, ändern sich die IDs nicht mehr; in der frühen Projektphase dürfen sie
|
||||
mehrmals neu vergeben werden.
|
||||
|
||||
**Befund:** Heute vergibt `tro_flow` laufende Nummern je Lauf (`TRO01`, `TRO02` …,
|
||||
`tro_flow.py` Zeile ~1163); `cfg/tro_overrides.ini` warnt bereits, dass sich die
|
||||
Nummern bei neuer Datenbasis verschieben. Die Layout-`TeileId` ist ebenfalls nicht
|
||||
stabil: Vergleich zweier Exporte derselben Anlage
|
||||
|
||||
| Vergleich | Bauteile | gleiche Id und Lage | gleiche Lage, andere Id |
|
||||
|---|---|---|---|
|
||||
| `mubea.csv` ↔ `mubea_export.csv` | 119 / 125 | 46 | 27 |
|
||||
| `HundM05.csv` ↔ `HundM05_14.csv` | 45 / 44 | 22 | 16 |
|
||||
|
||||
Stabil ist nur die **Lage** des Bauteils.
|
||||
|
||||
**Umgesetzt (2026-10-11, `lib/tro_ids.py`, Schalter in `tro_flow.py`;
|
||||
Einzelheiten in `doc/Python_Scripts.md` §4 "TRO-IDs"):**
|
||||
|
||||
- Eine versionierte ID-Datei je Anlage, `cfg/<csv>_tro_ids.ini`, Abschnitt
|
||||
`[tro]`: je TRO die ID und die Lage seines Anker-Separators (x, y in mm), der
|
||||
Kommentar dahinter nennt Typ, Bezeichnung und Separatoren. Von `tro_flow`
|
||||
angelegt, von den SPS-Programmierern umbenannt. Weitere Abschnitte (später
|
||||
Separator- und Förderernummern) bleiben bei jedem Lauf erhalten. Ein anderer
|
||||
Export derselben Anlage nutzt dieselbe Datei über `--ids`.
|
||||
|
||||
```ini
|
||||
[tro]
|
||||
TRO01 = -400.0, 13920.0 ; 1Sep1Swi | Separator 0003 auf Kreisel2 Bahn L | Sep 0003
|
||||
K0302_1 = 10310.0, -440.1 ; von Hand umbenannt
|
||||
```
|
||||
|
||||
- **Standard (IDs halten):** `tro_flow` liest die ID-Datei und findet jeden TRO
|
||||
über die Lage seiner Separatoren wieder (Toleranz `--id-tolerance`, Standard
|
||||
100 mm, nächster Treffer zuerst, je TRO ein Eintrag). Gefundene TROs behalten
|
||||
ihre ID, auch umbenannte. Neue TROs bekommen die nächste freie Nummer und
|
||||
werden an `[tro]` angehängt; weggefallene stehen im Bericht (mit dem TRO, der
|
||||
ihren Separator jetzt enthält), bleiben in der Datei, ihre ID wird nicht
|
||||
wiederverwendet. `tro_annotate` liest dieselbe Datei, schreibt sie aber nicht.
|
||||
- **Programmschalter `--renumber` (frühe Phase):** alle IDs neu entlang des
|
||||
Materialflusses vergeben (Tiefensuche ab den Beladestationen, auf Kreisel und
|
||||
Strecke in `TrackIds`-Reihenfolge) und `[tro]` neu schreiben. Ohne ID-Datei
|
||||
geschieht dasselbe beim ersten Lauf.
|
||||
- **Geprüft** mit `mubea.csv` → `mubea_export.csv` (andere TeileIds): 18 TROs
|
||||
wiedergefunden, darunter eine umbenannte ID; `TRO14` als weggefallen gemeldet,
|
||||
weil der Export die Separatoren 0005 und 0036 zu einem `2Sep2Swi` (`TRO13`)
|
||||
zusammenfasst.
|
||||
- **Noch offen:** die Abschnitte `[mubea.csv.tro.TRO03]` usw. in
|
||||
`cfg/tro_overrides.ini` hängen an der alten Nummerierung und passen schon
|
||||
heute nicht mehr (`TRO03` ist dort Sep 0010, im aktuellen Lauf Sep 0011).
|
||||
Sie werden auf die IDs der ID-Datei umgestellt, sobald `tro_flow` die
|
||||
Overrides wieder liest.
|
||||
- Dadurch werden auch die Abschnitte `[<csv>.tro.<TRO-ID>]` in
|
||||
`cfg/tro_overrides.ini` und die Zuordnungen von `hw_assign` stabil.
|
||||
|
||||
### Staubereiche
|
||||
|
||||
**Bisheriges Namensschema** (Referenzen UH01–UH05, `doc/TRO_Katalog/connect.ini`,
|
||||
286 Staubereiche):
|
||||
|
||||
```text
|
||||
"DB_JamArea<NNNN>".stJam<NNNN>_<k> Kurzform JA<NNNN>_<k>
|
||||
```
|
||||
|
||||
- `<NNNN>` = **Förderer**, auf dem der Staubereich liegt, ein DB je Förderer:
|
||||
`0102` = Kreisel K0102 (vgl. `#fbCarousel0102`), `0103`/`0104` = Strecken,
|
||||
`4103`/`4104` = Vario-Förderer.
|
||||
- `<k>` = laufende Nummer **entlang des Materialflusses** auf diesem Förderer;
|
||||
Kreisel K0102 hat `_1` … `_6` in Umlaufrichtung.
|
||||
- 249 der 286 folgen diesem Schema. Sonderfälle: Lagerlinien `Storage1.1.2` (24),
|
||||
Übergabe an eine andere Steuerung `IO_Device_UHxx_TO_UHyy…` bzw. lokal
|
||||
`#arCarrierHandoverJam5000` (9), `JamBuffer101` im Leerträgerpuffer (2), F2H (1).
|
||||
|
||||
**Prüfung der Annahme "ein Staubereich = Ausgang des Vorgängers und Eingang des
|
||||
Nachfolgers":**
|
||||
|
||||
- **Kein** Staubereich hat mehr als einen Nachfolger-TRO.
|
||||
- **65** Staubereiche haben mehrere Vorgänger: dort laufen Ströme zusammen, z. B.
|
||||
`JA0102_2` mit Vorgängern TRO101, TRO106 und TRO107 (Weichenausgang), alle mit
|
||||
Nachfolger TRO112.
|
||||
- Jeder TRO hat genau einen Eingangs-Staubereich; einzige Ausnahme sind die
|
||||
PinStore-TROs (zusätzlich die Lagerlinie).
|
||||
|
||||
Ein Staubereich ist also immer **der Eingang genau eines TRO**, aber nicht immer
|
||||
der Ausgang genau eines Vorgängers. Ein Name nach dem Schema `ja_tro3tro4` ist bei
|
||||
Zusammenführungen nicht eindeutig.
|
||||
|
||||
**Vorschlag:**
|
||||
|
||||
**Ergänzung (2026-10-11):** In einem Mubea-Layout heißen die Staubereiche nach
|
||||
**ihrem Separator**: der Staubereich vor `Sep1024` heißt `J1024`. Das passt zum
|
||||
Befund oben: der Staubereich ist die Warteschlange vor dem Stopper, also der
|
||||
Eingang genau eines TRO bzw. genau eines Separators. Das Layout liegt nicht im
|
||||
Repo; Quelle der Separatornummer (Zeichnungsbeschriftung, E-Planung, Vergabe durch
|
||||
die SPS-Programmierer) noch klären.
|
||||
|
||||
- **Kennung nach dem Separator:** `J<Separatornummer>`, z. B. `J1024`. Das ist
|
||||
feiner als eine Kennung je TRO und deckt TROs mit mehreren Separatoren
|
||||
(`2Sep1Swi` mit `JamEntr1`/`JamEntr2`) ohne Sonderregel ab. Zusammenführungen
|
||||
sind kein Problem, weil der Name nur am Nachfolger hängt.
|
||||
- **Separatornummern wie TRO-IDs behandeln:** in derselben ID-Datei, über die Lage
|
||||
des Separators wiedergefunden, von den SPS-Programmierern vergeben bzw.
|
||||
übernommen; `--renumber` vergibt sie in der frühen Phase neu.
|
||||
- **Name im SCL — festgelegt: Standard sind die SCL-Dateien UH01–UH05,** also
|
||||
`"DB_JamArea<NNNN>".stJam<NNNN>_<k>`. `J<Separatornummer>` ist nur die Kennung
|
||||
in der JSON (und die Beschriftung im Layout), nicht der SCL-Name. Für den
|
||||
SCL-Namen braucht es je Staubereich:
|
||||
- `<NNNN>`: die **Förderernummer** des Förderers, auf dem der Staubereich liegt
|
||||
(Kreisel K0102 → `0102`); vergeben von den SPS-Programmierern bzw. aus der
|
||||
Hardwareplanung (`KR-K0102`), gepflegt in der ID-Datei wie TRO-IDs und
|
||||
Separatornummern;
|
||||
- `<k>`: laufende Nummer **entlang des Materialflusses** auf diesem Förderer,
|
||||
aus dem Layout abgeleitet (Kreisel in Umlaufrichtung, beginnend an einer
|
||||
festzulegenden Stelle, z. B. nach der Einschleusung).
|
||||
|
||||
Ohne Förderernummer bleibt der Name `TODO(E-Planung)` wie heute.
|
||||
- Ausnahmen bleiben Sonderfälle mit eigenem Namen: Lagerlinien, Übergabe an eine
|
||||
andere Steuerung, Leerträgerpuffer.
|
||||
|
||||
### Korrekturen in BricsCAD
|
||||
|
||||
**Zurückgestellt.** Eine Alternative wäre, die `<csv>_tro.json` zu visualisieren
|
||||
und von Hand zu bearbeiten. Bis dahin bleibt `tro_extract.py` geparkt, `TROEDIT`
|
||||
unverändert.
|
||||
|
||||
## Reihenfolge
|
||||
|
||||
1. **TRO-IDs stabil machen:** ID-Datei, Wiederfinden über die Lage,
|
||||
`--renumber`. Darauf bauen Overrides, Staubereiche und Zuordnungen auf.
|
||||
**Erledigt** (2026-10-11).
|
||||
2. Slots und `jamAreas` in die TRO-JSON (Umbau 1), ein Eingangs-Staubereich je
|
||||
TRO — ohne Slots hat die Zuordnung kein Ziel. **Erster Teil erledigt**
|
||||
(2026-10-11, siehe Umbau 1 "Stand").
|
||||
3. `hw_import` mit dem TIA-Import als erster Quelle.
|
||||
4. `hw_assign` mit der räumlichen Logik aus `e_planning.py`; `e_planning.py` als
|
||||
Übergangshülle.
|
||||
5. `scl_skeleton` auf `<csv>_anlage.json` umstellen, `--eplan` entfernen;
|
||||
`tro_annotate` auf JSON umstellen.
|
||||
6. Defaults (`cfg/defaults.ini`), `FB_CallSensors`, EA-Liste ohne Positionen.
|
||||
|
||||
Zu jedem Schritt: `bin/`-Wrapper für neue Skripte, Tests unter `tests/`
|
||||
(kleine TIA-Datei als Testdaten), Golden Files bei Schemawechsel bewusst neu
|
||||
erzeugen, `doc/Python_Scripts.md` ergänzen.
|
||||
|
||||
## Offene Entscheidungen
|
||||
|
||||
- **Staubereiche:** woher die Separatornummer (`Sep1024` → `J1024`) stammt; wo auf
|
||||
einem Kreisel die Zählung `<k>` beginnt (Referenz K0102 prüfen). Das SCL-Schema
|
||||
ist festgelegt (UH01–UH05).
|
||||
- **Toleranz** beim Wiederfinden der TROs über die Lage: vorläufig 100 mm
|
||||
(`--id-tolerance`). Ein weiter verschobener Separator wird als "neu" und
|
||||
"weggefallen" gemeldet, der Programmierer übernimmt die ID von Hand in der
|
||||
ID-Datei. Mit echten Layoutänderungen prüfen, ob der Wert passt.
|
||||
- **Korrekturen in BricsCAD oder in der JSON:** zurückgestellt.
|
||||
+152
-14
@@ -1,14 +1,16 @@
|
||||
# Python Scripts — CLI Tooling Overview
|
||||
|
||||
**As of:** 2026-10-09
|
||||
**As of:** 2026-10-10
|
||||
|
||||
`lib/` currently holds ten Python modules (five of them runnable CLI tools,
|
||||
`lib/` currently holds eleven Python modules (six of them runnable CLI tools,
|
||||
five supporting libraries), plus one documentation-generator script that lives
|
||||
under `doc/TRO_Katalog/tro_graphs/`. Together the CLI tools turn a CSV export
|
||||
of the mechanical layout (ILS 2.0) into a material-flow graph, a derived TRO
|
||||
list/diagram, a TRO-JSON (shared model `lib/model.py`), an `FB_Main` SCL
|
||||
skeleton generated from that JSON, and an annotated copy of the BricsCAD
|
||||
drawing for visual checking.
|
||||
drawing for visual checking. `e_planning.py` bridges to the electrical
|
||||
planning: it assigns the sensors/valves of the E-planning drawing to the
|
||||
layout parts, so the skeleton can carry their real names.
|
||||
|
||||
This document is the practical "what does each script do and which switches
|
||||
does it take" reference. For the underlying domain model (TRO types, JSON
|
||||
@@ -34,6 +36,11 @@ tro_flow.py ──► <name>_tro_flow.dot / .svg, _tro_doc.md, _tro_error
|
||||
│ scl_skeleton.py ──► <name>_FB_Main.scl
|
||||
│ (validates the JSON against model.py; one REGION per TRO;
|
||||
│ E-planning values left as TODO(E-Planung) gaps)
|
||||
│ ▲
|
||||
│ │ --eplan <name>_eplan.json (optional)
|
||||
│ e_planning.py ◄── <drawing>_positionsdraw.json (kabellaengen)
|
||||
│ (assigns BG/MB of the E-planning drawing to the layout
|
||||
│ parts by position + numbering rule; see §5c)
|
||||
│
|
||||
└── same derivation ──► tro_annotate.py ──► <name>_annotated.dxf (VIEWER ONLY)
|
||||
(also needs the BricsCAD DXF; uses dxf_registration.py for the CSV↔DXF
|
||||
@@ -66,6 +73,10 @@ Supporting libraries (no CLI of their own, imported by the tools above):
|
||||
- **`lib/dxf_registration.py`** — derives, verifies and persists the rigid
|
||||
(rotation + offset) transform between the CSV export's coordinate system and
|
||||
the DXF drawing's.
|
||||
- **`lib/tro_ids.py`** — the per-plant ID file `%SKEL_CFG%\<name>_tro_ids.ini`:
|
||||
reads it, finds each TRO again by the position of its separators, assigns
|
||||
new IDs, writes/extends the file. Used by `tro_flow.py` (read and write) and
|
||||
`tro_annotate.py` (read only). See §4, "TRO-IDs".
|
||||
- **`lib/tro_overrides.py`** — reads the hand-maintained
|
||||
`%SKEL_CFG%\tro_overrides.ini`; only `material_flow.py --connect` still uses
|
||||
it. See §6a.
|
||||
@@ -91,6 +102,8 @@ Each CLI tool is invoked through its `bin/<name>.bat` (Windows) / `bin/<name>.sh
|
||||
bin\material_flow.bat --file mubea.csv --tosvg --use-cords --doc
|
||||
bin\tro_flow.bat --file mubea.csv --tosvg --use-cords --doc --json
|
||||
bin\scl_skeleton.bat --json mubea_tro.json
|
||||
bin\e_planning.bat --file mubea.csv --eplan Mubea_positionsdraw.json
|
||||
bin\scl_skeleton.bat --json mubea_tro.json --eplan mubea_eplan.json
|
||||
bin\tro_annotate.bat --file mubea.csv --dxf 500573_60_1.dxf --flow --fb --legend
|
||||
```
|
||||
|
||||
@@ -102,8 +115,9 @@ Conventions shared by the CLI tools:
|
||||
- `--file` / `--dxf` accept either a bare filename — resolved against
|
||||
`%SKEL_DATA%` — or a full path.
|
||||
- All generated output is written to `%SKEL_RESULTS%`.
|
||||
- `tro_annotate.py` additionally persists the verified CSV↔DXF coordinate
|
||||
offset to `%SKEL_CFG%\dxf_registration.json`.
|
||||
- `tro_annotate.py` and `e_planning.py` additionally persist the verified
|
||||
coordinate offset (CSV↔DXF, CSV↔E-planning drawing) to
|
||||
`%SKEL_CFG%\dxf_registration.json`.
|
||||
- Every run prints a `====` bordered report to stdout (counts, warnings,
|
||||
output file paths); non-fatal findings are printed as `?`/`!` lines and also
|
||||
do not stop the run.
|
||||
@@ -294,17 +308,45 @@ colors all come from `lib/tro_catalog.py` — nothing is hard-coded here.
|
||||
| `--doc` | — | off | Also write a Markdown report (`<name>_tro_doc.md`) with the TRO list, connections, findings and the full type catalog. |
|
||||
| `--connect` | — | off | Also write the TRO topology as `<name>_tro_connect.ini` (nodes, connections, system boundaries, predecessors/successors per TRO). Pure output, never read back. |
|
||||
| `--true-scale` | — | off | Only with `--use-cords`: draw to scale. By default empty stretches longer than 1500 mm between nodes are shortened (order and neighbourhood are kept). |
|
||||
| `--json` | — | off | Also write the TRO-JSON `<name>_tro.json` (schema `sps_skel-tro-v3`, model `TroDocument` in `lib/model.py`): TROs, system boundaries (`SYSn`), connections with their kind, findings. Input for `scl_skeleton.py`. |
|
||||
| `--json` | — | off | Also write the TRO-JSON `<name>_tro.json` (schema `sps_skel-tro-v4`, model `TroDocument` in `lib/model.py`): TROs with their `slots`, system boundaries (`SYSn`), connections with their kind, `jamAreas`, findings. Input for `scl_skeleton.py`. |
|
||||
| `--ids` | `DATEI` | `<name>_tro_ids.ini` | ID file of the plant (name in `%SKEL_CFG%`, or a path). Use it to run another export of the same plant against the same IDs, e.g. `--file mubea_export.csv --ids mubea_tro_ids.ini`. |
|
||||
| `--renumber` | — | off | Number all TROs afresh along the material flow and rewrite the `[tro]` section of the ID file (early project phase). Renamed IDs are lost; other sections of the file are kept. |
|
||||
| `--id-tolerance` | `MM` (float) | `100.0` | Maximum distance between an ID-file entry and a separator of a TRO for the TRO to count as found again. |
|
||||
|
||||
TROs, types and connections always come from the layout alone;
|
||||
`cfg/tro_overrides.ini` is not read. Errors in the derivation are fixed in
|
||||
`lib/tro_flow.py`, not in a correction file.
|
||||
|
||||
**TRO-IDs** (`lib/tro_ids.py`). The IDs belong to the SPS programmers and must
|
||||
survive a new layout export; the layout `TeileId` changes between exports, the
|
||||
position does not. So every run reads `%SKEL_CFG%\<name>_tro_ids.ini`:
|
||||
|
||||
```ini
|
||||
[tro]
|
||||
TRO01 = -400.0, 13920.0 ; 1Sep1Swi | Separator 0003 auf Kreisel2 Bahn L | Sep 0003
|
||||
K0302_1 = 10310.0, -440.1 ; renamed by hand
|
||||
```
|
||||
|
||||
- Each entry is `ID = x, y` (mm, CSV coordinates) of the TRO's anchor
|
||||
separator. An entry finds the TRO with a separator within `--id-tolerance`
|
||||
(closest first, one entry per TRO), and that TRO keeps the ID — renamed IDs
|
||||
included. An ID must be unique and a valid SCL identifier; `SYSn` is
|
||||
reserved for system boundaries.
|
||||
- No file yet: the TROs are numbered along the material flow (depth-first from
|
||||
the loading stations over the material-flow graph, on a carousel or Strecke in
|
||||
`TrackIds` order) and the file is created.
|
||||
- A TRO not found gets the next free `TRO<n>` and is appended to `[tro]`;
|
||||
an entry without a TRO is reported ("weggefallen", with the TRO that now
|
||||
holds its separator, e.g. after two TROs merged into a `2Sep2Swi`) and stays
|
||||
in the file; its ID is not reused.
|
||||
- The console report lists found / new / dropped IDs.
|
||||
|
||||
**Output** (in `%SKEL_RESULTS%`): `<name>_tro_flow.dot` (always, with `pos`
|
||||
attributes when `--use-cords` was used), `<name>_tro_flow.svg` (with
|
||||
`--tosvg`), `<name>_tro_doc.md` (with `--doc`), `<name>_tro.json` (with
|
||||
`--json`), `<name>_tro_errors.md` (only if TROs are unconnected or of an
|
||||
unknown type).
|
||||
unknown type). In `%SKEL_CFG%`: `<name>_tro_ids.ini` (created, extended or,
|
||||
with `--renumber`, rewritten — see above).
|
||||
|
||||
**Exit codes:** `0` ok · `1` input/CLI error · `2` SVG rendering failed
|
||||
(Graphviz) · `3` TRO validation errors found (error file written).
|
||||
@@ -355,6 +397,7 @@ overridden.
|
||||
| `--legend` | — | off | Draw a legend of the color groups. |
|
||||
| `--marker-size` | `MM` (float) | `300.0` | Marker radius in mm. |
|
||||
| `--text-height` | `MM` (float) | `220.0` | Text height in mm. |
|
||||
| `--ids` | `DATEI` | `<csv-stem>_tro_ids.ini` | ID file as for `tro_flow.py`, **read only**: the markers carry the same IDs as the TRO-JSON. Without the file the TROs are numbered along the material flow, as `tro_flow.py` would. |
|
||||
| `--offset` | `DX,DY` | — | Set the CSV↔DXF offset by hand instead of deriving/loading it. For a drawing built with dxfmakros (CSV exported from the same drawing) this is `0,0`. |
|
||||
| `--re-register` | — | off | Re-derive the coordinate offset even if a verified one is already cached in `%SKEL_CFG%`. |
|
||||
| `--check` | — | off | Dry run: only analyze and report, write no output file. |
|
||||
@@ -446,7 +489,8 @@ is missed.
|
||||
|---|---|
|
||||
| REGION order (material flow), instance list, FB type per TRO | sensor/actuator addresses (`cInBG`, `MB`, `MA`) |
|
||||
| number of separator / switch / scanner parameter blocks | separator and switch numbers, `cMainTro` |
|
||||
| `nTo1Destinations` when the TRO has exactly one switch | jam areas (`DB_JamArea…`) |
|
||||
| `nTo1Destinations` when the TRO has exactly one switch | jam-area DB names (`DB_JamArea…`) |
|
||||
| number of jam entries (one per own jam area, not per predecessor) and which jam area each exit leads to (`J<Separator>` in the comment) | |
|
||||
| destination list per switch exit (graph reachability) | times marked "pruefen" |
|
||||
| cross-reference to the layout parts (separator numbers, coordinate) | |
|
||||
|
||||
@@ -460,7 +504,7 @@ than a guess.
|
||||
|
||||
Switches: `--json NAME` (default `export_tro.json`, looked up in
|
||||
`%SKEL_RESULTS%`; written by `tro_flow.py --json`), `--out NAME` (default
|
||||
`<source>_FB_Main.scl`), `--start TRO`. The last one matters while the plant has
|
||||
`<source>_FB_Main.scl`), `--eplan NAME` (see below), `--start TRO`. The last one matters while the plant has
|
||||
no loading/unloading station: the flow is then a closed loop with no entry, so
|
||||
the order has to be broken somewhere arbitrary. The chosen start and the reason
|
||||
are recorded in the file header.
|
||||
@@ -494,6 +538,16 @@ by a PLC engineer (`doc/SCL_Templates.md` §6). `2Sep1Swi`, `EmptyCarrBuffer`,
|
||||
`LoadingBoom` and `2Sep2Swi` have no call site yet and stay generic. `--strict` stops (exit `1`, no SCL) if any FB type has no
|
||||
template. Without `--templates` the output is unchanged.
|
||||
|
||||
**Names from the E-planning (opt-in, `--eplan NAME`).** Reads the assignment
|
||||
`<csv>_eplan.json` written by `e_planning.py` (§5c; bare name looked up in
|
||||
`%SKEL_RESULTS%`). For every separator it assigns, the separator sensor
|
||||
(`stSenInSep := "DB_Inputs".Sensors["cInBG0642"]`) and the stopper valve
|
||||
(`xOutStopper<n> => "MB0643"`) are filled in and lose their `TODO(E-Planung)`
|
||||
marker (`SclName.todo = False`); everything else stays open. The header names
|
||||
the E-planning file. An assignment made for a different CSV (`csv_file` ≠ the
|
||||
TRO-JSON's `source`) stops the run with exit `1` — separator numbers are only
|
||||
unique within one export. Without `--eplan` the output is unchanged.
|
||||
|
||||
Tests: `tests/test_scl_context.py` (the derivations one by one),
|
||||
`tests/test_scl_templates.py` (each template rendered with the real values of
|
||||
its reference call site must equal that call site, comments and whitespace
|
||||
@@ -502,6 +556,86 @@ HundM05 byte for byte against `tests/fixtures/scl/*_FB_Main.scl` and
|
||||
`*_FB_Main_templates.scl`, timestamp masked; an intended change is written
|
||||
back with `SKEL_UPDATE_GOLDEN=1`).
|
||||
|
||||
## 5c. `e_planning.py` — assign the E-planning to the layout parts
|
||||
|
||||
The bridge between layout and electrical planning (background and findings:
|
||||
[`E-Planung_Bruecke.md`](E-Planung_Bruecke.md)). The layout knows which
|
||||
separators, switches and outfeed elements exist and where; the E-planning knows
|
||||
what their sensors and valves are called. The I/O list alone has no topology —
|
||||
but the E-planning is drawn as a layer over the hardware layout, every sensor or
|
||||
actuator symbol sits next to its part, and the cable-length tool
|
||||
(`kabellaengen/lib/getpositions.py`) writes those symbols with position and
|
||||
attributes to `<drawing>_positionsdraw.json`.
|
||||
|
||||
```
|
||||
<name>.csv (layout) + <drawing>_positionsdraw.json (E-planning)
|
||||
│
|
||||
▼
|
||||
e_planning.py ──► <name>_eplan.json (EplanDocument, schema sps_skel-eplan-v1)
|
||||
──► <name>_eplan.md (report: assignments, gaps, drives)
|
||||
│
|
||||
▼
|
||||
scl_skeleton.py --eplan <name>_eplan.json
|
||||
```
|
||||
|
||||
Steps:
|
||||
|
||||
1. **Read** `sensors` (field devices) and `schaltschrank_elemente` (cabinet) of
|
||||
the positionsdraw file into `EDevice` (BMK, `VERW`, `BEZEICHNUNG`,
|
||||
`KENNZEICHNUNG` → controller `UH01` / I/O station `KF107`, position). A BMK
|
||||
listed twice keeps the entry that has a `VERW`.
|
||||
2. **Registration** E-planning drawing → CSV with `dxf_registration.py`: the
|
||||
E symbols matching `[Registrierung] anker` (default `IN SEP`, the sensor that
|
||||
sits on the separator) against the CSV separators. Stored under the key
|
||||
`<csv>|<positionsdraw>` in `%SKEL_CFG%\dxf_registration.json` and re-verified
|
||||
on every run; `--re-register` forces a new derivation.
|
||||
3. **Rules** from `%SKEL_CFG%\e_planning.ini`, one section `<kind>.<role>` per
|
||||
rule, applied in file order; each E device is given out at most once, each
|
||||
role of a layout part filled at most once:
|
||||
|
||||
| key | meaning |
|
||||
|---|---|
|
||||
| `layout` | layout kind pattern (default: the part before the dot): `Separator`, `Ausschleus`, `Einschleus`, `Weiche`, `Scanner`, `Beladung`, `Entladung` |
|
||||
| `verw`, `praefix` | pattern(s) for the E attribute `VERW`, BMK prefix (`BG`, `MB`) |
|
||||
| `max_mm` | spatial: maximum distance after registration |
|
||||
| `soll_mm`, `toleranz_mm` | spatial: the symbol always sits this far from the part; candidates are ranked by their deviation from it, not by the smallest distance |
|
||||
| `nummer_von`, `nummer_versatz` | numbering rule: BMK number = number of the given role + offset (Mubea: valve = sensor + 1) — tried first, distance only for the rest |
|
||||
|
||||
Shipped rules (tuned on Mubea): `Separator.sensor`, `Separator.stopper`,
|
||||
`Ausschleus.stau`, `Ausschleus.ventil`, `Weiche.ventil`. `VERW` texts and the
|
||||
numbering rule are project-specific — in the reference SCL (UH01..UH05)
|
||||
sensor and stopper share the number (BG3242/MB3242), i.e. offset `0`.
|
||||
4. **Drives**: motor `MAnnnn` ↔ motor protection `FCnnnn` (same number), for the
|
||||
report.
|
||||
|
||||
Uncertain cases stay open on purpose: a gap is better than a plausible wrong
|
||||
name. The report lists, per role, the layout parts without a match, the field
|
||||
devices without a match, and distance and runner-up distance of every match
|
||||
(close values = check by hand).
|
||||
|
||||
Switches: `--file NAME` (CSV, default `export.csv`), `--eplan NAME`
|
||||
(positionsdraw JSON, required; bare name resolved against `%SKEL_DATA%`, or a
|
||||
full path), `--rules FILE` (default `%SKEL_CFG%\e_planning.ini`),
|
||||
`--re-register`. Exit `0` also when not everything was assigned; `1` on input
|
||||
errors or when the registration cannot be proven.
|
||||
|
||||
**Today only separator sensor and stopper reach the SCL.** Jam sensors and
|
||||
outfeed/switch valves are assigned in the JSON/report, but the TRO-JSON does not
|
||||
yet say which outfeed element or switch belongs to which TRO exit, so
|
||||
`scl_skeleton` cannot place them. The same holds for the storage lines of a
|
||||
`PinStore` TRO (`arInSep[n]`): their sensors are assigned but the generator does
|
||||
not emit per-line parameters yet.
|
||||
|
||||
Result on Mubea (`mubea.csv` + `Mubea_positionsdraw.json`): registration
|
||||
dx +50613.6 / dy +8555.8 mm; separator sensor 21/38, stopper 22/38 (all 20
|
||||
storage lines exact, 0–1 mm), jam sensor 20/23, outfeed valve 20/23, switch
|
||||
valve 1/1. The open separators are on the carousels K0102/K0104/K0105, where
|
||||
the E-planning drawing and the CSV apparently differ.
|
||||
|
||||
Tests: `tests/test_e_planning.py` (reading, rules, numbering vs. distance on a
|
||||
storage-line grid, target distance, uniqueness, registration, drives, the path
|
||||
into `scl_context` and `scl_skeleton --eplan`).
|
||||
|
||||
## 6. `tro_catalog.py` — TRO type catalog (library, no CLI)
|
||||
|
||||
The single source of truth for everything needed to generate or draw a TRO
|
||||
@@ -535,8 +669,8 @@ TRO in a plant, and the whole TRO-JSON, look like. It imports only the catalog.
|
||||
```
|
||||
tro_catalog.py type level TroDefinition (items, FB block, color, symbol)
|
||||
▲ get_tro()
|
||||
model.py instance level TroBase ─► DerivedTro, Connection, TroCandidate,
|
||||
Analysis, TroDocument
|
||||
model.py instance level TroBase ─► DerivedTro, Slot, JamArea, Connection,
|
||||
TroCandidate, Analysis, TroDocument
|
||||
▲
|
||||
tro_flow.py (writes) · scl_skeleton.py (reads) · tro_annotate.py (draws)
|
||||
```
|
||||
@@ -544,11 +678,13 @@ tro_flow.py (writes) · scl_skeleton.py (reads) · tro_annotate.py (draws)
|
||||
| Class | What it is |
|
||||
|---|---|
|
||||
| `TroBase` | Core of every TRO: `id`, `type`, `confidence`, `separators`, `predecessors`, `successors`, `position` (`{x,y,z}` in JSON). `definition()`, `fb_block` (JSON `fbType`) and `items` are **read from the catalog** on every access, never stored — so they follow a type change and cannot go stale. |
|
||||
| `DerivedTro` | `TroBase` + what `tro_flow.py` knows from the derivation: `label`, `hosts`, `scanners`, `reason`, `colocated`, `positionFrom`, `positionPoints`. Also used for the system boundaries `SYSn`. |
|
||||
| `DerivedTro` | `TroBase` + what `tro_flow.py` knows from the derivation: `label`, `hosts`, `scanners`, `reason`, `colocated`, `positionFrom`, `positionPoints`, `slots`. Also used for the system boundaries `SYSn` (no slots). |
|
||||
| `Slot` | A place on a TRO where hardware belongs: `slot` (`separator1`, `switch1`, `scanner1`, `drive` — the stable key together with the TRO-ID), `layoutId` (TeileId in the current export, for tracing only; empty for switches, whose element `tro_flow` does not keep yet), `roles` the hardware planning must fill (`sensor`, `stopper`, `jamSensor?` …; `?` = optional). Built by `derive_slots()` from the catalog type. |
|
||||
| `JamArea` | A jam area = the queue in front of a separator, the entry of exactly one TRO: `id` (`J<Separator>`, e.g. `J0042`), `tro`, `entry` (1, 2 for 2Sep types), `separator`, `host`, `predecessors` (all TROs/stations feeding it — merges have several), `note`, `position`. Built by `derive_jam_areas()`; the SCL name `"DB_JamArea<NNNN>".stJam<NNNN>_<k>` needs the conveyor number from the hardware planning and is not stored. |
|
||||
| `Connection` | Directed edge `from` → `to` with its `kind` (transfer / umlauf / weiche / unbestimmt / normal / bahn). |
|
||||
| `TroCandidate` | A spot reported in a review where a TRO is **expected** but not derived (formerly `OpenPoint`): `name`, `type` (the expected catalog type), `position`, `belongsTo` (the TRO that holds the part today), `note`. Not a TRO — no separators, no connections, no SCL. Built by `tro_overrides.py` from `[<csv>.openpoint.<Name>]`; empty in the TRO-JSON for now. |
|
||||
| `Analysis` | Working result of `tro_flow.analyse_tros` (moved here from `tro_flow.py`). |
|
||||
| `TroDocument` | The TRO-JSON: `schema` (`sps_skel-tro-v3`), `source`, `created`, `note`, `tros`, `boundaries`, `connections`, `candidates`, `findings`. `from_analysis()`, `to_json()`, `from_json()`. |
|
||||
| `TroDocument` | The TRO-JSON: `schema` (`sps_skel-tro-v4`), `source`, `created`, `note`, `tros`, `boundaries`, `connections`, `candidates`, `jamAreas`, `findings`. `from_analysis()` (derives slots and jam areas), `to_json()`, `from_json()`, `jam_areas_or_derived()` (a v3 file without `jamAreas` still renders). |
|
||||
|
||||
`TroBase` and `TroCandidate` share the `position` handling (JSON `{x,y,z}`,
|
||||
`x`/`y`/`z`, `describe_position()`) through the private mixin `_Positioned`.
|
||||
@@ -560,7 +696,9 @@ All models validate on creation and assignment and reject unknown keys
|
||||
(`extra="forbid"`). JSON keys keep the camelCase names of the TRO-JSON (`id`,
|
||||
`type`, `fbType`, `belongsTo` …); Python code uses the field names
|
||||
(`tro_id`, `type_name`, `fb_block`, `belongs_to`) — both are accepted on input.
|
||||
Schema v3 differs from v2 only in `candidates` replacing `openPoints`.
|
||||
Schema v3 differs from v2 only in `candidates` replacing `openPoints`; v4 adds
|
||||
`slots` per TRO and `jamAreas`. A v3 file still reads (both default to empty;
|
||||
`scl_skeleton` then derives the jam areas while rendering).
|
||||
|
||||
Tests: `tests/test_model.py` (round trip, catalog-backed fields, rejection of
|
||||
wrong keys/items, SCL rendering from a document) — run with
|
||||
|
||||
@@ -14,7 +14,7 @@ It is written so that it fits the existing main path unchanged:
|
||||
```
|
||||
export.csv ─► tro_flow --json ─► <name>_tro.json ─► scl_skeleton ─► <name>_FB_Main.scl
|
||||
(dxfmakros (derive) (TroDocument, (render) (skeleton with
|
||||
EXPORTCSV) sps_skel-tro-v3) TODO(E-Planung))
|
||||
EXPORTCSV) sps_skel-tro-v4) TODO(E-Planung))
|
||||
```
|
||||
|
||||
Nothing upstream of `scl_skeleton` changes. The TRO-JSON stays the only input, and the
|
||||
@@ -274,11 +274,11 @@ reference line":
|
||||
| Template (reference) | Decisions to confirm |
|
||||
|---|---|
|
||||
| `1Sep` (TRO100) | times 600/200 ms and `tSenFree`/`tSenWait` 1500/4000 ms as in the reference (`pruefen`); `xInCarouselRun` always written as TODO; `xInRelease := "DB_TRO_TEST".<id>` as in the reference |
|
||||
| `Vario` (TRO115) | times 800/300 ms, `xPluggedJam FALSE`, `tSenFree`/`tSenWait` 2000/5000 ms, `tRasterHigh/Low` 5 s (`pruefen`); `stInSettings.xIsBorner := TRUE` marked TODO (meaning unclear); `JamEntr` = first predecessor, `JamFinger` = first successor; `xInErrorQuit`/`xInAutomatic` open |
|
||||
| `Vario` (TRO115) | times 800/300 ms, `xPluggedJam FALSE`, `tSenFree`/`tSenWait` 2000/5000 ms, `tRasterHigh/Low` 5 s (`pruefen`); `stInSettings.xIsBorner := TRUE` marked TODO (meaning unclear); `JamEntr` = the TRO's own entry jam area (TRO-JSON v4: several predecessors merge into it), `JamFinger` = the entry jam area of the first successor; `xInErrorQuit`/`xInAutomatic` open |
|
||||
| `Vario_workStation` (TRO421) | `xPluggedScanner FALSE` although the catalog lists a scanner (it sits at the work station, not the separator — `pruefen`); work-station sensor, release and `xBeladenError` sensor open |
|
||||
| `1Sep2Swi` (TRO267) | separator without times, `xPluggedPart TRUE` + `stSenPart` as in the reference; **switch 2 has no `nSwitchNo` in the reference** — written as a commented-out proposal; `nTo1Destinations` per switch open (split of the exits); `bSepType.%X2 := TRUE` marked `pruefen`; one `xInCarouselRun<n>` per switch; **no `FC_Direction`** at this place in the reference — only the destination list as a comment; no `nOutStateLast` (not in the reference) |
|
||||
| `1Sep_SSCC` (TRO105) | second (SSCC) camera with its own interface structure, end-measurement sensor and HMI key open; `xInRelease` written as `"DB_TRO_TEST".<id>` with a TODO (the reference releases via the WCS connection); `FC_CARR_GLOB_CORRECTION` with `nInStation` open; the WCS telegram region of the reference is only noted as TODO |
|
||||
| `PinStore_Auto` (TRO208) | **number of lines from the layout** (one per storage lane, `nNoOfLines`), not the 19 of the reference; all plant constants (encoder, destinations, stations, thresholds, storage DB, removal handshake) open; pin separator times 0/50 ms (`pruefen`); simulation settings `stSimu` of the reference not written; the FB has one `JamEntr`/`JamExit` — a store with more feeds/exits (mubea: 2/2) gets a TODO in the REGION header |
|
||||
| `PinStore_Auto` (TRO208) | **number of lines from the layout** (one per storage lane, `nNoOfLines`), not the 19 of the reference; all plant constants (encoder, destinations, stations, thresholds, storage DB, removal handshake) open; pin separator times 0/50 ms (`pruefen`); simulation settings `stSimu` of the reference not written; the FB has one `JamEntr`/`JamExit` — since v4 several feeds merge into the one entry jam area (mubea: 2 feeds → 1 entry); a store with more than one entry or exit jam area (mubea: 2 exits) gets a TODO in the REGION header |
|
||||
|
||||
New context fields for these templates (`lib/scl_context.py`): Cognex wiring per scanner
|
||||
(`interface`, `tag`, `hw_results`), `part_sensor` per separator, `carriers`/`data` per jam
|
||||
@@ -290,7 +290,8 @@ path does not use them — its golden files are unchanged.
|
||||
Effect on the open-value count with `--templates`: with the pilot only, mubea 282 → 302
|
||||
and HundM05 323 → 377; with all seven templates mubea → 489 and HundM05 → 475
|
||||
`TODO(E-Planung)` (HundM05 now comes completely from templates, mubea except the
|
||||
`2Sep2Swi`). More reference lines are written and every open value is marked; the count
|
||||
`2Sep2Swi`). Since TRO-JSON v4 (one entry jam area per TRO instead of one per
|
||||
predecessor) it is: mubea 277 generic → 482 with templates, HundM05 316 → 461. More reference lines are written and every open value is marked; the count
|
||||
drops again with the naming rules (step 4).
|
||||
|
||||
---
|
||||
@@ -420,7 +421,7 @@ With `--templates` the console summary gets a `Templates` line (implemented); th
|
||||
|
||||
```
|
||||
Templates = 4 von 5 Typen (generisch: FB_ILS_MTRO_2Sep2Swi)
|
||||
Offene Stellen = 489 (Marker 'TODO(E-Planung)')
|
||||
Offene Stellen = 482 (Marker 'TODO(E-Planung)')
|
||||
```
|
||||
|
||||
---
|
||||
@@ -478,7 +479,7 @@ bin\scl_skeleton.bat --json <name>_tro.json [--templates [DIR]] [--strict] [--na
|
||||
| File | Where | Who writes it |
|
||||
|---|---|---|
|
||||
| `<name>.csv` | `%SKEL_DATA%` | dxfmakros `EXPORTCSV` |
|
||||
| `<name>_tro.json` | `%SKEL_RESULTS%` | `tro_flow --json` (schema `sps_skel-tro-v3`) |
|
||||
| `<name>_tro.json` | `%SKEL_RESULTS%` | `tro_flow --json` (schema `sps_skel-tro-v4`) |
|
||||
| `scl_naming.ini`, `<name>_scl_naming.ini` | `%SKEL_CFG%` | you, once per project (optional) |
|
||||
| `lib/scl_templates/*.scl.j2` | repo | maintained with the PLC engineers |
|
||||
|
||||
|
||||
@@ -0,0 +1,715 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Absichtlich ohne Shebang: der Windows-py-Launcher wuerde daraus "python3"
|
||||
# ableiten und scheitert, wenn keine PythonCore-Installation registriert ist.
|
||||
"""
|
||||
e_planning.py - Bruecke Layout <-> E-Planung ueber die Symbolpositionen.
|
||||
|
||||
Das Layout (CSV) sagt, welche Separatoren, Weichen, Aus-/Einschleuselemente
|
||||
es gibt und wo; die E-Planung, wie ihre Sensoren und Ventile heissen
|
||||
(BG0642, MB0643 ...). Die EA-Liste allein kennt keine Topologie. Die
|
||||
E-Planung wird aber als Layer ueber der Hardwareplanung gezeichnet: jedes
|
||||
Sensor-/Aktorsymbol liegt neben dem Bauteil, zu dem es gehoert. Das
|
||||
Kabellaengen-Tool (kabellaengen/lib/getpositions.py) schreibt diese Symbole
|
||||
mit Position und Attributen nach <zeichnung>_positionsdraw.json.
|
||||
|
||||
Hier wird daraus die Zuordnung Layout-Bauteil -> BMK:
|
||||
|
||||
1. E-Bauteile einlesen (`sensors` und `schaltschrank_elemente`).
|
||||
2. Lagebezug E-Zeichnung -> CSV mit lib/dxf_registration.py; Anker sind die
|
||||
Separator-Sensoren der E-Planung gegen die Separatoren des CSV. Der
|
||||
gepruefte Bezug wird in SKEL_CFG/dxf_registration.json abgelegt.
|
||||
3. Regeln aus SKEL_CFG/e_planning.ini anwenden, je Rolle (z. B.
|
||||
"Separator.sensor"): welche Layout-Art, welche E-Verwendung (VERW),
|
||||
welcher Hoechstabstand - oder "Nummer = Nummer des Partners + Versatz"
|
||||
(Ventil MB0643 zum Sensor BG0642). Die Zuordnung ist eindeutig: jedes
|
||||
E-Bauteil und jede Rolle eines Layout-Bauteils wird hoechstens einmal
|
||||
vergeben, die kuerzesten Abstaende zuerst.
|
||||
4. Ergebnis als <csv>_eplan.json (EplanDocument) und Bericht
|
||||
<csv>_eplan.md nach SKEL_RESULTS.
|
||||
|
||||
scl_skeleton.py --eplan liest die JSON und setzt die gefundenen Namen in die
|
||||
SCL ein (SclName mit todo=False). Was keine Regel trifft, bleibt dort
|
||||
TODO(E-Planung) - geraten wird nicht.
|
||||
|
||||
Schichten: e_planning importiert material_flow (CSV) und dxf_registration
|
||||
(Lagebezug); scl_context importiert nur das Modell EplanDocument.
|
||||
|
||||
Aufruf ueber bin/e_planning.bat bzw. bin/e_planning.sh.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import configparser
|
||||
import fnmatch
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from dxf_registration import (
|
||||
RegistrationError,
|
||||
Transform,
|
||||
derive_transform,
|
||||
load_transform,
|
||||
save_transform,
|
||||
verify_transform,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"EPLAN_SCHEMA",
|
||||
"EDevice",
|
||||
"LayoutPoint",
|
||||
"Rule",
|
||||
"Assignment",
|
||||
"Drive",
|
||||
"EplanDocument",
|
||||
"load_positionsdraw",
|
||||
"load_rules",
|
||||
"assign",
|
||||
"pair_drives",
|
||||
]
|
||||
|
||||
EPLAN_SCHEMA = "sps_skel-eplan-v1"
|
||||
RULES_FILE = "e_planning.ini"
|
||||
|
||||
_CONFIG = ConfigDict(extra="forbid")
|
||||
|
||||
# "=A1+UH01-KF107" -> Ort/Steuerung "UH01", Geraet "KF107"
|
||||
_KENNZEICHNUNG = re.compile(r"\+([A-Za-z0-9]+)-([A-Za-z0-9]+)")
|
||||
# "MB0643" -> ("MB", "0643")
|
||||
_BMK = re.compile(r"^([A-Za-z]+)(\d+)$")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Modell
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class EDevice(BaseModel):
|
||||
"""Ein Sensor/Aktor/Schrankelement der E-Planung."""
|
||||
|
||||
model_config = _CONFIG
|
||||
|
||||
bmk: str
|
||||
verw: str = ""
|
||||
bezeichnung: str = ""
|
||||
kennzeichnung: str = ""
|
||||
# Position in der E-Zeichnung (mm); None bei Schrankelementen ohne Lage
|
||||
pos: tuple[float, float] | None = None
|
||||
# True: aus "schaltschrank_elemente" (im Schrank, nicht im Feld)
|
||||
cabinet: bool = False
|
||||
|
||||
@property
|
||||
def controller(self) -> str:
|
||||
"""Steuerung/Ort aus der Kennzeichnung ("UH01"), "" wenn unbekannt."""
|
||||
m = _KENNZEICHNUNG.search(self.kennzeichnung)
|
||||
return m.group(1) if m else ""
|
||||
|
||||
@property
|
||||
def station(self) -> str:
|
||||
"""E/A-Station aus der Kennzeichnung ("KF107"), "" wenn unbekannt."""
|
||||
m = _KENNZEICHNUNG.search(self.kennzeichnung)
|
||||
return m.group(2) if m else ""
|
||||
|
||||
@property
|
||||
def prefix(self) -> str:
|
||||
m = _BMK.match(self.bmk)
|
||||
return m.group(1) if m else ""
|
||||
|
||||
@property
|
||||
def number(self) -> int | None:
|
||||
m = _BMK.match(self.bmk)
|
||||
return int(m.group(2)) if m else None
|
||||
|
||||
|
||||
@dataclass
|
||||
class LayoutPoint:
|
||||
"""Ein punktartiges Bauteil des Layouts in CSV-Koordinaten."""
|
||||
|
||||
layout_id: str
|
||||
kind: str
|
||||
x: float
|
||||
y: float
|
||||
name: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class Rule:
|
||||
"""Eine Zuordnungsregel aus e_planning.ini (Abschnitt "<Art>.<Rolle>")."""
|
||||
|
||||
role: str # "Separator.sensor"
|
||||
layout: list[str] # Muster fuer die Layout-Art
|
||||
verw: list[str] # Muster fuer VERW
|
||||
max_mm: float | None = None # raeumlich: Hoechstabstand
|
||||
number_from: str = "" # Nummernregel: Rolle des Partners ...
|
||||
number_offset: int = 0 # ... und Versatz der Nummer
|
||||
prefix: str = "" # BMK-Praefix, leer = egal
|
||||
# Sollabstand: das Symbol liegt immer gleich weit neben dem Bauteil.
|
||||
# Dann zaehlt die Abweichung vom Soll, nicht der kleinste Abstand.
|
||||
target_mm: float | None = None
|
||||
tolerance_mm: float = 150.0
|
||||
|
||||
def score(self, dist: float) -> float | None:
|
||||
"""Rangfolge eines Kandidaten (kleiner = besser), None = nicht zulaessig."""
|
||||
if self.max_mm is not None and dist > self.max_mm:
|
||||
return None
|
||||
if self.target_mm is None:
|
||||
return dist
|
||||
dev = abs(dist - self.target_mm)
|
||||
return dev if dev <= self.tolerance_mm else None
|
||||
|
||||
|
||||
class Assignment(BaseModel):
|
||||
"""Ein E-Bauteil in einer Rolle an einem Layout-Bauteil."""
|
||||
|
||||
model_config = _CONFIG
|
||||
|
||||
layout_id: str
|
||||
layout_kind: str
|
||||
role: str
|
||||
bmk: str
|
||||
verw: str = ""
|
||||
bezeichnung: str = ""
|
||||
controller: str = ""
|
||||
station: str = ""
|
||||
# Abstand nach dem Lagebezug in mm (auch bei der Nummernregel zur Kontrolle)
|
||||
distance: float
|
||||
# "raeumlich" | "nummer"
|
||||
rule: str
|
||||
# Abstand des naechstbesten Kandidaten derselben Rolle (None: keiner)
|
||||
runner_up: float | None = None
|
||||
|
||||
|
||||
class Drive(BaseModel):
|
||||
"""Motor im Feld und sein Motorschutz im Schrank (gleiche Nummer)."""
|
||||
|
||||
model_config = _CONFIG
|
||||
|
||||
motor: str
|
||||
protection: str = ""
|
||||
bezeichnung: str = ""
|
||||
protection_bezeichnung: str = ""
|
||||
|
||||
|
||||
class EplanDocument(BaseModel):
|
||||
"""Die Zuordnungs-JSON <csv>_eplan.json."""
|
||||
|
||||
model_config = ConfigDict(extra="forbid", populate_by_name=True)
|
||||
|
||||
schema_id: str = Field(default=EPLAN_SCHEMA, alias="schema")
|
||||
created: str = ""
|
||||
csv_file: str = ""
|
||||
eplan_file: str = ""
|
||||
transform: Transform | None = None
|
||||
assignments: list[Assignment] = Field(default_factory=list)
|
||||
drives: list[Drive] = Field(default_factory=list)
|
||||
# E-Bauteile im Feld ohne Zuordnung (BMK)
|
||||
unassigned: list[str] = Field(default_factory=list)
|
||||
warnings: list[str] = Field(default_factory=list)
|
||||
|
||||
def for_layout(self, layout_id: str) -> dict[str, Assignment]:
|
||||
"""Rollen eines Layout-Bauteils: {"sensor": Assignment, ...}."""
|
||||
return {
|
||||
a.role.split(".", 1)[-1]: a
|
||||
for a in self.assignments if a.layout_id == layout_id
|
||||
}
|
||||
|
||||
def to_json(self) -> str:
|
||||
return self.model_dump_json(by_alias=True, indent=2) + "\n"
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, text: str) -> "EplanDocument":
|
||||
return cls.model_validate_json(text)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Einlesen
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _device(key: str, raw: dict, cabinet: bool) -> EDevice:
|
||||
# Schluessel "BG0642@UNKNOWN" / "QA7000@A/B": vor dem @ steht das BMK.
|
||||
bmk = str(raw.get("IO") or raw.get("B") or key.split("@", 1)[0]).strip()
|
||||
pos = raw.get("pos")
|
||||
return EDevice(
|
||||
bmk=bmk,
|
||||
verw=str(raw.get("VERW") or "").strip(),
|
||||
bezeichnung=str(raw.get("BEZEICHNUNG") or "").strip(),
|
||||
kennzeichnung=str(raw.get("KENNZEICHNUNG") or "").strip(),
|
||||
pos=(float(pos[0]), float(pos[1])) if pos and len(pos) >= 2 else None,
|
||||
cabinet=cabinet,
|
||||
)
|
||||
|
||||
|
||||
def load_positionsdraw(path: Path, warnings: list[str] | None = None) -> list[EDevice]:
|
||||
"""E-Bauteile aus einer *_positionsdraw.json lesen.
|
||||
|
||||
Ein BMK kommt hoechstens einmal vor. Steht es doppelt da (im Schrank einmal
|
||||
mit und einmal ohne Attribute), gewinnt der Eintrag mit VERW.
|
||||
"""
|
||||
warnings = warnings if warnings is not None else []
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
devices: dict[str, EDevice] = {}
|
||||
for section, cabinet in (("sensors", False), ("schaltschrank_elemente", True)):
|
||||
for key, raw in (data.get(section) or {}).items():
|
||||
if not isinstance(raw, dict):
|
||||
continue
|
||||
dev = _device(key, raw, cabinet)
|
||||
if not dev.bmk:
|
||||
continue
|
||||
old = devices.get(dev.bmk)
|
||||
if old is None or (not old.verw and dev.verw):
|
||||
devices[dev.bmk] = dev
|
||||
for bmk, positions in (data.get("double_ids") or {}).items():
|
||||
warnings.append(f"E-Planung: {bmk} steht {len(positions)}x in der Zeichnung "
|
||||
f"- verwendet wird ein Eintrag")
|
||||
return list(devices.values())
|
||||
|
||||
|
||||
def _patterns(text: str) -> list[str]:
|
||||
return [p.strip() for p in text.split(",") if p.strip()]
|
||||
|
||||
|
||||
def load_rules(path: Path) -> tuple[list[Rule], list[str]]:
|
||||
"""Regeln und Anker-Verwendungen aus e_planning.ini lesen.
|
||||
|
||||
Abschnitt [Registrierung] -> anker (VERW-Muster fuer den Lagebezug);
|
||||
jeder Abschnitt "<Art>.<Rolle>" ist eine Regel, in Dateireihenfolge.
|
||||
"""
|
||||
parser = configparser.ConfigParser(inline_comment_prefixes=(";", "#"),
|
||||
interpolation=None)
|
||||
parser.optionxform = str # Schluessel nicht kleinschreiben
|
||||
with path.open(encoding="utf-8") as fh:
|
||||
parser.read_file(fh)
|
||||
anchors = _patterns(parser.get("Registrierung", "anker", fallback="IN SEP"))
|
||||
rules = []
|
||||
for section in parser.sections():
|
||||
if "." not in section:
|
||||
continue
|
||||
s = parser[section]
|
||||
max_mm = s.get("max_mm")
|
||||
soll = s.get("soll_mm")
|
||||
rules.append(Rule(
|
||||
role=section,
|
||||
layout=_patterns(s.get("layout", section.split(".", 1)[0])),
|
||||
verw=_patterns(s.get("verw", "*")),
|
||||
max_mm=float(max_mm) if max_mm else None,
|
||||
number_from=s.get("nummer_von", "").strip(),
|
||||
number_offset=int(s.get("nummer_versatz", "0")),
|
||||
prefix=s.get("praefix", "").strip(),
|
||||
target_mm=float(soll) if soll else None,
|
||||
tolerance_mm=float(s.get("toleranz_mm", "150")),
|
||||
))
|
||||
known = {r.role for r in rules}
|
||||
for r in rules:
|
||||
if r.number_from and r.number_from not in known:
|
||||
raise ValueError(f"{path.name} [{r.role}]: nummer_von = {r.number_from} "
|
||||
f"ist keine Regel")
|
||||
return rules, anchors
|
||||
|
||||
|
||||
def _match(text: str, patterns: list[str]) -> bool:
|
||||
return any(fnmatch.fnmatchcase(text.upper(), p.upper()) for p in patterns)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Zuordnung
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class _State:
|
||||
positions: dict[str, tuple[float, float]]
|
||||
used: set[str] = field(default_factory=set)
|
||||
result: list[Assignment] = field(default_factory=list)
|
||||
# (layout_id, role) -> Assignment
|
||||
by_slot: dict[tuple[str, str], Assignment] = field(default_factory=dict)
|
||||
|
||||
|
||||
def _make(point: LayoutPoint, rule: Rule, dev: EDevice, dist: float, how: str,
|
||||
runner_up: float | None) -> Assignment:
|
||||
return Assignment(
|
||||
layout_id=point.layout_id, layout_kind=point.kind, role=rule.role,
|
||||
bmk=dev.bmk, verw=dev.verw, bezeichnung=dev.bezeichnung,
|
||||
controller=dev.controller, station=dev.station,
|
||||
distance=round(dist, 1), rule=how,
|
||||
runner_up=None if runner_up is None else round(runner_up, 1),
|
||||
)
|
||||
|
||||
|
||||
def _candidates(rule: Rule, devices: list[EDevice], state: _State) -> list[EDevice]:
|
||||
return [
|
||||
d for d in devices
|
||||
if d.bmk not in state.used and d.bmk in state.positions
|
||||
and _match(d.verw, rule.verw)
|
||||
and (not rule.prefix or d.prefix.upper() == rule.prefix.upper())
|
||||
]
|
||||
|
||||
|
||||
def _assign_spatial(rule: Rule, points: list[LayoutPoint], devices: list[EDevice],
|
||||
state: _State) -> None:
|
||||
"""Beste Bewertung zuerst (Rule.score); je Bauteil und E-Bauteil hoechstens einmal."""
|
||||
cands = _candidates(rule, devices, state)
|
||||
pairs = []
|
||||
for p in points:
|
||||
if (p.layout_id, rule.role) in state.by_slot:
|
||||
continue
|
||||
for d in cands:
|
||||
dist = math.dist((p.x, p.y), state.positions[d.bmk])
|
||||
score = rule.score(dist)
|
||||
if score is not None:
|
||||
pairs.append((score, dist, p.layout_id, d.bmk))
|
||||
pairs.sort()
|
||||
# je Bauteil die Kandidaten in Rangfolge - fuer "naechstbester Kandidat"
|
||||
by_point: dict[str, list[tuple[str, float]]] = {}
|
||||
for _, dist, pid, bmk in pairs:
|
||||
by_point.setdefault(pid, []).append((bmk, dist))
|
||||
point_of = {p.layout_id: p for p in points}
|
||||
dev_of = {d.bmk: d for d in cands}
|
||||
for _, dist, pid, bmk in pairs:
|
||||
if (pid, rule.role) in state.by_slot or bmk in state.used:
|
||||
continue
|
||||
others = [x for b, x in by_point[pid] if b != bmk]
|
||||
a = _make(point_of[pid], rule, dev_of[bmk], dist, "raeumlich",
|
||||
others[0] if others else None)
|
||||
state.used.add(bmk)
|
||||
state.by_slot[(pid, rule.role)] = a
|
||||
state.result.append(a)
|
||||
|
||||
|
||||
def _assign_by_number(rule: Rule, points: list[LayoutPoint], devices: list[EDevice],
|
||||
state: _State) -> None:
|
||||
"""Nummer = Nummer des Partners (Rolle nummer_von) + Versatz."""
|
||||
cands = {(d.number): d for d in _candidates(rule, devices, state)
|
||||
if d.number is not None}
|
||||
for p in points:
|
||||
if (p.layout_id, rule.role) in state.by_slot:
|
||||
continue
|
||||
partner = state.by_slot.get((p.layout_id, rule.number_from))
|
||||
if partner is None:
|
||||
continue
|
||||
m = _BMK.match(partner.bmk)
|
||||
if not m:
|
||||
continue
|
||||
dev = cands.get(int(m.group(2)) + rule.number_offset)
|
||||
if dev is None or dev.bmk in state.used:
|
||||
continue
|
||||
dist = math.dist((p.x, p.y), state.positions[dev.bmk])
|
||||
# Auch die Nummernregel darf nicht quer ueber die Anlage greifen
|
||||
if rule.max_mm is not None and dist > rule.max_mm:
|
||||
continue
|
||||
a = _make(p, rule, dev, dist, "nummer", None)
|
||||
state.used.add(dev.bmk)
|
||||
state.by_slot[(p.layout_id, rule.role)] = a
|
||||
state.result.append(a)
|
||||
|
||||
|
||||
def assign(points: list[LayoutPoint], devices: list[EDevice], rules: list[Rule],
|
||||
transform: Transform) -> list[Assignment]:
|
||||
"""Regeln der Reihe nach anwenden.
|
||||
|
||||
Eine Regel mit nummer_von versucht zuerst die Nummernregel und nimmt fuer
|
||||
die verbleibenden Bauteile den Abstand (wenn max_mm/soll_mm gesetzt ist). So wird
|
||||
das Ventil eines Separators ueber die Nummer gefunden, auch wenn das
|
||||
Ventil des Nachbarn naeher liegt (Speicherlinien im 800-mm-Raster).
|
||||
"""
|
||||
state = _State(positions={
|
||||
d.bmk: transform.to_csv(*d.pos) for d in devices
|
||||
if d.pos is not None and not d.cabinet
|
||||
})
|
||||
for rule in rules:
|
||||
targets = [p for p in points if _match(p.kind, rule.layout)]
|
||||
if rule.number_from:
|
||||
_assign_by_number(rule, targets, devices, state)
|
||||
if rule.max_mm is not None or rule.target_mm is not None:
|
||||
_assign_spatial(rule, targets, devices, state)
|
||||
return sorted(state.result, key=lambda a: (a.layout_id, a.role))
|
||||
|
||||
|
||||
def pair_drives(devices: list[EDevice]) -> list[Drive]:
|
||||
"""Motor MAnnnn <-> Motorschutz FCnnnn (gleiche Nummer)."""
|
||||
fc = {d.number: d for d in devices if d.prefix.upper() == "FC" and d.number is not None}
|
||||
drives = []
|
||||
for d in sorted(devices, key=lambda d: d.bmk):
|
||||
if d.prefix.upper() != "MA" or d.number is None:
|
||||
continue
|
||||
schutz = fc.get(d.number)
|
||||
drives.append(Drive(
|
||||
motor=d.bmk,
|
||||
protection=schutz.bmk if schutz else "",
|
||||
bezeichnung=d.bezeichnung,
|
||||
protection_bezeichnung=schutz.bezeichnung if schutz else "",
|
||||
))
|
||||
return drives
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Layout und Lagebezug
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Element.kind der punktartigen Layout-Bauteile
|
||||
LAYOUT_KINDS = ("Separator", "Scanner", "Weiche", "Ausschleus", "Einschleus",
|
||||
"Beladung", "Entladung")
|
||||
|
||||
|
||||
def layout_points(elements) -> list[LayoutPoint]:
|
||||
"""Punktartige Bauteile aus den CSV-Elementen (material_flow.Element)."""
|
||||
points = []
|
||||
for e in elements:
|
||||
if e.position is None:
|
||||
continue
|
||||
kind = next((k for k in LAYOUT_KINDS
|
||||
if k.lower() in f"{e.kind} {e.teile_art}".lower()), "")
|
||||
if kind:
|
||||
points.append(LayoutPoint(e.teile_id, kind, e.x, e.y, e.name))
|
||||
return points
|
||||
|
||||
|
||||
def _anchor_points(points: list[LayoutPoint], devices: list[EDevice],
|
||||
anchors: list[str]):
|
||||
csv_pts = {"Separator": [(p.x, p.y) for p in points if p.kind == "Separator"]}
|
||||
e_pts = {"Separator": [d.pos for d in devices
|
||||
if d.pos is not None and not d.cabinet
|
||||
and _match(d.verw, anchors)]}
|
||||
return csv_pts, e_pts
|
||||
|
||||
|
||||
def resolve_transform(points, devices, anchors, cfg_dir: Path, csv_name: str,
|
||||
eplan_name: str, re_register: bool,
|
||||
warnings: list[str]) -> Transform:
|
||||
"""Abgelegten Lagebezug nachrechnen oder neu ermitteln und ablegen."""
|
||||
csv_pts, e_pts = _anchor_points(points, devices, anchors)
|
||||
if not re_register:
|
||||
stored = load_transform(cfg_dir, csv_name, eplan_name)
|
||||
if stored:
|
||||
verify_transform(stored, csv_pts, e_pts)
|
||||
return stored
|
||||
transform = derive_transform(csv_pts, e_pts, csv_name, eplan_name)
|
||||
saved = save_transform(cfg_dir, transform)
|
||||
warnings.append(f"Lagebezug neu ermittelt und abgelegt: {saved}")
|
||||
return transform
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bericht
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def render_report(doc: EplanDocument, rules: list[Rule], points: list[LayoutPoint],
|
||||
devices: list[EDevice]) -> str:
|
||||
out = []
|
||||
add = out.append
|
||||
add(f"# E-Planung -> Layout: {doc.csv_file}")
|
||||
add("")
|
||||
add(f"- E-Planung: `{doc.eplan_file}`")
|
||||
add(f"- Erzeugt: {doc.created}")
|
||||
if doc.transform:
|
||||
add(f"- Lagebezug: {doc.transform.describe()}")
|
||||
add("")
|
||||
|
||||
add("## Zuordnung je Rolle")
|
||||
add("")
|
||||
add("| Rolle | Layout-Bauteile | zugeordnet | davon Nummernregel |")
|
||||
add("|---|---|---|---|")
|
||||
for r in rules:
|
||||
n_pts = sum(1 for p in points if _match(p.kind, r.layout))
|
||||
hits = [a for a in doc.assignments if a.role == r.role]
|
||||
n_num = sum(1 for a in hits if a.rule == "nummer")
|
||||
add(f"| {r.role} | {n_pts} | {len(hits)} | {n_num} |")
|
||||
add("")
|
||||
|
||||
add("## Zuordnungen")
|
||||
add("")
|
||||
add("Abstand nach dem Lagebezug; \"2.\" = Abstand des naechstbesten Kandidaten "
|
||||
"(knapp beieinander = pruefen).")
|
||||
add("")
|
||||
add("| Layout | Art | Rolle | BMK | VERW | Bezeichnung | Abstand mm | 2. | Regel | Station |")
|
||||
add("|---|---|---|---|---|---|---|---|---|---|")
|
||||
for a in doc.assignments:
|
||||
second = "" if a.runner_up is None else f"{a.runner_up:.0f}"
|
||||
add(f"| {a.layout_id} | {a.layout_kind} | {a.role.split('.', 1)[-1]} | {a.bmk} "
|
||||
f"| {a.verw} | {a.bezeichnung} | {a.distance:.0f} | {second} | {a.rule} "
|
||||
f"| {a.controller}-{a.station} |")
|
||||
add("")
|
||||
|
||||
roles_of: dict[str, set[str]] = {}
|
||||
for a in doc.assignments:
|
||||
roles_of.setdefault(a.layout_id, set()).add(a.role)
|
||||
add("## Layout-Bauteile ohne Zuordnung")
|
||||
add("")
|
||||
any_missing = False
|
||||
for r in rules:
|
||||
missing = [p for p in points
|
||||
if _match(p.kind, r.layout) and r.role not in roles_of.get(p.layout_id, set())]
|
||||
if missing:
|
||||
any_missing = True
|
||||
add(f"- **{r.role}**: {', '.join(p.layout_id for p in missing)}")
|
||||
if not any_missing:
|
||||
add("- keine")
|
||||
add("")
|
||||
|
||||
by_bmk = {d.bmk: d for d in devices}
|
||||
add("## E-Bauteile im Feld ohne Zuordnung")
|
||||
add("")
|
||||
if doc.unassigned:
|
||||
add("| BMK | VERW | Bezeichnung | Station |")
|
||||
add("|---|---|---|---|")
|
||||
for bmk in doc.unassigned:
|
||||
d = by_bmk[bmk]
|
||||
add(f"| {bmk} | {d.verw} | {d.bezeichnung} | {d.controller}-{d.station} |")
|
||||
else:
|
||||
add("- keine")
|
||||
add("")
|
||||
|
||||
add("## Antriebe (Motor <-> Motorschutz)")
|
||||
add("")
|
||||
if doc.drives:
|
||||
add("| Motor | Bezeichnung | Motorschutz | Bezeichnung |")
|
||||
add("|---|---|---|---|")
|
||||
for dr in doc.drives:
|
||||
add(f"| {dr.motor} | {dr.bezeichnung} | {dr.protection or '-'} "
|
||||
f"| {dr.protection_bezeichnung} |")
|
||||
else:
|
||||
add("- keine Motoren in der E-Planung")
|
||||
add("")
|
||||
|
||||
if doc.warnings:
|
||||
add("## Hinweise")
|
||||
add("")
|
||||
for w in doc.warnings:
|
||||
add(f"- {w}")
|
||||
add("")
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CLI
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def build_document(csv_path: Path, eplan_path: Path, rules_path: Path, cfg_dir: Path,
|
||||
re_register: bool = False
|
||||
) -> tuple[EplanDocument, list[Rule], list[LayoutPoint], list[EDevice]]:
|
||||
"""Kompletter Lauf ohne Ausgabe: CSV + positionsdraw -> EplanDocument."""
|
||||
from material_flow import read_elements # erst hier: Tests ohne CSV brauchen es nicht
|
||||
|
||||
warnings: list[str] = []
|
||||
elements = read_elements(csv_path, warnings)
|
||||
points = layout_points(elements)
|
||||
devices = load_positionsdraw(eplan_path, warnings)
|
||||
rules, anchors = load_rules(rules_path)
|
||||
transform = resolve_transform(points, devices, anchors, cfg_dir, csv_path.name,
|
||||
eplan_path.name, re_register, warnings)
|
||||
assignments = assign(points, devices, rules, transform)
|
||||
used = {a.bmk for a in assignments}
|
||||
unassigned = sorted(d.bmk for d in devices
|
||||
if not d.cabinet and d.pos is not None and d.bmk not in used)
|
||||
doc = EplanDocument(
|
||||
created=datetime.now().strftime("%Y-%m-%d %H:%M"),
|
||||
csv_file=csv_path.name,
|
||||
eplan_file=eplan_path.name,
|
||||
transform=transform,
|
||||
assignments=assignments,
|
||||
drives=pair_drives(devices),
|
||||
unassigned=unassigned,
|
||||
warnings=warnings,
|
||||
)
|
||||
return doc, rules, points, devices
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
from material_flow import env_dir, resolve_input
|
||||
|
||||
args = parse_args(argv)
|
||||
csv_path = resolve_input(args.file)
|
||||
eplan_path = resolve_input(args.eplan)
|
||||
cfg_dir = env_dir("SKEL_CFG", "cfg")
|
||||
rules_path = Path(args.rules) if args.rules else cfg_dir / RULES_FILE
|
||||
for label, path in (("CSV", csv_path), ("E-Planung", eplan_path),
|
||||
("Regeln", rules_path)):
|
||||
if not path.is_file():
|
||||
print(f"FEHLER: {label} nicht gefunden: {path}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
try:
|
||||
doc, rules, points, devices = build_document(
|
||||
csv_path, eplan_path, rules_path, cfg_dir, args.re_register)
|
||||
except RegistrationError as exc:
|
||||
print(f"FEHLER: Lagebezug E-Planung -> CSV nicht nachweisbar: {exc}",
|
||||
file=sys.stderr)
|
||||
print(" Passen CSV und E-Zeichnung zusammen? Anker-VERW in "
|
||||
f"{rules_path.name} [Registrierung] pruefen.", file=sys.stderr)
|
||||
return 1
|
||||
except (OSError, ValueError) as exc:
|
||||
print(f"FEHLER: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
results = env_dir("SKEL_RESULTS", "results")
|
||||
results.mkdir(parents=True, exist_ok=True)
|
||||
json_file = results / f"{csv_path.stem}_eplan.json"
|
||||
md_file = results / f"{csv_path.stem}_eplan.md"
|
||||
json_file.write_text(doc.to_json(), encoding="utf-8")
|
||||
md_file.write_text(render_report(doc, rules, points, devices), encoding="utf-8")
|
||||
|
||||
print("")
|
||||
print("================================================================")
|
||||
print("E-PLANUNG -> LAYOUT")
|
||||
print("================================================================")
|
||||
print(f"CSV = {csv_path}")
|
||||
print(f"E-Planung = {eplan_path}")
|
||||
print(f"Lagebezug = {doc.transform.describe() if doc.transform else '-'}")
|
||||
for r in rules:
|
||||
n_pts = sum(1 for p in points if _match(p.kind, r.layout))
|
||||
hits = sum(1 for a in doc.assignments if a.role == r.role)
|
||||
print(f" {r.role:<26} {hits:4} von {n_pts}")
|
||||
print(f"Ohne Zuordnung = {len(doc.unassigned)} E-Bauteile im Feld")
|
||||
print(f"Antriebe = {len(doc.drives)} "
|
||||
f"({sum(1 for d in doc.drives if d.protection)} mit Motorschutz)")
|
||||
for w in doc.warnings:
|
||||
print(f" ! {w}")
|
||||
print(f"JSON = {json_file}")
|
||||
print(f"Bericht = {md_file}")
|
||||
print("================================================================")
|
||||
print("")
|
||||
return 0
|
||||
|
||||
|
||||
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="e_planning",
|
||||
description="Ordnet die Sensoren/Ventile der E-Planung "
|
||||
"(*_positionsdraw.json des Kabellaengen-Tools) den Bauteilen "
|
||||
"des Layouts (CSV) zu. Ergebnis: <csv>_eplan.json fuer "
|
||||
"scl_skeleton --eplan und ein Bericht <csv>_eplan.md.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="Beispiele:\n"
|
||||
" e_planning.bat --file mubea.csv --eplan Mubea_positionsdraw.json\n"
|
||||
" e_planning.bat --file mubea.csv --eplan C:\\pfad\\Mubea_positionsdraw.json "
|
||||
"--re-register\n"
|
||||
"\n"
|
||||
"Exit-Codes:\n"
|
||||
" 0 ok (auch wenn nicht alles zugeordnet wurde - siehe Bericht)\n"
|
||||
" 1 Eingabe-, Aufruf- oder Lagebezugsfehler\n",
|
||||
)
|
||||
parser.add_argument("--file", default="export.csv", metavar="NAME",
|
||||
help="CSV-Export in %%SKEL_DATA%% (oder ein Pfad). "
|
||||
"Standard: %(default)s")
|
||||
parser.add_argument("--eplan", required=True, metavar="NAME",
|
||||
help="*_positionsdraw.json der E-Planung in %%SKEL_DATA%% "
|
||||
"(oder ein Pfad)")
|
||||
parser.add_argument("--rules", metavar="DATEI",
|
||||
help=f"Regeldatei. Standard: %%SKEL_CFG%%\\{RULES_FILE}")
|
||||
parser.add_argument("--re-register", action="store_true",
|
||||
help="Lagebezug neu ermitteln, auch wenn in %%SKEL_CFG%% "
|
||||
"einer abgelegt ist")
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+145
-5
@@ -21,6 +21,11 @@ Klassen:
|
||||
Connection eine gerichtete Verbindung zwischen zwei TROs/Stationen
|
||||
TroCandidate eine im Review gemeldete Stelle, an der ein TRO erwartet
|
||||
wird, die aber (noch) keiner ist
|
||||
Slot eine Stelle eines TRO, an die Hardware gehoert (Separator,
|
||||
Weiche, Scanner, Antrieb) mit den Rollen, die die
|
||||
Hardwareplanung dort fuellen muss
|
||||
JamArea ein Staubereich: Eingang genau eines TRO (je Separator
|
||||
laut Katalog), mit allen Vorgaengern, die dort einfahren
|
||||
Analysis Arbeitsergebnis von tro_flow.analyse_tros
|
||||
TroDocument die TRO-JSON (<csv>_tro.json): von tro_flow --json
|
||||
geschrieben, von scl_skeleton gelesen und geprueft
|
||||
@@ -47,22 +52,28 @@ from pydantic import (
|
||||
model_validator,
|
||||
)
|
||||
|
||||
from tro_catalog import TroDefinition, get_tro
|
||||
from tro_catalog import TroDefinition, TroItem, get_tro
|
||||
|
||||
__all__ = [
|
||||
"SCHEMA",
|
||||
"TroBase",
|
||||
"Slot",
|
||||
"JamArea",
|
||||
"DerivedTro",
|
||||
"Connection",
|
||||
"TroCandidate",
|
||||
"Analysis",
|
||||
"TroDocument",
|
||||
"derive_slots",
|
||||
"derive_jam_areas",
|
||||
]
|
||||
|
||||
# Kennung der TRO-JSON. v1 war die Ausgabe von tro_extract (aus der Zeichnung),
|
||||
# v2 die von tro_flow --json (aus dem CSV, ueber dieses Modell), v3 wie v2 mit
|
||||
# `candidates` statt `openPoints`.
|
||||
SCHEMA = "sps_skel-tro-v3"
|
||||
# `candidates` statt `openPoints`, v4 wie v3 mit `slots` je TRO und `jamAreas`.
|
||||
# Eine v3-Datei liest sich weiter (die neuen Felder fehlen dann und werden beim
|
||||
# Rendern abgeleitet, siehe TroDocument.jam_areas_or_derived).
|
||||
SCHEMA = "sps_skel-tro-v4"
|
||||
|
||||
# Gemeinsame Einstellungen: Pruefung bei Erzeugung und Zuweisung, unbekannte
|
||||
# Schluessel sind ein Fehler (ein umbenannter JSON-Schluessel faellt auf statt
|
||||
@@ -191,6 +202,50 @@ class TroBase(_Positioned):
|
||||
return data
|
||||
|
||||
|
||||
class Slot(BaseModel):
|
||||
"""
|
||||
Eine Stelle eines TRO, an die Hardware gehoert (Konzept Umbau 1).
|
||||
|
||||
``slot`` ist der stabile Schluessel innerhalb des TRO (``separator1``,
|
||||
``switch1``, ``scanner1``, ``drive``); zusammen mit der TRO-ID adressiert er
|
||||
die Zuordnung der Hardwareplanung. ``layout_id`` ist die TeileId des
|
||||
Bauteils im aktuellen Export - nur zur Nachverfolgung, nicht stabil ("",
|
||||
wenn das Bauteil im Layout nicht bekannt ist). Rollen mit ``?`` sind
|
||||
optional: fehlt das Geraet, wird der zugehoerige xPlugged*-Schalter FALSE.
|
||||
"""
|
||||
|
||||
model_config = _CONFIG
|
||||
|
||||
slot: str
|
||||
layout_id: str = Field(default="", alias="layoutId")
|
||||
roles: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class JamArea(_Positioned):
|
||||
"""
|
||||
Ein Staubereich: die Warteschlange vor einem Separator, Eingang genau eines
|
||||
TRO (``tro``, Eingang Nummer ``entry``).
|
||||
|
||||
Kennung ``J<Separator>`` (Mubea: Sep1024 -> J1024). Mehrere Vorgaenger
|
||||
koennen in denselben Staubereich einfahren (Zusammenfuehrung); deren
|
||||
Ausgang zeigt dann auf diesen Staubereich. Der SCL-Name
|
||||
``"DB_JamArea<NNNN>".stJam<NNNN>_<k>`` (Standard UH01-UH05) braucht die
|
||||
Foerderernummer aus der Hardwareplanung und steht deshalb nicht hier.
|
||||
"""
|
||||
|
||||
model_config = _CONFIG
|
||||
|
||||
jam_id: str = Field(alias="id")
|
||||
tro: str
|
||||
entry: int = 1
|
||||
separator: str = ""
|
||||
# tragender Foerderer (erster Host des TRO im Layout)
|
||||
host: str = ""
|
||||
predecessors: list[str] = Field(default_factory=list)
|
||||
note: str = ""
|
||||
position: tuple[float, float, float] | None = None
|
||||
|
||||
|
||||
class DerivedTro(TroBase):
|
||||
"""
|
||||
Ein von tro_flow aus dem CSV abgeleiteter TRO (oder eine Systemgrenze).
|
||||
@@ -207,6 +262,8 @@ class DerivedTro(TroBase):
|
||||
# Mittelpunkt der Bauteile, aus denen dieser TRO gebildet wurde
|
||||
position_from: str = Field(default="", alias="positionFrom")
|
||||
position_points: int = Field(default=0, alias="positionPoints")
|
||||
# Stellen fuer die Hardwareplanung (derive_slots); leer bei Systemgrenzen
|
||||
slots: list[Slot] = Field(default_factory=list)
|
||||
|
||||
@property
|
||||
def connected(self) -> bool:
|
||||
@@ -288,16 +345,23 @@ class TroDocument(BaseModel):
|
||||
boundaries: list[DerivedTro] = Field(default_factory=list)
|
||||
connections: list[Connection] = Field(default_factory=list)
|
||||
candidates: list[TroCandidate] = Field(default_factory=list)
|
||||
jam_areas: list[JamArea] = Field(default_factory=list, alias="jamAreas")
|
||||
findings: list[str] = Field(default_factory=list)
|
||||
|
||||
@classmethod
|
||||
def from_analysis(cls, analysis: Analysis, source: str, note: str = "") -> TroDocument:
|
||||
"""Dokument aus dem Ergebnis von tro_flow.analyse_tros bilden."""
|
||||
"""Dokument aus dem Ergebnis von tro_flow.analyse_tros bilden.
|
||||
|
||||
Slots und Staubereiche werden hier abgeleitet, damit die JSON sie
|
||||
traegt und nicht erst das Rendern sie kennt.
|
||||
"""
|
||||
tros = [t.model_copy(update={"slots": derive_slots(t)}) for t in analysis.tros]
|
||||
return cls(
|
||||
source=source,
|
||||
note=note,
|
||||
tros=analysis.tros,
|
||||
tros=tros,
|
||||
boundaries=analysis.boundaries,
|
||||
jam_areas=derive_jam_areas(tros),
|
||||
connections=[
|
||||
Connection(src=src, dst=dst, kind=analysis.edge_kinds.get((src, dst), ""))
|
||||
for src, dst in analysis.edges
|
||||
@@ -310,6 +374,10 @@ class TroDocument(BaseModel):
|
||||
"""JSON-Text einlesen und pruefen (pydantic.ValidationError bei Fehlern)."""
|
||||
return cls.model_validate_json(text)
|
||||
|
||||
def jam_areas_or_derived(self) -> list[JamArea]:
|
||||
"""jamAreas aus der Datei; bei einer v3-Datei (ohne jamAreas) abgeleitet."""
|
||||
return self.jam_areas or derive_jam_areas(self.tros)
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Als JSON-Text mit den Schluesseln der TRO-JSON (id, type, fbType ...)."""
|
||||
return self.model_dump_json(by_alias=True, indent=2) + "\n"
|
||||
@@ -319,3 +387,75 @@ class TroDocument(BaseModel):
|
||||
if tro.tro_id == tro_id:
|
||||
return tro
|
||||
return None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Ableitungen aus Katalogtyp und Topologie
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Rollen je Slot-Art (doc/Konzept_Layout_Hardware_SCL.md, Umbau 1); "?" = optional
|
||||
_SEPARATOR_ROLES = ("sensor", "stopper", "jamSensor?", "partSensor?")
|
||||
_SWITCH_ROLES = ("valve", "exitSensor?")
|
||||
_SCANNER_ROLES = ("reader",)
|
||||
_DRIVE_ROLES = ("motor", "motorProtection", "senCarrInPos", "senFinger",
|
||||
"senLastPos", "senJam")
|
||||
|
||||
|
||||
def _at(values: list[str], index: int) -> str:
|
||||
return values[index] if index < len(values) else ""
|
||||
|
||||
|
||||
def derive_slots(tro: DerivedTro) -> list[Slot]:
|
||||
"""Slots eines TRO: Anzahl aus dem Katalog, Layout-ID aus dem TRO.
|
||||
|
||||
Weichen tragen noch keine Layout-ID - tro_flow speichert das Weichen-
|
||||
bzw. Ausschleuselement heute nicht am TRO.
|
||||
"""
|
||||
items = tro.items
|
||||
slots: list[Slot] = []
|
||||
for i in range(items.get(TroItem.SEPARATOR, 0)):
|
||||
slots.append(Slot(slot=f"separator{i + 1}", layout_id=_at(tro.separators, i),
|
||||
roles=list(_SEPARATOR_ROLES)))
|
||||
for i in range(items.get(TroItem.SWITCH, 0)):
|
||||
slots.append(Slot(slot=f"switch{i + 1}", roles=list(_SWITCH_ROLES)))
|
||||
for i in range(items.get(TroItem.SCANNER, 0)):
|
||||
slots.append(Slot(slot=f"scanner{i + 1}", layout_id=_at(tro.scanners, i),
|
||||
roles=list(_SCANNER_ROLES)))
|
||||
if items.get(TroItem.VARIO_DRIVE):
|
||||
slots.append(Slot(slot="drive", layout_id=_at(tro.hosts, 0),
|
||||
roles=list(_DRIVE_ROLES)))
|
||||
return slots
|
||||
|
||||
|
||||
def derive_jam_areas(tros: list[DerivedTro]) -> list[JamArea]:
|
||||
"""Staubereiche: je Separator laut Katalog ein Eingang des TRO.
|
||||
|
||||
Kennung J<Separator>; fehlt der Separator im Layout, J<TRO-ID>_<n>. Alle
|
||||
Vorgaenger fahren in den ersten Eingang ein - bei zwei Eingaengen
|
||||
(2Sep-Typen) ist die Aufteilung aus dem Layout nicht ableitbar und als
|
||||
Hinweis vermerkt.
|
||||
"""
|
||||
areas: list[JamArea] = []
|
||||
for tro in tros:
|
||||
n_entries = max(1, tro.items.get(TroItem.SEPARATOR, 1))
|
||||
if n_entries > 1 and tro.predecessors:
|
||||
note = "Aufteilung der Vorgaenger auf die Eingaenge offen"
|
||||
elif tro.items.get(TroItem.STORAGE_LINE):
|
||||
# Die Separatoren eines Speichers sitzen auf den Lagerlinien; der
|
||||
# Eingang liegt davor und hat im Layout keinen eigenen Separator.
|
||||
note = "Eingang des Speichers - Kennung vorlaeufig nach erstem Linien-Separator"
|
||||
else:
|
||||
note = ""
|
||||
for n in range(1, n_entries + 1):
|
||||
sep = _at(tro.separators, n - 1)
|
||||
areas.append(JamArea(
|
||||
jam_id=f"J{sep}" if sep else f"J{tro.tro_id}_{n}",
|
||||
tro=tro.tro_id,
|
||||
entry=n,
|
||||
separator=sep,
|
||||
host=_at(tro.hosts, 0),
|
||||
predecessors=list(tro.predecessors) if n == 1 else [],
|
||||
note=note,
|
||||
position=tro.position if n == 1 else None,
|
||||
))
|
||||
return areas
|
||||
|
||||
+105
-27
@@ -16,8 +16,11 @@ Schichten:
|
||||
|
||||
Namen (Separator-, Weichen-, MainTro-Nummer, Adressen ...) sind ein
|
||||
:class:`SclName`: Wert plus Merker, ob er noch aus der E-Planung kommen muss.
|
||||
Heute sind alle Platzhalter mit ``todo=True``; die Namensregeln aus Schritt 4
|
||||
(cfg/scl_naming.ini) ersetzen dann nur den Wert und loeschen den Merker.
|
||||
Ohne E-Planung sind alle Platzhalter ``todo=True``. Mit einer Zuordnung aus
|
||||
lib/e_planning.py (``eplan``) werden Separator-Sensor und Stopperventil der
|
||||
zugeordneten Separatoren eingesetzt und ``todo`` geloescht; alles andere
|
||||
bleibt offen. Die Namensregeln aus Schritt 4 (cfg/scl_naming.ini) arbeiten
|
||||
spaeter genauso: Wert ersetzen, Merker loeschen.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -25,12 +28,16 @@ from __future__ import annotations
|
||||
import re
|
||||
from collections import deque
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from model import TroBase, TroDocument
|
||||
from model import JamArea, TroBase, TroDocument
|
||||
from tro_catalog import TroItem
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from e_planning import EplanDocument
|
||||
|
||||
__all__ = [
|
||||
"TODO",
|
||||
"SclName",
|
||||
@@ -69,6 +76,16 @@ def _offen(platzhalter: str) -> SclName:
|
||||
return SclName(value=platzhalter, todo=True)
|
||||
|
||||
|
||||
def _bekannt(wert: str) -> SclName:
|
||||
return SclName(value=wert, todo=False)
|
||||
|
||||
|
||||
# Rolle in e_planning.ini -> wie der Name in der SCL geschrieben wird
|
||||
# (Sensor als Konstante der DB_Inputs-Sensorliste, Ventil als Variable)
|
||||
_SENSOR = "cIn{bmk}"
|
||||
_VENTIL = "{bmk}"
|
||||
|
||||
|
||||
class SclSeparator(BaseModel):
|
||||
"""Ein stInSeparator<n>-Block."""
|
||||
|
||||
@@ -130,12 +147,19 @@ class SclScanner(BaseModel):
|
||||
|
||||
|
||||
class SclJam(BaseModel):
|
||||
"""Ein Staubereich am Ein- oder Ausgang (JamEntr<n> / JamExit<n>)."""
|
||||
"""Ein Staubereich am Ein- oder Ausgang (JamEntr<n> / JamExit<n>).
|
||||
|
||||
Ein Eingang ist ein Staubereich des TRO selbst (TroDocument.jamAreas), ein
|
||||
Ausgang der Eingangs-Staubereich des Nachfolgers.
|
||||
"""
|
||||
|
||||
model_config = _CONFIG
|
||||
|
||||
n: int
|
||||
# Nachbar als Text fuer den Kommentar, SYSn als Station markiert
|
||||
# Kennung aus der TRO-JSON (J<Separator>); "" am Ausgang zu einer Station
|
||||
jam_id: str = ""
|
||||
# Eingang: die Vorgaenger, Ausgang: der Nachfolger - als Text fuer den
|
||||
# Kommentar, SYSn als Station markiert
|
||||
neighbour: str
|
||||
db: SclName
|
||||
# Stausensor des Ausgangs (stInSenJam<n>); None an Eingaengen
|
||||
@@ -145,6 +169,11 @@ class SclJam(BaseModel):
|
||||
carriers: SclName
|
||||
data: SclName
|
||||
|
||||
@property
|
||||
def title(self) -> str:
|
||||
"""Fuer den Kommentar: 'Staubereich J0003' (ohne Kennung nur 'Staubereich')."""
|
||||
return f"Staubereich {self.jam_id}" if self.jam_id else "Staubereich"
|
||||
|
||||
|
||||
class SclSscc(BaseModel):
|
||||
"""SSCC-Scanner eines 1Sep_SSCC: zweite Kamera, Endmessung, HMI-Anzeige."""
|
||||
@@ -209,6 +238,11 @@ class SclStore(BaseModel):
|
||||
# Eine Linie je Speicherbahn im Layout (nicht die 19 aus dem Katalog)
|
||||
lines: list[SclStoreLine]
|
||||
|
||||
@property
|
||||
def title(self) -> str:
|
||||
"""Fuer den Kommentar: 'Staubereich J0003' (ohne Kennung nur 'Staubereich')."""
|
||||
return f"Staubereich {self.jam_id}" if self.jam_id else "Staubereich"
|
||||
|
||||
|
||||
class SclTro(BaseModel):
|
||||
"""Alles, was eine REGION fuer einen TRO braucht."""
|
||||
@@ -282,6 +316,8 @@ class SclFile(BaseModel):
|
||||
created: str
|
||||
start_reason: str
|
||||
tros: list[SclTro] = Field(default_factory=list)
|
||||
# Zuordnung zur E-Planung, aus der Namen eingesetzt wurden ("" = keine)
|
||||
eplan_source: str = ""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -388,9 +424,10 @@ def _scanner(n: int, suffix: str, interface: str = "cScanner____") -> SclScanner
|
||||
)
|
||||
|
||||
|
||||
def _jam(n: int, neighbour: str, jam_db: str, sensor: SclName | None = None) -> SclJam:
|
||||
def _jam(n: int, neighbour: str, jam_db: str, sensor: SclName | None = None,
|
||||
jam_id: str = "") -> SclJam:
|
||||
return SclJam(
|
||||
n=n, neighbour=neighbour, db=_offen(jam_db), sensor=sensor,
|
||||
n=n, jam_id=jam_id, neighbour=neighbour, db=_offen(jam_db), sensor=sensor,
|
||||
carriers=_offen(f"{jam_db}.arCarrier"), data=_offen(f"{jam_db}.stData"),
|
||||
)
|
||||
|
||||
@@ -434,23 +471,55 @@ def _store(tro: TroBase) -> SclStore:
|
||||
)
|
||||
|
||||
|
||||
def _tro_context(tro: TroBase, alle: dict[str, TroBase]) -> SclTro:
|
||||
def _separator(i: int, layout: str, eplan: EplanDocument | None) -> SclSeparator:
|
||||
rollen = eplan.for_layout(layout) if eplan is not None and layout != "?" else {}
|
||||
sensor = rollen.get("sensor")
|
||||
stopper = rollen.get("stopper")
|
||||
return SclSeparator(
|
||||
n=i,
|
||||
layout=layout,
|
||||
no=_offen("cSep_____"),
|
||||
sensor=_bekannt(_SENSOR.format(bmk=sensor.bmk)) if sensor else _offen("cInBG____"),
|
||||
stopper=_bekannt(_VENTIL.format(bmk=stopper.bmk)) if stopper else _offen("MB____"),
|
||||
# Teilesensor kennt die E-Planung-Bruecke noch nicht - offen
|
||||
part_sensor=_offen("cInBG____"),
|
||||
)
|
||||
|
||||
|
||||
def _entries(tro: TroBase, eigene: list[JamArea],
|
||||
alle: dict[str, TroBase], jam_db: str) -> list[SclJam]:
|
||||
"""Ein JamEntr je eigenem Staubereich - nicht je Vorgaenger: mehrere
|
||||
Vorgaenger fahren in denselben Staubereich ein (Referenzen UH01-UH05)."""
|
||||
eingaenge = []
|
||||
for area in eigene:
|
||||
von = ", ".join(_nachbar(v, alle) for v in area.predecessors) or "-"
|
||||
if area.note:
|
||||
von = f"{von} ({area.note})"
|
||||
eingaenge.append(_jam(area.entry, von, jam_db, jam_id=area.jam_id))
|
||||
return eingaenge
|
||||
|
||||
|
||||
def _exit_area(tro_id: str, nach: str,
|
||||
bereiche: dict[str, list[JamArea]]) -> str:
|
||||
"""Kennung des Staubereichs, in den tro_id beim Nachfolger einfaehrt."""
|
||||
ziel = bereiche.get(nach, [])
|
||||
passend = [a for a in ziel if tro_id in a.predecessors] or ziel
|
||||
return passend[0].jam_id if passend else ""
|
||||
|
||||
|
||||
def _tro_context(tro: TroBase, alle: dict[str, TroBase],
|
||||
eplan: EplanDocument | None = None,
|
||||
bereiche: dict[str, list[JamArea]] | None = None) -> SclTro:
|
||||
items = tro.items
|
||||
n_sep = items.get(TroItem.SEPARATOR, 1)
|
||||
n_swi = items.get(TroItem.SWITCH, 0)
|
||||
n_scan = items.get(TroItem.SCANNER, 0)
|
||||
tro_id = tro.tro_id
|
||||
jam_db = '"DB_JamArea____".stJam____'
|
||||
bereiche = bereiche or {}
|
||||
|
||||
separators = [
|
||||
SclSeparator(
|
||||
n=i,
|
||||
layout=tro.separators[i - 1] if i <= len(tro.separators) else "?",
|
||||
no=_offen("cSep_____"),
|
||||
sensor=_offen("cInBG____"),
|
||||
stopper=_offen("MB____"),
|
||||
part_sensor=_offen("cInBG____"),
|
||||
)
|
||||
_separator(i, tro.separators[i - 1] if i <= len(tro.separators) else "?", eplan)
|
||||
for i in range(1, n_sep + 1)
|
||||
]
|
||||
switches = [
|
||||
@@ -475,15 +544,14 @@ def _tro_context(tro: TroBase, alle: dict[str, TroBase]) -> SclTro:
|
||||
SclSwitchExit(via=abgang, targets=reachable_via(tro_id, abgang, alle))
|
||||
for abgang in tro.successors
|
||||
] if n_swi else []
|
||||
# Staubereiche: je ankommender/abgehender Verbindung einer. Welche
|
||||
# DB-Struktur dahintersteht, ist eine Planungsentscheidung - hier steht
|
||||
# nur, WIE VIELE es sein muessen und zu welchem Nachbarn sie gehoeren.
|
||||
jam_entries = [
|
||||
_jam(n, _nachbar(vor, alle), jam_db)
|
||||
for n, vor in enumerate(tro.predecessors, start=1)
|
||||
]
|
||||
# Staubereiche aus der TRO-JSON (jamAreas): Eingaenge sind die eigenen
|
||||
# (ein JamEntr je eigenem Staubereich, nicht je Vorgaenger), Ausgaenge die
|
||||
# Eingaenge der Nachfolger. Der DB-Name braucht die Foerderernummer aus der
|
||||
# Hardwareplanung und bleibt offen.
|
||||
jam_entries = _entries(tro, bereiche.get(tro_id, []), alle, jam_db)
|
||||
jam_exits = [
|
||||
_jam(n, _nachbar(nach, alle), jam_db, sensor=_offen("cInBG____"))
|
||||
_jam(n, _nachbar(nach, alle), jam_db, sensor=_offen("cInBG____"),
|
||||
jam_id=_exit_area(tro_id, nach, bereiche))
|
||||
for n, nach in enumerate(tro.successors, start=2)
|
||||
]
|
||||
|
||||
@@ -541,13 +609,23 @@ def _tro_context(tro: TroBase, alle: dict[str, TroBase]) -> SclTro:
|
||||
|
||||
|
||||
def build_context(document: TroDocument, source: str, start_id: str = "",
|
||||
created: str | None = None) -> SclFile:
|
||||
"""TroDocument -> SclFile (TROs in Fliessrichtung)."""
|
||||
created: str | None = None,
|
||||
eplan: EplanDocument | None = None) -> SclFile:
|
||||
"""TroDocument -> SclFile (TROs in Fliessrichtung).
|
||||
|
||||
eplan: Zuordnung Layout -> E-Planung (e_planning.py); ohne sie bleiben
|
||||
alle Namen offen und die Ausgabe ist die bisherige.
|
||||
"""
|
||||
alle = {t.tro_id: t for t in document.tros}
|
||||
bereiche: dict[str, list[JamArea]] = {}
|
||||
for area in document.jam_areas_or_derived():
|
||||
bereiche.setdefault(area.tro, []).append(area)
|
||||
geordnet, startgrund = flow_order(document.tros, start_id)
|
||||
return SclFile(
|
||||
source=source,
|
||||
created=created or datetime.now().strftime("%Y-%m-%d %H:%M"),
|
||||
start_reason=startgrund,
|
||||
tros=[_tro_context(t, alle) for t in geordnet],
|
||||
tros=[_tro_context(t, alle, eplan, bereiche) for t in geordnet],
|
||||
eplan_source=(f"{eplan.eplan_file} (Zuordnung vom {eplan.created})"
|
||||
if eplan is not None else ""),
|
||||
)
|
||||
|
||||
+58
-6
@@ -51,6 +51,7 @@ from jinja2 import (
|
||||
)
|
||||
from pydantic import ValidationError
|
||||
|
||||
from e_planning import EPLAN_SCHEMA, EplanDocument
|
||||
from material_flow import env_dir
|
||||
from model import SCHEMA, TroDocument
|
||||
from scl_context import TODO, SclFile, SclName, SclTro, build_context
|
||||
@@ -220,11 +221,11 @@ def emit_call(add, tro: SclTro) -> None:
|
||||
|
||||
for jam in tro.jam_entries:
|
||||
zeilen.append((f"arInOutJamEntr{jam.n} := {jam.db.value}.arCarrier",
|
||||
offen(jam.db, f"Staubereich von {jam.neighbour}")))
|
||||
offen(jam.db, f"{jam.title} von {jam.neighbour}")))
|
||||
zeilen.append((f"stInOutJamEntr{jam.n} := {jam.db.value}.stData", ""))
|
||||
for jam in tro.jam_exits:
|
||||
zeilen.append((f"arInOutJamExit{jam.n} := {jam.db.value}.arCarrier",
|
||||
offen(jam.db, f"Staubereich nach {jam.neighbour}")))
|
||||
offen(jam.db, f"{jam.title} vor {jam.neighbour}")))
|
||||
zeilen.append((f"stInOutJamExit{jam.n} := {jam.db.value}.stData", ""))
|
||||
|
||||
for s in tro.separators:
|
||||
@@ -329,6 +330,12 @@ def render_context(context: SclFile, templates: SclTemplates | None = None) -> s
|
||||
add("//")
|
||||
add("// Reihenfolge der REGIONs = Materialfluss.")
|
||||
add(f"// Anfang bei {context.tros[0].id}: {context.start_reason}.")
|
||||
if context.eplan_source:
|
||||
add("//")
|
||||
add(f"// E-Planung: {context.eplan_source}")
|
||||
add("// Separator-Sensoren und Stopperventile aus der Zuordnung sind")
|
||||
add("// eingesetzt (ohne Marker); was die Zuordnung nicht sicher fand,")
|
||||
add(f"// bleibt '{TODO}'. Details: <csv>_eplan.md (lib/e_planning.py).")
|
||||
if templates is not None:
|
||||
mit, ohne = template_usage(context, templates)
|
||||
add("//")
|
||||
@@ -372,9 +379,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
args = parse_args(argv)
|
||||
results = env_dir("SKEL_RESULTS", "results")
|
||||
|
||||
json_file = Path(args.json)
|
||||
if not json_file.is_absolute() and json_file.parent == Path("."):
|
||||
json_file = results / args.json
|
||||
json_file = _in_results(args.json, results)
|
||||
if not json_file.is_file():
|
||||
print(f"FEHLER: JSON nicht gefunden: {json_file}", file=sys.stderr)
|
||||
print(" Zuerst bin/tro_flow.bat --file <name>.csv --json laufen lassen.",
|
||||
@@ -412,7 +417,28 @@ def main(argv: list[str] | None = None) -> int:
|
||||
return 1
|
||||
templates = SclTemplates(ordner)
|
||||
|
||||
context = build_context(document, json_file.name, args.start)
|
||||
eplan = None
|
||||
if args.eplan:
|
||||
eplan_file = _in_results(args.eplan, results)
|
||||
try:
|
||||
eplan = EplanDocument.from_json(eplan_file.read_text(encoding="utf-8"))
|
||||
except OSError as exc:
|
||||
print(f"FEHLER: E-Planungs-Zuordnung nicht lesbar: {exc}", file=sys.stderr)
|
||||
print(" Zuerst bin/e_planning.bat --file <name>.csv --eplan "
|
||||
"<zeichnung>_positionsdraw.json laufen lassen.", file=sys.stderr)
|
||||
return 1
|
||||
except ValidationError as exc:
|
||||
print(f"FEHLER: {eplan_file} passt nicht zum Schema '{EPLAN_SCHEMA}' "
|
||||
f"({exc.error_count()} Abweichungen).", file=sys.stderr)
|
||||
return 1
|
||||
# Eine Zuordnung zu einem anderen Layout setzt falsche Namen ein -
|
||||
# Separatornummern sind nur innerhalb eines CSV-Exports eindeutig.
|
||||
if Path(eplan.csv_file).name != Path(document.source).name:
|
||||
print(f"FEHLER: {eplan_file.name} gehoert zu '{eplan.csv_file}', die "
|
||||
f"TRO-JSON zu '{document.source}'.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
context = build_context(document, json_file.name, args.start, eplan=eplan)
|
||||
mit, ohne = template_usage(context, templates)
|
||||
if args.strict and ohne:
|
||||
print(f"FEHLER: kein Template fuer {', '.join(ohne)} (--strict) - "
|
||||
@@ -440,6 +466,9 @@ def main(argv: list[str] | None = None) -> int:
|
||||
if templates is not None:
|
||||
print(f"Templates = {len(mit)} von {len(mit) + len(ohne)} Typen "
|
||||
f"(generisch: {', '.join(ohne) or '-'})")
|
||||
if eplan is not None:
|
||||
print(f"E-Planung = {eplan_file}")
|
||||
print(f"Eingesetzt = {_eingesetzt(context)} Namen aus der E-Planung")
|
||||
print(f"Offene Stellen = {offen} (Marker '{TODO}')")
|
||||
print(f"SCL = {out_file}")
|
||||
print("================================================================")
|
||||
@@ -447,6 +476,21 @@ def main(argv: list[str] | None = None) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def _in_results(name: str, results: Path) -> Path:
|
||||
"""Blosser Dateiname -> in SKEL_RESULTS; Pfadangaben bleiben unveraendert."""
|
||||
path = Path(name)
|
||||
if not path.is_absolute() and path.parent == Path("."):
|
||||
return results / name
|
||||
return path
|
||||
|
||||
|
||||
def _eingesetzt(context: SclFile) -> int:
|
||||
"""Wie viele Separator-Namen aus der E-Planung kamen."""
|
||||
return sum(not name.todo
|
||||
for tro in context.tros for s in tro.separators
|
||||
for name in (s.sensor, s.stopper))
|
||||
|
||||
|
||||
def _schema_of(path: Path) -> str:
|
||||
"""Schema-Kennung einer JSON-Datei, nur fuer die Fehlermeldung ("" wenn unlesbar)."""
|
||||
try:
|
||||
@@ -465,6 +509,7 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
" scl_skeleton.bat\n"
|
||||
" scl_skeleton.bat --json export_tro.json --out FB_Main.scl\n"
|
||||
" scl_skeleton.bat --json mubea_tro.json --templates\n"
|
||||
" scl_skeleton.bat --json mubea_tro.json --eplan mubea_eplan.json\n"
|
||||
"\n"
|
||||
"Exit-Codes:\n"
|
||||
" 0 ok (auch wenn TROs ohne Template generisch erzeugt wurden)\n"
|
||||
@@ -500,6 +545,13 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
"Ohne DIR: lib/scl_templates. FB-Typen ohne Template bleiben "
|
||||
"generisch. Ohne diesen Schalter: bisherige Ausgabe.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--eplan",
|
||||
metavar="NAME",
|
||||
help="Zuordnung zur E-Planung aus e_planning.py (<csv>_eplan.json in "
|
||||
"%%SKEL_RESULTS%% oder ein Pfad): Separator-Sensoren und "
|
||||
"Stopperventile werden eingesetzt statt als TODO markiert.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--strict",
|
||||
action="store_true",
|
||||
|
||||
@@ -50,11 +50,11 @@ REGION {{ t.region_title }}
|
||||
{% set _ = z.append(("stInOutMachineState := " ~ t.machine_state.value, m.note(t.machine_state, "Schaltschrank"))) %}
|
||||
{% set _ = z.append(("stInOutCognexInterface := #stCognexInterfaceDummy", "")) %}
|
||||
{% for j in t.jam_entries %}
|
||||
{% set _ = z.append(("arInOutJamEntr" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, "Staubereich von " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamEntr" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, j.title ~ " von " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamEntr" ~ j.n ~ " := " ~ j.data.value, m.note(j.data))) %}
|
||||
{% endfor %}
|
||||
{% for j in t.jam_exits %}
|
||||
{% set _ = z.append(("arInOutJamExit" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, "Staubereich nach " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamExit" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, j.title ~ " vor " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamExit" ~ j.n ~ " := " ~ j.data.value, m.note(j.data))) %}
|
||||
{% endfor %}
|
||||
{% for s in t.separators %}
|
||||
|
||||
@@ -42,11 +42,11 @@ REGION {{ t.region_title }}
|
||||
{% set _ = z.append(("stInOutMachineState := " ~ t.machine_state.value, m.note(t.machine_state, "Schaltschrank"))) %}
|
||||
{% set _ = z.append(("stInOutCognexInterface := #stCognexInterfaceDummy", "")) %}
|
||||
{% for j in t.jam_entries %}
|
||||
{% set _ = z.append(("arInOutJamEntr" ~ j.n ~ " := " ~ j.db.value ~ ".arCarrier", m.note(j.db, "Staubereich von " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamEntr" ~ j.n ~ " := " ~ j.db.value ~ ".arCarrier", m.note(j.db, j.title ~ " von " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamEntr" ~ j.n ~ " := " ~ j.db.value ~ ".stData", m.note(j.db))) %}
|
||||
{% endfor %}
|
||||
{% for j in t.jam_exits %}
|
||||
{% set _ = z.append(("arInOutJamExit" ~ j.n ~ " := " ~ j.db.value ~ ".arCarrier", m.note(j.db, "Staubereich nach " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamExit" ~ j.n ~ " := " ~ j.db.value ~ ".arCarrier", m.note(j.db, j.title ~ " vor " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamExit" ~ j.n ~ " := " ~ j.db.value ~ ".stData", m.note(j.db))) %}
|
||||
{% endfor %}
|
||||
{% set _ = z.append(('stInOutHMI := "DB_Interface_HMI".stTRO.' ~ t.id, "")) %}
|
||||
|
||||
@@ -83,11 +83,11 @@ REGION {{ t.region_title }}
|
||||
{% set _ = z.append(("stInOutCognexInterface := #stCognexInterfaceDummy", "")) %}
|
||||
{% endif %}
|
||||
{% for j in t.jam_entries %}
|
||||
{% set _ = z.append(("arInOutJamEntr" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, "Staubereich von " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamEntr" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, j.title ~ " von " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamEntr" ~ j.n ~ " := " ~ j.data.value, m.note(j.data))) %}
|
||||
{% endfor %}
|
||||
{% for j in t.jam_exits %}
|
||||
{% set _ = z.append(("arInOutJamExit" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, "Staubereich nach " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamExit" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, j.title ~ " vor " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamExit" ~ j.n ~ " := " ~ j.data.value, m.note(j.data))) %}
|
||||
{% endfor %}
|
||||
{% for s in t.separators %}
|
||||
|
||||
@@ -77,11 +77,11 @@ REGION {{ t.region_title }}
|
||||
{% endif %}
|
||||
{% set _ = z.append(("stInOutCognexInterfaceSSCC := " ~ ss.scanner.interface.value, m.note(ss.scanner.interface))) %}
|
||||
{% for j in t.jam_entries %}
|
||||
{% set _ = z.append(("arInOutJamEntr" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, "Staubereich von " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamEntr" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, j.title ~ " von " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamEntr" ~ j.n ~ " := " ~ j.data.value, m.note(j.data))) %}
|
||||
{% endfor %}
|
||||
{% for j in t.jam_exits %}
|
||||
{% set _ = z.append(("arInOutJamExit" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, "Staubereich nach " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamExit" ~ j.n ~ " := " ~ j.carriers.value, m.note(j.carriers, j.title ~ " vor " ~ j.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamExit" ~ j.n ~ " := " ~ j.data.value, m.note(j.data))) %}
|
||||
{% endfor %}
|
||||
{% set _ = z.append(('stInOutHMI := "DB_Interface_HMI".stTRO.' ~ t.id, "")) %}
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
Sep Store n nJamLength offen (Referenz: 4 bzw. "cStorage111ExitJam<n>"),
|
||||
Zeiten ueber die Konstanten cTimeSep...StorageLine
|
||||
arInSep[7..19].stSimu Simulationsmodus der Referenz - nicht geschrieben
|
||||
JamEntr/JamExit je einer: erster Vorgaenger/Nachfolger. Hat das
|
||||
Lager mehr Zu-/Ablaeufe, steht ein TODO im Kopf.
|
||||
JamEntr/JamExit je einer: der eigene Eingangs-Staubereich (mehrere
|
||||
Zulaeufe laufen dort zusammen, TRO-JSON v4) und der
|
||||
des ersten Nachfolgers. Hat das Lager mehr
|
||||
Staubereiche, steht ein TODO im Kopf.
|
||||
JamBypass Platzhalter wie Referenz
|
||||
Carr_Glob_Correction nInStation offen (Referenz 1), Traeger vom Eingang
|
||||
Alle noch mit den SPS-Kollegen zu bestaetigen.
|
||||
@@ -28,9 +30,9 @@
|
||||
{% set kamera = "" if sc.tag.todo else " (" ~ (sc.tag.value|upper) ~ ")" %}
|
||||
REGION {{ t.region_title }}
|
||||
{{ m.kopf(t) }}
|
||||
{% if t.predecessors|length > 1 or t.successors|length > 1 %}
|
||||
// {{ TODO }}: {{ t.predecessors|length }} Zulaeufe / {{ t.successors|length }} Ablaeufe im Layout -
|
||||
// der FB hat je nur einen Staubereich (JamEntr/JamExit, dazu JamBypass)
|
||||
{% if t.jam_entries|length > 1 or t.jam_exits|length > 1 %}
|
||||
// {{ TODO }}: {{ t.jam_entries|length }} Eingangs- / {{ t.jam_exits|length }} Ausgangs-Staubereiche im Layout -
|
||||
// der FB hat je nur einen (JamEntr/JamExit, dazu JamBypass)
|
||||
{% endif %}
|
||||
// Linien (Separatoren aus dem Layout): {{ st.lines|map(attribute="layout")|join(", ") or "-" }}
|
||||
|
||||
@@ -103,9 +105,9 @@ REGION {{ t.region_title }}
|
||||
{% set _ = z.append(("xInConveyorRunOut := " ~ st.run_out.value, m.note(st.run_out, "Foerderer am Ablauf"))) %}
|
||||
{% set _ = z.append(("stInOutControlUnitCabinet := " ~ t.machine_state.value, m.note(t.machine_state, "Schaltschrank"))) %}
|
||||
{% set _ = z.append(("arInOutStorageLines := " ~ st.db.value ~ ".arLine", m.note(st.db))) %}
|
||||
{% set _ = z.append(("arInOutJamEntr := " ~ t.jam_in.carriers.value, m.note(t.jam_in.carriers, "Staubereich von " ~ t.jam_in.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamEntr := " ~ t.jam_in.carriers.value, m.note(t.jam_in.carriers, t.jam_in.title ~ " von " ~ t.jam_in.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamEntr := " ~ t.jam_in.data.value, m.note(t.jam_in.data))) %}
|
||||
{% set _ = z.append(("arInOutJamExit := " ~ t.jam_out.carriers.value, m.note(t.jam_out.carriers, "Staubereich nach " ~ t.jam_out.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamExit := " ~ t.jam_out.carriers.value, m.note(t.jam_out.carriers, t.jam_out.title ~ " vor " ~ t.jam_out.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamExit := " ~ t.jam_out.data.value, m.note(t.jam_out.data))) %}
|
||||
{% set _ = z.append(("arInOutJamBypass := #arCarrierPlaceHolder", "")) %}
|
||||
{% set _ = z.append(("stInOutJamBypass := #stJamDataPlaceHolder", "")) %}
|
||||
|
||||
@@ -69,9 +69,9 @@ REGION {{ t.region_title }}
|
||||
{% set _ = z.append(("xInErrorQuit := " ~ t.error_quit.value, m.note(t.error_quit, "Quittierung"))) %}
|
||||
{% set _ = z.append(("xInAutomatic := " ~ t.automatic.value, m.note(t.automatic, "Automatik des Schaltschranks"))) %}
|
||||
{% set _ = z.append(("xInRelease := " ~ t.release, "")) %}
|
||||
{% set _ = z.append(("arInOutJamEntr := " ~ t.jam_in.carriers.value, m.note(t.jam_in.carriers, "Staubereich von " ~ t.jam_in.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamEntr := " ~ t.jam_in.carriers.value, m.note(t.jam_in.carriers, t.jam_in.title ~ " von " ~ t.jam_in.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamEntr := " ~ t.jam_in.data.value, m.note(t.jam_in.data))) %}
|
||||
{% set _ = z.append(("arInOutJamFinger := " ~ t.jam_out.carriers.value, m.note(t.jam_out.carriers, "Staubereich nach " ~ t.jam_out.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamFinger := " ~ t.jam_out.carriers.value, m.note(t.jam_out.carriers, t.jam_out.title ~ " vor " ~ t.jam_out.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamFinger := " ~ t.jam_out.data.value, m.note(t.jam_out.data))) %}
|
||||
{% set _ = z.append(("stInOutMachineState := " ~ t.machine_state.value, m.note(t.machine_state, "Schaltschrank"))) %}
|
||||
{% set _ = z.append(("stInOutCognexInterface := #stCognexInterfaceDummy", "")) %}
|
||||
|
||||
@@ -71,9 +71,9 @@ REGION {{ t.region_title }}
|
||||
{% set _ = z.append(("xInMotorProtection := " ~ t.motor_protection.value, m.note(t.motor_protection))) %}
|
||||
{% set _ = z.append(('stInStationSensor := "DB_Inputs".Sensors["' ~ ws.station_sensor.value ~ '"]', m.note(ws.station_sensor, "Arbeitsplatz"))) %}
|
||||
{% set _ = z.append(("xInReleaseStation := " ~ ws.station_release.value, m.note(ws.station_release, "Freigabe Arbeitsplatz"))) %}
|
||||
{% set _ = z.append(("arInOutJamEntr := " ~ t.jam_in.carriers.value, m.note(t.jam_in.carriers, "Staubereich von " ~ t.jam_in.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamEntr := " ~ t.jam_in.carriers.value, m.note(t.jam_in.carriers, t.jam_in.title ~ " von " ~ t.jam_in.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamEntr := " ~ t.jam_in.data.value, m.note(t.jam_in.data))) %}
|
||||
{% set _ = z.append(("arInOutJamFinger := " ~ t.jam_out.carriers.value, m.note(t.jam_out.carriers, "Staubereich nach " ~ t.jam_out.neighbour))) %}
|
||||
{% set _ = z.append(("arInOutJamFinger := " ~ t.jam_out.carriers.value, m.note(t.jam_out.carriers, t.jam_out.title ~ " vor " ~ t.jam_out.neighbour))) %}
|
||||
{% set _ = z.append(("stInOutJamFinger := " ~ t.jam_out.data.value, m.note(t.jam_out.data))) %}
|
||||
{% set _ = z.append(("stInOutMachineState := " ~ t.machine_state.value, m.note(t.machine_state, "Schaltschrank"))) %}
|
||||
{% set _ = z.append(("stInOutCognexInterface := #stCognexInterfaceDummy", "")) %}
|
||||
|
||||
+16
-2
@@ -116,7 +116,9 @@ from tro_flow import (
|
||||
analyse_tros,
|
||||
find_sibling_raw_json,
|
||||
load_sepliste_counts,
|
||||
resolve_ids_file,
|
||||
)
|
||||
from tro_ids import TroIds
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Konstanten
|
||||
@@ -844,6 +846,9 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
metavar="MM", help="Markerradius in mm. Standard: %(default)s")
|
||||
parser.add_argument("--text-height", type=float, default=DEFAULT_TEXT,
|
||||
metavar="MM", help="Texthoehe in mm. Standard: %(default)s")
|
||||
parser.add_argument("--ids", metavar="DATEI",
|
||||
help="ID-Datei der TROs wie bei tro_flow (nur gelesen). "
|
||||
"Standard: <csv>_tro_ids.ini in %%SKEL_CFG%%.")
|
||||
parser.add_argument("--offset", metavar="DX,DY",
|
||||
help="Lagebezug von Hand setzen statt ermitteln.")
|
||||
parser.add_argument("--re-register", action="store_true",
|
||||
@@ -907,12 +912,21 @@ def main(argv: list[str] | None = None) -> int:
|
||||
return 1
|
||||
graph = build_graph(elements, csv_file.name, read_warnings)
|
||||
# Derselbe Aufruf wie in tro_flow.main (ohne cfg/tro_overrides.ini, mit
|
||||
# der Separator-Gegenprobe aus <name>_raw.json), damit die Zeichnung genau
|
||||
# der Separator-Gegenprobe aus <name>_raw.json und der ID-Datei), damit die Zeichnung genau
|
||||
# die TROs der TRO-JSON zeigt - sie ist nur Ansicht, keine Datenquelle.
|
||||
raw_json_file = find_sibling_raw_json(csv_file)
|
||||
sepliste_counts = (load_sepliste_counts(raw_json_file, read_warnings)
|
||||
if raw_json_file is not None else {})
|
||||
analysis = analyse_tros(graph, elements, sepliste_counts=sepliste_counts)
|
||||
# Dieselbe ID-Datei wie tro_flow, aber nur lesen: neue TROs bekommen
|
||||
# dieselbe vorlaeufige ID, die tro_flow anhaengen wuerde.
|
||||
ids_file = resolve_ids_file(args.ids, csv_file)
|
||||
try:
|
||||
tro_ids = TroIds.load(ids_file) if ids_file.is_file() else None
|
||||
except (OSError, ValueError) as exc:
|
||||
print(f"FEHLER: ID-Datei nicht lesbar: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
analysis = analyse_tros(graph, elements, sepliste_counts=sepliste_counts,
|
||||
tro_ids=tro_ids)
|
||||
if not analysis.tros:
|
||||
print(f"FEHLER: keine TROs aus {csv_file} ableitbar.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
+186
-8
@@ -90,6 +90,14 @@ Ausgabe (alles in SKEL_RESULTS)
|
||||
fuer scl_skeleton.py
|
||||
<name>_tro_errors.md nur wenn TRO-Fehler gefunden wurden
|
||||
|
||||
TRO-IDs (ID-Datei in SKEL_CFG)
|
||||
------------------------------
|
||||
Die IDs kommen aus %SKEL_CFG%/<name>_tro_ids.ini (lib/tro_ids.py): jeder TRO
|
||||
wird ueber die Lage seiner Separatoren wiedergefunden und behaelt seine ID,
|
||||
auch wenn die SPS-Programmierer sie umbenannt haben. Fehlt die Datei, werden
|
||||
die TROs entlang des Materialflusses durchnummeriert und die Datei angelegt;
|
||||
neue TROs werden angehaengt. --renumber nummeriert alles neu.
|
||||
|
||||
Aufruf ueber bin/tro_flow.bat bzw. bin/tro_flow.sh.
|
||||
"""
|
||||
|
||||
@@ -126,6 +134,16 @@ from material_flow import (
|
||||
resolve_input,
|
||||
)
|
||||
from model import Analysis, DerivedTro, TroDocument
|
||||
from tro_ids import (
|
||||
DEFAULT_TOLERANCE,
|
||||
TroIds,
|
||||
assign_ids,
|
||||
ids_path,
|
||||
insert_entries,
|
||||
other_sections,
|
||||
render_ids,
|
||||
render_new_entries,
|
||||
)
|
||||
from tro_catalog import (
|
||||
CATALOG_AS_OF,
|
||||
ITEM_FB,
|
||||
@@ -655,6 +673,7 @@ def derive_tros(
|
||||
findings: list[str],
|
||||
*,
|
||||
sepliste_counts: dict[str, int] | None = None,
|
||||
tro_ids: TroIds | None = None,
|
||||
) -> list[DerivedTro]:
|
||||
"""TRO-Liste aus den Separatoren und ihren angeschlossenen Objekten bilden.
|
||||
|
||||
@@ -666,6 +685,10 @@ def derive_tros(
|
||||
der CSV abgeleitete Separatorenzahl eines Strecke/Gefaellestrecke-Hosts
|
||||
nicht mit dieser Zahl ueberein, wird ein Finding angehaengt; die TRO-Daten
|
||||
selbst bleiben unangetastet (CSV bleibt die massgebliche Quelle).
|
||||
|
||||
tro_ids (optional): ID-Datei der Anlage. Ohne sie werden die TROs entlang
|
||||
des Materialflusses neu durchnummeriert; mit ihr behalten wiedergefundene
|
||||
TROs ihre ID (Ergebnis in tro_ids.result).
|
||||
"""
|
||||
def resolve_host(sep: Element, findings: list[str]) -> str | None:
|
||||
return _default_resolve_host(sep, findings, graph)
|
||||
@@ -1156,11 +1179,16 @@ def derive_tros(
|
||||
tro.reason = f"{tro.reason}; {len(exits)} abgehende Wege -> Weiche"
|
||||
tro.confidence = "niedrig"
|
||||
|
||||
# 5) Stabile, laufende Nummern in Reihenfolge der Host-Knoten
|
||||
tros.sort(key=lambda t: (t.hosts[0], t.separators[0] if t.separators else ""))
|
||||
width = max(2, len(str(len(tros))))
|
||||
for index, tro in enumerate(tros, start=1):
|
||||
tro.tro_id = f"TRO{index:0{width}d}"
|
||||
# 5) IDs: Reihenfolge entlang des Materialflusses, wiedergefundene TROs
|
||||
# behalten ihre ID aus der ID-Datei (siehe lib/tro_ids.py)
|
||||
tros.sort(key=_flow_key(graph, elements_by_id))
|
||||
anchors = separator_anchors(elements)
|
||||
if tro_ids is None:
|
||||
assign_ids(tros, anchors, [], renumber=True)
|
||||
else:
|
||||
tro_ids.result = assign_ids(
|
||||
tros, anchors, tro_ids.entries,
|
||||
tolerance=tro_ids.tolerance, renumber=tro_ids.renumber)
|
||||
|
||||
# 6) Scanner am Separator: 1Sep_SSCC laesst sich mechanisch nicht erkennen
|
||||
for tro in tros:
|
||||
@@ -1174,6 +1202,56 @@ def derive_tros(
|
||||
return tros
|
||||
|
||||
|
||||
def separator_anchors(elements: list[Element]) -> dict[str, tuple[float, float]]:
|
||||
"""Separator-TeileId -> (x, y) in mm, Bezugslage fuer die ID-Datei."""
|
||||
return {e.teile_id: (e.position[0], e.position[1])
|
||||
for e in elements if e.kind == "Separator" and e.position}
|
||||
|
||||
|
||||
def _flow_rank(graph: Graph) -> dict[str, int]:
|
||||
"""Knoten -> Rang entlang des Materialflusses (Tiefensuche).
|
||||
|
||||
Start an den Materialeingaengen (Beladung/Eingang), danach an jedem noch
|
||||
nicht erreichten Knoten - eine geschlossene Anlage ohne Station wird so
|
||||
trotzdem vollstaendig erfasst. Gleichrangige Nachfolger nach Knotennamen.
|
||||
"""
|
||||
succ: dict[str, list[str]] = {}
|
||||
for edge in graph.edges:
|
||||
succ.setdefault(edge.src, []).append(edge.dst)
|
||||
starts = sorted(n for n, node in graph.nodes.items() if node.element.is_entry)
|
||||
rank: dict[str, int] = {}
|
||||
for start in starts + sorted(graph.nodes):
|
||||
stack = [start]
|
||||
while stack:
|
||||
node = stack.pop()
|
||||
if node in rank:
|
||||
continue
|
||||
rank[node] = len(rank)
|
||||
stack.extend(n for n in sorted(succ.get(node, []), reverse=True)
|
||||
if n not in rank)
|
||||
return rank
|
||||
|
||||
|
||||
def _flow_key(graph: Graph, elements_by_id: dict[str, Element]):
|
||||
"""Sortierschluessel fuer TROs entlang des Materialflusses.
|
||||
|
||||
Erst der Rang des Host-Knotens, dann die Lage des Separators in der
|
||||
TrackIds-Kette des Hosts (Kreisel: Umlaufrichtung, Strecke: Baureihenfolge).
|
||||
"""
|
||||
rank = _flow_rank(graph)
|
||||
|
||||
def key(tro: DerivedTro) -> tuple:
|
||||
host_rank = min((rank.get(h, len(rank)) for h in tro.hosts),
|
||||
default=len(rank))
|
||||
sep = tro.separators[0] if tro.separators else ""
|
||||
host = elements_by_id.get(tro.hosts[0]) if tro.hosts else None
|
||||
track = host.track_ids if host else []
|
||||
in_track = track.index(sep) if sep in track else len(track)
|
||||
return (host_rank, in_track, sep)
|
||||
|
||||
return key
|
||||
|
||||
|
||||
# Typnamen fuer Systemgrenz-Knoten (siehe derive_boundaries) - bewusst NICHT
|
||||
# in TRO_CATALOG: eine Be-/Entladestation (BTMT oder generisches Eingang/
|
||||
# Ausgang-Symbol) ist kein Steuerobjekt mit eigenem FB-Baustein, nur der
|
||||
@@ -1781,11 +1859,15 @@ def analyse_tros(
|
||||
elements: list[Element],
|
||||
*,
|
||||
sepliste_counts: dict[str, int] | None = None,
|
||||
tro_ids: TroIds | None = None,
|
||||
) -> Analysis:
|
||||
"""TROs ermitteln, verknuepfen, verorten und gegen die Typdefinition pruefen."""
|
||||
"""TROs ermitteln, verknuepfen, verorten und gegen die Typdefinition pruefen.
|
||||
|
||||
tro_ids: ID-Datei der Anlage (siehe derive_tros); None = neu nummerieren.
|
||||
"""
|
||||
analysis = Analysis()
|
||||
analysis.tros = derive_tros(graph, elements, analysis.findings,
|
||||
sepliste_counts=sepliste_counts)
|
||||
sepliste_counts=sepliste_counts, tro_ids=tro_ids)
|
||||
analysis.boundaries = derive_boundaries(elements, graph)
|
||||
# Fuer Verortung und Verknuepfung zaehlen TROs und Systemgrenzen zusammen:
|
||||
# link_tros() registriert eine Kante nur zu einem Knoten, der Host eines
|
||||
@@ -2638,6 +2720,8 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
" tro_flow.bat --file mubea.csv --tosvg --doc\n"
|
||||
" tro_flow.bat --file mubea.csv --tosvg --use-cords\n"
|
||||
" tro_flow.bat --file mubea.csv --json\n"
|
||||
" tro_flow.bat --file mubea_export.csv --ids mubea_tro_ids.ini --json\n"
|
||||
" tro_flow.bat --file mubea.csv --renumber\n"
|
||||
"\n"
|
||||
"Exit-Codes:\n"
|
||||
" 0 ok\n"
|
||||
@@ -2694,9 +2778,93 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||
f"Leerstrecken ueber {COORD_MAX_LUECKE} mm zwischen den Knoten "
|
||||
"(Reihenfolge und Nachbarschaft bleiben erhalten).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ids",
|
||||
metavar="DATEI",
|
||||
help="ID-Datei der Anlage (Name in %%SKEL_CFG%% oder vollstaendiger "
|
||||
"Pfad). Standard: <name>_tro_ids.ini in %%SKEL_CFG%%. Fehlt sie, "
|
||||
"wird sie angelegt.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--renumber",
|
||||
action="store_true",
|
||||
help="Alle TRO-IDs neu entlang des Materialflusses vergeben und die "
|
||||
"Abschnitt [tro] der ID-Datei neu schreiben (fruehe Projektphase - "
|
||||
"umbenannte IDs gehen verloren, andere Abschnitte bleiben).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--id-tolerance",
|
||||
dest="id_tolerance",
|
||||
type=float,
|
||||
default=DEFAULT_TOLERANCE,
|
||||
metavar="MM",
|
||||
help="Hoechster Abstand (mm) zwischen Eintrag der ID-Datei und "
|
||||
"Separator, bis zu dem ein TRO als wiedergefunden gilt. "
|
||||
"Standard: %(default)s",
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def resolve_ids_file(arg: str | None, csv_file: Path) -> Path:
|
||||
"""Pfad der ID-Datei: --ids (Name in SKEL_CFG oder Pfad), sonst Standard."""
|
||||
cfg_dir = env_dir("SKEL_CFG", "cfg")
|
||||
if not arg:
|
||||
return ids_path(csv_file.stem, cfg_dir)
|
||||
path = Path(arg)
|
||||
if path.is_absolute() or path.parent != Path("."):
|
||||
return path
|
||||
return cfg_dir / path
|
||||
|
||||
|
||||
def write_ids_file(tro_ids: TroIds, analysis: Analysis, elements: list[Element],
|
||||
source: str) -> str:
|
||||
"""ID-Datei anlegen, neu schreiben oder neue TROs anhaengen.
|
||||
|
||||
Rueckgabe: kurze Beschreibung fuer den Bericht.
|
||||
"""
|
||||
result = tro_ids.result
|
||||
if result is None:
|
||||
return "unveraendert"
|
||||
anchors = separator_anchors(elements)
|
||||
if result.renumbered:
|
||||
tro_ids.path.parent.mkdir(parents=True, exist_ok=True)
|
||||
rest = other_sections(tro_ids.path)
|
||||
text = render_ids(analysis.tros, anchors, source)
|
||||
tro_ids.path.write_text(text + (f"\n{rest}\n" if rest else ""),
|
||||
encoding="utf-8")
|
||||
return "neu geschrieben" if tro_ids.exists else "angelegt"
|
||||
if result.new:
|
||||
insert_entries(tro_ids.path,
|
||||
render_new_entries(analysis.tros, anchors, result.new, source))
|
||||
return f"{len(result.new)} neue(r) TRO(s) angehaengt"
|
||||
return "unveraendert"
|
||||
|
||||
|
||||
def report_ids(tro_ids: TroIds, status: str) -> None:
|
||||
"""Abschnitt TRO-IDs im Konsolenbericht."""
|
||||
result = tro_ids.result
|
||||
if result is None:
|
||||
return
|
||||
print(f"TRO-IDs = {tro_ids.path} ({status})")
|
||||
if result.renumbered:
|
||||
grund = "--renumber" if tro_ids.renumber else "keine ID-Datei"
|
||||
print(f" neu entlang des Materialflusses nummeriert "
|
||||
f"({grund})")
|
||||
return
|
||||
print(f" wiedergefunden: {len(result.kept)}, "
|
||||
f"neu: {len(result.new)}, weggefallen: {len(result.missing)} "
|
||||
f"(Toleranz {tro_ids.tolerance:g} mm)")
|
||||
if result.new:
|
||||
print(f" + neu (vorlaeufige ID, in der ID-Datei umbenennbar): "
|
||||
f"{', '.join(result.new)}")
|
||||
for entry in result.missing:
|
||||
merged = result.merged_into.get(entry.tro_id)
|
||||
grund = (f"Separator gehoert jetzt zu {merged}" if merged else
|
||||
f"kein Separator innerhalb {tro_ids.tolerance:g} mm")
|
||||
print(f" - weggefallen: {entry.tro_id} bei {entry.x:.1f}, {entry.y:.1f} "
|
||||
f"({grund}; Eintrag bleibt stehen, ID wird nicht wiederverwendet)")
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
args = parse_args(argv)
|
||||
|
||||
@@ -2739,11 +2907,20 @@ def main(argv: list[str] | None = None) -> int:
|
||||
connect_target = results / f"{stem}_tro_connect.ini"
|
||||
json_target = results / f"{stem}_tro.json"
|
||||
|
||||
analysis = analyse_tros(graph, elements, sepliste_counts=sepliste_counts)
|
||||
try:
|
||||
tro_ids = TroIds.load(resolve_ids_file(args.ids, csv_file),
|
||||
tolerance=args.id_tolerance, renumber=args.renumber)
|
||||
except (OSError, ValueError) as exc:
|
||||
print(f"FEHLER: ID-Datei nicht lesbar: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
analysis = analyse_tros(graph, elements, sepliste_counts=sepliste_counts,
|
||||
tro_ids=tro_ids)
|
||||
if not analysis.tros:
|
||||
print(f"FEHLER: {csv_file} enthaelt keine Separatoren - keine TROs "
|
||||
f"ableitbar.", file=sys.stderr)
|
||||
return 1
|
||||
ids_status = write_ids_file(tro_ids, analysis, elements, csv_file.name)
|
||||
|
||||
dot_file = results / f"{stem}_tro_flow.dot"
|
||||
svg_file = results / f"{stem}_tro_flow.svg"
|
||||
@@ -2806,6 +2983,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
|
||||
report(analysis, csv_file, dot_file, created_svg, error_file, doc_file,
|
||||
warnings, engine=engine)
|
||||
report_ids(tro_ids, ids_status)
|
||||
if connect_file:
|
||||
print(f"connect.ini = {connect_file}")
|
||||
if json_file:
|
||||
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
tro_ids.py - stabile TRO-IDs ueber eine ID-Datei je Anlage.
|
||||
|
||||
Die TRO-IDs vergeben die SPS-Programmierer; eine ID muss nur eindeutig sein.
|
||||
Sobald SCL-Code steht, darf sich eine ID nicht mehr aendern, nur weil das
|
||||
Layout neu exportiert wurde. Die Layout-TeileId taugt dafuer nicht (sie
|
||||
wechselt zwischen zwei Exporten derselben Anlage), stabil ist nur die Lage.
|
||||
Siehe doc/Konzept_Layout_Hardware_SCL.md, "Festlegungen" -> "TRO-IDs".
|
||||
|
||||
Die ID-Datei (`%SKEL_CFG%/<csv>_tro_ids.ini`, versioniert, von Hand
|
||||
umbenennbar) haelt je TRO die ID und die Lage seines Anker-Separators:
|
||||
|
||||
[tro]
|
||||
TRO01 = 1234.5, -567.8 ; 1Sep1Swi | Separator 0003 auf Kreisel2 Bahn L
|
||||
K0102_1 = 2210.0, 410.0 ; von Hand umbenannt
|
||||
|
||||
Ablauf (assign_ids):
|
||||
|
||||
- ohne ID-Datei bzw. mit renumber: alle TROs in der uebergebenen Reihenfolge
|
||||
(entlang des Materialflusses) neu durchnummerieren, TRO01, TRO02, ...
|
||||
- mit ID-Datei: jeder Eintrag findet den TRO, dessen Separator seiner Lage am
|
||||
naechsten liegt (innerhalb der Toleranz, je TRO hoechstens ein Eintrag).
|
||||
Nicht gefundene TROs bekommen die naechste freie Nummer, nicht gefundene
|
||||
Eintraege bleiben in der Datei stehen und ihre ID wird nicht wiederverwendet.
|
||||
|
||||
Das Modul liest und schreibt nur die Datei; Lage und Reihenfolge der TROs
|
||||
liefert tro_flow.py.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import configparser
|
||||
import math
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
__all__ = [
|
||||
"SECTION",
|
||||
"DEFAULT_TOLERANCE",
|
||||
"IdEntry",
|
||||
"IdAssignment",
|
||||
"TroIds",
|
||||
"ids_path",
|
||||
"load_ids",
|
||||
"assign_ids",
|
||||
"render_ids",
|
||||
"render_new_entries",
|
||||
"insert_entries",
|
||||
"other_sections",
|
||||
]
|
||||
|
||||
SECTION = "tro"
|
||||
ID_FILE_SUFFIX = "_tro_ids.ini"
|
||||
PREFIX = "TRO"
|
||||
|
||||
# Abstand (mm) zwischen Eintrag und Separator, bis zu dem ein TRO als
|
||||
# wiedergefunden gilt. Separatoren derselben Bahn liegen deutlich weiter
|
||||
# auseinander; ein verschobenes Bauteil faellt so als "neu" auf.
|
||||
DEFAULT_TOLERANCE = 100.0
|
||||
|
||||
# Eine ID wird zum SCL-Bezeichner (Instanz #<ID>, "DB_TRO_TEST".<ID>).
|
||||
_ID_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
||||
# SYSn sind die Systemgrenzen (tro_flow.derive_boundaries), keine TROs.
|
||||
_RESERVED = re.compile(r"^SYS\d+$")
|
||||
_NUMBERED = re.compile(rf"^{PREFIX}(\d+)$")
|
||||
|
||||
|
||||
@dataclass
|
||||
class IdEntry:
|
||||
"""Ein Eintrag der ID-Datei: ID und Lage des Anker-Separators (mm)."""
|
||||
|
||||
tro_id: str
|
||||
x: float
|
||||
y: float
|
||||
|
||||
|
||||
@dataclass
|
||||
class IdAssignment:
|
||||
"""Ergebnis von assign_ids, fuer den Bericht."""
|
||||
|
||||
renumbered: bool = False
|
||||
# (ID, Abstand in mm) der wiedergefundenen TROs
|
||||
kept: list[tuple[str, float]] = field(default_factory=list)
|
||||
# IDs, die in diesem Lauf neu vergeben wurden (ohne renumbered)
|
||||
new: list[str] = field(default_factory=list)
|
||||
# Eintraege der Datei ohne passenden TRO
|
||||
missing: list[IdEntry] = field(default_factory=list)
|
||||
# weggefallene ID -> TRO, dessen Separator dort liegt, der aber schon eine
|
||||
# naehere ID traegt (z. B. zwei TROs zu einem 2Sep2Swi zusammengelegt)
|
||||
merged_into: dict[str, str] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TroIds:
|
||||
"""ID-Datei einer Anlage plus Einstellungen fuer einen Lauf.
|
||||
|
||||
entries ist leer, wenn die Datei (noch) nicht existiert. result fuellt
|
||||
tro_flow.derive_tros ueber assign_ids.
|
||||
"""
|
||||
|
||||
path: Path
|
||||
entries: list[IdEntry] = field(default_factory=list)
|
||||
exists: bool = False
|
||||
tolerance: float = DEFAULT_TOLERANCE
|
||||
renumber: bool = False
|
||||
result: IdAssignment | None = None
|
||||
|
||||
@classmethod
|
||||
def load(cls, path: Path, *, tolerance: float = DEFAULT_TOLERANCE,
|
||||
renumber: bool = False) -> "TroIds":
|
||||
exists = path.is_file()
|
||||
entries = load_ids(path) if exists else []
|
||||
return cls(path=path, entries=entries, exists=exists,
|
||||
tolerance=tolerance, renumber=renumber)
|
||||
|
||||
|
||||
def ids_path(csv_stem: str, cfg_dir: Path) -> Path:
|
||||
"""Standardpfad der ID-Datei zu einer CSV: <cfg>/<stem>_tro_ids.ini."""
|
||||
return cfg_dir / f"{csv_stem}{ID_FILE_SUFFIX}"
|
||||
|
||||
|
||||
def _parser() -> configparser.ConfigParser:
|
||||
parser = configparser.ConfigParser(
|
||||
inline_comment_prefixes=(";", "#"),
|
||||
interpolation=None,
|
||||
strict=True,
|
||||
)
|
||||
parser.optionxform = str # IDs nicht in Kleinbuchstaben wandeln
|
||||
return parser
|
||||
|
||||
|
||||
def load_ids(path: Path) -> list[IdEntry]:
|
||||
"""ID-Datei lesen. ValueError bei doppelter ID, falscher Lage oder ID."""
|
||||
parser = _parser()
|
||||
try:
|
||||
parser.read(path, encoding="utf-8")
|
||||
except configparser.Error as exc:
|
||||
raise ValueError(f"{path}: {exc}") from exc
|
||||
if not parser.has_section(SECTION):
|
||||
raise ValueError(f"{path}: Abschnitt [{SECTION}] fehlt")
|
||||
|
||||
entries: list[IdEntry] = []
|
||||
for tro_id, value in parser.items(SECTION):
|
||||
if not _ID_PATTERN.match(tro_id):
|
||||
raise ValueError(f"{path}: '{tro_id}' ist kein gueltiger SCL-Bezeichner")
|
||||
if _RESERVED.match(tro_id):
|
||||
raise ValueError(f"{path}: '{tro_id}' ist fuer Systemgrenzen reserviert")
|
||||
parts = [p.strip() for p in value.split(",")]
|
||||
try:
|
||||
x, y = (float(p) for p in parts)
|
||||
except ValueError:
|
||||
raise ValueError(f"{path}: {tro_id} = '{value}' - erwartet 'x, y' in mm") \
|
||||
from None
|
||||
entries.append(IdEntry(tro_id, x, y))
|
||||
return entries
|
||||
|
||||
|
||||
def _distance(tro, entry: IdEntry, anchors: dict[str, tuple[float, float]]) -> float:
|
||||
"""Kleinster Abstand eines Separators des TRO zum Eintrag (inf ohne Lage)."""
|
||||
best = math.inf
|
||||
for sep in tro.separators:
|
||||
pos = anchors.get(sep)
|
||||
if pos is not None:
|
||||
best = min(best, math.hypot(pos[0] - entry.x, pos[1] - entry.y))
|
||||
return best
|
||||
|
||||
|
||||
def _width(count: int) -> int:
|
||||
return max(2, len(str(count)))
|
||||
|
||||
|
||||
def assign_ids(
|
||||
tros: list,
|
||||
anchors: dict[str, tuple[float, float]],
|
||||
entries: list[IdEntry],
|
||||
*,
|
||||
tolerance: float = DEFAULT_TOLERANCE,
|
||||
renumber: bool = False,
|
||||
) -> IdAssignment:
|
||||
"""tro.tro_id fuer alle TROs setzen (Reihenfolge = Reihenfolge der Liste).
|
||||
|
||||
anchors: Separator-TeileId -> (x, y) in mm.
|
||||
"""
|
||||
result = IdAssignment(renumbered=renumber or not entries)
|
||||
if result.renumbered:
|
||||
width = _width(len(tros))
|
||||
for index, tro in enumerate(tros, start=1):
|
||||
tro.tro_id = f"{PREFIX}{index:0{width}d}"
|
||||
return result
|
||||
|
||||
# Paare nach Abstand; jeder TRO und jeder Eintrag hoechstens einmal
|
||||
pairs = sorted(
|
||||
(d, i, j)
|
||||
for i, tro in enumerate(tros)
|
||||
for j, entry in enumerate(entries)
|
||||
if (d := _distance(tro, entry, anchors)) <= tolerance
|
||||
)
|
||||
tro_taken: dict[int, tuple[int, float]] = {}
|
||||
entry_taken: set[int] = set()
|
||||
for d, i, j in pairs:
|
||||
if i in tro_taken or j in entry_taken:
|
||||
continue
|
||||
tro_taken[i] = (j, d)
|
||||
entry_taken.add(j)
|
||||
|
||||
used = {e.tro_id for e in entries}
|
||||
numbers = [int(m.group(1)) for e in entries if (m := _NUMBERED.match(e.tro_id))]
|
||||
widths = [len(m.group(1)) for e in entries if (m := _NUMBERED.match(e.tro_id))]
|
||||
width = max(widths + [_width(len(tros))])
|
||||
next_number = max(numbers, default=0) + 1
|
||||
|
||||
for i, tro in enumerate(tros):
|
||||
if i in tro_taken:
|
||||
j, d = tro_taken[i]
|
||||
tro.tro_id = entries[j].tro_id
|
||||
result.kept.append((tro.tro_id, d))
|
||||
continue
|
||||
while f"{PREFIX}{next_number:0{width}d}" in used:
|
||||
next_number += 1
|
||||
tro.tro_id = f"{PREFIX}{next_number:0{width}d}"
|
||||
used.add(tro.tro_id)
|
||||
next_number += 1
|
||||
result.new.append(tro.tro_id)
|
||||
|
||||
result.missing = [e for j, e in enumerate(entries) if j not in entry_taken]
|
||||
for d, i, j in pairs:
|
||||
if j not in entry_taken:
|
||||
result.merged_into.setdefault(entries[j].tro_id, tros[i].tro_id)
|
||||
return result
|
||||
|
||||
|
||||
def _anchor(tro, anchors: dict[str, tuple[float, float]]) -> tuple[float, float] | None:
|
||||
for sep in tro.separators:
|
||||
if sep in anchors:
|
||||
return anchors[sep]
|
||||
return None
|
||||
|
||||
|
||||
def _entry_lines(tros: list, anchors: dict[str, tuple[float, float]]) -> list[str]:
|
||||
lines = []
|
||||
for tro in tros:
|
||||
pos = _anchor(tro, anchors)
|
||||
if pos is None:
|
||||
continue
|
||||
seps = ", ".join(tro.separators)
|
||||
lines.append(f"{tro.tro_id} = {pos[0]:.1f}, {pos[1]:.1f}"
|
||||
f" ; {tro.type_name} | {tro.label} | Sep {seps}")
|
||||
return lines
|
||||
|
||||
|
||||
def render_ids(tros: list, anchors: dict[str, tuple[float, float]], source: str) -> str:
|
||||
"""Komplette ID-Datei (erster Lauf oder renumber)."""
|
||||
head = [
|
||||
"; ============================================================",
|
||||
f"; TRO-IDs zu {source} - erzeugt von lib/tro_flow.py "
|
||||
f"am {datetime.now():%Y-%m-%d}",
|
||||
";",
|
||||
"; Je TRO: ID = x, y (mm) seines Anker-Separators im CSV-Koordinatensystem.",
|
||||
"; Die IDs duerfen umbenannt werden (eindeutig, gueltiger SCL-Bezeichner);",
|
||||
"; tro_flow findet jeden TRO ueber die Lage wieder. Neue TROs werden",
|
||||
"; unten angehaengt, weggefallene bleiben stehen und werden gemeldet.",
|
||||
"; Alles neu nummerieren: tro_flow --renumber (ueberschreibt diese Datei).",
|
||||
"; Siehe doc/Konzept_Layout_Hardware_SCL.md, Abschnitt TRO-IDs.",
|
||||
"; ============================================================",
|
||||
"",
|
||||
f"[{SECTION}]",
|
||||
]
|
||||
return "\n".join(head + _entry_lines(tros, anchors)) + "\n"
|
||||
|
||||
|
||||
def render_new_entries(tros: list, anchors: dict[str, tuple[float, float]],
|
||||
new_ids: list[str], source: str) -> str:
|
||||
"""Anhang fuer neu gefundene TROs an eine bestehende ID-Datei."""
|
||||
neu = [t for t in tros if t.tro_id in set(new_ids)]
|
||||
head = [f"; neu aus {source} am {datetime.now():%Y-%m-%d}"]
|
||||
return "\n".join(head + _entry_lines(neu, anchors)) + "\n"
|
||||
|
||||
|
||||
def other_sections(path: Path) -> str:
|
||||
"""Alle Abschnitte ausser [tro] als Text (bleiben bei renumber erhalten)."""
|
||||
if not path.is_file():
|
||||
return ""
|
||||
header = re.compile(r"^\s*\[(.+)\]\s*$")
|
||||
keep: list[str] = []
|
||||
current = None
|
||||
for line in path.read_text(encoding="utf-8").splitlines():
|
||||
if m := header.match(line):
|
||||
current = m.group(1).strip()
|
||||
if current is not None and current != SECTION:
|
||||
keep.append(line)
|
||||
return "\n".join(keep).strip("\n")
|
||||
|
||||
|
||||
def insert_entries(path: Path, block: str) -> None:
|
||||
"""block am Ende des Abschnitts [tro] einfuegen, Rest der Datei unveraendert."""
|
||||
lines = path.read_text(encoding="utf-8").splitlines()
|
||||
header = re.compile(r"^\s*\[(.+)\]\s*$")
|
||||
start = next(i for i, line in enumerate(lines)
|
||||
if (m := header.match(line)) and m.group(1).strip() == SECTION)
|
||||
end = next((i for i in range(start + 1, len(lines)) if header.match(lines[i])),
|
||||
len(lines))
|
||||
# vor Leerzeilen am Abschnittsende einfuegen
|
||||
while end > start + 1 and not lines[end - 1].strip():
|
||||
end -= 1
|
||||
lines[end:end] = block.rstrip("\n").splitlines()
|
||||
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
+46
-60
@@ -72,13 +72,9 @@ REGION TRO20 (1Sep) Sep 0007
|
||||
xInRelease := "DB_TRO_TEST".TRO20,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von SYS1 (Station, kein TRO)
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0007 von SYS1 (Station, kein TRO), SYS2 (Station, kein TRO), TRO19
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von SYS2 (Station, kein TRO)
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO19
|
||||
stInOutJamEntr3 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO01
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0008 vor TRO01
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO20,
|
||||
@@ -122,11 +118,11 @@ REGION TRO01 (1Sep1Swi) Sep 0008
|
||||
xInRelease := "DB_TRO_TEST".TRO01,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO20
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0008 von TRO20
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO02
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0025 vor TRO02
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO06
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0010 vor TRO06
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutSw1ExTo3 => "MB____", // TODO(E-Planung)
|
||||
@@ -183,9 +179,9 @@ REGION TRO02 (Vario) Sep 0025
|
||||
xInAutomatic := #t_automatik,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO01
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0025 von TRO01
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO03
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0026 vor TRO03
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutVarioMotor => "MA____", // TODO(E-Planung)
|
||||
@@ -230,13 +226,11 @@ REGION TRO06 (1Sep1Swi) Sep 0010
|
||||
xInRelease := "DB_TRO_TEST".TRO06,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO01
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0010 von TRO01, TRO05
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO05
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO07
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0031 vor TRO07
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO09
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0012 vor TRO09
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutSw1ExTo3 => "MB____", // TODO(E-Planung)
|
||||
@@ -277,9 +271,9 @@ REGION TRO03 (1Sep) Sep 0026
|
||||
xInRelease := "DB_TRO_TEST".TRO03,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO02
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0026 von TRO02
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO04
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0027 vor TRO04
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO03,
|
||||
@@ -329,9 +323,9 @@ REGION TRO07 (Vario) Sep 0031
|
||||
xInAutomatic := #t_automatik,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO06
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0031 von TRO06
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO08
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0032 vor TRO08
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutVarioMotor => "MA____", // TODO(E-Planung)
|
||||
@@ -376,13 +370,11 @@ REGION TRO09 (1Sep1Swi) Sep 0012
|
||||
xInRelease := "DB_TRO_TEST".TRO09,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO06
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0012 von TRO06, TRO08
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO08
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO10
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0035 vor TRO10
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO12
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0014 vor TRO12
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutSw1ExTo3 => "MB____", // TODO(E-Planung)
|
||||
@@ -439,9 +431,9 @@ REGION TRO04 (Vario) Sep 0027
|
||||
xInAutomatic := #t_automatik,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO03
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0027 von TRO03
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO05
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0028 vor TRO05
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutVarioMotor => "MA____", // TODO(E-Planung)
|
||||
@@ -476,9 +468,9 @@ REGION TRO08 (1Sep) Sep 0032
|
||||
xInRelease := "DB_TRO_TEST".TRO08,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO07
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0032 von TRO07
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO09
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0012 vor TRO09
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO08,
|
||||
@@ -528,9 +520,9 @@ REGION TRO10 (Vario) Sep 0035
|
||||
xInAutomatic := #t_automatik,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO09
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0035 von TRO09
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO11
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0036 vor TRO11
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutVarioMotor => "MA____", // TODO(E-Planung)
|
||||
@@ -575,13 +567,11 @@ REGION TRO12 (1Sep1Swi) Sep 0014
|
||||
xInRelease := "DB_TRO_TEST".TRO12,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO09
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0014 von TRO09, TRO11
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO11
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO13
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0039 vor TRO13
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO15
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0016 vor TRO15
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutSw1ExTo3 => "MB____", // TODO(E-Planung)
|
||||
@@ -622,9 +612,9 @@ REGION TRO05 (1Sep) Sep 0028
|
||||
xInRelease := "DB_TRO_TEST".TRO05,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO04
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0028 von TRO04
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO06
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0010 vor TRO06
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO05,
|
||||
@@ -658,9 +648,9 @@ REGION TRO11 (1Sep) Sep 0036
|
||||
xInRelease := "DB_TRO_TEST".TRO11,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO10
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0036 von TRO10
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO12
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0014 vor TRO12
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO11,
|
||||
@@ -710,9 +700,9 @@ REGION TRO13 (Vario) Sep 0039
|
||||
xInAutomatic := #t_automatik,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO12
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0039 von TRO12
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO14
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0040 vor TRO14
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutVarioMotor => "MA____", // TODO(E-Planung)
|
||||
@@ -757,13 +747,11 @@ REGION TRO15 (1Sep1Swi) Sep 0016
|
||||
xInRelease := "DB_TRO_TEST".TRO15,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO12
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0016 von TRO12, TRO14
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO14
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO16
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0018 vor TRO16
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO17
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0043 vor TRO17
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutSw1ExTo3 => "MB____", // TODO(E-Planung)
|
||||
@@ -804,9 +792,9 @@ REGION TRO14 (1Sep) Sep 0040
|
||||
xInRelease := "DB_TRO_TEST".TRO14,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO13
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0040 von TRO13
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO15
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0016 vor TRO15
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO14,
|
||||
@@ -840,11 +828,9 @@ REGION TRO16 (1Sep) Sep 0018
|
||||
xInRelease := "DB_TRO_TEST".TRO16,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO15
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0018 von TRO15, TRO18
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO18
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO19
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0020 vor TRO19
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO16,
|
||||
@@ -894,9 +880,9 @@ REGION TRO17 (Vario) Sep 0043
|
||||
xInAutomatic := #t_automatik,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO15
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0043 von TRO15
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO18
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0044 vor TRO18
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutVarioMotor => "MA____", // TODO(E-Planung)
|
||||
@@ -931,11 +917,11 @@ REGION TRO19 (1Sep) Sep 0020
|
||||
xInRelease := "DB_TRO_TEST".TRO19,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO16
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0020 von TRO16
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach SYS3 (Station, kein TRO)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich vor SYS3 (Station, kein TRO)
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO20
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0007 vor TRO20
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO19,
|
||||
@@ -969,9 +955,9 @@ REGION TRO18 (1Sep) Sep 0044
|
||||
xInRelease := "DB_TRO_TEST".TRO18,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO17
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0044 von TRO17
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO16
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0018 vor TRO16
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO18,
|
||||
|
||||
+46
-60
@@ -79,13 +79,9 @@ REGION TRO20 (1Sep) Sep 0007
|
||||
xInRelease := "DB_TRO_TEST".TRO20,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von SYS1 (Station, kein TRO)
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0007 von SYS1 (Station, kein TRO), SYS2 (Station, kein TRO), TRO19
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von SYS2 (Station, kein TRO)
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamEntr3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO19
|
||||
stInOutJamEntr3 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO01
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0008 vor TRO01
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -133,11 +129,11 @@ REGION TRO01 (1Sep1Swi) Sep 0008
|
||||
xInRelease := TRUE,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO20
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0008 von TRO20
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO02
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0025 vor TRO02
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO06
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0010 vor TRO06
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO01,
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -204,9 +200,9 @@ REGION TRO02 (Vario) Sep 0025
|
||||
xInErrorQuit := "SF____", // TODO(E-Planung) - Quittierung
|
||||
xInAutomatic := #t_automatik, // TODO(E-Planung) - Automatik des Schaltschranks
|
||||
xInRelease := "DB_TRO_TEST".TRO02,
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO01
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0025 von TRO01
|
||||
stInOutJamEntr := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO03
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0026 vor TRO03
|
||||
stInOutJamFinger := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
@@ -257,13 +253,11 @@ REGION TRO06 (1Sep1Swi) Sep 0010
|
||||
xInRelease := TRUE,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO01
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0010 von TRO01, TRO05
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO05
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO07
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0031 vor TRO07
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO09
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0012 vor TRO09
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO06,
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -309,9 +303,9 @@ REGION TRO03 (1Sep) Sep 0026
|
||||
xInRelease := "DB_TRO_TEST".TRO03,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO02
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0026 von TRO02
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO04
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0027 vor TRO04
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -371,9 +365,9 @@ REGION TRO07 (Vario) Sep 0031
|
||||
xInErrorQuit := "SF____", // TODO(E-Planung) - Quittierung
|
||||
xInAutomatic := #t_automatik, // TODO(E-Planung) - Automatik des Schaltschranks
|
||||
xInRelease := "DB_TRO_TEST".TRO07,
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO06
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0031 von TRO06
|
||||
stInOutJamEntr := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO08
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0032 vor TRO08
|
||||
stInOutJamFinger := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
@@ -424,13 +418,11 @@ REGION TRO09 (1Sep1Swi) Sep 0012
|
||||
xInRelease := TRUE,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO06
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0012 von TRO06, TRO08
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO08
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO10
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0035 vor TRO10
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO12
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0014 vor TRO12
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO09,
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -497,9 +489,9 @@ REGION TRO04 (Vario) Sep 0027
|
||||
xInErrorQuit := "SF____", // TODO(E-Planung) - Quittierung
|
||||
xInAutomatic := #t_automatik, // TODO(E-Planung) - Automatik des Schaltschranks
|
||||
xInRelease := "DB_TRO_TEST".TRO04,
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO03
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0027 von TRO03
|
||||
stInOutJamEntr := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO05
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0028 vor TRO05
|
||||
stInOutJamFinger := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
@@ -541,9 +533,9 @@ REGION TRO08 (1Sep) Sep 0032
|
||||
xInRelease := "DB_TRO_TEST".TRO08,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO07
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0032 von TRO07
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO09
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0012 vor TRO09
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -603,9 +595,9 @@ REGION TRO10 (Vario) Sep 0035
|
||||
xInErrorQuit := "SF____", // TODO(E-Planung) - Quittierung
|
||||
xInAutomatic := #t_automatik, // TODO(E-Planung) - Automatik des Schaltschranks
|
||||
xInRelease := "DB_TRO_TEST".TRO10,
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO09
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0035 von TRO09
|
||||
stInOutJamEntr := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO11
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0036 vor TRO11
|
||||
stInOutJamFinger := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
@@ -656,13 +648,11 @@ REGION TRO12 (1Sep1Swi) Sep 0014
|
||||
xInRelease := TRUE,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO09
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0014 von TRO09, TRO11
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO11
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO13
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0039 vor TRO13
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO15
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0016 vor TRO15
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO12,
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -708,9 +698,9 @@ REGION TRO05 (1Sep) Sep 0028
|
||||
xInRelease := "DB_TRO_TEST".TRO05,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO04
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0028 von TRO04
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO06
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0010 vor TRO06
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -749,9 +739,9 @@ REGION TRO11 (1Sep) Sep 0036
|
||||
xInRelease := "DB_TRO_TEST".TRO11,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO10
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0036 von TRO10
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO12
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0014 vor TRO12
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -811,9 +801,9 @@ REGION TRO13 (Vario) Sep 0039
|
||||
xInErrorQuit := "SF____", // TODO(E-Planung) - Quittierung
|
||||
xInAutomatic := #t_automatik, // TODO(E-Planung) - Automatik des Schaltschranks
|
||||
xInRelease := "DB_TRO_TEST".TRO13,
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO12
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0039 von TRO12
|
||||
stInOutJamEntr := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO14
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0040 vor TRO14
|
||||
stInOutJamFinger := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
@@ -864,13 +854,11 @@ REGION TRO15 (1Sep1Swi) Sep 0016
|
||||
xInRelease := TRUE,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO12
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0016 von TRO12, TRO14
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO14
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO16
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0018 vor TRO16
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO17
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0043 vor TRO17
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO15,
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -916,9 +904,9 @@ REGION TRO14 (1Sep) Sep 0040
|
||||
xInRelease := "DB_TRO_TEST".TRO14,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO13
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0040 von TRO13
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO15
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0016 vor TRO15
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -957,11 +945,9 @@ REGION TRO16 (1Sep) Sep 0018
|
||||
xInRelease := "DB_TRO_TEST".TRO16,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO15
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0018 von TRO15, TRO18
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO18
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO19
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0020 vor TRO19
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -1021,9 +1007,9 @@ REGION TRO17 (Vario) Sep 0043
|
||||
xInErrorQuit := "SF____", // TODO(E-Planung) - Quittierung
|
||||
xInAutomatic := #t_automatik, // TODO(E-Planung) - Automatik des Schaltschranks
|
||||
xInRelease := "DB_TRO_TEST".TRO17,
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO15
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0043 von TRO15
|
||||
stInOutJamEntr := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO18
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0044 vor TRO18
|
||||
stInOutJamFinger := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
@@ -1066,11 +1052,11 @@ REGION TRO19 (1Sep) Sep 0020
|
||||
xInRelease := "DB_TRO_TEST".TRO19,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO16
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0020 von TRO16
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach SYS3 (Station, kein TRO)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich vor SYS3 (Station, kein TRO)
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO20
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0007 vor TRO20
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -1109,9 +1095,9 @@ REGION TRO18 (1Sep) Sep 0044
|
||||
xInRelease := "DB_TRO_TEST".TRO18,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO17
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0044 von TRO17
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO16
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0018 vor TRO16
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
|
||||
Vendored
+44
-54
@@ -70,11 +70,9 @@ REGION TRO03 (1Sep) Sep 0042
|
||||
xInRelease := "DB_TRO_TEST".TRO03,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von SYS1 (Station, kein TRO)
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0042 von SYS1 (Station, kein TRO), TRO04
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO04
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO05
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0044 vor TRO05
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO03,
|
||||
@@ -108,9 +106,9 @@ REGION TRO05 (1Sep) Sep 0044
|
||||
xInRelease := "DB_TRO_TEST".TRO05,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO03
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0044 von TRO03
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO18
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0066 vor TRO18
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO05,
|
||||
@@ -150,13 +148,11 @@ REGION TRO18 (PinStore_Auto) Sep 0066, 0069, 0072, 0075, 0078, 0081, 0084, 0087,
|
||||
xInRelease := "DB_TRO_TEST".TRO18,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO05
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0066 von TRO05, TRO12 (Eingang des Speichers - Kennung vorlaeufig nach erstem Linien-Separator)
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO12
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO07
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0045 vor TRO07
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO10
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0005 vor TRO10
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO18,
|
||||
@@ -190,11 +186,9 @@ REGION TRO07 (1Sep) Sep 0045
|
||||
xInRelease := "DB_TRO_TEST".TRO07,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO09
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0045 von TRO09, TRO18
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO18
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO04
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0043 vor TRO04
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO07,
|
||||
@@ -265,17 +259,15 @@ REGION TRO10 (2Sep2Swi) Sep 0005, 0036
|
||||
xInRelease := "DB_TRO_TEST".TRO10,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO01
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0005 von TRO01, TRO06, TRO18 (Aufteilung der Vorgaenger auf die Eingaenge offen)
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO06
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0036 von - (Aufteilung der Vorgaenger auf die Eingaenge offen)
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO18
|
||||
stInOutJamEntr3 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO01
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0040 vor TRO01
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO02
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0012 vor TRO02
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit4 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO11
|
||||
arInOutJamExit4 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0058 vor TRO11
|
||||
stInOutJamExit4 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutStopper2 => "MB____", // TODO(E-Planung)
|
||||
@@ -322,9 +314,9 @@ REGION TRO04 (1Sep) Sep 0043
|
||||
xInRelease := "DB_TRO_TEST".TRO04,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO07
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0043 von TRO07
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO03
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0042 vor TRO03
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO04,
|
||||
@@ -358,9 +350,9 @@ REGION TRO01 (1Sep) Sep 0040
|
||||
xInRelease := "DB_TRO_TEST".TRO01,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO10
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0040 von TRO10
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO10
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0005 vor TRO10
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO01,
|
||||
@@ -394,13 +386,13 @@ REGION TRO02 (1Sep) Sep 0012
|
||||
xInRelease := "DB_TRO_TEST".TRO02,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO10
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0012 von TRO10
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach SYS2 (Station, kein TRO)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich vor SYS2 (Station, kein TRO)
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach SYS3 (Station, kein TRO)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich vor SYS3 (Station, kein TRO)
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit4 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO13
|
||||
arInOutJamExit4 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0011 vor TRO13
|
||||
stInOutJamExit4 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO02,
|
||||
@@ -450,9 +442,9 @@ REGION TRO11 (Vario) Sep 0058
|
||||
xInAutomatic := #t_automatik,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO10
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0058 von TRO10
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO12
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0059 vor TRO12
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutVarioMotor => "MA____", // TODO(E-Planung)
|
||||
@@ -497,11 +489,11 @@ REGION TRO13 (1Sep1Swi) Sep 0011
|
||||
xInRelease := "DB_TRO_TEST".TRO13,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO02
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0011 von TRO02
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO16
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0062 vor TRO16
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO17
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0063 vor TRO17
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutSw1ExTo3 => "MB____", // TODO(E-Planung)
|
||||
@@ -542,9 +534,9 @@ REGION TRO12 (1Sep) Sep 0059
|
||||
xInRelease := "DB_TRO_TEST".TRO12,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO11
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0059 von TRO11
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO18
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0066 vor TRO18
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO12,
|
||||
@@ -594,9 +586,9 @@ REGION TRO16 (Vario) Sep 0062
|
||||
xInAutomatic := #t_automatik,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO13
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0062 von TRO13
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO15
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0038 vor TRO15
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutVarioMotor => "MA____", // TODO(E-Planung)
|
||||
@@ -631,11 +623,9 @@ REGION TRO17 (1Sep) Sep 0063
|
||||
xInRelease := "DB_TRO_TEST".TRO17,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO13
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0063 von TRO13, TRO14
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO14
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO06
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0003 vor TRO06
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO17,
|
||||
@@ -669,9 +659,9 @@ REGION TRO15 (1Sep) Sep 0038
|
||||
xInRelease := "DB_TRO_TEST".TRO15,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO16
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0038 von TRO16
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO14
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0037 vor TRO14
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO15,
|
||||
@@ -715,11 +705,11 @@ REGION TRO06 (1Sep1Swi) Sep 0003
|
||||
xInRelease := "DB_TRO_TEST".TRO06,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO17
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0003 von TRO17
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO08
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0054 vor TRO08
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO10
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0005 vor TRO10
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutSw1ExTo3 => "MB____", // TODO(E-Planung)
|
||||
@@ -760,9 +750,9 @@ REGION TRO14 (1Sep) Sep 0037
|
||||
xInRelease := "DB_TRO_TEST".TRO14,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO15
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0037 von TRO15
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO17
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0063 vor TRO17
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO14,
|
||||
@@ -812,9 +802,9 @@ REGION TRO08 (Vario) Sep 0054
|
||||
xInAutomatic := #t_automatik,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO06
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0054 von TRO06
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO09
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0055 vor TRO09
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutVarioMotor => "MA____", // TODO(E-Planung)
|
||||
@@ -849,9 +839,9 @@ REGION TRO09 (1Sep) Sep 0055
|
||||
xInRelease := "DB_TRO_TEST".TRO09,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO08
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0055 von TRO08
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO07
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0045 vor TRO07
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO09,
|
||||
|
||||
+45
-53
@@ -77,11 +77,9 @@ REGION TRO03 (1Sep) Sep 0042
|
||||
xInRelease := "DB_TRO_TEST".TRO03,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von SYS1 (Station, kein TRO)
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0042 von SYS1 (Station, kein TRO), TRO04
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO04
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO05
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0044 vor TRO05
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -120,9 +118,9 @@ REGION TRO05 (1Sep) Sep 0044
|
||||
xInRelease := "DB_TRO_TEST".TRO05,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO03
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0044 von TRO03
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO18
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0066 vor TRO18
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -136,8 +134,8 @@ REGION TRO18 (PinStore_Auto) Sep 0066, 0069, 0072, 0075, 0078, 0081, 0084, 0087,
|
||||
// Nachfolger: TRO07, TRO10
|
||||
// Einstufung: hoch
|
||||
// Aufbau nach Template FB_ILS_MTRO_PinStore_Auto.scl.j2 (Referenz: doc/TRO_Katalog/scl_templates)
|
||||
// TODO(E-Planung): 2 Zulaeufe / 2 Ablaeufe im Layout -
|
||||
// der FB hat je nur einen Staubereich (JamEntr/JamExit, dazu JamBypass)
|
||||
// TODO(E-Planung): 1 Eingangs- / 2 Ausgangs-Staubereiche im Layout -
|
||||
// der FB hat je nur einen (JamEntr/JamExit, dazu JamBypass)
|
||||
// Linien (Separatoren aus dem Layout): 0066, 0069, 0072, 0075, 0078, 0081, 0084, 0087, 0090, 0093, 0096, 0099, 0102, 0105, 0108, 0111, 0114, 0117, 0120, 0123
|
||||
|
||||
REGION Encoder Settings
|
||||
@@ -417,9 +415,9 @@ REGION TRO18 (PinStore_Auto) Sep 0066, 0069, 0072, 0075, 0078, 0081, 0084, 0087,
|
||||
xInConveyorRunOut := #fbExitConv____.xOutConveyorFullSpeed, // TODO(E-Planung) - Foerderer am Ablauf
|
||||
stInOutControlUnitCabinet := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
arInOutStorageLines := "DB_Storage____".arLine, // TODO(E-Planung)
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO05
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0066 von TRO05, TRO12 (Eingang des Speichers - Kennung vorlaeufig nach erstem Linien-Separator)
|
||||
stInOutJamEntr := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO07
|
||||
arInOutJamExit := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0045 vor TRO07
|
||||
stInOutJamExit := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamBypass := #arCarrierPlaceHolder,
|
||||
stInOutJamBypass := #stJamDataPlaceHolder,
|
||||
@@ -501,11 +499,9 @@ REGION TRO07 (1Sep) Sep 0045
|
||||
xInRelease := "DB_TRO_TEST".TRO07,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO09
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0045 von TRO09, TRO18
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO18
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO04
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0043 vor TRO04
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -577,17 +573,15 @@ REGION TRO10 (2Sep2Swi) Sep 0005, 0036
|
||||
xInRelease := "DB_TRO_TEST".TRO10,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet,
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO01
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0005 von TRO01, TRO06, TRO18 (Aufteilung der Vorgaenger auf die Eingaenge offen)
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO06
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0036 von - (Aufteilung der Vorgaenger auf die Eingaenge offen)
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamEntr3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO18
|
||||
stInOutJamEntr3 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO01
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0040 vor TRO01
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO02
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0012 vor TRO02
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData,
|
||||
arInOutJamExit4 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO11
|
||||
arInOutJamExit4 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0058 vor TRO11
|
||||
stInOutJamExit4 := "DB_JamArea____".stJam____.stData,
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
xOutStopper2 => "MB____", // TODO(E-Planung)
|
||||
@@ -638,9 +632,9 @@ REGION TRO04 (1Sep) Sep 0043
|
||||
xInRelease := "DB_TRO_TEST".TRO04,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO07
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0043 von TRO07
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO03
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0042 vor TRO03
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -679,9 +673,9 @@ REGION TRO01 (1Sep) Sep 0040
|
||||
xInRelease := "DB_TRO_TEST".TRO01,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO10
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0040 von TRO10
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO10
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0005 vor TRO10
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -722,13 +716,13 @@ REGION TRO02 (1Sep) Sep 0012
|
||||
xInRelease := "DB_TRO_TEST".TRO02,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO10
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0012 von TRO10
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach SYS2 (Station, kein TRO)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich vor SYS2 (Station, kein TRO)
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach SYS3 (Station, kein TRO)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich vor SYS3 (Station, kein TRO)
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit4 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO13
|
||||
arInOutJamExit4 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0011 vor TRO13
|
||||
stInOutJamExit4 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -788,9 +782,9 @@ REGION TRO11 (Vario) Sep 0058
|
||||
xInErrorQuit := "SF____", // TODO(E-Planung) - Quittierung
|
||||
xInAutomatic := #t_automatik, // TODO(E-Planung) - Automatik des Schaltschranks
|
||||
xInRelease := "DB_TRO_TEST".TRO11,
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO10
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0058 von TRO10
|
||||
stInOutJamEntr := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO12
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0059 vor TRO12
|
||||
stInOutJamFinger := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
@@ -841,11 +835,11 @@ REGION TRO13 (1Sep1Swi) Sep 0011
|
||||
xInRelease := TRUE,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO02
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0011 von TRO02
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO16
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0062 vor TRO16
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO17
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0063 vor TRO17
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO13,
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -891,9 +885,9 @@ REGION TRO12 (1Sep) Sep 0059
|
||||
xInRelease := "DB_TRO_TEST".TRO12,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO11
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0059 von TRO11
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO18
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0066 vor TRO18
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -953,9 +947,9 @@ REGION TRO16 (Vario) Sep 0062
|
||||
xInErrorQuit := "SF____", // TODO(E-Planung) - Quittierung
|
||||
xInAutomatic := #t_automatik, // TODO(E-Planung) - Automatik des Schaltschranks
|
||||
xInRelease := "DB_TRO_TEST".TRO16,
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO13
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0062 von TRO13
|
||||
stInOutJamEntr := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO15
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0038 vor TRO15
|
||||
stInOutJamFinger := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
@@ -997,11 +991,9 @@ REGION TRO17 (1Sep) Sep 0063
|
||||
xInRelease := "DB_TRO_TEST".TRO17,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO13
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0063 von TRO13, TRO14
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamEntr2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO14
|
||||
stInOutJamEntr2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO06
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0003 vor TRO06
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -1040,9 +1032,9 @@ REGION TRO15 (1Sep) Sep 0038
|
||||
xInRelease := "DB_TRO_TEST".TRO15,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO16
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0038 von TRO16
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO14
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0037 vor TRO14
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -1090,11 +1082,11 @@ REGION TRO06 (1Sep1Swi) Sep 0003
|
||||
xInRelease := TRUE,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO17
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0003 von TRO17
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO08
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0054 vor TRO08
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO10
|
||||
arInOutJamExit3 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0005 vor TRO10
|
||||
stInOutJamExit3 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.TRO06,
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -1140,9 +1132,9 @@ REGION TRO14 (1Sep) Sep 0037
|
||||
xInRelease := "DB_TRO_TEST".TRO14,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO15
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0037 von TRO15
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO17
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0063 vor TRO17
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
@@ -1202,9 +1194,9 @@ REGION TRO08 (Vario) Sep 0054
|
||||
xInErrorQuit := "SF____", // TODO(E-Planung) - Quittierung
|
||||
xInAutomatic := #t_automatik, // TODO(E-Planung) - Automatik des Schaltschranks
|
||||
xInRelease := "DB_TRO_TEST".TRO08,
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO06
|
||||
arInOutJamEntr := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0054 von TRO06
|
||||
stInOutJamEntr := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO09
|
||||
arInOutJamFinger := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0055 vor TRO09
|
||||
stInOutJamFinger := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
@@ -1246,9 +1238,9 @@ REGION TRO09 (1Sep) Sep 0055
|
||||
xInRelease := "DB_TRO_TEST".TRO09,
|
||||
stInOutMachineState := #stInOutControlUnitCabinet, // TODO(E-Planung) - Schaltschrank
|
||||
stInOutCognexInterface := #stCognexInterfaceDummy,
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich von TRO08
|
||||
arInOutJamEntr1 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0055 von TRO08
|
||||
stInOutJamEntr1 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich nach TRO07
|
||||
arInOutJamExit2 := "DB_JamArea____".stJam____.arCarrier, // TODO(E-Planung) - Staubereich J0045 vor TRO07
|
||||
stInOutJamExit2 := "DB_JamArea____".stJam____.stData, // TODO(E-Planung)
|
||||
xOutStopper1 => "MB____", // TODO(E-Planung)
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro____"], // TODO(E-Planung)
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests fuer lib/e_planning.py - Zuordnung Layout <-> E-Planung.
|
||||
|
||||
Die Geometrie ist synthetisch und nachgebaut nach den Befunden an Mubea
|
||||
(doc/E-Planung_Bruecke.md): Speicherlinien im 800-mm-Raster, Sensor auf dem
|
||||
Separator, Ventil ~270 mm daneben, Stausensor konstant ~1390 mm vom
|
||||
Ausschleuselement. Der Lagebezug ist hier die Identitaet.
|
||||
"""
|
||||
|
||||
import json
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import scl_skeleton
|
||||
from dxf_registration import Transform
|
||||
from e_planning import (
|
||||
EDevice,
|
||||
EplanDocument,
|
||||
LayoutPoint,
|
||||
Rule,
|
||||
assign,
|
||||
load_positionsdraw,
|
||||
load_rules,
|
||||
pair_drives,
|
||||
)
|
||||
from scl_context import build_context
|
||||
from scl_skeleton import load_document
|
||||
|
||||
FIXTURES = Path(__file__).resolve().parent / "fixtures" / "scl"
|
||||
RULES = Path(__file__).resolve().parent.parent / "cfg" / "e_planning.ini"
|
||||
IDENT = Transform(dx=0.0, dy=0.0)
|
||||
|
||||
|
||||
def _dev(bmk: str, verw: str, x: float, y: float, **extra) -> EDevice:
|
||||
return EDevice(bmk=bmk, verw=verw, pos=(x, y),
|
||||
kennzeichnung="=A1+UH01-KF107", **extra)
|
||||
|
||||
|
||||
def _rolle(assignments, layout_id: str, role: str):
|
||||
return next((a for a in assignments
|
||||
if a.layout_id == layout_id and a.role == role), None)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Einlesen
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_positionsdraw_einlesen(tmp_path):
|
||||
datei = tmp_path / "x_positionsdraw.json"
|
||||
datei.write_text(json.dumps({
|
||||
"sensors": {
|
||||
"BG0642@UNKNOWN": {"B": "BG0642", "VERW": "IN SEP",
|
||||
"BEZEICHNUNG": "TEIL IN SEPARATOR STR. 20",
|
||||
"KENNZEICHNUNG": "=A1+UH01-KF107", "pos": [1.0, 2.0]},
|
||||
},
|
||||
"schaltschrank_elemente": {
|
||||
# Derselbe Motorschutz zweimal: einmal mit, einmal ohne Attribute
|
||||
"FC0200@": {"IO": "FC0200", "VERW": "MOTORSCHUTZ",
|
||||
"BEZEICHNUNG": "MOTORSCHUTZ CONVEYER #100", "pos": [5, 5]},
|
||||
"FC0200": {"B": "FC0200", "pos": [6, 6]},
|
||||
},
|
||||
"double_ids": {"BG0516": [[0, 0], [1, 1]]},
|
||||
}), encoding="utf-8")
|
||||
warnungen: list[str] = []
|
||||
geraete = {d.bmk: d for d in load_positionsdraw(datei, warnungen)}
|
||||
|
||||
assert set(geraete) == {"BG0642", "FC0200"}
|
||||
bg = geraete["BG0642"]
|
||||
assert (bg.verw, bg.pos, bg.cabinet) == ("IN SEP", (1.0, 2.0), False)
|
||||
assert (bg.controller, bg.station, bg.prefix, bg.number) == ("UH01", "KF107", "BG", 642)
|
||||
assert geraete["FC0200"].verw == "MOTORSCHUTZ"
|
||||
assert geraete["FC0200"].cabinet
|
||||
assert any("BG0516" in w for w in warnungen)
|
||||
|
||||
|
||||
def test_regeln_aus_der_projektkonfiguration():
|
||||
regeln, anker = load_rules(RULES)
|
||||
rollen = [r.role for r in regeln]
|
||||
assert anker == ["IN SEP"]
|
||||
# Reihenfolge = Dateireihenfolge; der Sensor muss vor dem Ventil kommen
|
||||
assert rollen.index("Separator.sensor") < rollen.index("Separator.stopper")
|
||||
stopper = next(r for r in regeln if r.role == "Separator.stopper")
|
||||
assert (stopper.layout, stopper.number_from, stopper.number_offset) == (
|
||||
["Separator"], "Separator.sensor", 1)
|
||||
|
||||
|
||||
def test_nummer_von_muss_eine_regel_sein(tmp_path):
|
||||
ini = tmp_path / "r.ini"
|
||||
ini.write_text("[Separator.stopper]\nverw = SEP\nnummer_von = Separator.gibtsnicht\n",
|
||||
encoding="utf-8")
|
||||
with pytest.raises(ValueError, match="gibtsnicht"):
|
||||
load_rules(ini)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Zuordnung
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SENSOR = Rule(role="Separator.sensor", layout=["Separator"], verw=["IN SEP"],
|
||||
prefix="BG", max_mm=300)
|
||||
STOPPER = Rule(role="Separator.stopper", layout=["Separator"], verw=["SEP"],
|
||||
prefix="MB", number_from="Separator.sensor", number_offset=1,
|
||||
max_mm=600, target_mm=270)
|
||||
|
||||
|
||||
def _speicher():
|
||||
"""Drei Speicherlinien im 800-mm-Raster.
|
||||
|
||||
Das Ventil jeder Linie liegt 530 mm von ihrem Separator - und nur 270 mm
|
||||
vom Separator der Nachbarlinie. Raeumlich waere das falsch; die
|
||||
Nummernregel (MB = BG + 1) muss gewinnen.
|
||||
"""
|
||||
punkte = [LayoutPoint(f"00{i}", "Separator", 0.0, 800.0 * i) for i in (1, 2, 3)]
|
||||
geraete = []
|
||||
for i, bg in ((1, 642), (2, 644), (3, 646)):
|
||||
geraete.append(_dev(f"BG0{bg}", "IN SEP", 0.0, 800.0 * i))
|
||||
geraete.append(_dev(f"MB0{bg + 1}", "SEP", 0.0, 800.0 * i + 530.0))
|
||||
return punkte, geraete
|
||||
|
||||
|
||||
def test_ventil_ueber_die_nummer_nicht_ueber_den_abstand():
|
||||
punkte, geraete = _speicher()
|
||||
ergebnis = assign(punkte, geraete, [SENSOR, STOPPER], IDENT)
|
||||
|
||||
for sep, bg, mb in (("001", "BG0642", "MB0643"), ("002", "BG0644", "MB0645"),
|
||||
("003", "BG0646", "MB0647")):
|
||||
assert _rolle(ergebnis, sep, "Separator.sensor").bmk == bg
|
||||
stopper = _rolle(ergebnis, sep, "Separator.stopper")
|
||||
assert (stopper.bmk, stopper.rule) == (mb, "nummer")
|
||||
|
||||
|
||||
def test_jedes_e_bauteil_nur_einmal():
|
||||
"""Zwei Separatoren, ein Sensor dazwischen: nur der naehere bekommt ihn."""
|
||||
punkte = [LayoutPoint("A", "Separator", 0, 0), LayoutPoint("B", "Separator", 0, 250)]
|
||||
geraete = [_dev("BG0100", "IN SEP", 0, 100)]
|
||||
ergebnis = assign(punkte, geraete, [SENSOR], IDENT)
|
||||
assert [(a.layout_id, a.bmk) for a in ergebnis] == [("A", "BG0100")]
|
||||
assert ergebnis[0].runner_up is None
|
||||
|
||||
|
||||
def test_zu_weit_weg_bleibt_offen():
|
||||
punkte = [LayoutPoint("A", "Separator", 0, 0)]
|
||||
geraete = [_dev("BG0100", "IN SEP", 0, 400)]
|
||||
assert assign(punkte, geraete, [SENSOR], IDENT) == []
|
||||
|
||||
|
||||
def test_sollabstand_schlaegt_kleinsten_abstand():
|
||||
"""Mubea 0080: ein fremder Stausensor liegt 269 mm nah, der eigene 1391 mm."""
|
||||
stau = Rule(role="Ausschleus.stau", layout=["Ausschleus"], verw=["STAU*"],
|
||||
prefix="BG", max_mm=1600, target_mm=1390)
|
||||
punkte = [LayoutPoint("0080", "Ausschleus", 0, 0)]
|
||||
geraete = [_dev("BG0525", "STAU_REF-LT", 269, 0),
|
||||
_dev("BG0550", "STAU_REF-LT", -1391, 0)]
|
||||
ergebnis = assign(punkte, geraete, [stau], IDENT)
|
||||
assert [a.bmk for a in ergebnis] == ["BG0550"]
|
||||
|
||||
|
||||
def test_lagebezug_wird_angewendet():
|
||||
"""E-Koordinaten werden mit dem Lagebezug ins CSV gebracht."""
|
||||
punkte = [LayoutPoint("A", "Separator", 1000, 2000)]
|
||||
geraete = [_dev("BG0100", "IN SEP", 0, 0)]
|
||||
ergebnis = assign(punkte, geraete, [SENSOR], Transform(dx=1000, dy=2000))
|
||||
assert ergebnis[0].bmk == "BG0100" and ergebnis[0].distance == 0
|
||||
|
||||
|
||||
def test_schrankelemente_werden_nicht_raeumlich_zugeordnet():
|
||||
punkte = [LayoutPoint("A", "Separator", 0, 0)]
|
||||
geraete = [_dev("BG0100", "IN SEP", 0, 0, cabinet=True)]
|
||||
assert assign(punkte, geraete, [SENSOR], IDENT) == []
|
||||
|
||||
|
||||
def test_motor_und_motorschutz_ueber_die_nummer():
|
||||
geraete = [_dev("MA0200", "MOT_0,37kW", 0, 0, bezeichnung="PIN CONVEYOR#100"),
|
||||
EDevice(bmk="FC0200", verw="MOTORSCHUTZ", cabinet=True),
|
||||
_dev("MA0300", "MOT", 0, 0)]
|
||||
antriebe = {d.motor: d.protection for d in pair_drives(geraete)}
|
||||
assert antriebe == {"MA0200": "FC0200", "MA0300": ""}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bis in die SCL
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _eplan_fuer_mubea(csv_file: str = "mubea.csv") -> EplanDocument:
|
||||
"""Zuordnung fuer Separator 0012 (TRO02 der Fixture) - nur Sensor und Ventil."""
|
||||
punkte = [LayoutPoint("0012", "Separator", 0, 0)]
|
||||
geraete = [_dev("BG0712", "IN SEP", 0, 0), _dev("MB0713", "SEP", 0, 300)]
|
||||
return EplanDocument(csv_file=csv_file, eplan_file="Mubea_positionsdraw.json",
|
||||
created="2026-01-01 00:00",
|
||||
assignments=assign(punkte, geraete, [SENSOR, STOPPER], IDENT))
|
||||
|
||||
|
||||
def test_kontext_setzt_namen_ein_und_loescht_den_merker():
|
||||
document = load_document(FIXTURES / "mubea_tro.json")
|
||||
kontext = build_context(document, "mubea_tro.json", eplan=_eplan_fuer_mubea())
|
||||
tro02 = next(t for t in kontext.tros if t.id == "TRO02")
|
||||
sep = tro02.separators[0]
|
||||
assert (sep.layout, sep.sensor.value, sep.sensor.todo) == ("0012", "cInBG0712", False)
|
||||
assert (sep.stopper.value, sep.stopper.todo) == ("MB0713", False)
|
||||
# Was die E-Planung nicht liefert, bleibt offen
|
||||
assert sep.no.todo
|
||||
tro01 = next(t for t in kontext.tros if t.id == "TRO01")
|
||||
assert tro01.separators[0].sensor.todo
|
||||
assert kontext.eplan_source.startswith("Mubea_positionsdraw.json")
|
||||
|
||||
|
||||
def _lauf(tmp_path, monkeypatch, eplan: EplanDocument,
|
||||
*weitere: str) -> tuple[int, Path]:
|
||||
shutil.copy(FIXTURES / "mubea_tro.json", tmp_path / "mubea_tro.json")
|
||||
(tmp_path / "mubea_eplan.json").write_text(eplan.to_json(), encoding="utf-8")
|
||||
monkeypatch.setenv("SKEL_RESULTS", str(tmp_path))
|
||||
rc = scl_skeleton.main(["--json", "mubea_tro.json", "--eplan", "mubea_eplan.json",
|
||||
*weitere])
|
||||
return rc, tmp_path / "mubea_FB_Main.scl"
|
||||
|
||||
|
||||
def test_scl_skeleton_mit_eplan(tmp_path, monkeypatch):
|
||||
rc, scl = _lauf(tmp_path, monkeypatch, _eplan_fuer_mubea())
|
||||
text = scl.read_text(encoding="utf-8")
|
||||
assert rc == 0
|
||||
assert '#TRO02.stInSeparator1.stSenInSep := "DB_Inputs".Sensors["cInBG0712"];\n' in text
|
||||
assert 'xOutStopper1 => "MB0713",\n' in text
|
||||
assert "// E-Planung: Mubea_positionsdraw.json" in text
|
||||
|
||||
|
||||
def test_scl_skeleton_mit_eplan_und_templates(tmp_path, monkeypatch):
|
||||
"""Die Namen aus der E-Planung kommen auch in den Templates an (TRO02 = 1Sep)."""
|
||||
rc, scl = _lauf(tmp_path, monkeypatch, _eplan_fuer_mubea(), "--templates")
|
||||
text = scl.read_text(encoding="utf-8")
|
||||
assert rc == 0
|
||||
region = text[text.index("REGION TRO02 "):]
|
||||
region = region[:region.index("\nEND_REGION")]
|
||||
assert "Aufbau nach Template FB_ILS_MTRO_1Sep.scl.j2" in region
|
||||
assert '#TRO02.stInSeparator1.stSenInSep := "DB_Inputs".Sensors["cInBG0712"];\n' in region
|
||||
assert 'xOutStopper1 => "MB0713",\n' in region
|
||||
|
||||
|
||||
def test_scl_skeleton_lehnt_fremde_zuordnung_ab(tmp_path, monkeypatch):
|
||||
rc, scl = _lauf(tmp_path, monkeypatch, _eplan_fuer_mubea("anderes.csv"))
|
||||
assert rc == 1
|
||||
assert not scl.exists()
|
||||
|
||||
|
||||
def test_eplan_json_rundlauf():
|
||||
doc = _eplan_fuer_mubea()
|
||||
zurueck = EplanDocument.from_json(doc.to_json())
|
||||
assert zurueck == doc
|
||||
assert json.loads(doc.to_json())["schema"] == "sps_skel-eplan-v1"
|
||||
assert set(zurueck.for_layout("0012")) == {"sensor", "stopper"}
|
||||
@@ -136,3 +136,65 @@ def test_scl_skeleton_renders_from_document(document):
|
||||
# Systemgrenzen sind keine Instanzen
|
||||
assert "SYS1 :" not in scl
|
||||
assert "Anfang bei TRO01" in scl
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Schema v4: Slots und Staubereiche
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_slots_aus_katalog_und_layout(document):
|
||||
weiche = document.by_id("TRO01")
|
||||
assert [(s.slot, s.layout_id) for s in weiche.slots] == [
|
||||
("separator1", "0040"), ("switch1", "")]
|
||||
assert weiche.slots[0].roles == ["sensor", "stopper", "jamSensor?", "partSensor?"]
|
||||
vario = document.by_id("TRO03")
|
||||
assert [s.slot for s in vario.slots] == ["separator1", "drive"]
|
||||
assert vario.slots[1].layout_id == "0001" # Host = angetriebene Strecke
|
||||
# Systemgrenzen haben keine Slots
|
||||
assert document.by_id("SYS1").slots == []
|
||||
|
||||
|
||||
def test_ein_staubereich_je_tro_eingang_nach_separator(document):
|
||||
areas = {a.jam_id: a for a in document.jam_areas}
|
||||
assert sorted(areas) == ["J0040", "J0041", "J0042"]
|
||||
assert areas["J0040"].tro == "TRO01"
|
||||
assert areas["J0040"].predecessors == ["SYS1"]
|
||||
assert areas["J0040"].position == (1030.0, 4700.0, 1500.0)
|
||||
assert areas["J0041"].predecessors == ["TRO01"]
|
||||
|
||||
|
||||
def test_zusammenfuehrung_ein_staubereich_mehrere_vorgaenger():
|
||||
analysis = Analysis(tros=[
|
||||
_tro("TRO01", "1Sep", separators=["0001"], successors=["TRO03"]),
|
||||
_tro("TRO02", "1Sep", separators=["0002"], successors=["TRO03"]),
|
||||
_tro("TRO03", "1Sep", separators=["0003"], predecessors=["TRO01", "TRO02"]),
|
||||
])
|
||||
doc = TroDocument.from_analysis(analysis, source="t")
|
||||
eingang = [a for a in doc.jam_areas if a.tro == "TRO03"]
|
||||
assert [(a.jam_id, a.predecessors) for a in eingang] == [("J0003", ["TRO01", "TRO02"])]
|
||||
|
||||
|
||||
def test_zwei_separatoren_zwei_eingaenge_aufteilung_offen():
|
||||
analysis = Analysis(tros=[
|
||||
_tro("TRO01", "2Sep2Swi", separators=["0005", "0036"], predecessors=["TRO09"]),
|
||||
])
|
||||
areas = TroDocument.from_analysis(analysis, source="t").jam_areas
|
||||
assert [(a.jam_id, a.entry, a.predecessors) for a in areas] == [
|
||||
("J0005", 1, ["TRO09"]), ("J0036", 2, [])]
|
||||
assert all("offen" in a.note for a in areas)
|
||||
|
||||
|
||||
def test_v4_round_trip_und_v3_ohne_jam_areas(document):
|
||||
data = json.loads(document.to_json())
|
||||
assert data["schema"] == "sps_skel-tro-v4"
|
||||
assert data["jamAreas"][0]["id"] == "J0040"
|
||||
assert data["tros"][0]["slots"][0]["layoutId"] == "0040"
|
||||
assert TroDocument.from_json(document.to_json()) == document
|
||||
# v3-Datei: keine jamAreas/slots -> beim Rendern abgeleitet
|
||||
del data["jamAreas"]
|
||||
for tro in data["tros"]:
|
||||
del tro["slots"]
|
||||
data["schema"] = "sps_skel-tro-v3"
|
||||
alt = TroDocument.from_json(json.dumps(data))
|
||||
assert alt.jam_areas == []
|
||||
assert [a.jam_id for a in alt.jam_areas_or_derived()] == ["J0040", "J0041", "J0042"]
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests fuer lib/tro_ids.py: ID-Datei lesen/schreiben, TROs ueber die Lage
|
||||
wiederfinden (doc/Konzept_Layout_Hardware_SCL.md, Abschnitt TRO-IDs)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
import pytest
|
||||
|
||||
from tro_ids import (
|
||||
IdEntry,
|
||||
TroIds,
|
||||
assign_ids,
|
||||
insert_entries,
|
||||
load_ids,
|
||||
other_sections,
|
||||
render_ids,
|
||||
render_new_entries,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class FakeTro:
|
||||
separators: list[str]
|
||||
type_name: str = "1Sep"
|
||||
label: str = ""
|
||||
tro_id: str = ""
|
||||
hosts: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
ANCHORS = {
|
||||
"0001": (0.0, 0.0),
|
||||
"0002": (1000.0, 0.0),
|
||||
"0003": (2000.0, 0.0),
|
||||
"0004": (2000.0, 500.0),
|
||||
}
|
||||
|
||||
|
||||
def _tros(*seps: list[str]) -> list[FakeTro]:
|
||||
return [FakeTro(separators=list(s)) for s in seps]
|
||||
|
||||
|
||||
def test_ohne_eintraege_wird_durchnummeriert():
|
||||
tros = _tros(["0001"], ["0002"], ["0003"])
|
||||
result = assign_ids(tros, ANCHORS, [])
|
||||
assert result.renumbered
|
||||
assert [t.tro_id for t in tros] == ["TRO01", "TRO02", "TRO03"]
|
||||
|
||||
|
||||
def test_wiederfinden_ueber_lage_auch_umbenannt():
|
||||
# Neuer Export: andere TeileIds, gleiche Lage, andere Reihenfolge
|
||||
anchors = {"0101": (2000.0, 0.0), "0102": (0.0, 0.0), "0103": (1000.0, 30.0)}
|
||||
tros = _tros(["0101"], ["0102"], ["0103"])
|
||||
entries = [IdEntry("TRO01", 0.0, 0.0), IdEntry("K0102_1", 1000.0, 0.0),
|
||||
IdEntry("TRO03", 2000.0, 0.0)]
|
||||
result = assign_ids(tros, anchors, entries)
|
||||
assert [t.tro_id for t in tros] == ["TRO03", "TRO01", "K0102_1"]
|
||||
assert not result.new and not result.missing
|
||||
|
||||
|
||||
def test_neue_tros_naechste_freie_nummer_weggefallene_nicht_wiederverwendet():
|
||||
tros = _tros(["0001"], ["0003"])
|
||||
entries = [IdEntry("TRO01", 0.0, 0.0), IdEntry("TRO02", 1000.0, 0.0)]
|
||||
result = assign_ids(tros, ANCHORS, entries)
|
||||
assert [t.tro_id for t in tros] == ["TRO01", "TRO03"]
|
||||
assert result.new == ["TRO03"]
|
||||
assert [e.tro_id for e in result.missing] == ["TRO02"]
|
||||
|
||||
|
||||
def test_ausserhalb_toleranz_gilt_als_neu():
|
||||
tros = _tros(["0001"])
|
||||
entries = [IdEntry("TRO01", 150.0, 0.0)]
|
||||
result = assign_ids(tros, ANCHORS, entries, tolerance=100.0)
|
||||
assert tros[0].tro_id == "TRO02"
|
||||
assert result.missing[0].tro_id == "TRO01"
|
||||
assert assign_ids(tros, ANCHORS, entries, tolerance=200.0).kept[0][0] == "TRO01"
|
||||
|
||||
|
||||
def test_zusammengelegte_tros_naechster_eintrag_gewinnt():
|
||||
# zwei Separatoren, frueher zwei TROs, jetzt ein 2Sep-TRO
|
||||
tros = _tros(["0003", "0004"])
|
||||
entries = [IdEntry("TRO05", 2000.0, 0.0), IdEntry("TRO06", 2000.0, 510.0)]
|
||||
result = assign_ids(tros, ANCHORS, entries)
|
||||
assert tros[0].tro_id == "TRO05"
|
||||
assert result.merged_into == {"TRO06": "TRO05"}
|
||||
|
||||
|
||||
def test_renumber_ignoriert_eintraege():
|
||||
tros = _tros(["0001"], ["0002"])
|
||||
result = assign_ids(tros, ANCHORS, [IdEntry("X", 1000.0, 0.0)], renumber=True)
|
||||
assert result.renumbered
|
||||
assert [t.tro_id for t in tros] == ["TRO01", "TRO02"]
|
||||
|
||||
|
||||
def test_datei_rundlauf_und_anhaengen(tmp_path):
|
||||
path = tmp_path / "anlage_tro_ids.ini"
|
||||
tros = _tros(["0001"], ["0002"])
|
||||
assign_ids(tros, ANCHORS, [])
|
||||
path.write_text(render_ids(tros, ANCHORS, "anlage.csv")
|
||||
+ "\n[separator]\n; spaeter\n", encoding="utf-8")
|
||||
entries = load_ids(path)
|
||||
assert [(e.tro_id, e.x, e.y) for e in entries] == [
|
||||
("TRO01", 0.0, 0.0), ("TRO02", 1000.0, 0.0)]
|
||||
|
||||
tros = _tros(["0001"], ["0002"], ["0003"])
|
||||
result = assign_ids(tros, ANCHORS, entries)
|
||||
insert_entries(path, render_new_entries(tros, ANCHORS, result.new, "anlage.csv"))
|
||||
assert [e.tro_id for e in load_ids(path)] == ["TRO01", "TRO02", "TRO03"]
|
||||
# neuer Eintrag steht in [tro], nicht im folgenden Abschnitt
|
||||
assert other_sections(path) == "[separator]\n; spaeter"
|
||||
assert TroIds.load(path).exists
|
||||
|
||||
|
||||
@pytest.mark.parametrize("inhalt, meldung", [
|
||||
("[tro]\nTRO01 = 1, 2\nTRO01 = 3, 4\n", "already exists"),
|
||||
("[tro]\nSYS1 = 1, 2\n", "reserviert"),
|
||||
("[tro]\nTRO-1 = 1, 2\n", "SCL-Bezeichner"),
|
||||
("[tro]\nTRO01 = 1\n", "erwartet"),
|
||||
("[andere]\n", "fehlt"),
|
||||
])
|
||||
def test_fehlerhafte_datei(tmp_path, inhalt, meldung):
|
||||
path = tmp_path / "x.ini"
|
||||
path.write_text(inhalt, encoding="utf-8")
|
||||
with pytest.raises(ValueError, match=meldung):
|
||||
load_ids(path)
|
||||
Reference in New Issue
Block a user