An open API service indexing awesome lists of open source software.

https://github.com/zonfacter/twincat3_tpy_csv

Konvertiert Beckhoff TwinCAT *.tpy in SPS-Analyzer-kompatible CSV – inkl. ARRAY/UDT-Entfaltung, rekursiv (optional), Chunking & Whitelist/Blacklist.
https://github.com/zonfacter/twincat3_tpy_csv

analyzer converter csv tpy twincat twincat2 twincat3

Last synced: 2 months ago
JSON representation

Konvertiert Beckhoff TwinCAT *.tpy in SPS-Analyzer-kompatible CSV – inkl. ARRAY/UDT-Entfaltung, rekursiv (optional), Chunking & Whitelist/Blacklist.

Awesome Lists containing this project

README

          

# convert\_tpy\_csv – README

Konvertiert eine **Beckhoff TwinCAT .tpy** in eine CSV im Format des **SPS‑Analyzer 6** (TwinCAT‑Modul), inkl. Entfaltung von **ARRAYs** und **STRUCT/UDT‑SubItems**.


TwinCAT .tpy → CSV – SPS-Analyzer 6 kompatibel

---

## TL;DR

```bash
python convert_tpy_csv.py [--gui] [--no-recurse]
```

* **Header** wie vom SPS‑Analyzer erwartet:

1. `Beckhoff TwinCat V2-PLC-Symbolfile`
2. Anzahl der Datensätze
3. 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-recurse` kann 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:

1. `Beckhoff TwinCat V2-PLC-Symbolfile`
2. Anzahl der Datensätze
3. Ab Zeile 3: Datensätze (Semikolon‑separiert)
* **Großdateien** werden automatisch in Teile gesplittet (max. **1 670 000 Gesamtzeilen** je Datei, inkl. Header).

---

## Voraussetzungen

* **Python ≥ 3.10** (wegen `int | None` Type‑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`).

---

## Aufruf / Parameter

```bash
python convert_tpy_csv.py [--gui] [--no-recurse] [--no-array-recurse] [--only ] [--skip ]
```

**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 ` → **Whitelist** (Regex je Zeile). Nur UDT‑Namen, die einem Muster entsprechen, werden rekursiv entfaltet.
* `--skip ` → **Blacklist** (Regex je Zeile). UDT‑Namen, die einem Muster entsprechen, werden **nicht** rekursiv entfaltet.

**Beispiele (Windows CMD):**

````bat
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]
````

**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):**

```bat
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
```

> Achtung: `\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`

---

## Ausgabeformat (CSV)

**Spaltenreihenfolge:**

```
IGroup; IOffset; Name; Comment; Type; BitSize; BitOffs; DefaultValue; ActualAddress
```

**Header:**

```
Beckhoff TwinCat V2-PLC-Symbolfile

```

### Semantik der Spalten

* **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 `` vorhanden
* **ActualAddress**: `Basisadresse + (BitOffs // 8)`

---

## Entfaltungs‑/Adressierungsregeln

### Rekursive Entfaltung (Standard: EIN)

* **Top‑UDTs/FBs:** SubItems, deren **Type** wiederum ein `` ist (z. B. `Tc2_Standard.R_TRIG`, `Tc2_Standard.TON`, `Tc2_MC2.*`, `TC3_UniLib.*`), werden **weiter entfaltet**.
* **Arrays von UDTs:** `ARRAY [...] OF ` → 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 `BitOffs` wird kumuliert (Summe der relativen Offsets); `ActualAddress = Basis + (BitOffs // 8)`; `IOffset = ActualAddress`.
* **Deaktivieren:**

* alle Rekursionen: `--no-recurse`
* nur Array‑Rekursion: `--no-array-recurse`
* **Whitelist/Blacklist:**

* `--only `: es werden **nur** UDT‑Namen rekursiv entfaltet, die auf **mindestens ein** Regex in der Datei matchen.
* `--skip `: 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.

### Top‑Symbol

```
IGroup; IOffset=basis; Name; …; BitOffs=""; ActualAddress=basis
```

### ARRAY

* **Basisadresse** = `IOffset` aus Top‑Symbol
* **Element‑Name** = `Name[index]`
* **BitOffs (Element)** = `(index - start) * per_element_bits`
* **ActualAddress (Element)** = `Basis + (BitOffs // 8)`
* **IOffset (Element)** = **ActualAddress (Element)**

### STRUCT / UDT (SubItems)

* **Basisadresse** = `IOffset` aus Top‑Symbol
* **SubItem‑Name** = `Parent.SubItem`
* **ActualAddress (SubItem)** = `Basis + (BitOffs // 8)`
* **IOffset (SubItem)** = **ActualAddress (SubItem)**

---

## Größenauflösung (BitSize je Element)

Reihenfolge der Ermittlung (erste zutreffende Regel gewinnt):

1. **Primitive** (`PRIM_BITS`): `BOOL, BYTE, SINT, USINT, WORD, INT, UINT, DWORD, DINT, UDINT, REAL, LWORD, LINT, ULINT, LREAL`
2. **STRING/WSTRING**: `STRING(n)` → `(n+1) * 8` Bit; `WSTRING(n)` → `(n+1) * 16` Bit
3. **Zeit/Datum** (`SPECIAL_BITS`): z. B. `TIME: 32`, `DATE_AND_TIME: 32`, `LTIME: 64`, …
4. **UDT/Funktionsbausteine aus ``**: nutzt `` des passenden ``
5. **Fallback**: `symbol_bitsize / element_count`, mindestens **8 Bit**

Damit werden u. a. korrekt behandelt:

* **Tc2\_Standard**: `TON`, `R_TRIG` (über ``)
* **Tc2\_MC2.\*:** `ST_McOutputs`, `AXIS_REF`, `MC_ReadParameter`, `MC_MoveAbsolute/Velocity/Modulo`, …
* **TC3\_UniLib.\*:** `ST_UniBaustein`, `ST_NcAchsen`, `FB_UniWkzgAnstg`, …

---

## Multi‑File‑Output (Chunking)

* 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:**

```python
MAX_TOTAL_LINES_PER_FILE = 1_670_000
HEADER_LINES = 2
```

## Datenmenge & Performance

* 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-recurse` starten, 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).

---

## Beispiele

### ARRAY (BOOL)

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

### STRUCT/UDT – qualifizierte SubItems (`Tc2_Standard.TON`)

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

---

## Fehlerbehebung

* **FileNotFoundError**: In Windows kein führender Backslash (UNC). Pfad absolut oder relativ angeben, z. B. `tpy\Plc.tpy` statt `\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 `` automatisch erkannt.
* **Excel‑Kompatibilität**: Standard‑Encoding ist `UTF‑8`. Falls nötig, Ausgabe auf `cp1252` ändern.

---

## Anpassungspunkte im Code

* **Typgrößen**: `PRIM_BITS`, `SPECIAL_BITS`
* **Chunk‑Größe**: `MAX_TOTAL_LINES_PER_FILE`, `HEADER_LINES`
* **Kommentar‑Länge**: in `limit_comment()`

---

## Lizenz / Autor

Interner Projekt‑Helper; keine externe Lizenzangabe erforderlich. Änderungen nach Bedarf.