Auto: s7-c2 — TSAP / Connection Type selector

Closes #295
This commit is contained in:
Joseph Doherty
2026-04-26 00:49:10 -04:00
parent bcf83bf39b
commit 3b98e4d366
7 changed files with 513 additions and 1 deletions

View File

@@ -573,6 +573,86 @@ S7 driver health without reaching for a Wireshark capture:
The values render alongside Modbus / OPC UA Client metrics in the Admin
UI driver-diagnostics panel — same RPC, same dashboard row layout.
## TSAP / Connection Type
S7comm runs on top of ISO-on-TCP (RFC 1006), and the COTP connection-request
PDU carries a 16-bit **TSAP pair** (local + remote) that the CPU validates
before any S7comm payload flows. S7netplus's default `Plc(CpuType, host, port,
rack, slot)` constructor picks a **PG-class** TSAP pair via
`TsapPair.GetDefaultTsapPair`. That choice works against most lab S7-1200 /
S7-1500 CPUs and against TIA Portal itself, but **hardened deployments**
(security-config'd S7-1500, ET 200SP, locked-down PROFINET projects) reject
PG class outright at COTP-handshake time, returning the same connection-refused
shape as a wrong slot byte.
PR-S7-C2 surfaces a `TsapMode` enum on `S7DriverOptions` so an operator can
force a specific class without re-flashing the PLC project. It applies equally
to the Admin-UI-driven config DB row and to the `otopcua-s7-cli` test client.
### Raw-TSAP byte table
The high byte is the connection class. The local low byte is conventionally
`0x00` (caller / unprivileged), and the remote low byte is
`(rack << 5) | slot` per the S7 spec — the same convention S7netplus's
`TsapPair.GetDefaultTsapPair(CpuType, rack, slot)` uses for the remote endpoint.
| Class | High byte | Local TSAP (rack=0/slot=0) | Remote TSAP (rack=0/slot=0) | Remote TSAP (rack=0/slot=2) | Typical use |
|----------|-----------|----------------------------|------------------------------|------------------------------|----------------------------------------------|
| PG | `0x01` | `0x0100` | `0x0100` | `0x0102` | TIA Portal, dev laptops, lab S7-1200/1500 |
| OP | `0x02` | `0x0200` | `0x0200` | `0x0202` | Operator panels, hardened-CPU S7-1500 |
| S7-Basic | `0x03` | `0x0300` | `0x0300` | `0x0302` | WinCC BasicPanel SDK, S7-Basic clients |
| Other | caller | caller-supplied | caller-supplied | caller-supplied | escape hatch — unusual fixed-TSAP firmware |
### `TsapMode` enum
| Mode | Behaviour |
|-----------|----------------------------------------------------------------------------------------------------------------------------------|
| `Auto` | Existing behaviour — S7netplus picks the TSAP pair from `CpuType`. Explicit `LocalTsap` / `RemoteTsap` are ignored under `Auto`. |
| `Pg` | Force PG class (high byte `0x01`). Local / remote computed from rack + slot. |
| `Op` | Force OP class (high byte `0x02`). |
| `S7Basic` | Force S7-Basic class (high byte `0x03`). |
| `Other` | Caller-supplied `LocalTsap` + `RemoteTsap`. Both must be set or driver init throws `InvalidOperationException`. |
Explicit `LocalTsap` / `RemoteTsap` overrides win over the class-derived
defaults under any non-`Auto` mode — a site that needs a fixed source-TSAP for
firewall reasons can pin `LocalTsap` while keeping `TsapMode = Pg` for the
remote computation.
### Worked example: hardened S7-1500 requiring OP class
```jsonc
{
"Host": "10.50.12.30",
"CpuType": "S71500",
"Rack": 0,
"Slot": 0,
"TsapMode": "Op",
"Tags": [ /* … */ ]
}
```
This produces local = `0x0200`, remote = `0x0200` (rack=0, slot=0). The same
PLC under `TsapMode = "Auto"` (PG class) returns COTP rejection — same packet
capture shape as a wrong-slot misconfig, which is the failure-mode footnote
under §5 of `driver-specs.md`.
### Why not just expose `LocalTsap` / `RemoteTsap` directly?
Most operators don't know the byte format off-hand and reach for `Pg` /
`Op` / `S7Basic` based on Siemens-doc terminology. Keeping the enum lets the
Admin UI render a dropdown with sensible labels, while the `ushort?` fields
stay available as the manual escape hatch when a site has truly unusual
firmware (e.g. third-party S7-protocol gateways with fixed proprietary
TSAPs). Both paths are exercised in the unit-test mapping table.
### Live-firmware verification
The PG/OP/S7-Basic byte table above is the documented Siemens convention; the
actual handshake is verified against the dev-box S7-1500 lab rig (a hardened
project that rejects PG and accepts OP). That test is documented in
`tests/ZB.MOM.WW.OtOpcUa.Driver.S7.IntegrationTests` but only runs against
real firmware — the pymodbus-style "TSAP simulator" doesn't exist for S7.
## References
1. Siemens Industry Online Support, *Modbus/TCP Communication between SIMATIC S7-1500 / S7-1200 and Modbus/TCP Controllers with Instructions `MB_CLIENT` and `MB_SERVER`*, Entry ID 102020340, V6 (Feb 2021). https://cache.industry.siemens.com/dl/files/340/102020340/att_118119/v6/net_modbus_tcp_s7-1500_s7-1200_en.pdf