erste Fassung eines Python Skripts zur Skeleton Erzeugung über ein json Zwischenformat geschrieben
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
@echo off
|
||||
REM ================================================================
|
||||
REM HundM_Fortna - Python Virtual Environment aktivieren
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
|
||||
if not exist "%PROJECT%\.venv" (
|
||||
echo FEHLER: Virtual environment nicht gefunden.
|
||||
echo Bitte zuerst bin\install_py.bat ausfuehren.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call "%PROJECT%\.venv\Scripts\activate.bat"
|
||||
echo Virtuelle Umgebung aktiviert.
|
||||
echo Python-Version:
|
||||
python --version
|
||||
echo.
|
||||
echo Installierte Pakete:
|
||||
pip list
|
||||
@@ -0,0 +1,22 @@
|
||||
@echo off
|
||||
REM ================================================================
|
||||
REM HundM_Fortna - Skeleton-Generator aufrufen
|
||||
REM
|
||||
REM Liest die Eingabelisten (%PV_EINGABEN%) und erzeugt je Steuerung
|
||||
REM ein Skeleton der JSON-Zwischendatei sowie ein SCL-Grundgeruest
|
||||
REM nach %PV_RESULTS%.
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
|
||||
REM .venv bevorzugen, sonst System-Python
|
||||
set "PYTHON=python"
|
||||
if exist "%PROJECT%\.venv\Scripts\python.exe" (
|
||||
set "PYTHON=%PROJECT%\.venv\Scripts\python.exe"
|
||||
)
|
||||
|
||||
"%PYTHON%" "%PV_LIB%\create_skel.py" %*
|
||||
if errorlevel 1 (
|
||||
echo FEHLER: create_skel.py wurde mit Fehler beendet.
|
||||
exit /b 1
|
||||
)
|
||||
@@ -18,15 +18,20 @@ if errorlevel 1 (
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
where dot >nul 2>nul
|
||||
set "DOT_EXE=dot"
|
||||
where %DOT_EXE% >nul 2>nul
|
||||
if errorlevel 1 (
|
||||
echo dot.exe wurde nicht gefunden. Bitte Graphviz installieren:
|
||||
echo winget install --id Graphviz.Graphviz -e
|
||||
exit /b 1
|
||||
if exist "C:\Program Files\Graphviz\bin\dot.exe" (
|
||||
set "DOT_EXE=C:\Program Files\Graphviz\bin\dot.exe"
|
||||
) else (
|
||||
echo dot.exe wurde nicht gefunden. Bitte Graphviz installieren:
|
||||
echo winget install --id Graphviz.Graphviz -e
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
echo Rendere %SVG_FILE% ...
|
||||
dot -Tsvg "%DOT_FILE%" -o "%SVG_FILE%"
|
||||
"%DOT_EXE%" -Tsvg "%DOT_FILE%" -o "%SVG_FILE%"
|
||||
if errorlevel 1 (
|
||||
echo Fehler beim Rendern der SVG-Datei.
|
||||
exit /b 1
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
REM ================================================================
|
||||
REM HundM_Fortna - Shell mit gesetzten Umgebungsvariablen oeffnen
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
start cmd /k "echo HundM_Fortna Umgebung aktiv. && echo PROJECT=%PROJECT%"
|
||||
@@ -0,0 +1,21 @@
|
||||
@echo off
|
||||
REM ================================================================
|
||||
REM HundM_Fortna - Python Virtual Environment einrichten
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
|
||||
if not exist "%PROJECT%\.venv" (
|
||||
echo Initialisiere Python virtual environment...
|
||||
py -m venv "%PROJECT%\.venv" --upgrade-deps
|
||||
echo Erfolgreich.
|
||||
|
||||
call "%PROJECT%\.venv\Scripts\activate.bat"
|
||||
echo Installiere erforderliche Python Packages...
|
||||
pip install -r "%PROJECT%\requirements.txt" -q
|
||||
echo Erfolgreich.
|
||||
deactivate
|
||||
) else (
|
||||
echo .venv existiert bereits - Abbruch.
|
||||
echo Zum Neuaufsetzen .venv loeschen und install_py.bat erneut ausfuehren.
|
||||
)
|
||||
@@ -0,0 +1,62 @@
|
||||
@echo off
|
||||
REM ================================================================
|
||||
REM HundM_Fortna - Umgebungsvariablen Setup
|
||||
REM ================================================================
|
||||
|
||||
echo Setting up environment variables for HundM_Fortna ...
|
||||
|
||||
REM Basis-Projektpfad (Elternordner von bin\)
|
||||
set "PROJECT=%~dp0.."
|
||||
if "%PROJECT:~-6%"=="bin\.." set "PROJECT=%PROJECT:~0,-6%"
|
||||
if "%PROJECT:~-1%"=="\" set "PROJECT=%PROJECT:~0,-1%"
|
||||
|
||||
REM Pfade fuer die Komponenten
|
||||
set "PV_BIN=%PROJECT%\bin"
|
||||
set "PV_LIB=%PROJECT%\lib"
|
||||
set "PV_CFG=%PROJECT%\cfg"
|
||||
set "PV_DATA=%PROJECT%\data"
|
||||
set "PV_DOC=%PROJECT%\doc"
|
||||
set "PV_LOG=%PROJECT%\log"
|
||||
set "PV_RESULTS=%PROJECT%\results"
|
||||
set "PV_EXAMPLES=%PROJECT%\examples"
|
||||
set "PV_TESTS=%PROJECT%\tests"
|
||||
|
||||
REM PYTHONPATH um PV_LIB erweitern (nur wenn noch nicht enthalten)
|
||||
if not defined PYTHONPATH (
|
||||
set "PYTHONPATH=%PV_LIB%"
|
||||
) else (
|
||||
echo.%PYTHONPATH% | find /i "%PV_LIB%" >nul || set "PYTHONPATH=%PYTHONPATH%;%PV_LIB%"
|
||||
)
|
||||
|
||||
REM Ueberladen von Umgebungsvariablen durch _setenv.bat
|
||||
REM (z.B. PV_DATA auf einen Netzlaufwerk-Pfad umbiegen)
|
||||
if exist "%~dp0_setenv.bat" (
|
||||
echo Lade lokale Umgebungseinstellungen aus _setenv.bat...
|
||||
call "%~dp0_setenv.bat"
|
||||
)
|
||||
|
||||
REM Ordner erstellen falls sie nicht existieren
|
||||
if not exist "%PV_BIN%" mkdir "%PV_BIN%"
|
||||
if not exist "%PV_LIB%" mkdir "%PV_LIB%"
|
||||
if not exist "%PV_CFG%" mkdir "%PV_CFG%"
|
||||
if not exist "%PV_DATA%" mkdir "%PV_DATA%"
|
||||
if not exist "%PV_LOG%" mkdir "%PV_LOG%"
|
||||
if not exist "%PV_RESULTS%" mkdir "%PV_RESULTS%"
|
||||
if not exist "%PV_EXAMPLES%" mkdir "%PV_EXAMPLES%"
|
||||
if not exist "%PV_TESTS%" mkdir "%PV_TESTS%"
|
||||
|
||||
REM Umgebungsvariablen anzeigen
|
||||
echo.
|
||||
echo ================================================================
|
||||
echo PROJECT = %PROJECT%
|
||||
echo PV_BIN = %PV_BIN%
|
||||
echo PV_LIB = %PV_LIB%
|
||||
echo PV_CFG = %PV_CFG%
|
||||
echo PV_DATA = %PV_DATA%
|
||||
echo PV_LOG = %PV_LOG%
|
||||
echo PV_RESULTS = %PV_RESULTS%
|
||||
echo PV_EXAMPLES = %PV_EXAMPLES%
|
||||
echo PV_TESTS = %PV_TESTS%
|
||||
echo PYTHONPATH = %PYTHONPATH%
|
||||
echo ================================================================
|
||||
echo.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
# Analyse der Ein-/Ausgabelisten (doc/Eingaben)
|
||||
# Analyse der Ein-/Ausgabelisten (data/Eingaben)
|
||||
|
||||
## Kontext
|
||||
|
||||
In `doc/Eingaben/` liegen je Steuerung (ILS-1 bis ILS-5, entspricht UH01-UH05) drei Excel-Dateien mit denselben E/A-Punkten in drei Sichten:
|
||||
In `data/Eingaben/` liegen je Steuerung (ILS-1 bis ILS-5, entspricht UH01-UH05) drei Excel-Dateien mit denselben E/A-Punkten in drei Sichten:
|
||||
|
||||
| Datei | Inhalt |
|
||||
| --- | --- |
|
||||
@@ -87,7 +87,7 @@ Die Listen enthalten eine durchgaengige **Anlagenhierarchie**, die im aktuellen
|
||||
|
||||
## Verwendung von Positionen
|
||||
|
||||
Zusaetzlich zu den Excel-Listen liegen in `doc/Eingaben/` vier JSON-Dateien mit Positions- und Verkabelungsdaten, offenbar Exporte eines WSCAD/CAD-Auswertungstools:
|
||||
Zusaetzlich zu den Excel-Listen liegen in `data/Eingaben/` vier JSON-Dateien mit Positions- und Verkabelungsdaten, offenbar Exporte eines WSCAD/CAD-Auswertungstools:
|
||||
|
||||
| Datei | Inhalt |
|
||||
| --- | --- |
|
||||
|
||||
@@ -1092,6 +1092,100 @@ flowchart LR
|
||||
|
||||
---
|
||||
|
||||
### 14. Erweiterungen aus der E/A-Listen- und Positionsdaten-Analyse
|
||||
|
||||
Die Analyse der Eingabelisten in `data/Eingaben/` (siehe [EA-Listen-Analyse.md](EA-Listen-Analyse.md)) hat gezeigt, dass grosse Teile des JSON automatisch aus den Projektierungsdaten befuellbar sind. Dafuer werden folgende Schema-Erweiterungen eingefuehrt:
|
||||
|
||||
#### 14.1 Feld `unit` je TRO/Conveyor (Schluessel zur E/A-Liste)
|
||||
|
||||
Jede TRO-/Conveyor-Instanz erhaelt die Foerdertechnik-Einheit (M-Nummer) als expliziten Schluessel. Die M-Nummern stehen in den Kommentaren der E/A-Listen und identisch in den REGION-Namen des SCL-Codes:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "TRO115",
|
||||
"unit": "M4104",
|
||||
"unitKind": "CV",
|
||||
"type": "Vario",
|
||||
"pos": { "x": 341906, "y": 164862 },
|
||||
"cabinet": "UC1101"
|
||||
}
|
||||
```
|
||||
|
||||
| Feld | Quelle | Bedeutung |
|
||||
|---|---|---|
|
||||
| `unit` | Kommentar E/A-Liste (`(ILS-CV M4104)`) | M-Nummer der mechanischen Einheit |
|
||||
| `unitKind` | dito | `KR` = Kreisel/Carousel, `CV` = Conveyor, `K` = Kreisel-Einfahrt |
|
||||
| `pos` | `..._positions.json` -> `sensors[<BMK>@<SPS>].pos` | x/y-Lage in mm (Referenzsensor der Einheit) |
|
||||
| `cabinet` | `..._positions.json` -> `mappings` | Verteiler/Schaltschrank aus der realen Verkabelung |
|
||||
|
||||
#### 14.2 Gruppierungsebene `areas`
|
||||
|
||||
Neue Top-Level-Ebene, die TROs/Conveyors zu den in den Listen gefundenen Bereichsgruppen buendelt:
|
||||
|
||||
```json
|
||||
"areas": [
|
||||
{
|
||||
"id": "Block 1.1.1",
|
||||
"kind": "pinStoreBlock",
|
||||
"members": ["TRO208", "TRO209", "TRO210"],
|
||||
"ucStations": ["UC2101"],
|
||||
"safetyZone": "UZ0201",
|
||||
"storageDb": "DB_Storage1.1.1",
|
||||
"lines": 8
|
||||
},
|
||||
{
|
||||
"id": "LS1",
|
||||
"kind": "loadingStation",
|
||||
"members": ["TRO103", "TRO104", "TRO105", "TRO106", "LoadingBoom1"],
|
||||
"safetyZone": "UZ0110"
|
||||
},
|
||||
{
|
||||
"id": "WS1.1",
|
||||
"kind": "workStation",
|
||||
"members": ["TRO421"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Gefundene `kind`-Werte: `pinStoreBlock` (Block x.y.z, UH02/UH03), `loadingStation` (LS 1/2, UH01), `workStation` (WS x.y, UH04/UH05), `safetyZone` (UZ0x0y), `ucStation` (Feldstation/Verteiler).
|
||||
|
||||
#### 14.3 Rollenklassifikation ueber das `VERW`-Vokabular
|
||||
|
||||
Die Positions-JSONs (`..._positions.json`) enthalten je Signal das Feld `VERW` — ein kontrolliertes Vokabular, das zuverlaessiger ist als das Parsen der freien Kommentartexte:
|
||||
|
||||
| `VERW`-Wert | Rolle im JSON | Hinweis auf FB-Typ |
|
||||
|---|---|---|
|
||||
| `In sep` | `separator.sensorInSep` | — |
|
||||
| `Sep` | `outputs.stopper*` (MB-Ausgang) | — |
|
||||
| `Jam detector` / `Jam detector (LP)` | `sensorJam*` | — |
|
||||
| `ES branch` | `outputs.sw*ExTo*` (Weichenausgang) | `1SepNSwi` |
|
||||
| `conveyor full` | `vario.sensors.jam` / Puffer voll | `Vario` |
|
||||
| `MT in Position` | `vario.sensors.carrInPos` | `Vario` |
|
||||
| `Finger in position` | `vario.sensors.finger` | `Vario` |
|
||||
| `PIN query` | PinStore-Abfrage | `PinStore_Auto` |
|
||||
| `Sync P1`..`P4` | Synchronisationspunkte | PinStore/Vario-Sonderform |
|
||||
| `Rad dreht` | LoadingBoom Rad-Sensor | LoadingBoom |
|
||||
|
||||
Heuristik fuer den FB-Typ je Einheit: `ES branch` vorhanden -> Weiche; `Finger`/`MT in Position`/`conveyor full` -> `Vario`; `PIN query` -> `PinStore_Auto`; Anzahl `In sep` (1/2) -> `1Sep`/`2Sep`; nur FC/MA-Paar ohne Separator -> `FB_Conveyor`.
|
||||
|
||||
#### 14.4 Quellen-Uebersicht
|
||||
|
||||
| Datenquelle | Liefert |
|
||||
|---|---|
|
||||
| `..._TIA.xlsx` "PLC Tags" | BMK-Name, Datentyp, `%E/%A`-Adresse, Kommentar -> `sensors[]`, `outputs` |
|
||||
| `..._TIA.xlsx` "Constants" | `cIn...`-Indexkonstanten -> Konstanten-Tabelle, `sensors[].constant` |
|
||||
| `..._WSCAD.xlsx` "Bezug" | Ortskennzeichen `=A01+UCxxxx-KF1DIx` -> Karten-/Stationszuordnung |
|
||||
| `..._positions.json` | `pos` (x/y), `VERW` (Rolle), `mappings` (Signal->Verteiler), `distributors` (Verteiler-Position) |
|
||||
| `..._todraw.json` | Kabelwege und -laengen (Doku/BOM, nicht SPS-relevant) |
|
||||
|
||||
Nicht aus den Listen ableitbar bleiben: Timings, Priority-Manager, Release-Ausdruecke, Scanner-Zuordnung (BX = PROFINET), `customCode` und die Verkettungsrichtung der Topologie (`connections`/`destinations`). Die Positionen dienen dabei als Plausibilitaets-Check der Topologie (kurze Abstaende = wahrscheinliche Nachbarn) und fuer massstabsgetreue Lageplaene.
|
||||
|
||||
#### 14.5 Skeleton-Generator
|
||||
|
||||
Das Skript [lib/create_skel.py](../lib/create_skel.py) (Aufruf ueber `bin\create_skel.bat`) liest die Eingabelisten und erzeugt je Steuerung ein Skeleton der JSON-Zwischendatei (`results/skeleton_UH0x.json`) sowie ein SCL-Grundgeruest. Die SCL-Aufruf-Templates sind als globale Definitionen im Skript hinterlegt und koennen spaeter in eine `.cfg` ausgelagert werden.
|
||||
|
||||
---
|
||||
|
||||
## Welche SCL-Bausteine werden daraus generiert?
|
||||
|
||||
| Ziel-Baustein | Generiert aus JSON-Abschnitt |
|
||||
|
||||
@@ -0,0 +1,610 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""create_skel.py — Skeleton-Generator fuer die JSON-Zwischendatei und ein
|
||||
SCL-Grundgeruest, abgeleitet aus den Eingabelisten in data/.
|
||||
|
||||
Datenquellen (Pfade ueber Umgebungsvariablen aus bin\\setenv.bat):
|
||||
PV_DATA Ordner mit den Eingabelisten
|
||||
ST500592_10_5-14_ILS-<n>_TIA.xlsx (PLC Tags + Constants)
|
||||
ST500592_10_5-9_ILS_positions.json (Positionen, VERW, mappings)
|
||||
PV_RESULTS Zielordner fuer die erzeugten Dateien
|
||||
PV_CFG optional: create_skel.cfg ueberschreibt die globalen
|
||||
Definitionen (Templates, Vokabular)
|
||||
|
||||
Erzeugt je Steuerung UH01..UH05:
|
||||
skeleton_UH0x.json JSON-Zwischendatei-Skeleton (Kap. 14 im
|
||||
Json_Layout-Konzept.md)
|
||||
FB_Main_skel_UH0x.scl SCL-Grundgeruest (VAR-Deklaration + Aufrufe)
|
||||
FB_CallSensors_skel_UH0x.scl Sensor-Aufrufe
|
||||
|
||||
Aufruf: bin\\create_skel.bat [--uh 1..5] [--no-scl]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import configparser
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import openpyxl
|
||||
except ImportError:
|
||||
sys.exit("openpyxl fehlt — bitte bin\\install_py.bat ausfuehren.")
|
||||
|
||||
# ============================================================================
|
||||
# GLOBALE DEFINITIONEN
|
||||
# Alle folgenden Bloecke sind bewusst als Modul-Konstanten definiert, damit
|
||||
# sie spaeter unveraendert in eine .cfg (PV_CFG\create_skel.cfg) ausgelagert
|
||||
# werden koennen. load_cfg() ueberschreibt sie, wenn die Datei existiert.
|
||||
# ============================================================================
|
||||
|
||||
#: Dateinamen der Eingabelisten ({n} = ILS-Nummer 1..5)
|
||||
FILES = {
|
||||
"tia_xlsx": "ST500592_10_5-14_ILS-{n}_TIA.xlsx",
|
||||
"positions_json": "ST500592_10_5-9_ILS_positions.json",
|
||||
}
|
||||
|
||||
#: VERW-Vokabular (Positions-JSON) -> Signalrolle
|
||||
#: (siehe doc/EA-Listen-Analyse.md, Abschnitt "Verwendung von Positionen")
|
||||
VERW_ROLES = {
|
||||
"In sep": "sensorInSep",
|
||||
"Sep": "stopperOutput",
|
||||
"Jam detector": "sensorJam",
|
||||
"Jam detector (LP)": "sensorJam",
|
||||
"jam detector (LP)": "sensorJam",
|
||||
"ES branch": "switchOutput",
|
||||
"ES brunch": "switchOutput", # Schreibvariante in den Daten
|
||||
"conveyor full": "sensorConvFull",
|
||||
"MT in Position": "sensorCarrInPos",
|
||||
"MT in position": "sensorCarrInPos",
|
||||
"Finger in position": "sensorFinger",
|
||||
"Finger in Position": "sensorFinger",
|
||||
"PIN query": "sensorPinQuery",
|
||||
"Rad dreht ": "sensorWheel",
|
||||
"Rad dreht": "sensorWheel",
|
||||
}
|
||||
|
||||
#: Fallback: Regex auf den Kommentartext -> Signalrolle
|
||||
#: (falls kein VERW vorliegt, z.B. Signal nicht im Positions-JSON)
|
||||
COMMENT_ROLES = [
|
||||
(r"^in Separator", "sensorInSep"),
|
||||
(r"^Separator", "stopperOutput"),
|
||||
(r"Stausensor|Staumelder", "sensorJam"),
|
||||
(r"AE Abzweig", "switchOutput"),
|
||||
(r"F.?rderer voll", "sensorConvFull"),
|
||||
(r"MT in Position", "sensorCarrInPos"),
|
||||
(r"Mitnehmerfinger|Finger in", "sensorFinger"),
|
||||
(r"Pin Abfrage|Pr.?fung MT Pos", "sensorPinQuery"),
|
||||
(r"Rad dreht", "sensorWheel"),
|
||||
(r"^Motor ", "motorOutput"),
|
||||
(r"MSS ausgel", "motorProtection"),
|
||||
]
|
||||
|
||||
#: Regeln zur FB-Typ-Bestimmung je Einheit.
|
||||
#: Reihenfolge = Prioritaet; "roles" muessen alle vorhanden sein.
|
||||
FBTYPE_RULES = [
|
||||
{"roles": ["sensorPinQuery"], "fbType": "FB_ILS_MTRO_PinStore_Auto"},
|
||||
{"roles": ["sensorFinger"], "fbType": "FB_ILS_MTRO_Vario"},
|
||||
{"roles": ["sensorCarrInPos"], "fbType": "FB_ILS_MTRO_Vario"},
|
||||
{"roles": ["switchOutput", "sensorInSep"], "fbType": "FB_ILS_MTRO_1Sep1Swi"},
|
||||
{"roles": ["sensorInSep"], "fbType": "FB_ILS_MTRO_1Sep"},
|
||||
{"roles": ["motorOutput"], "fbType": "FB_Conveyor"},
|
||||
{"roles": ["motorProtection"], "fbType": "FB_Conveyor"},
|
||||
]
|
||||
|
||||
#: Erkennung der Foerdertechnik-Einheit (M-Nummer) im Kommentartext
|
||||
UNIT_RE = r"\b(KR|CV)[-\s]{0,2}(P?[MK]\d{4}(?:_[A-Z])?)\b"
|
||||
|
||||
#: Erkennung der Bereichsgruppen im Kommentartext
|
||||
AREA_RES = [
|
||||
(r"Block ?(\d\.\d(?:\.\d)?)", "pinStoreBlock", "Block {m}"),
|
||||
(r"\bLS ?(\d)\b", "loadingStation", "LS{m}"),
|
||||
(r"\bWS ?(\d\.\d)\b", "workStation", "WS{m}"),
|
||||
(r"\bUZ(\d{4})\b", "safetyZone", "UZ{m}"),
|
||||
]
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# SCL-Templates: die "ueblichen Codebloecke" fuer die Aufrufe in FB_Main /
|
||||
# FB_CallSensors. Platzhalter im str.format()-Stil.
|
||||
# ----------------------------------------------------------------------------
|
||||
SCL_TEMPLATES = {
|
||||
# VAR-Deklaration einer Instanz
|
||||
"var_decl": ' {inst} : "{fbtype}"; // {comment}\n',
|
||||
|
||||
# Aufruf FB_ILS_MTRO_1Sep (Standard-Separator)
|
||||
"call_1sep": """\
|
||||
REGION {inst} ({shorttype}) — {comment}
|
||||
#{inst}.stInSeparator1.Settings.nSeparatorNo := "cSep{tro_no}.1";
|
||||
#{inst}.stInSeparator1.Settings.tDelayToNextItem := "TimeForSepWaiting_Short";
|
||||
#{inst}.stInSeparator1.Settings.tTrailingTime := T#500ms; // TODO Timing
|
||||
#{inst}.stInSeparator1.Settings.tHandlingTime := T#200ms; // TODO Timing
|
||||
#{inst}.stInSeparator1.Settings.tJam := "TimeForSepJam";
|
||||
#{inst}.stInSeparator1.stSenInSep := "DB_Inputs".Sensors["cIn{sen_in_sep}"];
|
||||
#{inst}.stInSenJam2 := "DB_Inputs".Sensors["cIn{sen_jam}"];
|
||||
|
||||
#{inst}.stInPriorityManager.xReleaseOk := TRUE; // TODO Priority
|
||||
|
||||
#{inst}(nInMainTroNo := "cMainTro{tro_no}",
|
||||
xInSftyOk := TRUE, // TODO Safety
|
||||
xInAllRdyToStart := TRUE,
|
||||
xInCarouselRun := TRUE, // TODO Conveyor
|
||||
xInRelease := TRUE, // TODO Release
|
||||
stInOutMachineState := #stInOutControlUnit, // TODO ControlUnit
|
||||
arInOutJamEntr1 := "DB_JamArea{tro_no}".stJam{tro_no}_1.arCarrier, // TODO Topologie
|
||||
stInOutJamEntr1 := "DB_JamArea{tro_no}".stJam{tro_no}_1.stData,
|
||||
arInOutJamExit2 := "DB_JamArea{tro_no}".stJam{tro_no}_2.arCarrier, // TODO Topologie
|
||||
stInOutJamExit2 := "DB_JamArea{tro_no}".stJam{tro_no}_2.stData,
|
||||
xOutStopper1 => "{stopper}",
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro{tro_no}"],
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.{inst});
|
||||
END_REGION
|
||||
|
||||
""",
|
||||
|
||||
# Aufruf FB_ILS_MTRO_1Sep1Swi (Separator + Weiche)
|
||||
"call_1sep1swi": """\
|
||||
REGION {inst} ({shorttype}) — {comment}
|
||||
#{inst}.stInSeparator1.Settings.nSeparatorNo := "cSep{tro_no}.1";
|
||||
#{inst}.stInSeparator1.Settings.tDelayToNextItem := "TimeForSepWaiting_Short";
|
||||
#{inst}.stInSeparator1.Settings.tTrailingTime := T#100ms; // TODO Timing
|
||||
#{inst}.stInSeparator1.Settings.tHandlingTime := T#50ms; // TODO Timing
|
||||
#{inst}.stInSeparator1.Settings.tJam := "TimeForSepJam";
|
||||
#{inst}.stInSeparator1.stSenInSep := "DB_Inputs".Sensors["cIn{sen_in_sep}"];
|
||||
#{inst}.stInSenJam2 := "DB_Inputs".Sensors["cIn{sen_jam}"];
|
||||
#{inst}.stInSwitch1.Settings.nSwitchNo := "cSwi{tro_no}.1";
|
||||
|
||||
#{inst}(nInMainTroNo := "cMainTro{tro_no}",
|
||||
xInSftyOk := TRUE, // TODO Safety
|
||||
xInAllRdyToStart := TRUE,
|
||||
xInCarouselRun1 := TRUE, // TODO Conveyor
|
||||
xInRelease := TRUE, // TODO Release
|
||||
stInOutMachineState := #stInOutControlUnit, // TODO ControlUnit
|
||||
arInOutJamEntr1 := "DB_JamArea{tro_no}".stJam{tro_no}_1.arCarrier, // TODO Topologie
|
||||
stInOutJamEntr1 := "DB_JamArea{tro_no}".stJam{tro_no}_1.stData,
|
||||
arInOutJamExit2 := "DB_JamArea{tro_no}".stJam{tro_no}_2.arCarrier, // TODO Topologie (Dir1)
|
||||
stInOutJamExit2 := "DB_JamArea{tro_no}".stJam{tro_no}_2.stData,
|
||||
arInOutJamExit3 := "DB_JamArea{tro_no}".stJam{tro_no}_3.arCarrier, // TODO Topologie (Dir2)
|
||||
stInOutJamExit3 := "DB_JamArea{tro_no}".stJam{tro_no}_3.stData,
|
||||
xOutStopper1 => "{stopper}",
|
||||
xOutSw1ExTo3 => "{switch_out}",
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro{tro_no}"],
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.{inst});
|
||||
END_REGION
|
||||
|
||||
""",
|
||||
|
||||
# Aufruf FB_ILS_MTRO_Vario (Kettenfoerderer)
|
||||
"call_vario": """\
|
||||
REGION {inst} ({shorttype}) — {comment}
|
||||
#{inst}.stInSeparator1.Settings.nSeparatorNo := "cSep{tro_no}.1";
|
||||
#{inst}.stInSeparator1.Settings.tDelayToNextItem := "TimeForSepWaiting_Short";
|
||||
#{inst}.stInSeparator1.stSenInSep := "DB_Inputs".Sensors["cIn{sen_in_sep}"];
|
||||
#{inst}.stInVario.stSenCarrInPos := "DB_Inputs".Sensors["cIn{sen_carr_in_pos}"];
|
||||
#{inst}.stInVario.stSenFinger := "DB_Inputs".Sensors["cIn{sen_finger}"];
|
||||
#{inst}.stInVario.stSenJam := "DB_Inputs".Sensors["cIn{sen_jam}"];
|
||||
|
||||
#{inst}(nInMainTroNo := "cMainTro{tro_no}",
|
||||
xInSftyOk := TRUE, // TODO Safety
|
||||
xInAllRdyToStart := TRUE,
|
||||
xInRelease := TRUE, // TODO Release
|
||||
stInOutMachineState := #stInOutControlUnit, // TODO ControlUnit
|
||||
arInOutJamEntr1 := "DB_JamArea{tro_no}".stJam{tro_no}_1.arCarrier, // TODO Topologie
|
||||
stInOutJamEntr1 := "DB_JamArea{tro_no}".stJam{tro_no}_1.stData,
|
||||
arInOutJamFinger := "DB_JamArea{tro_no}".stJam{tro_no}_2.arCarrier, // TODO Topologie
|
||||
xOutStopper => "{stopper}",
|
||||
xOutVarioMotor => "{motor}",
|
||||
nOutStateLast => "DB_GLOB_TroState".arTroState["cMainTro{tro_no}"],
|
||||
stInOutHMI := "DB_Interface_HMI".stTRO.{inst});
|
||||
END_REGION
|
||||
|
||||
""",
|
||||
|
||||
# Aufruf FB_Conveyor (Kreisel / Foerderer ohne TRO-Logik)
|
||||
"call_conveyor": """\
|
||||
REGION {inst} — {comment}
|
||||
#{inst}.Settings.tEnergySafeTime := "cTimeEnergySafe";
|
||||
#{inst}.Settings.xGridActive := FALSE; // TODO Grid
|
||||
#{inst}.Settings.sMotorProtectName := '{motor_protection}';
|
||||
|
||||
#{inst}(xInEnabled := TRUE, // TODO Enable
|
||||
xInMotorProtection := "{motor_protection}",
|
||||
xInPowerContactor := "{motor}",
|
||||
stInOutHMI := "DB_Interface_HMI".stConveyors.{inst});
|
||||
END_REGION
|
||||
|
||||
""",
|
||||
|
||||
# Eintrag in FB_CallSensors je Sensor
|
||||
"call_sensor": """\
|
||||
// {comment}
|
||||
#fbSensorInput(stInSensorHW := "{bmk}",
|
||||
nInSensorNo := "cIn{bmk}",
|
||||
stInOutSensor := "DB_Inputs".Sensors["cIn{bmk}"]);
|
||||
""",
|
||||
|
||||
# Rahmen FB_Main
|
||||
"fb_main_frame": """\
|
||||
FUNCTION_BLOCK "FB_Main_skel_{uh}"
|
||||
{{ S7_Optimized_Access := 'TRUE' }}
|
||||
VERSION : 0.1
|
||||
// ============================================================
|
||||
// AUTOMATISCH GENERIERTES GRUNDGERUEST — create_skel.py
|
||||
// Quelle: E/A-Listen {uh} (data/)
|
||||
// TODO-Marken kennzeichnen manuell zu ergaenzende Stellen
|
||||
// (Timings, Topologie/JamAreas, Safety, Release, Priority).
|
||||
// ============================================================
|
||||
VAR
|
||||
{var_block} END_VAR
|
||||
|
||||
BEGIN
|
||||
{call_block}END_FUNCTION_BLOCK
|
||||
""",
|
||||
|
||||
# Rahmen FB_CallSensors
|
||||
"fb_sensors_frame": """\
|
||||
FUNCTION_BLOCK "FB_CallSensors_skel_{uh}"
|
||||
{{ S7_Optimized_Access := 'TRUE' }}
|
||||
VERSION : 0.1
|
||||
// AUTOMATISCH GENERIERT — create_skel.py ({count} Sensoren)
|
||||
VAR
|
||||
fbSensorInput : "FB_SensorInput";
|
||||
END_VAR
|
||||
|
||||
BEGIN
|
||||
{call_block}END_FUNCTION_BLOCK
|
||||
""",
|
||||
}
|
||||
|
||||
# Zuordnung FB-Typ -> Template-Schluessel
|
||||
FBTYPE_TEMPLATE = {
|
||||
"FB_ILS_MTRO_1Sep": "call_1sep",
|
||||
"FB_ILS_MTRO_1Sep1Swi": "call_1sep1swi",
|
||||
"FB_ILS_MTRO_Vario": "call_vario",
|
||||
"FB_ILS_MTRO_PinStore_Auto": "call_1sep", # eigenes Template folgt spaeter
|
||||
"FB_Conveyor": "call_conveyor",
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Ende der globalen Definitionen
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def load_cfg():
|
||||
"""Ueberschreibt die globalen Definitionen aus PV_CFG\\create_skel.cfg.
|
||||
|
||||
Unterstuetzte Sektionen: [files], [verw_roles], [scl_templates].
|
||||
Werte in [scl_templates] sind mehrzeilige Raw-Strings.
|
||||
"""
|
||||
cfg_dir = os.environ.get("PV_CFG")
|
||||
if not cfg_dir:
|
||||
return
|
||||
cfg_file = Path(cfg_dir) / "create_skel.cfg"
|
||||
if not cfg_file.exists():
|
||||
return
|
||||
print(f"Lade Konfiguration: {cfg_file}")
|
||||
cp = configparser.ConfigParser(interpolation=None)
|
||||
cp.read(cfg_file, encoding="utf-8")
|
||||
if cp.has_section("files"):
|
||||
FILES.update(cp["files"])
|
||||
if cp.has_section("verw_roles"):
|
||||
VERW_ROLES.update(cp["verw_roles"])
|
||||
if cp.has_section("scl_templates"):
|
||||
for key, val in cp["scl_templates"].items():
|
||||
SCL_TEMPLATES[key] = val.replace("\\n", "\n")
|
||||
|
||||
|
||||
def project_paths():
|
||||
"""Pfade aus den setenv.bat-Umgebungsvariablen (mit Fallbacks)."""
|
||||
root = Path(os.environ.get("PROJECT", Path(__file__).resolve().parent.parent))
|
||||
data_dir = Path(os.environ.get("PV_DATA", root / "data"))
|
||||
results = Path(os.environ.get("PV_RESULTS", root / "results"))
|
||||
results.mkdir(parents=True, exist_ok=True)
|
||||
return root, data_dir, results
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Einlesen
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def read_positions(data_dir):
|
||||
"""Positions-JSON: (BMK, SPS) -> {verw, pos, cabinet, bezeichnung}."""
|
||||
path = data_dir / FILES["positions_json"]
|
||||
if not path.exists():
|
||||
print(f"WARNUNG: {path} nicht gefunden — Positionen/VERW entfallen.")
|
||||
return {}
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
index = {}
|
||||
for section in ("sensors", "schaltschrank_elemente"):
|
||||
for key, val in data.get(section, {}).items():
|
||||
if "@" not in key:
|
||||
continue
|
||||
bmk, sps = key.rsplit("@", 1)
|
||||
index[(bmk, sps)] = {
|
||||
"verw": (val.get("VERW") or "").strip(),
|
||||
"pos": val.get("pos"),
|
||||
"bezeichnung": val.get("BEZEICHNUNG") or "",
|
||||
"kennzeichnung": val.get("KENNZEICHNUNG") or "",
|
||||
}
|
||||
# mappings: Verteiler -> [BMK@SPS] umdrehen zu (BMK, SPS) -> Verteiler
|
||||
for dist, members in data.get("mappings", {}).items():
|
||||
for key in members:
|
||||
if "@" not in key:
|
||||
continue
|
||||
bmk, sps = key.rsplit("@", 1)
|
||||
index.setdefault((bmk, sps), {})["cabinet"] = dist
|
||||
return index
|
||||
|
||||
|
||||
def read_tia(data_dir, n):
|
||||
"""TIA-Liste ILS-n: Liste von Tag-Dicts + Konstanten-Map."""
|
||||
path = data_dir / FILES["tia_xlsx"].format(n=n)
|
||||
wb = openpyxl.load_workbook(path, read_only=True, data_only=True)
|
||||
tags = []
|
||||
for row in wb["PLC Tags"].iter_rows(min_row=2, values_only=True):
|
||||
name, _path, dtype, addr, comment = (tuple(row) + (None,) * 5)[:5]
|
||||
if not name:
|
||||
continue
|
||||
tags.append({
|
||||
"name": str(name),
|
||||
"dataType": str(dtype) if dtype else "Bool",
|
||||
"address": str(addr) if addr else "",
|
||||
"comment": str(comment) if comment else "",
|
||||
})
|
||||
constants = {}
|
||||
if "Constants" in wb.sheetnames:
|
||||
for row in wb["Constants"].iter_rows(min_row=2, values_only=True):
|
||||
cname, _path, _dt, value = (tuple(row) + (None,) * 4)[:4]
|
||||
if cname:
|
||||
constants[str(cname)] = value
|
||||
return tags, constants
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Klassifikation
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def classify_role(tag, posinfo):
|
||||
"""Signalrolle: primaer VERW-Vokabular, sonst Kommentar-Heuristik."""
|
||||
verw = (posinfo or {}).get("verw", "")
|
||||
if verw in VERW_ROLES:
|
||||
return VERW_ROLES[verw]
|
||||
for pattern, role in COMMENT_ROLES:
|
||||
if re.search(pattern, tag["comment"], re.I):
|
||||
return role
|
||||
return ""
|
||||
|
||||
|
||||
def extract_unit(comment):
|
||||
"""M-Nummer der Foerdertechnik-Einheit aus dem Kommentar."""
|
||||
m = re.search(UNIT_RE, comment)
|
||||
if m:
|
||||
return m.group(2), m.group(1) # ("M0101", "KR")
|
||||
return None, None
|
||||
|
||||
|
||||
def extract_areas(comment):
|
||||
"""Bereichsgruppen (Block/LS/WS/UZ) aus dem Kommentar."""
|
||||
found = []
|
||||
for pattern, kind, fmt in AREA_RES:
|
||||
for m in re.finditer(pattern, comment, re.I):
|
||||
found.append({"id": fmt.format(m=m.group(1)), "kind": kind})
|
||||
return found
|
||||
|
||||
|
||||
def guess_fbtype(roles):
|
||||
"""FB-Typ einer Einheit aus der Menge ihrer Signalrollen."""
|
||||
for rule in FBTYPE_RULES:
|
||||
if all(r in roles for r in rule["roles"]):
|
||||
return rule["fbType"]
|
||||
return "UNBEKANNT"
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Skeleton-Aufbau
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def build_skeleton(n, tags, constants, positions):
|
||||
"""Baut das JSON-Skeleton fuer eine Steuerung (ILS-n = UH0n)."""
|
||||
uh = f"UH0{n}"
|
||||
sps = str(n)
|
||||
|
||||
sensors = []
|
||||
units = defaultdict(lambda: {"signals": [], "roles": set(), "areas": [],
|
||||
"kind": None, "pos": None, "cabinets": set()})
|
||||
areas = defaultdict(lambda: {"kind": None, "members": set(),
|
||||
"cabinets": set()})
|
||||
unassigned = []
|
||||
|
||||
for tag in tags:
|
||||
posinfo = positions.get((tag["name"], sps))
|
||||
role = classify_role(tag, posinfo)
|
||||
unit_id, unit_kind = extract_unit(tag["comment"])
|
||||
area_hits = extract_areas(tag["comment"])
|
||||
cabinet = (posinfo or {}).get("cabinet")
|
||||
|
||||
entry = {
|
||||
"id": tag["name"],
|
||||
"constant": f"cIn{tag['name']}" if tag["address"].startswith("%E") else None,
|
||||
"hwAddress": tag["address"],
|
||||
"comment": tag["comment"],
|
||||
"role": role or None,
|
||||
"unit": unit_id,
|
||||
"cabinet": cabinet,
|
||||
"pos": (posinfo or {}).get("pos"),
|
||||
}
|
||||
if tag["address"].startswith("%E"):
|
||||
sensors.append(entry)
|
||||
|
||||
if unit_id:
|
||||
u = units[unit_id]
|
||||
u["kind"] = unit_kind
|
||||
u["signals"].append(entry)
|
||||
if role:
|
||||
u["roles"].add(role)
|
||||
if u["pos"] is None and entry["pos"]:
|
||||
u["pos"] = entry["pos"]
|
||||
if cabinet:
|
||||
u["cabinets"].add(cabinet)
|
||||
for a in area_hits:
|
||||
if a not in u["areas"]:
|
||||
u["areas"].append(a)
|
||||
elif not area_hits and not role:
|
||||
unassigned.append(tag["name"])
|
||||
|
||||
for a in area_hits:
|
||||
ar = areas[a["id"]]
|
||||
ar["kind"] = a["kind"]
|
||||
if unit_id:
|
||||
ar["members"].add(unit_id)
|
||||
if cabinet:
|
||||
ar["cabinets"].add(cabinet)
|
||||
|
||||
unit_list = []
|
||||
for unit_id in sorted(units):
|
||||
u = units[unit_id]
|
||||
fbtype = guess_fbtype(u["roles"])
|
||||
io = {}
|
||||
for sig in u["signals"]:
|
||||
if sig["role"]:
|
||||
io.setdefault(sig["role"], []).append(sig["id"])
|
||||
unit_list.append({
|
||||
"unit": unit_id,
|
||||
"unitKind": u["kind"],
|
||||
"fbType": fbtype,
|
||||
"areas": u["areas"],
|
||||
"cabinet": sorted(u["cabinets"])[0] if u["cabinets"] else None,
|
||||
"pos": u["pos"],
|
||||
"io": io,
|
||||
"signalCount": len(u["signals"]),
|
||||
})
|
||||
|
||||
area_list = [{
|
||||
"id": aid,
|
||||
"kind": a["kind"],
|
||||
"members": sorted(a["members"]),
|
||||
"cabinets": sorted(a["cabinets"]),
|
||||
} for aid, a in sorted(areas.items())]
|
||||
|
||||
return {
|
||||
"$schema": "hmf-layout-v1.schema.json",
|
||||
"generatedBy": "create_skel.py",
|
||||
"plc": {"id": uh, "configName": f"=A01+{uh}-KF00"},
|
||||
"statistics": {
|
||||
"tags": len(tags),
|
||||
"sensors": len(sensors),
|
||||
"constants": len(constants),
|
||||
"units": len(unit_list),
|
||||
"areas": len(area_list),
|
||||
"unassignedSignals": len(unassigned),
|
||||
},
|
||||
"sensors": sensors,
|
||||
"units": unit_list,
|
||||
"areas": area_list,
|
||||
"unassignedSignals": sorted(unassigned),
|
||||
"todo": [
|
||||
"connections[] (Topologie) manuell ergaenzen oder aus SCL extrahieren",
|
||||
"Timings, Priority, Release, customCode manuell pflegen",
|
||||
"TRO-IDs den Einheiten zuweisen (Schema TRO<UH><lfd>)",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# SCL-Rendering
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def first(io, role, default="BGxxxx"):
|
||||
vals = io.get(role)
|
||||
return vals[0] if vals else default
|
||||
|
||||
|
||||
def render_scl(skeleton, uh):
|
||||
"""FB_Main- und FB_CallSensors-Grundgeruest aus den Templates."""
|
||||
var_block = []
|
||||
call_block = []
|
||||
tro_seq = 0
|
||||
for u in skeleton["units"]:
|
||||
fbtype = u["fbType"]
|
||||
if fbtype == "UNBEKANNT":
|
||||
continue
|
||||
tro_seq += 1
|
||||
inst = f"TRO{uh[-1]}{tro_seq:02d}" if fbtype != "FB_Conveyor" \
|
||||
else f"fbConveyor{u['unit'][1:]}"
|
||||
comment = f"{u['unitKind']}-{u['unit']}" + \
|
||||
(f" / {u['areas'][0]['id']}" if u["areas"] else "")
|
||||
var_block.append(SCL_TEMPLATES["var_decl"].format(
|
||||
inst=inst, fbtype=fbtype, comment=comment))
|
||||
|
||||
tpl = SCL_TEMPLATES[FBTYPE_TEMPLATE[fbtype]]
|
||||
io = u["io"]
|
||||
call_block.append(tpl.format(
|
||||
inst=inst,
|
||||
shorttype=fbtype.replace("FB_ILS_MTRO_", "").replace("FB_", ""),
|
||||
comment=comment,
|
||||
tro_no=f"{uh[-1]}{tro_seq:02d}",
|
||||
sen_in_sep=first(io, "sensorInSep"),
|
||||
sen_jam=first(io, "sensorJam"),
|
||||
sen_finger=first(io, "sensorFinger"),
|
||||
sen_carr_in_pos=first(io, "sensorCarrInPos"),
|
||||
stopper=first(io, "stopperOutput", "MBxxxx"),
|
||||
switch_out=first(io, "switchOutput", "MBxxxx"),
|
||||
motor=first(io, "motorOutput", "MAxxxx"),
|
||||
motor_protection=first(io, "motorProtection", "FCxxxx"),
|
||||
))
|
||||
|
||||
fb_main = SCL_TEMPLATES["fb_main_frame"].format(
|
||||
uh=uh, var_block="".join(var_block), call_block="".join(call_block))
|
||||
|
||||
sensor_calls = [SCL_TEMPLATES["call_sensor"].format(
|
||||
bmk=s["id"], comment=s["comment"]) for s in skeleton["sensors"]]
|
||||
fb_sensors = SCL_TEMPLATES["fb_sensors_frame"].format(
|
||||
uh=uh, count=len(sensor_calls), call_block="".join(sensor_calls))
|
||||
|
||||
return fb_main, fb_sensors
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# main
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__)
|
||||
ap.add_argument("--uh", type=int, choices=range(1, 6),
|
||||
help="nur diese Steuerung (1..5), sonst alle")
|
||||
ap.add_argument("--no-scl", action="store_true",
|
||||
help="nur JSON-Skeleton, kein SCL-Grundgeruest")
|
||||
args = ap.parse_args()
|
||||
|
||||
load_cfg()
|
||||
root, data_dir, results = project_paths()
|
||||
print(f"Daten: {data_dir}")
|
||||
print(f"Results: {results}")
|
||||
|
||||
positions = read_positions(data_dir)
|
||||
print(f"Positions-JSON: {len(positions)} Signale indiziert")
|
||||
|
||||
for n in ([args.uh] if args.uh else range(1, 6)):
|
||||
uh = f"UH0{n}"
|
||||
tags, constants = read_tia(data_dir, n)
|
||||
skel = build_skeleton(n, tags, constants, positions)
|
||||
|
||||
out_json = results / f"skeleton_{uh}.json"
|
||||
out_json.write_text(json.dumps(skel, indent=2, ensure_ascii=False),
|
||||
encoding="utf-8", newline="\n")
|
||||
s = skel["statistics"]
|
||||
print(f"{uh}: {s['tags']} Tags, {s['units']} Einheiten, "
|
||||
f"{s['areas']} Bereiche -> {out_json.name}")
|
||||
|
||||
if not args.no_scl:
|
||||
fb_main, fb_sensors = render_scl(skel, uh)
|
||||
(results / f"FB_Main_skel_{uh}.scl").write_text(
|
||||
fb_main, encoding="utf-8", newline="\n")
|
||||
(results / f"FB_CallSensors_skel_{uh}.scl").write_text(
|
||||
fb_sensors, encoding="utf-8", newline="\n")
|
||||
print(f"{uh}: FB_Main_skel_{uh}.scl, FB_CallSensors_skel_{uh}.scl")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,2 @@
|
||||
# Python-Abhaengigkeiten HundM_Fortna
|
||||
openpyxl>=3.1
|
||||
Reference in New Issue
Block a user