Files
lmxopcua/code-reviews/Driver.AbLegacy.Contracts/findings.md
T
Joseph Doherty ab57e53b92 fix(code-review): resolve Batch 2 open findings (AbCip, AbLegacy, Galaxy, FOCAS)
- Driver.AbCip.Contracts-001: parse 'writable' from TagConfig JSON (default true) instead of hardcoding
- Driver.AbCip.Contracts-002/-003: Dt type comment; drop dead [Display]/[Range] annotations
- Driver.AbCip.Contracts-004: dedicated AbCipEquipmentTagParser test class (+15)
- Driver.AbCip-017: document Tick severity Low-fallback on Bad severity read
- Driver.AbLegacy.Contracts-002/-003/-004: isArray-scalar remarks (+tests), MaxTagBytes/ForFamily docs
- Driver.Galaxy.Browser-003 + Driver.Galaxy.Contracts-003: extract ResolveApiKey -> GalaxySecretRef (dedup)
- Driver.Galaxy-019: cache buffered-interval only on Ok + ILogger warnings + ClassifyIntervalReply (+tests)
- Driver.FOCAS.Contracts-002: thread WriteIdempotent through DiscoverAsync (+test)
2026-06-20 22:43:36 -04:00

7.7 KiB

Code Review — Driver.AbLegacy.Contracts

Field Value
Module src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts
Reviewer Claude Code
Review date 2026-06-19
Commit reviewed a19b0f86
Status Reviewed
Open findings 0

Checklist coverage

A comprehensive review completes every category, recording "No issues found" where a category produced nothing rather than leaving it blank.

# Category Result
1 Correctness & logic bugs No issues found
2 OtOpcUa conventions 1 finding (Driver.AbLegacy.Contracts-003)
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 No issues found
8 Code organization & conventions 1 finding (Driver.AbLegacy.Contracts-004)
9 Testing coverage No issues found (contracts exercised by Driver.AbLegacy.Tests and AdminUI.Tests)
10 Documentation & comments 2 findings (Driver.AbLegacy.Contracts-001 fixed, Driver.AbLegacy.Contracts-002 open)

Findings

Driver.AbLegacy.Contracts-001

Field Value
Severity Low
Category Documentation & comments
Location src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts/AbLegacyDriverOptions.cs:48
Status Resolved

Description: The <param name="ArrayLength"> XML doc on AbLegacyTagDefinition stated <c>1</c> reads as a scalar. This was incorrect. Both the parser (AbLegacyEquipmentTagParser, line 44: if (rawLength >= 1)) and the driver (AbLegacyDriver.IsArrayTag, checks len >= 1) treat ArrayLength: 1 as a valid one-element array, exposing a [1] OPC UA array node -- not a scalar. The in-source comment at AbLegacyEquipmentTagParser.cs:36 correctly stated "A 1-element array (isArray:true, arrayLength:1) is a valid [1] array." The XML doc contradicted this.

Recommendation: Change the offending sentence from <c>1</c> reads as a scalar. to <c>1</c> is a valid one-element array exposed as a <c>[1]</c> OPC UA array node.

Resolution: Fixed in same review session, 2026-06-19. Changed the misleading sentence in AbLegacyDriverOptions.cs:48 to correctly describe ArrayLength 1 as a one-element array; verified by build (no test project in this module).


Driver.AbLegacy.Contracts-002

Field Value
Severity Low
Category Documentation & comments
Location src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts/AbLegacyEquipmentTagParser.cs:15
Status Resolved

Description: AbLegacyEquipmentTagParser.TryParse has an undocumented edge case: when isArray is the JSON literal true but arrayLength is absent, zero, or negative, ReadInt returns 0, the rawLength >= 1 guard fails, and arrayLength stays null -- producing a scalar tag despite isArray:true. This is intentional (the in-source comment at lines 33-39 references "review C-2"), but it is invisible from the public <summary> and <returns> XML docs, and there is no unit test covering this case. An operator who hand-edits the raw TagConfig JSON with {"isArray":true} and no arrayLength will silently get a scalar OPC UA node instead of the expected array shape.

Recommendation: Add a <remarks> element to the XML doc noting that isArray:true without a valid positive arrayLength silently produces a scalar (null ArrayLength). Optionally add a unit test to AbLegacyEquipmentTagTests covering isArray:true, arrayLength:0/absent -> null for regression protection.

Resolution: Resolved 2026-06-20. Added a <remarks> block to TryParse in AbLegacyEquipmentTagParser.cs documenting that isArray:true without a valid positive arrayLength silently produces a scalar (ArrayLength is null), referencing the in-source review C-2 rationale. Added three regression unit tests to AbLegacyEquipmentTagTests: IsArray_true_with_arrayLength_zero_produces_scalar, IsArray_true_with_no_arrayLength_produces_scalar, and IsArray_true_with_negative_arrayLength_produces_scalar. Suite: 199 passed, 0 failed.


Driver.AbLegacy.Contracts-003

Field Value
Severity Low
Category OtOpcUa conventions
Location src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts/AbLegacyPlcFamilyProfile.cs:10
Status Resolved

Description: AbLegacyPlcFamilyProfile.MaxTagBytes is a record constructor parameter populated with distinct values per family (240/232/240/240), but a global search finds zero call sites that read this property anywhere in the codebase -- not in AbLegacyDriver.cs, the probe, the CLI, or any test. The field is dead weight in the public contract surface. Keeping it may mislead future implementors into assuming it is enforced: a caller that adds array-size clamping logic on MaxTagBytes without knowing the values are approximate (e.g. SLC 5/05 240 is the PCCC packet payload cap, not a libplctag fragment limit) could produce off-by-one sizing. If reserved for future clamping, the intent is undocumented.

Recommendation: Either (a) add XML doc noting it is reserved for future array-length clamping and is not currently enforced, or (b) remove the field and its four initialisers in a dedicated cleanup PR (signature change is out of scope for this review).

Resolution: Resolved 2026-06-20. Applied option (a): added XML doc on the MaxTagBytes constructor parameter in AbLegacyPlcFamilyProfile.cs noting it is reserved for future array-length clamping, is NOT currently enforced anywhere in the driver, and that the values are approximate PCCC packet payload caps (not libplctag fragment limits). Field and initialisers unchanged.


Driver.AbLegacy.Contracts-004

Field Value
Severity Low
Category Code organization & conventions
Location src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Contracts/AbLegacyPlcFamilyProfile.cs:22
Status Resolved

Description: AbLegacyPlcFamilyProfile.ForFamily has a catch-all arm _ => Slc500 that silently returns the SLC 500 profile for any unrecognised AbLegacyPlcFamily value (e.g. an integer cast). The XML doc on ForFamily does not mention the fallback. A misconfigured device (or a future enum member added without updating the switch) will silently receive SLC 500 libplctag attributes (plc="slc500", path="1,0") and a MaxTagBytes of 240, which may produce libplctag session errors or wrong data only at runtime, far from where the enum value was set. The parallel FOCAS driver throws on unexpected values.

Recommendation: Either (a) document the silent fallback in the XML doc and note it is intentional (e.g. for forward-compatibility with configs authored before a new family was added), or (b) replace _ => Slc500 with _ => throw new ArgumentOutOfRangeException(nameof(family), family, null) and apply it in a cleanup PR. Semantic change -- defer.

Resolution: Resolved 2026-06-20. Applied option (a): added a <remarks> block to the ForFamily XML doc in AbLegacyPlcFamilyProfile.cs documenting that any unrecognised family value silently returns the Slc500 profile, and noting this is intentional for forward-compatibility with configs authored before a new family enum member is added. The switch body is unchanged.