v3(galaxy): re-key GalaxyDriver to RawPath identity (dial attributeRef)
Wave-B driver re-keying. Under v3 the server addresses Galaxy nodes by RawPath and the driver dials the Galaxy attributeRef (tag_name.AttributeName). The dialled address moved into TagConfig under the camelCase key `attributeRef` (renamed from the pre-v3 PascalCase FullName) and rides on RawTagEntry. Options + factory: - GalaxyDriverOptions gains `IReadOnlyList<RawTagEntry> RawTags` (Contracts now references the zero-dep Core.Abstractions leaf for RawTagEntry — same as Modbus.Contracts). Factory binds `List<RawTagEntry>? RawTags` from DriverConfig. Boundary translation (built once from options.RawTags): - RawPath -> attributeRef (forward dial) on Read / Write / Subscribe / Ack. - attributeRef -> RawPath (reverse) on the value fan-out (OnDataChange) and the alarm ConditionId — so the alarm resolves back to the same RawPath the value path surfaces. Both legs fall back to identity on a miss, so the live-browse discovery path (no authored tags) and synthetic alarm sub-refs (attributeRef.InAlarm) dial straight through unchanged. - WriteAsync dials the attributeRef but resolves security via the RawPath-keyed map the discoverer captured (reverse-maps in the resolver closure). Discovery + alarms: - GalaxyDiscoverer emits FullName = RawPath for authored attributes (falls back to attributeRef), threads RawTagEntry.WriteIdempotent onto DriverAttributeInfo, and gains an optional attributeRef -> RawTagEntry resolver ctor param. - AlarmRefBuilder.Build(conditionReference, dialReference): SourceName/ConditionId = RawPath identity; the five live-state/ack sub-refs dial off the attributeRef. ReinitializeAsync equivalence now compares the session-shape sections field-wise, excluding RawTags (address-space state re-applied via rediscovery, and a record's synthesized equality would compare the lists by reference). Tests: +9 (discoverer RawPath/WriteIdempotent + alarm identity; factory RawTags binding; driver read/write/subscribe/alarm boundary translation). Existing 304 preserved via identity fallback. 313 pass, 5 live-gated skips.
This commit is contained in:
@@ -79,6 +79,38 @@ public sealed class GalaxyDriverFactoryTests
|
||||
driver.Options.Reconnect.ReplayOnSessionLost.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the factory binds the v3 RawTags array from the driver config JSON.</summary>
|
||||
[Fact]
|
||||
public void CreateInstance_BindsRawTags_FromConfig()
|
||||
{
|
||||
const string configWithRawTags = """
|
||||
{
|
||||
"Gateway": { "Endpoint": "https://mxgw.test:5001", "ApiKeySecretRef": "galaxy:apiKey" },
|
||||
"MxAccess": { "ClientName": "OtOpcUa-A" },
|
||||
"RawTags": [
|
||||
{ "RawPath": "area/line/tank1/level", "TagConfig": "{\"attributeRef\":\"Tank1.Level\"}", "WriteIdempotent": true },
|
||||
{ "RawPath": "area/line/tank1/sp", "TagConfig": "{\"attributeRef\":\"Tank1.SP\"}", "WriteIdempotent": false }
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
var driver = GalaxyDriverFactoryExtensions.CreateInstance("galaxy-rawtags", configWithRawTags);
|
||||
|
||||
driver.Options.RawTags.Count.ShouldBe(2);
|
||||
driver.Options.RawTags[0].RawPath.ShouldBe("area/line/tank1/level");
|
||||
driver.Options.RawTags[0].TagConfig.ShouldContain("Tank1.Level");
|
||||
driver.Options.RawTags[0].WriteIdempotent.ShouldBeTrue();
|
||||
driver.Options.RawTags[1].WriteIdempotent.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that config without a RawTags array binds to an empty list (identity path).</summary>
|
||||
[Fact]
|
||||
public void CreateInstance_NoRawTags_DefaultsToEmpty()
|
||||
{
|
||||
var driver = GalaxyDriverFactoryExtensions.CreateInstance("galaxy-none", MinimalConfig);
|
||||
driver.Options.RawTags.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that missing endpoint throws an exception.</summary>
|
||||
[Fact]
|
||||
public void CreateInstance_MissingEndpoint_Throws()
|
||||
|
||||
Reference in New Issue
Block a user