refactor(uns): drop dead LoadEquipmentChildrenAsync + LinesForCluster; fix stale comment

This commit is contained in:
Joseph Doherty
2026-06-11 15:11:28 -04:00
parent 6e7127c396
commit f1c4392b0d
4 changed files with 4 additions and 153 deletions
@@ -151,18 +151,6 @@
.Select(a => (a.EntityId!, a.DisplayName))
.ToList();
/// <summary>Returns the <c>(Id, Display)</c> line options inside a single cluster, for the equipment picker.</summary>
private IReadOnlyList<(string Id, string Display)> LinesForCluster(string? clusterId) =>
_roots
.SelectMany(ent => ent.Children)
.Where(c => c.Kind == UnsNodeKind.Cluster && c.ClusterId == clusterId)
.SelectMany(c => c.Children)
.Where(a => a.Kind == UnsNodeKind.Area)
.SelectMany(a => a.Children)
.Where(l => l.Kind == UnsNodeKind.Line && l.EntityId is not null)
.Select(l => (l.EntityId!, l.DisplayName))
.ToList();
/// <summary>
/// Toggles a structural node's expansion. Equipment nodes are leaves (no expander),
/// so this path only ever fires for Enterprise/Cluster/Area/Line.
@@ -370,8 +358,8 @@
}
/// <summary>
/// Expands every structural node (Enterprise/Cluster/Area/Line). Equipment nodes
/// are intentionally left collapsed because expanding them would trigger lazy loads.
/// Expands every structural container node (Enterprise/Cluster/Area/Line) in the tree.
/// Equipment is a leaf node and has no expansion; <see cref="ExpandStructural"/> skips it.
/// </summary>
private void ExpandAll()
{
@@ -103,8 +103,8 @@ public sealed record EquipmentImportResult(int Inserted, int Skipped, IReadOnlyL
/// <summary>
/// Loads the structural portion of the unified-namespace (UNS) browse tree —
/// Enterprise → Cluster → Area → Line → Equipment — from the config database.
/// Equipment children (tags/virtual tags) are summarised by count only and loaded
/// lazily by the renderer via <see cref="LoadEquipmentChildrenAsync"/>.
/// Equipment is a leaf in the tree; its tags and virtual tags are accessed via the
/// equipment detail page, not via lazy tree expansion.
/// </summary>
public interface IUnsTreeService
{
@@ -117,16 +117,6 @@ public interface IUnsTreeService
/// <returns>The enterprise root nodes, each populated down to equipment.</returns>
Task<IReadOnlyList<UnsNode>> LoadStructureAsync(CancellationToken ct = default);
/// <summary>
/// Lazily loads the Tag and VirtualTag leaf nodes for a single equipment node.
/// Tags are returned first (ordered by Name), followed by VirtualTags (ordered by Name).
/// Leaf nodes carry <c>ChildCount = 0</c> and <c>HasLazyChildren = false</c>.
/// </summary>
/// <param name="equipmentId">The equipment whose children to load.</param>
/// <param name="ct">A token to cancel the load.</param>
/// <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
@@ -82,51 +82,6 @@ public sealed class UnsTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
return UnsTreeAssembly.Build(clusters, areas, lines, equipment);
}
/// <inheritdoc />
public async Task<IReadOnlyList<UnsNode>> LoadEquipmentChildrenAsync(
string equipmentId,
CancellationToken ct = default)
{
await using var db = await dbFactory.CreateDbContextAsync(ct);
var tagNodes = await db.Tags
.AsNoTracking()
.Where(t => t.EquipmentId == equipmentId)
.OrderBy(t => t.Name)
.Select(t => new UnsNode
{
Kind = UnsNodeKind.Tag,
Key = $"tag:{t.TagId}",
DisplayName = $"{t.Name} ({t.DataType})",
EntityId = t.TagId,
ClusterId = null,
ChildCount = 0,
HasLazyChildren = false,
})
.ToListAsync(ct);
var vtagNodes = await db.VirtualTags
.AsNoTracking()
.Where(v => v.EquipmentId == equipmentId)
.OrderBy(v => v.Name)
.Select(v => new UnsNode
{
Kind = UnsNodeKind.VirtualTag,
Key = $"vtag:{v.VirtualTagId}",
DisplayName = $"{v.Name} (VirtualTag)",
EntityId = v.VirtualTagId,
ClusterId = null,
ChildCount = 0,
HasLazyChildren = false,
})
.ToListAsync(ct);
var result = new List<UnsNode>(tagNodes.Count + vtagNodes.Count);
result.AddRange(tagNodes);
result.AddRange(vtagNodes);
return result;
}
/// <inheritdoc />
public async Task<IReadOnlyList<EquipmentTagRow>> LoadTagsForEquipmentAsync(string equipmentId, CancellationToken ct = default)
{
@@ -1,82 +0,0 @@
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
/// <summary>
/// Verifies <see cref="UnsTreeService.LoadEquipmentChildrenAsync"/> returns Tag leaf nodes
/// followed by VirtualTag leaf nodes, in Name order, with the correct keys and display names.
/// </summary>
[Trait("Category", "Unit")]
public sealed class UnsTreeServiceLazyTests
{
private static UnsTreeService SeededService()
{
var dbName = $"uns-lazy-{Guid.NewGuid():N}";
UnsTreeTestDb.SeedNamed(dbName);
return new UnsTreeService(UnsTreeTestDb.Factory(dbName));
}
/// <summary>
/// Tags come first, ordered by Name, then VirtualTags; keys follow the tag:/vtag: scheme
/// and the Tag display name embeds the DataType.
/// </summary>
[Fact]
public async Task LoadEquipmentChildren_returns_tags_then_vtags()
{
var service = SeededService();
var children = await service.LoadEquipmentChildrenAsync(UnsTreeTestDb.SeededEquipmentId);
// Seed has 2 tags (speed=Float, running=Boolean) + 1 vtag (computed).
// Tags are ordered by Name: "running" < "speed".
children.Count.ShouldBe(3);
var running = children[0];
running.Kind.ShouldBe(UnsNodeKind.Tag);
running.Key.ShouldBe("tag:TAG-2");
running.EntityId.ShouldBe("TAG-2");
running.ClusterId.ShouldBeNull();
running.DisplayName.ShouldContain("running");
running.DisplayName.ShouldContain("Boolean");
running.ChildCount.ShouldBe(0);
running.HasLazyChildren.ShouldBeFalse();
running.Children.ShouldBeEmpty();
var speed = children[1];
speed.Kind.ShouldBe(UnsNodeKind.Tag);
speed.Key.ShouldBe("tag:TAG-1");
speed.EntityId.ShouldBe("TAG-1");
speed.DisplayName.ShouldContain("speed");
speed.DisplayName.ShouldContain("Float");
var vtag = children[2];
vtag.Kind.ShouldBe(UnsNodeKind.VirtualTag);
vtag.Key.ShouldBe("vtag:VTAG-1");
vtag.EntityId.ShouldBe("VTAG-1");
vtag.ClusterId.ShouldBeNull();
vtag.DisplayName.ShouldContain("computed");
vtag.DisplayName.ShouldContain("VirtualTag");
vtag.ChildCount.ShouldBe(0);
vtag.HasLazyChildren.ShouldBeFalse();
vtag.Children.ShouldBeEmpty();
}
/// <summary>An equipment with no tags or virtual tags returns an empty list.</summary>
[Fact]
public async Task LoadEquipmentChildren_empty_for_equipment_with_none()
{
// Use a fresh named store and add an equipment with no tags/vtags.
var dbName = $"uns-lazy-empty-{Guid.NewGuid():N}";
UnsTreeTestDb.SeedNamed(dbName);
// The orphan equipment id is not in the seeded fixture, so just use a novel id.
const string emptyEquipmentId = "EQ-NO-TAGS";
var service = new UnsTreeService(UnsTreeTestDb.Factory(dbName));
var children = await service.LoadEquipmentChildrenAsync(emptyEquipmentId);
children.ShouldBeEmpty();
}
}