@@ -34,6 +34,28 @@ Enable it in TIA Portal: *Device config → Protection & Security → Connection
|
||||
mechanisms → "Permit access with PUT/GET communication from remote partner"*.
|
||||
Without it the CLI's first read will surface `BadNotSupported`.
|
||||
|
||||
### Pre-flight PUT/GET enablement (PR-S7-C5)
|
||||
|
||||
The driver issues a tiny 2-byte read against `Probe.ProbeAddress` (default
|
||||
`MW0`) immediately after `OpenAsync` and **fails `InitializeAsync` with a
|
||||
typed `S7PutGetDisabledException`** when the PLC rejects the read with the
|
||||
wire-level "function not allowed" response. The exception message names the
|
||||
exact TIA Portal toggle to flip — operators see the configuration fix at
|
||||
init time, not after the first per-tag read produces `BadDeviceFailure`.
|
||||
|
||||
Two opt-out knobs on the JSON `Probe` block:
|
||||
|
||||
- `ProbeAddress` — set to `""` (empty string) to skip the pre-flight read
|
||||
entirely. Useful when no fingerprint address has been wired.
|
||||
- `SkipPreflight` — set to `true` to defer the check to runtime while
|
||||
keeping the background liveness loop. Per-tag reads still surface
|
||||
`BadDeviceFailure` until PUT/GET is enabled, but Init succeeds and the
|
||||
driver becomes visible in the Admin UI.
|
||||
|
||||
See [s7.md "Pre-flight PUT/GET enablement"](v2/s7.md#pre-flight-putget-enablement)
|
||||
for the full rationale, classifier behaviour, and the wire-level
|
||||
`ErrorCode` matching.
|
||||
|
||||
## S7 address grammar cheat sheet
|
||||
|
||||
| Form | Meaning |
|
||||
|
||||
@@ -113,6 +113,18 @@ arrays of structs — not covered.
|
||||
lab rig but not CI.
|
||||
3. **Real S7 lab rig** — cheapest physical PLC (CPU 1212C) on a dedicated
|
||||
network port, wired via self-hosted runner.
|
||||
4. **PR-S7-C5 — PUT/GET-disabled pre-flight rejection.** Snap7 does *not*
|
||||
model the hardened-CPU PUT/GET response (it accepts every read once the
|
||||
COTP handshake completes), so the **failure** path of the pre-flight
|
||||
probe — `S7PutGetDisabledException` thrown from `InitializeAsync` when
|
||||
the PLC rejects the probe read with `ErrorCode.WrongCPU_Type` /
|
||||
`ErrorCode.ReadData` — needs a real S7-1500 with PUT/GET disabled in TIA
|
||||
Portal. The integration suite covers the *happy* path
|
||||
(`Driver_preflight_passes_when_probe_address_seeded`); the failure path
|
||||
should be added as a `--with-real-plc` opt-in test that the self-hosted
|
||||
runner with the lab rig executes. The classifier branch
|
||||
(`S7PreflightClassifier.IsPutGetDisabled`) is unit-tested without a
|
||||
network in `S7PreflightTests.Classifier_matches_only_PUT_GET_disabled_error_codes`.
|
||||
|
||||
Without any of these, S7 driver correctness against real hardware is trusted
|
||||
from field deployments, not from the test suite.
|
||||
|
||||
@@ -768,6 +768,98 @@ is satisfied.
|
||||
whether to invoke `OnDataChange`. The mailbox / PDU / coalescing path
|
||||
is untouched.
|
||||
|
||||
## Pre-flight PUT/GET enablement
|
||||
|
||||
S7-1200 / S7-1500 CPUs ship with **PUT/GET communication disabled by
|
||||
default**. The COTP / S7comm handshake itself succeeds against these
|
||||
locked-down CPUs (you can `OpenAsync` / negotiate PDU size cleanly), so
|
||||
the failure surfaces only on the *first* `Plc.ReadAsync` — at which
|
||||
point the driver is already past `InitializeAsync`, has flipped to
|
||||
`DriverState.Healthy`, and dependent code (subscriptions, Admin UI) is
|
||||
binding against a connection it can't actually use. Operators see
|
||||
`BadDeviceFailure` per tag instead of a single, actionable
|
||||
configuration error.
|
||||
|
||||
PR-S7-C5 adds a **post-`OpenAsync` pre-flight probe**: a tiny 2-byte
|
||||
read against `Probe.ProbeAddress` (default `MW0`). If the PLC rejects
|
||||
that read with the wire-level "function not allowed in current
|
||||
operating state" response (S7 error family `D6 05` / `85 00`),
|
||||
S7netplus surfaces the rejection as `PlcException` with one of
|
||||
`ErrorCode.WrongCPU_Type` (CPU drops the connection mid-response) or
|
||||
`ErrorCode.ReadData` (CPU sends an S7-level error byte). The driver
|
||||
classifies that pair as "PUT/GET disabled" and throws a typed
|
||||
`S7PutGetDisabledException` from `InitializeAsync` so the operator sees
|
||||
the TIA-Portal fix path immediately:
|
||||
|
||||
> PUT/GET communication is disabled on the PLC. Enable it in TIA Portal:
|
||||
> *Device → Properties → Protection & Security → Connection mechanisms →
|
||||
> "Permit access with PUT/GET communication from remote partner"*.
|
||||
> Re-deploy the hardware config and restart the S7 driver.
|
||||
|
||||
`S7PreflightClassifier.IsPutGetDisabled(PlcException)` is the pure
|
||||
function that decides whether a given `PlcException` qualifies; it
|
||||
matches **only** `WrongCPU_Type` and `ReadData`. Other error codes
|
||||
(`ConnectionError`, `IPAddressNotAvailable`, `WrongVarFormat`, …)
|
||||
indicate transport / framing faults rather than PUT/GET gating, so the
|
||||
driver re-throws the original `PlcException` unchanged and the existing
|
||||
`DriverState.Faulted` path takes over with the original message.
|
||||
|
||||
### Knobs
|
||||
|
||||
Two opt-out knobs on `S7ProbeOptions`:
|
||||
|
||||
- `ProbeAddress` (`string?`, default `"MW0"`) — address probed by both
|
||||
the background liveness loop and the pre-flight read. Set to `null`
|
||||
(or empty string in JSON) to skip the pre-flight entirely. Useful
|
||||
for sites where no fingerprint address has been wired and an arbitrary
|
||||
read at `MW0` would itself be misleading.
|
||||
- `SkipPreflight` (`bool`, default `false`) — opt out of the pre-flight
|
||||
read while keeping the background probe. Init succeeds against a
|
||||
PUT/GET-disabled CPU; per-tag reads still surface `BadDeviceFailure`
|
||||
at runtime. Useful for staged deployments where the operator hasn't
|
||||
enabled PUT/GET yet but wants the driver visible in the Admin UI.
|
||||
|
||||
### Why `MW0`?
|
||||
|
||||
The convention from `Driver.S7.Cli.md`'s `probe` command. `MW0` exists
|
||||
on every S7 CPU regardless of project — Merker memory is universal —
|
||||
so it's a safe default that doesn't require a per-site DB to be wired.
|
||||
Sites with a dedicated fingerprint DB can override to e.g.
|
||||
`DB1.DBW0`.
|
||||
|
||||
### JSON config example
|
||||
|
||||
```json
|
||||
{
|
||||
"Host": "10.0.0.50",
|
||||
"Probe": {
|
||||
"Enabled": true,
|
||||
"IntervalMs": 5000,
|
||||
"TimeoutMs": 2000,
|
||||
"ProbeAddress": "DB1.DBW0",
|
||||
"SkipPreflight": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To skip the pre-flight (defer the check to first read):
|
||||
|
||||
```json
|
||||
{
|
||||
"Host": "10.0.0.50",
|
||||
"Probe": { "SkipPreflight": true }
|
||||
}
|
||||
```
|
||||
|
||||
To skip the probe entirely (no pre-flight, no liveness loop):
|
||||
|
||||
```json
|
||||
{
|
||||
"Host": "10.0.0.50",
|
||||
"Probe": { "Enabled": false, "ProbeAddress": "" }
|
||||
}
|
||||
```
|
||||
|
||||
## TSAP / Connection Type
|
||||
|
||||
S7comm runs on top of ISO-on-TCP (RFC 1006), and the COTP connection-request
|
||||
|
||||
Reference in New Issue
Block a user