docs(s7): wide-type/Timer-Counter support — CLI help + driver-specs + S7 driver doc
Drop the "not yet implemented / BadNotSupported" stale note from all three S7 CLI --type option descriptions (ReadCommand, WriteCommand, SubscribeCommand) and replace with accurate help listing the full supported type set, byte-anchored addressing for wide types, and Timer/Counter read-only status. docs/v2/driver-specs.md §5: add Supported Data Types table, Byte-Anchored Addressing table (DBB/MB/IB/QB + examples), Timer/Counter read section with the Counter-BCD known-limitation, and Deferrals list. docs/drivers/S7.md: expand Data types to a full table, add "Wide types & Timer/Counter" section (byte-anchored addressing, Timer/Counter read-only, Counter BCD known-limitation, deferrals), update Address forms table and 1-D array Deferrals note.
This commit is contained in:
+81
-15
@@ -68,28 +68,93 @@ Addresses use Siemens TIA-Portal / STEP 7 Classic syntax, parsed by
|
||||
|
||||
| Area | Example | Meaning |
|
||||
|------|---------|---------|
|
||||
| Data block | `DB1.DBX0.0` / `DB1.DBW0` / `DB1.DBD4` | DB number + size suffix `X`(bit) / `B`(byte) / `W`(word) / `D`(dword), optional `.bit` for `DBX` |
|
||||
| Data block | `DB1.DBX0.0` / `DB1.DBW0` / `DB1.DBD4` / `DB1.DBB8` | DB number + size suffix `X`(bit) / `B`(byte) / `W`(word) / `D`(dword), optional `.bit` for `DBX` |
|
||||
| Merker (M) | `MB0` / `MW0` / `MD4` / `M0.0` | Marker byte; size prefix `B`/`W`/`D`, or bare offset `.bit` for bit access |
|
||||
| Input (I) | `IB0` / `IW0` / `I0.0` | Process-image input |
|
||||
| Output (Q) | `QB0` / `QW0` / `Q0.0` | Process-image output |
|
||||
| Timer | `T0` / `T15` | Timer area (read-only — see Wide types section) |
|
||||
| Counter | `C0` / `C10` | Counter area (read-only — see Wide types section) |
|
||||
|
||||
Parsing is strict and runs once at `InitializeAsync` so a config typo fails fast
|
||||
at load instead of surfacing as `BadInternalError` on every read. Bit offsets
|
||||
must be 0-7, byte offsets non-negative, DB numbers >= 1.
|
||||
|
||||
> **Timer (`T{n}`) and Counter (`C{n}`)** addresses parse cleanly but the read
|
||||
> path has no decode case for them yet — the driver rejects them at init with an
|
||||
> explicit error rather than letting them surface a misleading type-mismatch.
|
||||
|
||||
## Data types
|
||||
|
||||
`S7DataType` declares the **semantic** type; `S7.Net` returns an unsigned boxed
|
||||
value (bool / byte / ushort / uint) that the driver reinterprets without an
|
||||
extra PLC round-trip. Wired through today: `Bool`, `Byte`, `Int16`, `UInt16`,
|
||||
`Int32`, `UInt32`, `Float32`. `Int64`, `UInt64`, `Float64`, `String`, and
|
||||
`DateTime` are declared in the enum but **rejected at init** — half-implemented
|
||||
types must not create OPC UA nodes that then return `BadNotSupported` on every
|
||||
access.
|
||||
extra PLC round-trip.
|
||||
|
||||
| DataType | S7 Type | Width | Read | Write |
|
||||
|----------|---------|-------|------|-------|
|
||||
| `Bool` | BOOL | 1 bit | yes | yes |
|
||||
| `Byte` | BYTE | 1 byte | yes | yes |
|
||||
| `Int16` | INT | 2 bytes | yes | yes |
|
||||
| `UInt16` | WORD | 2 bytes | yes | yes |
|
||||
| `Int32` | DINT | 4 bytes | yes | yes |
|
||||
| `UInt32` | DWORD | 4 bytes | yes | yes |
|
||||
| `Float32` | REAL | 4 bytes | yes | yes |
|
||||
| `Int64` | LINT | 8 bytes | yes | yes |
|
||||
| `UInt64` | ULINT/LWORD | 8 bytes | yes | yes |
|
||||
| `Float64` | LREAL | 8 bytes | yes | yes |
|
||||
| `String` | STRING | `StringLength + 2` bytes | yes | yes |
|
||||
| `DateTime` | DATE_AND_TIME | 8 bytes | yes | yes |
|
||||
| Timer (`T{n}`) | TIME | — | yes | **read-only** |
|
||||
| Counter (`C{n}`) | COUNTER | — | yes | **read-only** |
|
||||
|
||||
Wide types (`Int64`, `UInt64`, `Float64`, `String`, `DateTime`) and Timer/Counter require
|
||||
byte-anchored addressing — see the section below.
|
||||
|
||||
## Wide types & Timer/Counter
|
||||
|
||||
### Byte-anchored addressing for wide / structured types
|
||||
|
||||
Wide types (`Int64`, `UInt64`, `Float64`/LReal, `String`, `DateTime`) are
|
||||
**byte-anchored**: the address must use the `B` suffix pointing at the **first
|
||||
byte** of the value; the driver reads the correct number of contiguous bytes
|
||||
based on the DataType.
|
||||
|
||||
| DataType | Address form | Bytes read | Example |
|
||||
|----------|-------------|------------|---------|
|
||||
| `Int64` | `DB{n}.DBB{offset}` / `MB{offset}` / `IB{offset}` / `QB{offset}` | 8 | `DB1.DBB8` → LINT at bytes 8–15 |
|
||||
| `UInt64` | same | 8 | `DB2.DBB16` → ULINT at bytes 16–23 |
|
||||
| `Float64` | same | 8 | `DB1.DBB8` → LREAL at bytes 8–15 |
|
||||
| `DateTime` | same | 8 | `DB3.DBB0` → DATE_AND_TIME at bytes 0–7 |
|
||||
| `String` | same | `StringLength + 2` | `DB4.DBB0`, StringLength=40 → 42 bytes |
|
||||
|
||||
Using a `W` or `D` suffix with a wide DataType is a config error caught at
|
||||
`InitializeAsync`. For array tags (`isArray: true`), wide-type element arrays
|
||||
are deferred — see Deferrals below.
|
||||
|
||||
### Timer read
|
||||
|
||||
**Address**: `T{n}` (e.g. `T0`, `T15`). **DataType must be `Float64`**. The OPC UA node
|
||||
is a scalar `Float64` whose value is the timer's elapsed time in **seconds** (as a double).
|
||||
Timers are **read-only** this phase; a write attempt returns `BadNotWritable`.
|
||||
|
||||
### Counter read
|
||||
|
||||
**Address**: `C{n}` (e.g. `C0`, `C10`). **DataType must be `Int32`**. The OPC UA node is
|
||||
a scalar `Int32` whose value is the current **count** (raw word from the PLC).
|
||||
Counters are **read-only** this phase; a write attempt returns `BadNotWritable`.
|
||||
|
||||
> **Known limitation — Counter BCD encoding on S7-300/400**: `S7.Net`'s
|
||||
> `Counter.FromByteArray` returns the raw big-endian word without BCD decode.
|
||||
> On **classic S7-300/400** the C-area word is BCD-encoded (0–999), so the
|
||||
> surfaced value can differ from the actual count on that hardware. S7-1200/1500
|
||||
> use IEC/DB counters (plain integers) where the raw word is correct. BCD
|
||||
> reinterpretation for legacy C-area is a live-hardware-gated follow-up.
|
||||
|
||||
### Deferrals (not yet implemented)
|
||||
|
||||
The following are explicitly deferred and will produce a config error or
|
||||
`BadNotSupported` if attempted:
|
||||
|
||||
- **Wide-type arrays** — array tags (`isArray: true`) with element types `Int64`,
|
||||
`UInt64`, `Float64`, `String`, or `DateTime`.
|
||||
- **`S7WString`** (2-byte character strings; distinct from classic `STRING`).
|
||||
- **`DTL` / `DateTimeLong`** (12-byte Siemens date-and-time-long).
|
||||
- **Timer / Counter writes** — surfaced as `BadNotWritable` until write support lands.
|
||||
|
||||
## Capability surface
|
||||
|
||||
@@ -152,8 +217,9 @@ loops over the response buffer decoding each element individually using the same
|
||||
reinterpret/box logic as scalar reads. This keeps wire round-trips at 1 per array tag
|
||||
regardless of N.
|
||||
|
||||
**Supported element types** — any `S7DataType` that has a scalar decode path today
|
||||
(`Bool`, `Byte`, `Int16`, `UInt16`, `Int32`, `UInt32`, `Float32`). The `ReadBytesAsync`
|
||||
**Supported element types** — the narrow scalar types (`Bool`, `Byte`, `Int16`, `UInt16`,
|
||||
`Int32`, `UInt32`, `Float32`). Wide types (`Int64`, `UInt64`, `Float64`, `String`,
|
||||
`DateTime`) and Timer/Counter are deferred for array contexts. The `ReadBytesAsync`
|
||||
network half is a thin `S7.Net` call; the decode half is fully unit-tested.
|
||||
|
||||
**Unit test coverage** — `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/` covers
|
||||
@@ -162,9 +228,9 @@ normal dev (the S7 sim is on the shared Docker host but currently offline).
|
||||
|
||||
**Live-verify** — integration-fixture-gated (not Mac-verifiable without the S7 sim up).
|
||||
|
||||
**Deferrals** — array *writes*, multi-dimensional arrays, per-element historization. The
|
||||
`Timer` / `Counter` address types that are rejected at init for scalars are also excluded
|
||||
for arrays.
|
||||
**Deferrals** — array *writes*, multi-dimensional arrays, per-element historization,
|
||||
wide-type array elements (`Int64`, `UInt64`, `Float64`, `String`, `DateTime`), and
|
||||
Timer/Counter array elements.
|
||||
|
||||
See [Uns.md §Array tags](../Uns.md#array-tags-1-d) for the cross-driver coverage matrix
|
||||
and the UI authoring flow.
|
||||
|
||||
Reference in New Issue
Block a user