diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Composer.cs b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Composer.cs index ad2c67bf..a0189cab 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Composer.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Composer.cs @@ -335,43 +335,18 @@ public static class Phase7Composer .Select(a => new ScriptedAlarmPlan(a.ScriptedAlarmId, a.EquipmentId, a.PredicateScriptId, a.MessageTemplate)) .ToList(); - // SystemPlatform tags = Galaxy tags. Match each tag to its DriverInstance and that - // driver's Namespace; emit only when the namespace kind is SystemPlatform AND the tag - // has no EquipmentId (per the entity invariant for SystemPlatform). var driversById = driverInstances.ToDictionary(d => d.DriverInstanceId, StringComparer.Ordinal); var namespacesById = namespaces.ToDictionary(n => n.NamespaceId, StringComparer.Ordinal); - var galaxyTags = tags - .Where(t => t.EquipmentId is null) - .Where(t => driversById.TryGetValue(t.DriverInstanceId, out var di) - && namespacesById.TryGetValue(di.NamespaceId, out var ns) - && ns.Kind == NamespaceKind.SystemPlatform) - .OrderBy(t => t.DriverInstanceId, StringComparer.Ordinal) - .ThenBy(t => t.FolderPath, StringComparer.Ordinal) - .ThenBy(t => t.Name, StringComparer.Ordinal) - .Select(t => new GalaxyTagPlan( - TagId: t.TagId, - DriverInstanceId: t.DriverInstanceId, - FolderPath: t.FolderPath ?? string.Empty, - DisplayName: t.Name, - DataType: t.DataType, - // MXAccess reference: "FolderPath.Name" when FolderPath is set, else just "Name". - MxAccessRef: string.IsNullOrWhiteSpace(t.FolderPath) ? t.Name : $"{t.FolderPath}.{t.Name}")) - .ToList(); - - // Equipment tags = the inverse filter: a Tag bound to an Equipment (non-null EquipmentId) - // whose driver's namespace is Equipment-kind. FullName is the driver-side wire reference - // pulled from TagConfig — it becomes the variable's NodeId + read/write routing key. - // A Galaxy alias is the one exception to the Equipment-kind namespace rule: a GalaxyMxGateway - // driver lives in a SystemPlatform-kind namespace, yet an equipment-scoped alias Tag bound to - // it must still surface as an equipment tag — so admit those by DriverType. The galaxyTags - // producer keeps its `t.EquipmentId is null` guard, so an alias (EquipmentId set) never - // double-counts there. + // Equipment tags = a Tag bound to an Equipment (non-null EquipmentId) whose driver's namespace + // is Equipment-kind. FullName is the driver-side wire reference pulled from TagConfig — it + // becomes the variable's NodeId + read/write routing key. Galaxy points are ordinary equipment + // tags now (GalaxyMxGateway is a standard Equipment-kind driver), so no driver-type exception. var equipmentTags = tags .Where(t => t.EquipmentId is not null) .Where(t => driversById.TryGetValue(t.DriverInstanceId, out var di) && namespacesById.TryGetValue(di.NamespaceId, out var ns) - && (ns.Kind == NamespaceKind.Equipment || di.DriverType == "GalaxyMxGateway")) + && ns.Kind == NamespaceKind.Equipment) .OrderBy(t => t.EquipmentId, StringComparer.Ordinal) .ThenBy(t => t.FolderPath ?? string.Empty, StringComparer.Ordinal) // coalesce so the sort matches the artifact-decode side exactly .ThenBy(t => t.Name, StringComparer.Ordinal) @@ -465,7 +440,7 @@ public static class Phase7Composer Enabled: a.Enabled)); } - return new Phase7CompositionResult(areas, lines, nodes, plans, alarms, galaxyTags) + return new Phase7CompositionResult(areas, lines, nodes, plans, alarms, Array.Empty()) { EquipmentTags = equipmentTags, EquipmentVirtualTags = equipmentVirtualTags, diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ComposerAliasTagTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ComposerAliasTagTests.cs index 348f476d..f5ad9e28 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ComposerAliasTagTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/Phase7ComposerAliasTagTests.cs @@ -6,35 +6,36 @@ using ZB.MOM.WW.OtOpcUa.Configuration.Enums; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests; /// -/// Verifies the live-edit compose seam () admits a Galaxy -/// alias tag — an equipment-scoped (non-null ) -/// bound to a GalaxyMxGateway driver that lives in a SystemPlatform-kind namespace. -/// The broadened equipment-tag filter must surface the alias under -/// (carrying its driver-side FullName) while a -/// sibling SystemPlatform mirror tag (null EquipmentId) on the same driver stays in -/// and never double-counts. +/// Verifies the live-edit compose seam () treats a Galaxy +/// point as an ordinary equipment tag now that GalaxyMxGateway is a standard +/// Equipment-kind driver. An equipment-scoped (non-null +/// ) bound to a GalaxyMxGateway driver living in an +/// Equipment-kind namespace must surface under +/// (carrying its driver-side FullName), and +/// the retired SystemPlatform-mirror producer means +/// is always empty. /// public sealed class Phase7ComposerAliasTagTests { - /// A GalaxyMxGateway driver in a SystemPlatform namespace carries two tags: an - /// equipment-scoped alias (EquipmentId set, FolderPath null, TagConfig FullName = the Galaxy ref) - /// and a SystemPlatform mirror (EquipmentId null, FolderPath set). Compose must put the alias in - /// EquipmentTags with its FullName and keep the mirror in GalaxyTags only. + /// A GalaxyMxGateway driver in an Equipment-kind namespace carries an + /// equipment-scoped Galaxy tag (EquipmentId set, FolderPath null, TagConfig FullName = the Galaxy + /// ref). Compose must put it in EquipmentTags with its FullName, and GalaxyTags must be empty + /// (the SystemPlatform mirror producer is gone). [Fact] - public void Compose_admits_galaxy_alias_tag_in_equipment_tags() + public void Compose_admits_galaxy_equipment_tag_in_equipment_tags() { var ns = new Namespace { - NamespaceId = "ns-sp", + NamespaceId = "ns-eq", ClusterId = "c1", - Kind = NamespaceKind.SystemPlatform, - NamespaceUri = "urn:sp", + Kind = NamespaceKind.Equipment, + NamespaceUri = "urn:eq", }; var driver = new DriverInstance { DriverInstanceId = "drv-galaxy", ClusterId = "c1", - NamespaceId = "ns-sp", + NamespaceId = "ns-eq", Name = "Galaxy1", DriverType = "GalaxyMxGateway", DriverConfig = "{}", @@ -50,10 +51,10 @@ public sealed class Phase7ComposerAliasTagTests MachineCode = "TESTMACHINE_020", }; - // (a) Equipment-scoped alias: EquipmentId set, FolderPath null, FullName = Galaxy ref. - var aliasTag = new Tag + // Equipment-scoped Galaxy tag: EquipmentId set, FolderPath null, FullName = Galaxy ref. + var galaxyTag = new Tag { - TagId = "tag-alias", + TagId = "tag-galaxy", DriverInstanceId = "drv-galaxy", EquipmentId = "eq-1", FolderPath = null, @@ -63,36 +64,21 @@ public sealed class Phase7ComposerAliasTagTests TagConfig = "{\"FullName\":\"TestMachine_020.TestChangingInt\"}", }; - // (b) SystemPlatform mirror: EquipmentId null, FolderPath set → stays a Galaxy tag. - var mirrorTag = new Tag - { - TagId = "tag-mirror", - DriverInstanceId = "drv-galaxy", - EquipmentId = null, - FolderPath = "TestMachine_020", - Name = "Speed", - DataType = "Float", - AccessLevel = TagAccessLevel.Read, - TagConfig = "{\"FullName\":\"TestMachine_020.Speed\"}", - }; - var result = Phase7Composer.Compose( new[] { area }, new[] { line }, new[] { equip }, new[] { driver }, Array.Empty(), - new[] { aliasTag, mirrorTag }, new[] { ns }); + new[] { galaxyTag }, new[] { ns }); - // (a) the alias survives composition as an equipment tag, carrying its Galaxy FullName. - var alias = result.EquipmentTags.ShouldHaveSingleItem(); - alias.TagId.ShouldBe("tag-alias"); - alias.EquipmentId.ShouldBe("eq-1"); - alias.DriverInstanceId.ShouldBe("drv-galaxy"); - alias.Name.ShouldBe("TestChangingInt"); - alias.DataType.ShouldBe("Int32"); - alias.FullName.ShouldBe("TestMachine_020.TestChangingInt"); + // The Galaxy point survives composition as an equipment tag, carrying its Galaxy FullName. + var tag = result.EquipmentTags.ShouldHaveSingleItem(); + tag.TagId.ShouldBe("tag-galaxy"); + tag.EquipmentId.ShouldBe("eq-1"); + tag.DriverInstanceId.ShouldBe("drv-galaxy"); + tag.Name.ShouldBe("TestChangingInt"); + tag.DataType.ShouldBe("Int32"); + tag.FullName.ShouldBe("TestMachine_020.TestChangingInt"); - // (b) the SystemPlatform mirror stays in GalaxyTags and is NOT double-counted as equipment. - result.GalaxyTags.ShouldContain(g => g.TagId == "tag-mirror"); - result.GalaxyTags.ShouldNotContain(g => g.TagId == "tag-alias"); - result.EquipmentTags.ShouldNotContain(t => t.TagId == "tag-mirror"); + // The SystemPlatform-mirror producer is retired → GalaxyTags is always empty. + result.GalaxyTags.ShouldBeEmpty(); } }