feat(uns): per-equipment tag/virtual-tag list service methods
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
|
||||
|
||||
/// <summary>A tag row for the equipment page's Tags tab table — display columns plus the id used to
|
||||
/// open the edit modal. RowVersion is re-read fresh on delete (matching the tree's delete path).</summary>
|
||||
public sealed record EquipmentTagRow(string TagId, string Name, string DriverInstanceId, string DataType, TagAccessLevel AccessLevel);
|
||||
|
||||
/// <summary>A virtual-tag row for the equipment page's Virtual Tags tab table.</summary>
|
||||
public sealed record EquipmentVirtualTagRow(string VirtualTagId, string Name, string DataType, string ScriptId, bool Enabled);
|
||||
@@ -127,6 +127,28 @@ public interface IUnsTreeService
|
||||
/// <returns>Tag nodes followed by VirtualTag nodes; empty if the equipment has none.</returns>
|
||||
Task<IReadOnlyList<UnsNode>> LoadEquipmentChildrenAsync(string equipmentId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the driver tags bound to a single equipment as flat row projections for the equipment
|
||||
/// page's Tags tab table, ordered by Name. Each row carries the display columns plus the
|
||||
/// <c>TagId</c> the table uses to open the edit modal. Reads untracked. Returns an empty list when
|
||||
/// the equipment has no tags.
|
||||
/// </summary>
|
||||
/// <param name="equipmentId">The equipment whose tags to load.</param>
|
||||
/// <param name="ct">A token to cancel the load.</param>
|
||||
/// <returns>The equipment's tag rows ordered by Name; empty if it has none.</returns>
|
||||
Task<IReadOnlyList<EquipmentTagRow>> LoadTagsForEquipmentAsync(string equipmentId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the virtual tags scoped to a single equipment as flat row projections for the equipment
|
||||
/// page's Virtual Tags tab table, ordered by Name. Each row carries the display columns plus the
|
||||
/// <c>VirtualTagId</c> the table uses to open the edit modal. Reads untracked. Returns an empty list
|
||||
/// when the equipment has no virtual tags.
|
||||
/// </summary>
|
||||
/// <param name="equipmentId">The equipment whose virtual tags to load.</param>
|
||||
/// <param name="ct">A token to cancel the load.</param>
|
||||
/// <returns>The equipment's virtual-tag rows ordered by Name; empty if it has none.</returns>
|
||||
Task<IReadOnlyList<EquipmentVirtualTagRow>> LoadVirtualTagsForEquipmentAsync(string equipmentId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Loads a single UNS area projected for editing, or <c>null</c> if it no longer exists.
|
||||
/// Reads untracked and captures the current concurrency token for last-write-wins saves.
|
||||
|
||||
@@ -127,6 +127,28 @@ public sealed class UnsTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<EquipmentTagRow>> LoadTagsForEquipmentAsync(string equipmentId, CancellationToken ct = default)
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(ct);
|
||||
return await db.Tags.AsNoTracking()
|
||||
.Where(t => t.EquipmentId == equipmentId)
|
||||
.OrderBy(t => t.Name)
|
||||
.Select(t => new EquipmentTagRow(t.TagId, t.Name, t.DriverInstanceId, t.DataType, t.AccessLevel))
|
||||
.ToListAsync(ct);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<EquipmentVirtualTagRow>> LoadVirtualTagsForEquipmentAsync(string equipmentId, CancellationToken ct = default)
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(ct);
|
||||
return await db.VirtualTags.AsNoTracking()
|
||||
.Where(v => v.EquipmentId == equipmentId)
|
||||
.OrderBy(v => v.Name)
|
||||
.Select(v => new EquipmentVirtualTagRow(v.VirtualTagId, v.Name, v.DataType, v.ScriptId, v.Enabled))
|
||||
.ToListAsync(ct);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<AreaEditDto?> LoadAreaAsync(string unsAreaId, CancellationToken ct = default)
|
||||
{
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class UnsTreeServiceEquipmentChildRowsTests
|
||||
{
|
||||
private static UnsTreeService SeededService()
|
||||
{
|
||||
var dbName = $"uns-childrows-{Guid.NewGuid():N}";
|
||||
UnsTreeTestDb.SeedNamed(dbName);
|
||||
return new UnsTreeService(UnsTreeTestDb.Factory(dbName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadTagsForEquipment_returns_tags_in_name_order_scoped()
|
||||
{
|
||||
var rows = await SeededService().LoadTagsForEquipmentAsync(UnsTreeTestDb.SeededEquipmentId);
|
||||
rows.Count.ShouldBe(2); // the EquipmentId=null orphan tag is excluded
|
||||
rows[0].TagId.ShouldBe("TAG-2"); // "running" < "speed"
|
||||
rows[0].Name.ShouldBe("running");
|
||||
rows[0].DataType.ShouldBe("Boolean");
|
||||
rows[1].TagId.ShouldBe("TAG-1");
|
||||
rows[1].DataType.ShouldBe("Float");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadVirtualTagsForEquipment_returns_vtags_in_name_order()
|
||||
{
|
||||
var rows = await SeededService().LoadVirtualTagsForEquipmentAsync(UnsTreeTestDb.SeededEquipmentId);
|
||||
rows.Count.ShouldBe(1);
|
||||
rows[0].VirtualTagId.ShouldBe("VTAG-1");
|
||||
rows[0].Name.ShouldBe("computed");
|
||||
rows[0].DataType.ShouldBe("Double");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadTagsForEquipment_empty_for_unknown_equipment()
|
||||
=> (await SeededService().LoadTagsForEquipmentAsync("EQ-NONE")).ShouldBeEmpty();
|
||||
}
|
||||
Reference in New Issue
Block a user