diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Uns/GlobalUns.razor b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Uns/GlobalUns.razor index 666bc43a..2ca36cf3 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Uns/GlobalUns.razor +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Uns/GlobalUns.razor @@ -157,7 +157,7 @@ private bool _tagModalIsNew; private string? _tagModalEquipmentId; private TagEditDto? _tagModalExisting; - private IReadOnlyList<(string Id, string Display)> _tagModalDriverOptions = Array.Empty<(string, string)>(); + private IReadOnlyList<(string Id, string Display, string DriverType)> _tagModalDriverOptions = Array.Empty<(string, string, string)>(); // --- Virtual-tag modal state --- private bool _vtagModalVisible; @@ -613,7 +613,7 @@ _tagModalIsNew = false; _tagModalEquipmentId = null; _tagModalExisting = null; - _tagModalDriverOptions = Array.Empty<(string, string)>(); + _tagModalDriverOptions = Array.Empty<(string, string, string)>(); _vtagModalVisible = false; _vtagModalIsNew = false; _vtagModalEquipmentId = null; diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/TagModal.razor b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/TagModal.razor index c9c9d701..78a66422 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/TagModal.razor +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/TagModal.razor @@ -43,7 +43,7 @@ - @foreach (var (id, display) in Drivers) + @foreach (var (id, display, _) in Drivers) { } @@ -125,7 +125,7 @@ [Parameter] public TagEditDto? Existing { get; set; } /// The candidate drivers — scoped to the equipment's cluster by the host — as (Id, Display) pairs. - [Parameter] public IReadOnlyList<(string Id, string Display)> Drivers { get; set; } = Array.Empty<(string, string)>(); + [Parameter] public IReadOnlyList<(string Id, string Display, string DriverType)> Drivers { get; set; } = Array.Empty<(string, string, string)>(); /// Raised after a successful create/save so the host can refresh the equipment's children and close. [Parameter] public EventCallback OnSaved { get; set; } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IUnsTreeService.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IUnsTreeService.cs index d6361d21..d2151e86 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IUnsTreeService.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IUnsTreeService.cs @@ -316,8 +316,9 @@ public interface IUnsTreeService /// /// The equipment whose candidate drivers to load. /// A token to cancel the load. - /// The eligible drivers projected to (DriverInstanceId, Display) pairs. - Task> LoadTagDriversForEquipmentAsync(string equipmentId, CancellationToken ct = default); + /// The eligible drivers projected to (DriverInstanceId, Display, DriverType) triples, + /// where DriverType lets the TagModal dispatch to a per-driver-type typed config editor. + Task> LoadTagDriversForEquipmentAsync(string equipmentId, CancellationToken ct = default); /// /// Creates a new equipment-bound tag. FolderPath is always null (decision #110 — diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/UnsTreeService.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/UnsTreeService.cs index c50209ad..55bf08e0 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/UnsTreeService.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/UnsTreeService.cs @@ -704,7 +704,7 @@ public sealed class UnsTreeService(IDbContextFactory dbF } /// - public async Task> LoadTagDriversForEquipmentAsync( + public async Task> LoadTagDriversForEquipmentAsync( string equipmentId, CancellationToken ct = default) { @@ -713,7 +713,7 @@ public sealed class UnsTreeService(IDbContextFactory dbF var equipmentCluster = await ResolveEquipmentClusterAsync(db, equipmentId, ct); if (equipmentCluster is null) { - return Array.Empty<(string, string)>(); + return Array.Empty<(string, string, string)>(); } // Drivers in the equipment's cluster whose namespace is Equipment-kind (decision #110). @@ -725,11 +725,11 @@ public sealed class UnsTreeService(IDbContextFactory dbF var drivers = await db.DriverInstances .Where(d => d.ClusterId == equipmentCluster && equipmentNamespaceIds.Contains(d.NamespaceId)) .OrderBy(d => d.DriverInstanceId) - .Select(d => new { d.DriverInstanceId, d.Name }) + .Select(d => new { d.DriverInstanceId, d.Name, d.DriverType }) .ToListAsync(ct); return drivers - .Select(d => (d.DriverInstanceId, Display: $"{d.DriverInstanceId} — {d.Name}")) + .Select(d => (d.DriverInstanceId, Display: $"{d.DriverInstanceId} — {d.Name}", d.DriverType)) .ToList(); } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/UnsTreeServiceTagDriversTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/UnsTreeServiceTagDriversTests.cs new file mode 100644 index 00000000..11976c3d --- /dev/null +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/UnsTreeServiceTagDriversTests.cs @@ -0,0 +1,73 @@ +using Shouldly; +using Xunit; +using ZB.MOM.WW.OtOpcUa.AdminUI.Uns; +using ZB.MOM.WW.OtOpcUa.Configuration.Entities; +using ZB.MOM.WW.OtOpcUa.Configuration.Enums; + +namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns; + +/// +/// Verifies that surfaces each +/// candidate driver's DriverType alongside its id and display string, so the UNS TagModal +/// can later dispatch to a per-driver-type typed tag-config editor (F-uns-1). +/// +[Trait("Category", "Unit")] +public sealed class UnsTreeServiceTagDriversTests +{ + /// + /// A driver loaded for an equipment carries its DriverType in the returned tuple. + /// + [Fact] + public async Task LoadTagDriversForEquipment_surfaces_driver_type() + { + var dbName = $"uns-tagdrivers-{Guid.NewGuid():N}"; + + using (var db = UnsTreeTestDb.CreateNamed(dbName)) + { + db.ServerClusters.Add(new ServerCluster + { + ClusterId = "MAIN", + Name = "Main", + Enterprise = "zb", + Site = "warsaw-west", + RedundancyMode = RedundancyMode.None, + CreatedBy = "test", + }); + db.UnsAreas.Add(new UnsArea { UnsAreaId = "AREA-1", ClusterId = "MAIN", Name = "a" }); + db.UnsLines.Add(new UnsLine { UnsLineId = "LINE-1", UnsAreaId = "AREA-1", Name = "l" }); + db.Equipment.Add(new Equipment + { + EquipmentId = "EQ-1", + EquipmentUuid = Guid.NewGuid(), + UnsLineId = "LINE-1", + Name = "machine-1", + MachineCode = "machine_001", + }); + db.Namespaces.Add(new Namespace + { + NamespaceId = "NS-EQ", + ClusterId = "MAIN", + Kind = NamespaceKind.Equipment, + NamespaceUri = "urn:zb:eq", + }); + db.DriverInstances.Add(new DriverInstance + { + DriverInstanceId = "DRV-EQ", + ClusterId = "MAIN", + NamespaceId = "NS-EQ", + Name = "equipment driver", + DriverType = "ModbusTcp", + DriverConfig = "{}", + }); + db.SaveChanges(); + } + + var service = new UnsTreeService(UnsTreeTestDb.Factory(dbName)); + + var drivers = await service.LoadTagDriversForEquipmentAsync("EQ-1"); + + drivers.Count.ShouldBe(1); + drivers[0].DriverInstanceId.ShouldBe("DRV-EQ"); + drivers[0].DriverType.ShouldBe("ModbusTcp"); + } +}