9.7 KiB
S7 — TIA Portal CSV & STEP 7 Classic AWL symbol import
PR-S7-D1 / #299 — bulk-import
TIA Portal "Show all tags" CSV exports and STEP 7 Classic AWL declaration files
into the S7 driver. Saves operators from hand-typing every %MW0 /
%DB1.DBW0 row of a several-hundred-tag PLC into appsettings.json.
Supported formats — v1
| Format | Status | Notes |
|---|---|---|
TIA Portal .CSV ("Show all tags" export) |
supported | Header columns Name,Path,Data type,Logical address,Comment,Hmi accessible,…; en-US (,) and DE-locale (; separator + , decimal) auto-detected |
STEP 7 Classic .AWL (VAR_GLOBAL + DATA_BLOCK) |
supported, best-effort | Position-based offset assignment (no exact byte offsets in hand-exported AWL — see below) |
STEP 7 / TIA Portal native binary (.s7p, .zap) |
out of scope | Proprietary; no community parser. Use TIA's "Show all tags" CSV export |
| TIA Portal Openness API | out of scope | Requires a licensed TIA install + OpenAPI license; future PR |
TIA Portal CSV column reference
| Column | Required | Notes |
|---|---|---|
Name |
yes | OPC UA tag name. TIA symbols are stable across deployments; the importer uses them verbatim |
Logical address (or Address) |
yes | TIA-style address with leading % (e.g. %MW0, %DB1.DBW10, %DB1.DBX2.3). Stripped on import |
Data type |
recommended | TIA primitive type (Int, Real, Bool, String, …) — drives the imported S7DataType |
Comment |
no | Parsed but currently unused — S7TagDefinition has no Description field at the v2 schema layer (see #248). Held in the column contract for future schema bumps |
Hmi accessible |
no | Filter — rows with False / FALSCH / nein are skipped (internal symbols TIA shows in the editor but doesn't expose to client interfaces). Missing column defaults to True |
Hmi visible / Hmi writeable |
no | Currently unused — held for future Admin-UI-side metadata |
Length |
no | For String rows: max length. Default 254. Drives StringLength on the imported tag |
Path |
no | TIA tag-table path (Default tag table, custom names). Currently unused; held in the contract |
TIA Data type → S7DataType mapping
| TIA type | Maps to | Notes |
|---|---|---|
Bool |
Bool |
Bit access; address must include a .bit suffix |
Byte, SInt, USInt |
Byte |
1-byte unsigned/signed |
Int |
Int16 |
Signed 16-bit |
Word, UInt |
UInt16 |
Unsigned 16-bit |
DInt |
Int32 |
Signed 32-bit |
DWord, UDInt |
UInt32 |
Unsigned 32-bit |
LInt |
Int64 |
64-bit signed (S7-1500 only) |
LWord, ULInt |
UInt64 |
64-bit unsigned (S7-1500 only) |
Real |
Float32 |
IEEE-754 32-bit |
LReal |
Float64 |
IEEE-754 64-bit (S7-1500 only) |
String |
String |
S7 STRING with 2-byte header; Length column drives StringLength |
WString |
WString |
S7 WSTRING (UTF-16BE) |
Char / WChar |
Char / WChar |
Single-character |
Date |
Date |
UInt16 days since 1990-01-01 |
Time |
Time |
Int32 ms |
TOD / Time_Of_Day |
TimeOfDay |
UInt32 ms since midnight |
DT / Date_And_Time |
DateAndTime |
8-byte BCD |
DTL |
Dtl |
12-byte structured (S7-1200 / S7-1500) |
S5Time |
S5Time |
16-bit BCD duration |
Struct / quoted UDT name |
UDT placeholder | See below |
UDT placeholders
UDT-typed symbols (TIA Data type = "MyUdt" quoted, or the literal Struct)
import as a placeholder — the resulting tag lands in the driver options so
it shows up in the Admin UI tag list, but its data type is forced to Byte
and the row is marked Writable = false. PR-S7-D2 will replace the placeholder
with proper UDT layout once the symbol table covers nested struct fields.
S7ImportResult.UdtPlaceholderCount tracks how many of the imported tags
landed in this bucket.
DE locale handling
TIA Portal honours the Windows display locale when writing CSV. A DE-locale install emits:
- Field separator
;(because,is the decimal separator) - Decimal-comma in addresses:
%MW0,5rather than%MW0.5for bit addresses - Boolean column values
WAHR/FALSCHrather thanTrue/False
The importer auto-detects the locale from the first non-blank line:
- Field-separator detection: counts
;vs,occurrences in the header - Decimal-comma detection: scans the first data row's address column for a digit-comma-digit pattern
- Boolean column values: recognises both languages (
true/false/wahr/falsch/yes/no/ja/nein, case-insensitive) plus bare0/1
The address column is rewritten to en-US shape (%MW0,5 → MW0.5) before the
strict S7AddressParser runs, so the rest of the driver pipeline sees a
single canonical address shape.
STEP 7 Classic AWL — VAR_GLOBAL + DATA_BLOCK
Best-effort parser for legacy STEP 7 Classic projects:
VAR_GLOBAL … END_VAR— global memory area declarations. Each entry maps to a sequentialM{B|W|D}{offset}address based on declaration order.DATA_BLOCK DBn … END_DATA_BLOCK— DB declarations. Each field maps to aDB{n}.DB{B|W|D}{offset}address based on declaration order; the DB number is parsed from theDATA_BLOCKline'sDBnkeyword.
Position-based addressing — heuristic
Real STEP 7 Classic projects carry exact byte offsets in the symbol table / .gr8 deployment artefact, but a hand-exported AWL file omits them. The importer assumes:
| Type | Bytes |
|---|---|
BOOL |
1 (rounded up to byte alignment) |
BYTE / SINT / USINT / CHAR |
1 |
INT / WORD / UINT |
2 |
DINT / DWORD / UDINT / REAL |
4 |
LREAL / LINT / ULINT / LWORD |
8 |
STRING[N] |
N + 2 (2-byte header) |
STRING (no length) |
256 |
STRUCT / Array[…] of … / quoted UDT name |
UDT placeholder (8-bit Byte at next aligned offset) |
S7 alignment rule: offsets round up to a 2-byte boundary for any 16-bit-or-larger type. Sites needing exact offsets should drive their symbol import from the TIA Portal CSV path instead — the CSV carries the offsets verbatim.
Comments ((* ... *) block, // ... line) are stripped before declaration
parsing. Initial-value clauses (:= 0) are recognised and discarded.
CLI subcommand — import-symbols
otopcua-s7-cli import-symbols --help
| Flag | Default | Purpose |
|---|---|---|
-f / --file |
required | Path to the TIA CSV or .AWL file |
--format |
tia |
tia (CSV) or awl (STEP 7 Classic) |
-d / --device |
none | Optional documentation tag (reserved for symmetry with import-rslogix) |
--emit |
appsettings-fragment |
appsettings-fragment (JSON) or summary (one-line counter) |
-o / --output |
stdout | Optional path; when set the JSON fragment is written there + summary line goes to stdout |
--max-rows |
unlimited | Defensive cap on rows imported |
--strict |
off | Fail-fast on the first malformed row (default permissive: skip + log) |
appsettings-fragment output shape
The default --emit appsettings-fragment mode writes a JSON object whose
Tags array is shaped like the S7DriverConfigDto.Tags array — paste
straight into the driver-instance config under
Drivers/<instance>/Config/Tags.
{
"Tags": [
{
"Name": "MotorSpeed",
"Address": "MW0",
"DataType": "Int16",
"Writable": true,
"StringLength": 254
},
…
]
}
Summary line
--emit summary writes a single line:
Imported 142 tag(s), skipped 3, errors 0, udt-placeholders 5.
Skipped covers HMI-accessible-false rows + missing-required-field rows;
errors covers rows whose Address failed to parse as an S7 address;
udt-placeholders covers UDT-typed rows that imported as placeholders.
API surface — IS7SymbolImporter + AddTiaCsvImport / AddAwlImport
For server-side / bootstrap use-cases the importer is reachable via:
using ZB.MOM.WW.OtOpcUa.Driver.S7;
using ZB.MOM.WW.OtOpcUa.Driver.S7.SymbolImport;
var options = new S7DriverOptions { Host = "192.168.1.30", CpuType = CpuType.S71500 };
// Append imported tags onto an existing options object.
var updated = options.AddTiaCsvImport(
path: @"C:\plc\tia-export.csv",
out var result);
Console.WriteLine($"Imported {result.ParsedCount} tags ({result.UdtPlaceholderCount} placeholders)");
// AWL variant — same shape.
var withAwl = updated.AddAwlImport(
path: @"C:\plc\classic.awl",
out var awlResult);
For a hand-managed importer instance (e.g. supplying a custom ILogger) call
new TiaCsvImporter(logger).Parse(stream, opts) or
new AwlImporter(logger).Parse(stream, opts) directly.
Operational notes
- The importers are additive —
AddTiaCsvImport/AddAwlImportconcatenate onto the existingTagslist rather than replacing it. Hand-rolled tags (system-status variables, computed fields the operator added by hand) survive a re-import. - Re-imports are not idempotent — calling
AddTiaCsvImporttwice will produce duplicate tag rows. Operators are expected to start from a clean options object or de-duplicate themselves; a future schema rev may add areplace=trueswitch. - UDT placeholders surface in the Admin UI as non-writable Byte tags. PR-S7-D2 will replace the placeholder rows with proper UDT layout (one tag per primitive field); operators should not bind dependent client tags to placeholder rows because the addresses will be rewritten when D2 lands.
- Description metadata is dropped on the floor today — see the column
reference above. When #248
lands a
Descriptionfield onS7TagDefinitionthe importer will start populating it without further changes to the CSV contract.