fix(s7): refuse unservable tag shapes per tag at every gate (R07:H3, R07:U4)
The init-time tag guards THREW: one mis-authored tag (DB1.DBZ0, DB1.DBW0 typed Float64, T5 typed Int32, a writable Bool array) faulted the whole driver and the actor retried the same init every 10 s forever, darkening every tag on the PLC. Neither the editor nor the deploy gate checked any of it. - S7AddressParser moves to S7.Contracts (same namespace) and a new S7TagShapeRules there is the single authority on servable shapes. - S7TagDefinitionFactory.FromTagConfig applies it, so the AdminUI editor, the deploy gate (Inspect, with the specific reason) and the driver init refuse the same tags. Init now skips a refused tag (ISkippedTagReporter) instead of throwing; the throwing RejectUnsupportedTagConfigs is gone. - New rules on top of the old guards: a narrow type at the wrong access width (every read failed) and a Bool array not starting at bit 0 (U4: DB1.DBX0.3 silently read bit 0 of bytes 0..n). - AdminUI test fixtures that used addresses the driver could never serve (DB1.DBS0, a browse "SomeAddress", Int32/Float32 on DBW) now use real ones. Refs #742 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
+13
-4
@@ -103,9 +103,18 @@ Addresses use Siemens TIA-Portal / STEP 7 Classic syntax, parsed by
|
||||
| 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.
|
||||
Parsing is strict. Bit offsets must be 0-7, byte offsets non-negative, DB numbers >= 1.
|
||||
|
||||
**Tag shape rules are judged per tag, at every gate** (`S7TagShapeRules`, called by
|
||||
`S7TagDefinitionFactory.FromTagConfig`): the AdminUI editor refuses the tag, the deploy gate
|
||||
(`Inspect`) warns, and the driver's init **skips** it (reported through `ISkippedTagReporter`; it reads
|
||||
`BadNodeIdUnknown`) while every other tag keeps working. Refused: an address that does not parse, a
|
||||
wide/structured type that is an array or is not byte-addressed (`DBB`/`MB`/`IB`/`QB`), a narrow type at the
|
||||
wrong access width (`Int32` on `DBW0`, `Bool` on `DBB0`...), a Timer not typed `Float64` or a Counter not typed
|
||||
`Int32`, a writable or array Timer/Counter, a writable `Bool` array, and a `Bool` array that does not start
|
||||
at bit 0 (a Bool array reads bit 0 of each byte, so `DB1.DBX0.3` used to silently return bit 0 of bytes
|
||||
0..n). These used to be init guards that THREW, so one typo faulted every tag on the PLC and the actor
|
||||
retried the same init every 10 s forever (R07:H3).
|
||||
|
||||
## Data types
|
||||
|
||||
@@ -359,7 +368,7 @@ normal dev (the S7 sim is on the shared Docker host but currently offline).
|
||||
**Array writes (S7-016)** — a writable `Byte` / `Int16` / `UInt16` / `Int32` / `UInt32` / `Float32` array
|
||||
is written as ONE contiguous `WriteBytesAsync` of `count x element-bytes` (the inverse of the read
|
||||
decode), and the value must be an array of exactly `arrayLength` elements (else `BadTypeMismatch`, no
|
||||
health impact). **Writable `Bool` arrays are refused at init** (`NotSupportedException`): the array
|
||||
health impact). **Writable `Bool` arrays are refused per tag** (see the tag shape rules above): the array
|
||||
read is byte-granular, so a matching write would overwrite the 7 neighbouring bits of every byte it
|
||||
touches, and a safe bit-packed write needs read-modify-write. Mark Bool arrays `writable: false`. A `null` array element is rejected (`BadTypeMismatch`, nothing written)
|
||||
rather than coerced to 0. An array write larger than one PDU is split by S7.Net into several writes and is
|
||||
|
||||
Reference in New Issue
Block a user