feat(uns): surface DriverType to the TagModal driver dropdown (F-uns-1 T1)
This commit is contained in:
@@ -157,7 +157,7 @@
|
|||||||
private bool _tagModalIsNew;
|
private bool _tagModalIsNew;
|
||||||
private string? _tagModalEquipmentId;
|
private string? _tagModalEquipmentId;
|
||||||
private TagEditDto? _tagModalExisting;
|
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 ---
|
// --- Virtual-tag modal state ---
|
||||||
private bool _vtagModalVisible;
|
private bool _vtagModalVisible;
|
||||||
@@ -613,7 +613,7 @@
|
|||||||
_tagModalIsNew = false;
|
_tagModalIsNew = false;
|
||||||
_tagModalEquipmentId = null;
|
_tagModalEquipmentId = null;
|
||||||
_tagModalExisting = null;
|
_tagModalExisting = null;
|
||||||
_tagModalDriverOptions = Array.Empty<(string, string)>();
|
_tagModalDriverOptions = Array.Empty<(string, string, string)>();
|
||||||
_vtagModalVisible = false;
|
_vtagModalVisible = false;
|
||||||
_vtagModalIsNew = false;
|
_vtagModalIsNew = false;
|
||||||
_vtagModalEquipmentId = null;
|
_vtagModalEquipmentId = null;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
<label class="form-label" for="tag-driver">Driver instance</label>
|
<label class="form-label" for="tag-driver">Driver instance</label>
|
||||||
<InputSelect id="tag-driver" @bind-Value="_form.DriverInstanceId" class="form-select form-select-sm">
|
<InputSelect id="tag-driver" @bind-Value="_form.DriverInstanceId" class="form-select form-select-sm">
|
||||||
<option value="">— pick a driver —</option>
|
<option value="">— pick a driver —</option>
|
||||||
@foreach (var (id, display) in Drivers)
|
@foreach (var (id, display, _) in Drivers)
|
||||||
{
|
{
|
||||||
<option value="@id">@display</option>
|
<option value="@id">@display</option>
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
[Parameter] public TagEditDto? Existing { get; set; }
|
[Parameter] public TagEditDto? Existing { get; set; }
|
||||||
|
|
||||||
/// <summary>The candidate drivers — scoped to the equipment's cluster by the host — as <c>(Id, Display)</c> pairs.</summary>
|
/// <summary>The candidate drivers — scoped to the equipment's cluster by the host — as <c>(Id, Display)</c> pairs.</summary>
|
||||||
[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)>();
|
||||||
|
|
||||||
/// <summary>Raised after a successful create/save so the host can refresh the equipment's children and close.</summary>
|
/// <summary>Raised after a successful create/save so the host can refresh the equipment's children and close.</summary>
|
||||||
[Parameter] public EventCallback OnSaved { get; set; }
|
[Parameter] public EventCallback OnSaved { get; set; }
|
||||||
|
|||||||
@@ -316,8 +316,9 @@ public interface IUnsTreeService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="equipmentId">The equipment whose candidate drivers to load.</param>
|
/// <param name="equipmentId">The equipment whose candidate drivers to load.</param>
|
||||||
/// <param name="ct">A token to cancel the load.</param>
|
/// <param name="ct">A token to cancel the load.</param>
|
||||||
/// <returns>The eligible drivers projected to <c>(DriverInstanceId, Display)</c> pairs.</returns>
|
/// <returns>The eligible drivers projected to <c>(DriverInstanceId, Display, DriverType)</c> triples,
|
||||||
Task<IReadOnlyList<(string DriverInstanceId, string Display)>> LoadTagDriversForEquipmentAsync(string equipmentId, CancellationToken ct = default);
|
/// where <c>DriverType</c> lets the TagModal dispatch to a per-driver-type typed config editor.</returns>
|
||||||
|
Task<IReadOnlyList<(string DriverInstanceId, string Display, string DriverType)>> LoadTagDriversForEquipmentAsync(string equipmentId, CancellationToken ct = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new equipment-bound tag. <c>FolderPath</c> is always <c>null</c> (decision #110 —
|
/// Creates a new equipment-bound tag. <c>FolderPath</c> is always <c>null</c> (decision #110 —
|
||||||
|
|||||||
@@ -704,7 +704,7 @@ public sealed class UnsTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<IReadOnlyList<(string DriverInstanceId, string Display)>> LoadTagDriversForEquipmentAsync(
|
public async Task<IReadOnlyList<(string DriverInstanceId, string Display, string DriverType)>> LoadTagDriversForEquipmentAsync(
|
||||||
string equipmentId,
|
string equipmentId,
|
||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
@@ -713,7 +713,7 @@ public sealed class UnsTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
|||||||
var equipmentCluster = await ResolveEquipmentClusterAsync(db, equipmentId, ct);
|
var equipmentCluster = await ResolveEquipmentClusterAsync(db, equipmentId, ct);
|
||||||
if (equipmentCluster is null)
|
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).
|
// Drivers in the equipment's cluster whose namespace is Equipment-kind (decision #110).
|
||||||
@@ -725,11 +725,11 @@ public sealed class UnsTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
|||||||
var drivers = await db.DriverInstances
|
var drivers = await db.DriverInstances
|
||||||
.Where(d => d.ClusterId == equipmentCluster && equipmentNamespaceIds.Contains(d.NamespaceId))
|
.Where(d => d.ClusterId == equipmentCluster && equipmentNamespaceIds.Contains(d.NamespaceId))
|
||||||
.OrderBy(d => d.DriverInstanceId)
|
.OrderBy(d => d.DriverInstanceId)
|
||||||
.Select(d => new { d.DriverInstanceId, d.Name })
|
.Select(d => new { d.DriverInstanceId, d.Name, d.DriverType })
|
||||||
.ToListAsync(ct);
|
.ToListAsync(ct);
|
||||||
|
|
||||||
return drivers
|
return drivers
|
||||||
.Select(d => (d.DriverInstanceId, Display: $"{d.DriverInstanceId} — {d.Name}"))
|
.Select(d => (d.DriverInstanceId, Display: $"{d.DriverInstanceId} — {d.Name}", d.DriverType))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies that <see cref="UnsTreeService.LoadTagDriversForEquipmentAsync"/> surfaces each
|
||||||
|
/// candidate driver's <c>DriverType</c> 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).
|
||||||
|
/// </summary>
|
||||||
|
[Trait("Category", "Unit")]
|
||||||
|
public sealed class UnsTreeServiceTagDriversTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A driver loaded for an equipment carries its <c>DriverType</c> in the returned tuple.
|
||||||
|
/// </summary>
|
||||||
|
[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");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user