Auto: s7-d1 — TIA Portal CSV + STEP 7 Classic AWL symbol import
Closes #299
This commit is contained in:
215
docs/drivers/S7-TIA-Import.md
Normal file
215
docs/drivers/S7-TIA-Import.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# S7 — TIA Portal CSV & STEP 7 Classic AWL symbol import
|
||||
|
||||
PR-S7-D1 / [#299](https://github.com/dohertj2/lmxopcua/issues/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](https://github.com/dohertj2/lmxopcua/issues/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,5` rather than `%MW0.5` for bit addresses
|
||||
- Boolean column values `WAHR` / `FALSCH` rather than `True` / `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 bare `0`/`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 sequential `M{B|W|D}{offset}` address based on declaration order.
|
||||
- `DATA_BLOCK DBn … END_DATA_BLOCK` — DB declarations. Each field maps to a
|
||||
`DB{n}.DB{B|W|D}{offset}` address based on declaration order; the DB number
|
||||
is parsed from the `DATA_BLOCK` line's `DBn` keyword.
|
||||
|
||||
### 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`
|
||||
|
||||
```powershell
|
||||
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`.
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
|
||||
```csharp
|
||||
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` / `AddAwlImport` concatenate
|
||||
onto the existing `Tags` list 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 `AddTiaCsvImport` twice 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 a
|
||||
`replace=true` switch.
|
||||
- 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](https://github.com/dohertj2/lmxopcua/issues/248)
|
||||
lands a `Description` field on `S7TagDefinition` the importer will start
|
||||
populating it without further changes to the CSV contract.
|
||||
Reference in New Issue
Block a user