Konvertiert eine Beckhoff TwinCAT .tpy in eine CSV im Format des SPS‑Analyzer 6 (TwinCAT‑Modul), inkl. Entfaltung von ARRAYs und STRUCT/UDT‑SubItems.
python convert_tpy_csv.py [--gui] [--no-recurse] <Eingabe.tpy> <Ausgabe.csv>-
Header wie vom SPS‑Analyzer erwartet:
Beckhoff TwinCat V2-PLC-Symbolfile- Anzahl der Datensätze
- Ab Zeile 3: Datensätze (Semikolon‑separiert)
-
Großdateien werden automatisch in Teile gesplittet (max. 1 670 000 Gesamtzeilen je Datei, inkl. Header).
-
Rekursive Entfaltung von verschachtelten UDTs/FBs ist standardmäßig aktiv (siehe unten). Mit
--no-recursekann sie deaktiviert werden. -
GUI-Start mit
--gui(öffnet eine Tkinter-Oberfläche zur Dateiauswahl; wird ebenfalls geöffnet, wenn keine Pfade angegeben sind und die Default-Datei nicht existiert). -
Header wie vom SPS‑Analyzer erwartet:
Beckhoff TwinCat V2-PLC-Symbolfile- Anzahl der Datensätze
- Ab Zeile 3: Datensätze (Semikolon‑separiert)
-
Großdateien werden automatisch in Teile gesplittet (max. 1 670 000 Gesamtzeilen je Datei, inkl. Header).
- Python ≥ 3.10 (wegen
int | NoneType‑Hints). Getestet mit 3.13. - Keine externen Abhängigkeiten. Nur Python‑Standardbibliothek (
xml.etree.ElementTree,csv,re,pathlib,sys). - Optional für GUI: Tkinter (unter Linux z. B. Paket
python3-tk).
python convert_tpy_csv.py [--gui] [--no-recurse] [--no-array-recurse] [--only <Whitelist.txt>] [--skip <Blacklist.txt>] <Eingabe.tpy> <Ausgabe.csv>Optionale Flags:
--no-recurse→ deaktiviert alle rekursiven Entfaltungen (Top‑UDTs/FBs & Arrays).--no-array-recurse→ deaktiviert nur die rekursive Entfaltung von UDT‑Array‑Elementen.--gui→ öffnet eine Tkinter-Oberfläche zur Auswahl von TPY/CSV/Listen.--only <Datei>→ Whitelist (Regex je Zeile). Nur UDT‑Namen, die einem Muster entsprechen, werden rekursiv entfaltet.--skip <Datei>→ Blacklist (Regex je Zeile). UDT‑Namen, die einem Muster entsprechen, werden nicht rekursiv entfaltet.
Beispiele (Windows CMD):
REM absolut
python convert_tpy_csv.py C:\Projekte\TwinCAT\Plc.tpy C:\Export\output.csv
REM relativ (aus C:\Projekte)
python convert_tpy_csv.py TwinCAT\Plc.tpy Export\output.csv
REM ohne Rekursion (alles)
python convert_tpy_csv.py --no-recurse TwinCAT\Plc.tpy Export\output.csv
REM nur Array‑Rekursion aus
python convert_tpy_csv.py --no-array-recurse TwinCAT\Plc.tpy Export\output.csv
REM Whitelist verwenden
python convert_tpy_csv.py --only cfg\udt_whitelist.txt TwinCAT\Plc.tpy Export\output.csv
REM Blacklist verwenden
python convert_tpy_csv.py --skip cfg\udt_blacklist.txt TwinCAT\Plc.tpy Export\output.csv
REM Whitelist + Blacklist (Whitelist zuerst, dann Blacklist)
python convert_tpy_csv.py --only cfg\udt_whitelist.txt --skip cfg\udt_blacklist.txt TwinCAT\Plc.tpy Export\output.csv
```bash
python convert_tpy_csv.py [--no-recurse] [--no-array-recurse] <Eingabe.tpy> <Ausgabe.csv>Optionale Flags:
--no-recurse→ deaktiviert alle rekursiven Entfaltungen (Top‑UDTs/FBs & Arrays).--no-array-recurse→ deaktiviert nur die rekursive Entfaltung von UDT‑Array‑Elementen.
Beispiele (Windows CMD):
REM absolut
python convert_tpy_csv.py C:\Projekte\TwinCAT\Plc.tpy C:\Export\output.csv
REM relativ (aus C:\Projekte)
python convert_tpy_csv.py TwinCAT\Plc.tpy Export\output.csv
REM ohne Rekursion (alles)
python convert_tpy_csv.py --no-recurse TwinCAT\Plc.tpy Export\output.csv
REM nur Array‑Rekursion aus
python convert_tpy_csv.py --no-array-recurse TwinCAT\Plc.tpy Export\output.csvAchtung:
\tpy\Plc.tpy(führender Backslash) wird als UNC‑Pfad interpretiert und führt zu FileNotFoundError. Entweder relativ ohne führenden Backslash oder absolut angeben.
Standardwerte (nur als Fallback in dev/test):
- Eingabe:
/mnt/data/Plc.tpy - Ausgabe:
/mnt/data/output.csv
Spaltenreihenfolge:
IGroup; IOffset; Name; Comment; Type; BitSize; BitOffs; DefaultValue; ActualAddress
Header:
Beckhoff TwinCat V2-PLC-Symbolfile
<Anzahl_Datensätze>
<Datensätze …>
-
IGroup: wie in der .tpy
-
IOffset:
- ARRAY‑Elemente: = ActualAddress des Elements
- STRUCT/UDT‑SubItems: = ActualAddress des SubItems
- Top‑Symbolzeilen: = Basisadresse des Symbols
-
Name:
- Top‑Symbol: Original‑Name aus .tpy
- ARRAY‑Element:
Name[index] - STRUCT/UDT‑SubItem: qualifizierter Name
Parent.SubItem(z. B.prgMain.tonTempDaten2.IN)
-
Comment: gekürzt auf 200 Zeichen, ohne Zeilenumbrüche
-
Type: Datentyp (inkl.
ARRAY [...] OF …) -
BitSize: Bitgröße des Elements (s. Auflösung unten)
-
BitOffs: Bit‑Offset relativ zur Basis (ARRAY‑Basis bzw. STRUCT‑Parent)
-
DefaultValue: falls im
<Default><Value>vorhanden -
ActualAddress:
Basisadresse + (BitOffs // 8)
-
Top‑UDTs/FBs: SubItems, deren Type wiederum ein
<DataType>ist (z. B.Tc2_Standard.R_TRIG,Tc2_Standard.TON,Tc2_MC2.*,TC3_UniLib.*), werden weiter entfaltet. -
Arrays von UDTs:
ARRAY [...] OF <UDT>→ jedes Element wird zusätzlich rekursiv entfaltet (z. B..arrAxis[1].PlcToNc.*). -
Name: bei jedem Schritt vollständig qualifiziert (
Parent.SubItem[.SubSubItem…]bzw.ArrayName[i].SubItem…). -
Offset/Adresse: absolute
BitOffswird kumuliert (Summe der relativen Offsets);ActualAddress = Basis + (BitOffs // 8);IOffset = ActualAddress. -
Deaktivieren:
- alle Rekursionen:
--no-recurse - nur Array‑Rekursion:
--no-array-recurse
- alle Rekursionen:
-
Whitelist/Blacklist:
--only <Datei>: es werden nur UDT‑Namen rekursiv entfaltet, die auf mindestens ein Regex in der Datei matchen.--skip <Datei>: UDT‑Namen, die auf irgendein Regex matchen, werden nicht rekursiv entfaltet (greift nach der Whitelist‑Prüfung).- Dateiformat: eine Regex je Zeile; leere Zeilen und Zeilen beginnend mit
#,;oder//sind Kommentare. - Case‑Sensitivity: Regex ist standardmäßig case‑sensitiv; für Case‑Insensitive
(?i)als Präfix im Regex verwenden.
IGroup; IOffset=basis; Name; …; BitOffs=""; ActualAddress=basis
- Basisadresse =
IOffsetaus Top‑Symbol - Element‑Name =
Name[index] - BitOffs (Element) =
(index - start) * per_element_bits - ActualAddress (Element) =
Basis + (BitOffs // 8) - IOffset (Element) = ActualAddress (Element)
- Basisadresse =
IOffsetaus Top‑Symbol - SubItem‑Name =
Parent.SubItem - ActualAddress (SubItem) =
Basis + (BitOffs // 8) - IOffset (SubItem) = ActualAddress (SubItem)
Reihenfolge der Ermittlung (erste zutreffende Regel gewinnt):
- Primitive (
PRIM_BITS):BOOL, BYTE, SINT, USINT, WORD, INT, UINT, DWORD, DINT, UDINT, REAL, LWORD, LINT, ULINT, LREAL - STRING/WSTRING:
STRING(n)→(n+1) * 8Bit;WSTRING(n)→(n+1) * 16Bit - Zeit/Datum (
SPECIAL_BITS): z. B.TIME: 32,DATE_AND_TIME: 32,LTIME: 64, … - UDT/Funktionsbausteine aus
<DataTypes>: nutzt<BitSize>des passenden<DataType> - Fallback:
symbol_bitsize / element_count, mindestens 8 Bit
Damit werden u. a. korrekt behandelt:
- Tc2_Standard:
TON,R_TRIG(über<DataTypes>) - Tc2_MC2.*:
ST_McOutputs,AXIS_REF,MC_ReadParameter,MC_MoveAbsolute/Velocity/Modulo, … - TC3_UniLib.*:
ST_UniBaustein,ST_NcAchsen,FB_UniWkzgAnstg, …
- Max. 1 670 000 Gesamtzeilen pro Datei (inkl. 2 Headerzeilen) → 1 669 998 Datensätze je Datei.
- Erste Datei heißt wie angegeben (z. B.
output.csv). - Folge‑Dateien:
output_2.csv,output_3.csv, … (Zeile 2 enthält dort jeweils die Teil‑Anzahl der Datensätze).
Konstanten im Script:
MAX_TOTAL_LINES_PER_FILE = 1_670_000
HEADER_LINES = 2- Die Datensatzanzahl kann durch die rekursive Entfaltung von UDTs/FBs sehr stark ansteigen.
- Praxisbeispiel: ohne Rekursion ≈ 41 575 Datensätze → mit Rekursion 666 130 Datensätze (gleiche .tpy).
- Plane entsprechend Laufzeit, RAM und Dateigröße ein. Der SPS‑Analyzer lädt große CSVs spürbar langsamer.
- Wenn die Datei zu groß wird: Script mit
--no-recursestarten, oder zusätzliche Filterlogik einbauen (kann bei Bedarf ergänzt werden). - Das Chunking splittet automatisch; jede Teil‑Datei hat eine eigene Zeile‑2‑Zählung (Datensätze dieses Teils).
61472;51520300;.arrTwinSafeGroupOtherError;;ARRAY [1..5] OF BOOL;40;;;51520300
61472;51520300;.arrTwinSafeGroupOtherError[1];;BOOL;8;0;;51520300
61472;51520301;.arrTwinSafeGroupOtherError[2];;BOOL;8;8;;51520301
61472;51520302;.arrTwinSafeGroupOtherError[3];;BOOL;8;16;;51520302
61472;51520303;.arrTwinSafeGroupOtherError[4];;BOOL;8;24;;51520303
61472;51520304;.arrTwinSafeGroupOtherError[5];;BOOL;8;32;;51520304
16448;777600;prgMain.tonTempDaten2;;Tc2_Standard.TON;256;;;777600
16448;777608;prgMain.tonTempDaten2.IN;;BOOL;8;64;;777608
16448;777612;prgMain.tonTempDaten2.PT;;TIME;32;96;;777612
16448;777616;prgMain.tonTempDaten2.Q;;BOOL;8;128;;777616
16448;777620;prgMain.tonTempDaten2.ET;;TIME;32;160;;777620
16448;777624;prgMain.tonTempDaten2.M;;BOOL;8;192;;777624
16448;777628;prgMain.tonTempDaten2.StartTime;;TIME;32;224;;777628
- FileNotFoundError: In Windows kein führender Backslash (UNC). Pfad absolut oder relativ angeben, z. B.
tpy\Plc.tpystatt\tpy\Plc.tpy. - Falsche IOffset‑Werte: Prüfe, ob der Fall ARRAY (Element →
IOffset=ActualAddress) oder STRUCT (SubItem →IOffset=ActualAddress) ist. Top‑Symbolzeilen behalten die Basisadresse. - Sondertypen fehlen: Ergänze bei Bedarf
PRIM_BITS/SPECIAL_BITS. UDTs werden i. d. R. über<DataTypes>automatisch erkannt. - Excel‑Kompatibilität: Standard‑Encoding ist
UTF‑8. Falls nötig, Ausgabe aufcp1252ändern.
- Typgrößen:
PRIM_BITS,SPECIAL_BITS - Chunk‑Größe:
MAX_TOTAL_LINES_PER_FILE,HEADER_LINES - Kommentar‑Länge: in
limit_comment()
Interner Projekt‑Helper; keine externe Lizenzangabe erforderlich. Änderungen nach Bedarf.
