review(Driver.S7): reject writable array tags at init instead of silent write failure

Re-review at 7286d320. S7-015 (Medium): a Writable array tag had no WriteArrayAsync path
and silently returned BadCommunicationError on write; now rejected at init with a clear
NotSupportedException (read-only arrays still accepted) + TDD. S7-016 (factory JSON can't
produce array tags; needs AdminUI DTO) deferred.
This commit is contained in:
Joseph Doherty
2026-06-19 11:34:34 -04:00
parent a914b73d57
commit f2bdd8bc1c
3 changed files with 128 additions and 2 deletions
+47 -2
View File
@@ -4,8 +4,8 @@
|---|---|
| Module | `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7` |
| Reviewer | Claude Code |
| Review date | 2026-05-22 |
| Commit reviewed | `76d35d1` |
| Review date | 2026-06-19 |
| Commit reviewed | `7286d320` |
| Status | Reviewed |
| Open findings | 0 |
@@ -27,6 +27,21 @@ a category produced nothing rather than leaving it blank.
| 9 | Testing coverage | Driver.S7-014 |
| 10 | Documentation & comments | Driver.S7-012 (shared) |
## Re-review 2026-06-19 (commit 7286d320)
| # | Category | Result |
|---|---|---|
| 1 | Correctness & logic bugs | Driver.S7-015 |
| 2 | OtOpcUa conventions | No issues found |
| 3 | Concurrency & thread safety | No issues found |
| 4 | Error handling & resilience | No issues found |
| 5 | Security | No issues found |
| 6 | Performance & resource management | No issues found |
| 7 | Design-document adherence | Driver.S7-016 |
| 8 | Code organization & conventions | No issues found |
| 9 | Testing coverage | No issues found |
| 10 | Documentation & comments | No issues found |
## Findings
### Driver.S7-001
@@ -450,3 +465,33 @@ live/mock-server happy-path coverage as an explicit follow-up rather than an
open-ended deferral.
**Resolution:** Resolved 2026-05-22 — factored `ReadOneAsync` type-reinterpret into `internal static ReinterpretRawValue` and `WriteOneAsync` boxing into `internal static BoxValueForWrite`; added `S7TypeMappingTests.cs` (26 tests) covering every implemented type round-trip (Bool/Byte/UInt16/Int16/UInt32/Int32/Float32), unsupported-type `NotSupportedException` assertions, and write overflow paths.
### Driver.S7-015
| Field | Value |
|---|---|
| Severity | Medium |
| Category | Correctness & logic bugs |
| Location | `S7Driver.cs``RejectUnsupportedTagConfigs`, `WriteOneAsync` |
| Status | Resolved |
**Description:** `WriteOneAsync` has no array write path. Array reads are fully implemented (`ReadArrayAsync` / `DecodeArrayBlock`), but `WriteOneAsync` has no symmetric `WriteArrayAsync` branch. When a writable array tag (e.g., `Int16[4]`) receives a write, `WriteOneAsync` falls through to `BoxValueForWrite(tag.DataType, value)` with the whole typed array as the value argument. `Convert.ToInt16(short[])` throws `InvalidCastException`, which the outer catch maps to `BadCommunicationError` — misleading to an operator who expects `BadNotSupported`. An init-time guard that rejects writable array tags existed only for wide-type arrays (guard-a in `RejectUnsupportedTagConfigs`); non-wide arrays (Int16[], UInt16[], Float32[], etc.) were accepted at init, created writable OPC UA nodes, and failed on every write with an uninformative status code.
**Recommendation:** Add init-time guard-(e) in `RejectUnsupportedTagConfigs` rejecting any `Writable=true` array tag (non-wide types). Also add a defensive `NotSupportedException` at the top of `WriteOneAsync` for transient equipment-tag refs that bypass init validation. Both paths correctly map to `BadNotSupported` via the existing catch ladder.
**Resolution:** Resolved 2026-06-19 (commit SHA blank) — added guard-(e) to `RejectUnsupportedTagConfigs` that throws `NotSupportedException` for any non-wide writable array tag; added a defensive `NotSupportedException` at the top of `WriteOneAsync` for transient equipment-tag refs. Regression tests: `Initialize_rejects_writable_array_tag_with_NotSupportedException` (Theory, 7 element types × non-wide path) and `Initialize_accepts_readonly_array_tag` (verifies read-only arrays still pass).
### Driver.S7-016
| Field | Value |
|---|---|
| Severity | Low |
| Category | Design-document adherence |
| Location | `S7DriverFactoryExtensions.cs``S7TagDto`, `BuildTag` |
| Status | Deferred |
**Description:** `S7TagDto` lacks `ArrayCount`/`IsArray` fields and `BuildTag` never passes `ArrayCount` to `S7TagDefinition`. As a result, tags authored via the standard driver JSON config (the factory path) can never be configured as arrays — `ArrayCount` is always null. Only equipment-tag refs resolved transiently by `S7EquipmentTagParser` can carry `ArrayCount`. The array infrastructure (read + init guards) is in place; the factory DTO simply doesn't surface the knob. A site that wants to configure a multi-element tag via the standard tag table has no way to do so.
**Recommendation:** Add `int? ArrayCount` and `bool? IsArray` to `S7TagDto` and propagate them through `BuildTag`. Mirror the `S7EquipmentTagParser` semantics (honour `arrayLength` only when `isArray` is true and the value is positive).
**Resolution:** Deferred — requires an AdminUI page/DTO change and a corresponding config-form field addition. Tracked as a follow-up to the array-read Phase 4d work. No code change in this review; the existing init guard-(e) correctly rejects any programmatically-constructed writable array reaching the driver until the DTO is wired.