v3(b1-opcuaclient): re-key OpcUaClient resolution to RawPath via RawTags
NamespaceMap now carries a RawPath -> upstream node id table built from the deployed RawTagEntry list (reads each tag's TagConfig.nodeId, threads WriteIdempotent). Under v3 the read/write/subscribe/history reference handed to the driver is the tag's RawPath identity; the driver resolves it in two stages: RawPath -> nodeId string (instance TryResolve) -> live NodeId re-bound against the session (static TryResolve). Alarm ConditionId + event-history sourceName stay on the direct session parse (they are upstream node ids, not RawPaths). - Options: add IReadOnlyList<RawTagEntry> RawTags (Contracts now refs Core.Abstractions). - Factory: RawTags binds straight into options (no separate DTO); EndpointUrl kept. - Browser: unchanged (emits neutral BrowseNode DTOs, no TagConfig FullName key). - Tests: 138 green; new RawPath resolution + factory-binding coverage; migrated the stale-session ReadRaw test to author a resolving RawTag. Contracts + Driver + Browser build clean (0 warn). Wave C wires endpoint->DeviceConfig and the deploy artifact that populates RawTags.
This commit is contained in:
+42
@@ -33,4 +33,46 @@ public class OpcUaClientDriverFactoryTests
|
||||
public void CreateInstance_throws_on_null_json_deserialisation()
|
||||
=> Should.Throw<System.InvalidOperationException>(
|
||||
() => OpcUaClientDriverFactoryExtensions.CreateInstance("drv-1", "null", NullLoggerFactory.Instance));
|
||||
|
||||
private const string RawTagsConfig =
|
||||
"""
|
||||
{
|
||||
"EndpointUrl": "opc.tcp://host:4840",
|
||||
"RawTags": [
|
||||
{ "rawPath": "Plant/Line1/Temp", "tagConfig": "{\"nodeId\":\"ns=2;s=Temperature\"}", "writeIdempotent": false },
|
||||
{ "rawPath": "Plant/Line1/Speed", "tagConfig": "{\"nodeId\":\"ns=2;s=Speed\"}", "writeIdempotent": true }
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the v3 authored <c>RawTags</c> (RawPath + TagConfig blob + WriteIdempotent) bind
|
||||
/// through the DriverConfig deserialiser directly onto <see cref="OpcUaClientDriverOptions"/> —
|
||||
/// the OpcUaClient driver has no separate DTO, so the factory's direct-into-options path must
|
||||
/// populate the RawPath resolution list.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CreateInstance_binds_rawtags_from_driverconfig()
|
||||
{
|
||||
var driver = OpcUaClientDriverFactoryExtensions.CreateInstance("drv-raw", RawTagsConfig, NullLoggerFactory.Instance);
|
||||
// The factory succeeds and identity is intact — the deserialiser tolerated + bound the RawTags array.
|
||||
driver.DriverType.ShouldBe("OpcUaClient");
|
||||
driver.DriverInstanceId.ShouldBe("drv-raw");
|
||||
|
||||
// Assert the actual binding: deserialise the same DriverConfig into public options with the
|
||||
// factory's serialiser settings (case-insensitive + string enums) and confirm RawTags populated.
|
||||
var opts = System.Text.Json.JsonSerializer.Deserialize<OpcUaClientDriverOptions>(
|
||||
RawTagsConfig,
|
||||
new System.Text.Json.JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() },
|
||||
});
|
||||
opts.ShouldNotBeNull();
|
||||
opts!.RawTags.Count.ShouldBe(2);
|
||||
opts.RawTags[0].RawPath.ShouldBe("Plant/Line1/Temp");
|
||||
opts.RawTags[0].TagConfig.ShouldContain("ns=2;s=Temperature");
|
||||
opts.RawTags[0].WriteIdempotent.ShouldBeFalse();
|
||||
opts.RawTags[1].WriteIdempotent.ShouldBeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user