Merge #610: server-side array writes gated per driver capability (DriverArrayWriteSupport); ArrayWritable on the surgical whitelist + UNS Backing; shape/IndexRange refusal; ValueRank>1 out of scope
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe # Conflicts: # docs/drivers/S7.md
This commit is contained in:
+3
-2
@@ -80,8 +80,9 @@ removed later.
|
||||
|
||||
Editing a raw **tag's** attributes is no longer a fleet-wide event. A **non-alarm**
|
||||
raw-tag delta whose only differences are the in-place-updatable attributes —
|
||||
`Writable`, `IsHistorized`, `HistorianTagname`, `DataType`, `IsArray`, `ArrayLength` —
|
||||
and/or its set of referencing equipment is applied **in place** on the existing Raw-realm
|
||||
`Writable`, `IsHistorized`, `HistorianTagname`, `DataType`, `IsArray`, `ArrayLength`,
|
||||
`ArrayWritable` (the driver's array-write capability, #610 — see
|
||||
[Uns.md §Array writes](Uns.md#array-writes-610)) — and/or its set of referencing equipment is applied **in place** on the existing Raw-realm
|
||||
node (`ISurgicalAddressSpaceSink.UpdateTagAttributes`), so every client subscription on
|
||||
that tag and on the rest of the server survives. The same is true of **every** UNS
|
||||
tag-reference change: adding, removing or re-pointing a reference used to flip the
|
||||
|
||||
+42
-2
@@ -312,10 +312,50 @@ Combined with historization (values are arrays — history of the whole array sn
|
||||
| **AB Legacy** | PCCC multi-element file read via libplctag (cap 256 elements) | Unit-proven (sim fixture down) |
|
||||
| **TwinCAT** | Raw-bytes read of the declared `SymbolPath` at the resolved array type's layout + per-element decode; native ADS notifications carry the whole array (same decode). Multi-dim PLC arrays publish flattened; a shorter `arrayLength` reads the leading sub-range | Live-verified 2026-10-07 on two TwinCAT 2.11 PLCs (see [TwinCAT.md §Array tags](drivers/TwinCAT.md#array-tags)) |
|
||||
|
||||
### Array writes (#610)
|
||||
|
||||
An authored-`ReadWrite` array tag is **writable only when its driver can write that array** —
|
||||
the server never advertises a write the driver will refuse. The capability is one table,
|
||||
`DriverArrayWriteSupport` (`Core.Abstractions`), consulted by the composer at compose time and
|
||||
carried on the plan records (`RawTagPlan.ArrayWritable`, mirrored onto every UNS reference as
|
||||
`UnsReferenceVariable.BackingArrayWritable`); the node is writable iff
|
||||
`Writable && (!IsArray || ArrayWritable)`, on the raw node and every UNS mirror alike.
|
||||
|
||||
| Driver | Array writes | Element types |
|
||||
|---|---|---|
|
||||
| **Modbus** | Yes | `region: Coils` (FC15, any element); `region: HoldingRegisters` with `Int16`/`UInt16`/`Bcd16`/`Int32`/`UInt32`/`Bcd32`/`Float32`/`Int64`/`UInt64`/`Float64` (FC16). Not BitInRegister / String / Bool-in-register; input regions are read-only |
|
||||
| **S7** | Yes | `Byte`/`Int16`/`UInt16`/`Int32`/`UInt32`/`Float32` (one contiguous block). Not `Bool` (would clobber neighbouring bits) nor the wide / String / DateTime types |
|
||||
| **OpcUaClient** | Yes | All — forwarded upstream unchanged; the upstream server decides |
|
||||
| **Galaxy** | Yes | OPC UA `Boolean`/`Int32`/`Int64`/`Float`/`Double`/`String`/`DateTime` (the `MxValueEncoder` array variants) |
|
||||
| AB CIP, AB Legacy, TwinCAT, FOCAS, MQTT, MTConnect, Sql, Calculation | **No** — scalar-only write encoders; array nodes stay read-only and a client write is `BadNotWritable` | — |
|
||||
|
||||
Each "Yes" row is pinned against the driver's real encoder by a parity test in that driver's
|
||||
test project (`*ArrayWriteSupportParityTests`), so a driver gaining or losing an array encoder
|
||||
fails a test until the table follows.
|
||||
|
||||
What a client sees on a writable array node:
|
||||
|
||||
- **Whole-array writes only.** The value must be a 1-D array of exactly `arrayLength` elements
|
||||
(an unbounded `arrayLength`/`ArrayDimensions = [0]` takes any length); anything else is
|
||||
`BadTypeMismatch`, refused inline before dispatch. A partial **IndexRange** write is
|
||||
`BadWriteNotSupported` — every array-capable driver writes the whole block, and the server
|
||||
does not hold the device array authoritatively enough to read-modify-write it.
|
||||
- The same realm-qualified `WriteOperate` / ACL gate as a scalar applies to both NodeIds, and a
|
||||
device-refused array write is reverted on the NodeId it was written through (the scalar
|
||||
write-outcome self-correction, unchanged).
|
||||
- Flipping a tag's array-write capability (a driver-type or element-type edit on an existing
|
||||
array tag) is a **surgical** in-place writability update, not a rebuild: `ArrayWritable` is on
|
||||
the classifier's raw-tag whitelist, and the mirrors repair through `BackingArrayWritable`.
|
||||
|
||||
### Out of scope (named deferrals)
|
||||
|
||||
- **Array writes** (inbound client→device write of an array value) — tagged for a follow-up phase. The S7 driver's array write path exists (`Byte`/`Int16`/`UInt16`/`Int32`/`UInt32`/`Float32`, not `Bool`; see [S7.md](drivers/S7.md)) but the applier still forces array nodes read-only (#610 item 1).
|
||||
- **Multi-dimensional arrays** (`ValueRank > 1`) — not supported; all arrays are 1-D.
|
||||
- **Multi-dimensional arrays** (`ValueRank > 1`) — **out of scope by decision** (#610 item 3),
|
||||
not merely deferred. Every driver's array read decodes a flat 1-D block (TwinCAT already
|
||||
publishes multi-dim PLC arrays flattened), and the node manager materialises every array
|
||||
`ValueRank = OneDimension` with a single `ArrayDimensions` entry. Supporting rank > 1 would
|
||||
need per-rank dimensions in TagConfig, the plan records, the classifier whitelist, the node
|
||||
manager and every driver's decode/encode, and no consumer has asked for it. Author a
|
||||
multi-dimensional PLC array as a flat 1-D tag (row-major), or as several 1-D tags.
|
||||
- **Array historization** — a historized array tag materialises with the correct `Historizing` flag, but the historian backend treats the value as an opaque blob; per-element history is out of scope. (Continuous historization is numeric-analog only — array / non-numeric values are not recorded.)
|
||||
|
||||
See the individual driver docs under `docs/drivers/` for per-driver implementation details.
|
||||
|
||||
+4
-3
@@ -255,12 +255,13 @@ read is byte-granular, so a matching write would overwrite the 7 neighbouring bi
|
||||
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
|
||||
**not atomic** — a mid-write failure can leave the PLC array partly updated.
|
||||
Note the *server* still presents every array node read-only to OPC UA clients ([Uns.md §Array
|
||||
tags](../Uns.md#array-tags-1-d), #610 item 1) — the driver path is reachable today through the Client
|
||||
The server exposes these as **writable OPC UA array nodes** (#610) for exactly the element types above —
|
||||
a `Bool` / wide-type S7 array stays read-only at the node (`BadNotWritable`), see [Uns.md §Array
|
||||
writes](../Uns.md#array-writes-610). The driver path is also reachable directly through the Client
|
||||
CLI / driver-level callers and is proven by the unit suite plus the snap7 fixture test
|
||||
(`S7_1500ArrayWriteTests`); the AdminUI tag editor rejects wide-type and writable-Bool arrays at author time.
|
||||
|
||||
**Deferrals** — server-side exposure of array writes, multi-dimensional arrays, per-element historization,
|
||||
**Deferrals** — multi-dimensional arrays (out of scope by decision, see Uns.md), per-element historization,
|
||||
wide-type array elements (`Int64`, `UInt64`, `Float64`, `String`, `DateTime`), and
|
||||
Timer/Counter array elements.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user