Browse-committed MTConnect tags coerce to String at runtime while their nodes are numerically typed (RawBrowseCommitMapper drops the browsed native type — #536 class) #560
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found by the 2026-08-10 review-coverage round — recorded as
Driver.MTConnect-003(High, Correctness) incode-reviews/Driver.MTConnect/findings.md, where the full analysis lives. Filed here because the fix belongs inZB.MOM.WW.OtOpcUa.AdminUI, outside the reviewed driver modules.Defect
Every browse-committed MTConnect tag is coerced to
Stringat runtime while its OPC UA node is typed from the same probe inference — a browsedSAMPLEpublishes text into aDouble/Int64node.RawBrowseCommitMapper.MapLeaf(src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/RawBrowseCommitMapper.cs:256) does two things with one browsed leaf:DataTypefrom the leaf's inferredDriverDataType(MapDataType:Float64→Double,Int64→Int64); andBuildTagConfig, whose MTConnect branch isnew MTConnectTagConfigModel { FullName = address }.ToJson().MTConnectTagConfigModel.DataTypedefaults toDriverDataType.StringandToJson()unconditionally writesdataType, so the committed blob is{"fullName":"dev1_pos","dataType":"String"}. That is a non-blank authored type, so it wins the driver's documented coercion precedence and step 3 — the Agent's own/probedeclaration viaMTConnectDataTypeInference— is never reached for a browse-committed tag.This is the #536 defect class: the AbCip, TwinCAT and FOCAS branches of
BuildTagConfigforward the browsed native type viaNativeType(addressFields, …)for exactly this reason. MTConnect got the typed-model branch without that forwarding.Why High: the address picker is the primary authoring path for this driver; the mistyping is silent (the blob is perfectly valid, so no authoring warning and no deploy-gate rejection); and the resulting node advertises a numeric type it will never carry.
Fix direction
In
RawBrowseCommitMapper, forward the browsed leaf'sDriverDataTypeinto the MTConnect branch as the other three typed-model branches already do —MapLeafalready holdsdriverDataType; either thread it intoBuildTagConfigalongsideaddressFields, or supply it throughaddressFieldsunder thedataTypekeyNativeTypealready reads.Test guidance from the review: add the MTConnect case to whatever pins the #536 behaviour for AbCip/TwinCAT/FOCAS, and assert the pair — the tag row's OPC UA type and the blob's coercion type agree — rather than either alone: asserting only the blob would pass against a mapper that had stopped setting the row's type.
Already corrected alongside (merged to master)
The driver-side comments that concealed this —
MTConnectDriver.BuildBinding's claim that the/probestep "is what makes a browse-committed tag work",MTConnectDataTypeInference's remark naming call sites that don't exist, anddocs/drivers/MTConnect.md— were rewritten at HEAD to state the live defect and point at the finding (Driver.MTConnect-008/Driver.MTConnect.Contracts-001). On close, also update the coercion-precedence warning block indocs/drivers/MTConnect.mdand the finding's Status/Resolution incode-reviews/Driver.MTConnect/findings.md(thenpython code-reviews/regen-readme.py).Fixed — merged to master as
5226f7fd(fix commits64be41d8+1c67d019).The fix, in
RawBrowseCommitMapperas this issue specified:MapLeafthreads itsdriverDataTypeintoBuildTagConfig, and the MTConnect branch now emitsDataType = NativeType(addressFields, ParseNativeType(driverDataType, DriverDataType.String)). A threaded parameter rather than a fabricatedaddressFieldsentry, deliberately: MTConnect's coercion type IS the driver-agnosticDriverDataTypethe browse leaf already carries (the driver never stampsNativeDataType, soNativeTypeFieldsemits nothing for it), and inventing an address field would contradictNativeType's documented "the type is READ, never derived" contract. A stated nativedataTypefield still wins where a browser supplies one, so the branch reads exactly like its AbCip/TwinCAT/FOCAS siblings — only the fallback differs (the leaf's inference instead of the fixed model default). The numeric-string guard and unparseable-name fallback were extracted into a sharedParseNativeTypeso both routes are judged by one rule.TDD, red first: the new pair theory failed against unfixed code with
dataType should be "Float64" but was "String"(same for Int64/Boolean), while the String-leaf case and the no-type-stated control passed — proving the fix changes exactly the mistyped cases. Per the finding's test guidance the assertions cover the pair (the committed row's OPC UA type AND the blob's coercion type agree with the browsed inference), not either alone.Verified on the merged tree: AdminUI.Tests 1,466 → 1,474 green (+8: 4-case pair theory,
BuildTagConfigforwarding test, String-default parity control, 2-case unusable-name fallback theory); Driver.MTConnect.Tests 538 green; full solution build clean.Docs followed the fix (they had been rewritten during the review round to document the defect as live at HEAD):
docs/drivers/MTConnect.md's coercion-precedence warning block,MTConnectDriver.BuildBinding's remark, and theMTConnectDataTypeInference/MTConnectDriverOptionsremarks now describe the fixed behaviour;code-reviews/Driver.MTConnect/findings.mdfindingDriver.MTConnect-003is Resolved (module open count 5 → 4, README regenerated).