refactor(opcuaclient): remove vestigial TargetNamespaceKind + UnsMappingTable (#648)

Nothing read the mapping table beyond the startup validator; v3 UNS projection is
explicit via UnsTagReference. Legacy keys in stored DriverConfig are ignored (pinned).
Reverts the docker-dev opc-plc seed workaround from fdee129f.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 19:14:37 -04:00
parent 8801631739
commit dd05baedd6
16 changed files with 90 additions and 246 deletions
@@ -39,7 +39,6 @@ public sealed class OpcUaClientDriverPageFormSerializationTests
BrowseRoot = "i=85",
MaxDiscoveredNodes = 5000,
MaxBrowseDepth = 6,
TargetNamespaceKind = OpcUaTargetNamespaceKind.SystemPlatform,
ProbeTimeoutSeconds = 20,
};
@@ -64,7 +63,6 @@ public sealed class OpcUaClientDriverPageFormSerializationTests
back.BrowseRoot.ShouldBe("i=85");
back.MaxDiscoveredNodes.ShouldBe(5000);
back.MaxBrowseDepth.ShouldBe(6);
back.TargetNamespaceKind.ShouldBe(OpcUaTargetNamespaceKind.SystemPlatform);
back.ProbeTimeoutSeconds.ShouldBe(20);
}
@@ -86,16 +84,9 @@ public sealed class OpcUaClientDriverPageFormSerializationTests
[Fact]
public void FormModel_RoundTrip_PreservesAllFields()
{
// Construct options with non-default values for every editable property plus a
// non-empty UnsMappingTable (read-only in the form, round-tripped via the original
// record). EndpointUrls is now edited via the CollectionEditor on the page and is
// Construct options with non-default values for every editable property. EndpointUrls is now edited via the CollectionEditor on the page and is
// threaded into ToRecord explicitly; see EndpointUrls_ListRoundTrip_PreservesOrder.
var endpointUrls = new List<string> { "opc.tcp://primary:4840", "opc.tcp://backup:4840" };
var unsMappingTable = new Dictionary<string, string>
{
["Line1/"] = "Site/Area1/Line1",
["Line2/"] = "Site/Area1/Line2",
};
var original = new OpcUaClientDriverOptions
{
@@ -119,8 +110,6 @@ public sealed class OpcUaClientDriverPageFormSerializationTests
BrowseRoot = "i=85",
MaxDiscoveredNodes = 3000,
MaxBrowseDepth = 5,
TargetNamespaceKind = OpcUaTargetNamespaceKind.SystemPlatform,
UnsMappingTable = unsMappingTable,
ProbeTimeoutSeconds = 25,
};
@@ -149,10 +138,6 @@ public sealed class OpcUaClientDriverPageFormSerializationTests
result.BrowseRoot.ShouldBe("i=85");
result.MaxDiscoveredNodes.ShouldBe(3000);
result.MaxBrowseDepth.ShouldBe(5);
result.TargetNamespaceKind.ShouldBe(OpcUaTargetNamespaceKind.SystemPlatform);
result.UnsMappingTable.Count.ShouldBe(2);
result.UnsMappingTable["Line1/"].ShouldBe("Site/Area1/Line1");
result.UnsMappingTable["Line2/"].ShouldBe("Site/Area1/Line2");
result.ProbeTimeoutSeconds.ShouldBe(25);
}
@@ -199,6 +184,32 @@ public sealed class OpcUaClientDriverPageFormSerializationTests
def.DiscoveryUrl.ShouldBeNull();
}
/// <summary>
/// #648: stored DriverConfig JSON from before <c>targetNamespaceKind</c>/<c>unsMappingTable</c> were removed
/// must still load into the form (the form's options use <c>UnmappedMemberHandling.Skip</c>) and the legacy
/// keys must NOT be re-emitted on save.
/// </summary>
[Fact]
public void LegacyNamespaceKeys_LoadIntoForm_AndAreNotReEmitted()
{
var formOpts = new JsonSerializerOptions(_opts)
{
UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
Converters = { new JsonStringEnumConverter() },
};
const string legacy =
"""{"endpointUrl":"opc.tcp://plc:4840","targetNamespaceKind":"Equipment","unsMappingTable":{"Line1/":"Site/Area1/Line1"}}""";
var opts = JsonSerializer.Deserialize<OpcUaClientDriverOptions>(legacy, formOpts);
opts.ShouldNotBeNull();
opts.EndpointUrl.ShouldBe("opc.tcp://plc:4840");
var saved = JsonSerializer.Serialize(
OpcUaClientDriverForm.OpcUaClientFormModel.FromRecord(opts).ToRecord([]), formOpts);
saved.ShouldNotContain("targetNamespaceKind", Case.Insensitive);
saved.ShouldNotContain("unsMappingTable", Case.Insensitive);
}
[Fact]
public void EndpointUrlRow_FromUrl_ToUrl_Trims()
{