review(Driver.AbCip): thread ElementCount/IsArray through factory tag DTOs

Cross-module fix from the review sweep. -018 (Medium): AbCipTagDto/AbCipMemberDto dropped
elementCount/isArray, so driver-config-authored array tags read as a single scalar. Added the
two optional JSON fields (additive; missing -> scalar as before) threaded into the tag def +
TDD.
This commit is contained in:
Joseph Doherty
2026-06-19 12:29:40 -04:00
parent 40749d3f67
commit 3e789dcafc
3 changed files with 190 additions and 3 deletions
+37 -1
View File
@@ -269,7 +269,7 @@ were spot-checked as still in force.
| # | Category | Result |
|---|---|---|
| 1 | Correctness & logic bugs | Driver.AbCip-016 |
| 1 | Correctness & logic bugs | Driver.AbCip-016, Driver.AbCip-018 |
| 2 | OtOpcUa conventions | No issues found |
| 3 | Concurrency & thread safety | No issues found |
| 4 | Error handling & resilience | No issues found |
@@ -356,3 +356,39 @@ severity read is Bad, or add an XML/inline comment on `Tick` stating that severi
`Low` when its read fails. Deferred (Open): the right fallback is a small alarm-semantics design
decision (what severity to surface when it is genuinely unknown) rather than a mechanical fix,
and the impact is negligible given the single-batch read shape.
### Driver.AbCip-018
| Field | Value |
|---|---|
| Severity | Medium |
| Category | Correctness & logic bugs |
| Location | `AbCipDriverFactoryExtensions.cs:89-109` (`BuildTag`), `AbCipDriverFactoryExtensions.cs:205-246` (`AbCipTagDto`), `AbCipDriverFactoryExtensions.cs:248-269` (`AbCipMemberDto`) |
| Status | Resolved |
**Description:** The driver-config factory path dropped the array shape of a pre-declared tag.
`AbCipTagDto` and `AbCipMemberDto` had no `ElementCount` / `IsArray` fields, so a `tags[]` entry
in the driver-config JSON authored with an explicit `"isArray": true, "elementCount": 4`
deserialised into a **scalar** `AbCipTagDefinition` (both fields silently dropped → defaulting to
`IsArray=false` / `ElementCount=1`). At read time `IsArrayTag(def)` was therefore `false`, so the
tag decoded via `DecodeValue` (single scalar) instead of the `DecodeArray` path — a declared
`REAL[4]` array tag read as one element. This is the factory/driver-config path; the
equipment-tag path (`AbCipEquipmentTagParser` in Driver.AbCip.Contracts) already threads array
shape correctly, and the in-driver UDT member fan-out was fixed under Driver.AbCip-016 — but the
factory `tags[]` array still lost the signal.
#### Recommendation
Add `ElementCount` (int?) and `IsArray` (bool) to `AbCipTagDto` and `AbCipMemberDto` with the
correct camelCase `[JsonPropertyName]` (`elementCount` / `isArray`), and thread them into the
`AbCipTagDefinition` / `AbCipStructureMember` construction in `BuildTag`, applying the
positive-value guard (`elementCount is > 0` ⇒ value, else `1`). Additive/optional — a config
without the fields keeps the legacy scalar default.
**Resolution:** Resolved 2026-06-19 — `AbCipTagDto` + `AbCipMemberDto` gained `IsArray` (bool,
`[JsonPropertyName("isArray")]`) and `ElementCount` (int?, `[JsonPropertyName("elementCount")]`);
`BuildTag` threads them into the `AbCipTagDefinition` and each fanned-out `AbCipStructureMember`
with the `m.ElementCount is > 0 ? m.ElementCount.Value : 1` guard (mirroring the equipment-tag
parser), so a driver-config array tag now reads as a typed array. Additive — missing fields still
produce a scalar. Regression: `AbCipFactoryArrayTagTests` (4 cases — top-level tag, UDT member,
absent-fields-stay-scalar, non-positive-elementCount-clamps-to-1). Full suite green (307 tests).