feat(s7): STRING/WSTRING/CHAR/WCHAR and DTL/DATE/TOD/TIME/S5TIME codecs, TSAP/connection-type override, negotiated PDU surfaced (#632 #288 #289 #294 #295)
New S7DataType members WString/Char/WChar/Dtl/Date/Tod/Time/S5Time mapped onto existing DriverDataType (String / DateTime / Int32 ms); writes refuse out-of-range with BadOutOfRange. Optional localTsap/remoteTsap/connectionType through options, factory JSON, probe and AdminUI form. NegotiatedPduSize snapshotted at connect and exposed via GetDiagnostics (DriverHealth has no bag). Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+65
-8
@@ -65,6 +65,23 @@ hardware. A wrong slot causes a connection refusal during the handshake. See
|
||||
`src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts/S7DriverOptions.cs` for the
|
||||
per-field defaults.
|
||||
|
||||
### TSAP / connection-type override
|
||||
|
||||
By default S7.Net derives both ISO-TSAPs from `CpuType` / `Rack` / `Slot` (local `01.00`; remote
|
||||
`03.<rack×0x20+slot>` for the S7-300/400/1200/1500 families). Three optional keys override that
|
||||
for CPUs and gateways that hand out connection resources by TSAP (S7-400H, CP343, LOGO!):
|
||||
|
||||
| Key | Form | Effect |
|
||||
|---|---|---|
|
||||
| `localTsap` | `"01.00"` (also `"0100"`, `"0x0100"`, or a 16-bit number) | replaces the local TSAP |
|
||||
| `remoteTsap` | `"03.01"` (same forms) | replaces the whole remote TSAP |
|
||||
| `connectionType` | `"PG"` \| `"OP"` \| `"S7Basic"` (string) | writes `1` / `2` / `3` into the **first** remote-TSAP byte, keeping `rack×0x20+slot` as the second |
|
||||
|
||||
`remoteTsap` and `connectionType` are mutually exclusive (a config carrying both fails to load); a
|
||||
malformed TSAP fails to load rather than silently falling back to the slot-derived default. With none of
|
||||
the three set the connection is built exactly as before. The override applies to the driver, the AdminUI
|
||||
Test Connect probe and the AdminUI S7 driver form (Connection type / Local TSAP / Remote TSAP fields).
|
||||
|
||||
## Address forms
|
||||
|
||||
Addresses use Siemens TIA-Portal / STEP 7 Classic syntax, parsed by
|
||||
@@ -102,19 +119,50 @@ extra PLC round-trip.
|
||||
| `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 |
|
||||
| `WString` | WSTRING | `4 + 2 × StringLength` bytes | yes | yes |
|
||||
| `Char` | CHAR | 1 byte | yes | yes |
|
||||
| `WChar` | WCHAR | 2 bytes | yes | yes |
|
||||
| `DateTime` | DATE_AND_TIME (DT) | 8 bytes (BCD) | yes | yes |
|
||||
| `Dtl` | DTL | 12 bytes | yes | yes |
|
||||
| `Date` | DATE | 2 bytes | yes | yes |
|
||||
| `Tod` | TIME_OF_DAY | 4 bytes | yes | yes |
|
||||
| `Time` | TIME | 4 bytes | yes | yes |
|
||||
| `S5Time` | S5TIME | 2 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 (`Int64`, `UInt64`, `Float64`, `String`, `DateTime`, and the string / date-time
|
||||
types below) and Timer/Counter require byte-anchored addressing — see the section below.
|
||||
|
||||
### String, character and date/time/duration types
|
||||
|
||||
These add no new OPC UA data types — they map onto existing ones:
|
||||
|
||||
| DataType | OPC UA type | Representation | Write refuses (`BadOutOfRange`, nothing sent) |
|
||||
|---|---|---|---|
|
||||
| `String` | String | S7 `[maxLen][curLen][chars]`, ASCII | over `StringLength` (S7.Net behaviour) |
|
||||
| `WString` | String | `[maxLen UInt16][curLen UInt16][UTF-16BE]`; `stringLength` is the declared max in characters, **0–16382** (default 254) | more UTF-16 code units than `StringLength` |
|
||||
| `Char` | String (1 char) | 1 byte, Latin-1 | a character above U+00FF |
|
||||
| `WChar` | String (1 char) | UTF-16BE code unit | — (a string whose length is not 1 is `BadTypeMismatch`) |
|
||||
| `DateTime` | DateTime | DT, 8-byte BCD (S7.Net codec; 1990–2089) | out-of-range year |
|
||||
| `Dtl` | DateTime | year UInt16 + month/day/weekday/h/m/s + UInt32 nanoseconds; 100 ns resolution | year outside 1970–2262 |
|
||||
| `Date` | DateTime (midnight) | UInt16 days since 1990-01-01 (max 2169-06-06) | before 1990-01-01 or after 2169-06-06 |
|
||||
| `Tod` | **Int32 ms** since midnight | UInt32 ms | outside 0–86 399 999 |
|
||||
| `Time` | **Int32 ms** (signed duration) | Int32 ms | outside the Int32 range |
|
||||
| `S5Time` | **Int32 ms** | 2-bit time base (10 ms / 100 ms / 1 s / 10 s) + 3 BCD digits | above 9 990 000 ms, negative, or not exactly representable (resolution is 10 ms up to 9 990 ms, then coarser) — **never rounded** |
|
||||
|
||||
A write of a `TimeSpan` is also accepted for `Tod` / `Time` / `S5Time`. A PLC buffer the codec cannot
|
||||
decode — an uninitialised (all-zero) `Dtl`, a non-BCD `S5Time` digit, a `WString` whose current length
|
||||
exceeds its reserved length — reads `BadOutOfRange` for that tag only, without degrading driver health
|
||||
or condemning the connection. `S5Time` writes pick the finest time base that holds the value. The
|
||||
`Timer` area (`T{n}`) keeps its own S5TIME-to-**seconds** (`Float64`) decode.
|
||||
|
||||
## 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
|
||||
Wide types (`Int64`, `UInt64`, `Float64`/LReal, `String`, `DateTime`, `WString`, `Char`,
|
||||
`WChar`, `Dtl`, `Date`, `Tod`, `Time`, `S5Time`) 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.
|
||||
|
||||
@@ -125,6 +173,8 @@ based on the DataType.
|
||||
| `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 |
|
||||
| `WString` | same | `4 + 2 × StringLength` | `DB5.DBB0`, StringLength=10 → 24 bytes |
|
||||
| `Dtl` | same | 12 | `DB6.DBB0` → DTL at bytes 0–11 |
|
||||
|
||||
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
|
||||
@@ -155,11 +205,18 @@ 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).
|
||||
`UInt64`, `Float64`, `String`, `DateTime`, or any of the string / date-time types above.
|
||||
- **Timer / Counter writes** — surfaced as `BadNotWritable` until write support lands.
|
||||
|
||||
## Negotiated PDU size
|
||||
|
||||
The PDU size the CPU negotiates during the connection handshake (typically 240 bytes; 480 / 960 with
|
||||
extended PDU) is snapshotted at every (re)connect and exposed as `S7Driver.NegotiatedPduSize` /
|
||||
`S7Driver.GetDiagnostics()["S7.NegotiatedPduSize"]` (0 before the first connect and after shutdown). It is
|
||||
also written to the `S7Driver connected` / `reconnected` log lines and to the AdminUI Test Connect
|
||||
message (`S7 connected (CPU S71500, PDU 240 bytes)`). `DriverHealth` has no diagnostics bag in this
|
||||
codebase, so the value is not yet on the `/hosts` health snapshot — adding one is a Core change.
|
||||
|
||||
## Capability surface
|
||||
|
||||
`S7Driver : IDriver, ITagDiscovery, IReadable, IWritable, ISubscribable, IHostConnectivityProbe`
|
||||
|
||||
Reference in New Issue
Block a user