feat(adminui): alias DTO + Galaxy gateway lookup + Source/IsAlias on tag rows
This commit is contained in:
@@ -86,11 +86,34 @@ public sealed class UnsTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
||||
public async Task<IReadOnlyList<EquipmentTagRow>> LoadTagsForEquipmentAsync(string equipmentId, CancellationToken ct = default)
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(ct);
|
||||
return await db.Tags.AsNoTracking()
|
||||
|
||||
// Left-join each tag to its driver so we can tell Galaxy aliases apart while still surfacing a
|
||||
// tag whose driver row is missing (it is simply treated as a non-alias). EF can't parse the
|
||||
// TagConfig JSON in-query, so we materialise then map IsAlias/Source in memory.
|
||||
var rows = 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))
|
||||
.GroupJoin(db.DriverInstances.AsNoTracking(), t => t.DriverInstanceId, d => d.DriverInstanceId,
|
||||
(t, ds) => new { Tag = t, Drivers = ds })
|
||||
.SelectMany(x => x.Drivers.DefaultIfEmpty(),
|
||||
(x, d) => new
|
||||
{
|
||||
x.Tag.TagId,
|
||||
x.Tag.Name,
|
||||
x.Tag.DriverInstanceId,
|
||||
x.Tag.DataType,
|
||||
x.Tag.AccessLevel,
|
||||
DriverType = d != null ? d.DriverType : null,
|
||||
x.Tag.TagConfig,
|
||||
})
|
||||
.ToListAsync(ct);
|
||||
|
||||
return rows.Select(r =>
|
||||
{
|
||||
var isAlias = r.DriverType == "GalaxyMxGateway";
|
||||
var source = isAlias ? $"galaxy:{ExtractTagConfigFullName(r.TagConfig)}" : null;
|
||||
return new EquipmentTagRow(r.TagId, r.Name, r.DriverInstanceId, r.DataType, r.AccessLevel, isAlias, source);
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -740,6 +763,29 @@ public sealed class UnsTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<(string DriverInstanceId, string Display, string DriverConfig)>>
|
||||
LoadGalaxyGatewaysForEquipmentAsync(string equipmentId, CancellationToken ct = default)
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(ct);
|
||||
|
||||
var cluster = await ResolveEquipmentClusterAsync(db, equipmentId, ct);
|
||||
if (cluster is null)
|
||||
{
|
||||
return Array.Empty<(string, string, string)>();
|
||||
}
|
||||
|
||||
var gateways = await db.DriverInstances
|
||||
.Where(d => d.ClusterId == cluster && d.DriverType == "GalaxyMxGateway")
|
||||
.OrderBy(d => d.DriverInstanceId)
|
||||
.Select(d => new { d.DriverInstanceId, d.Name, d.DriverConfig })
|
||||
.ToListAsync(ct);
|
||||
|
||||
return gateways
|
||||
.Select(d => (d.DriverInstanceId, Display: $"{d.DriverInstanceId} — {d.Name}", d.DriverConfig))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<UnsMutationResult> CreateTagAsync(
|
||||
string equipmentId,
|
||||
@@ -1153,6 +1199,32 @@ public sealed class UnsTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the <c>FullName</c> string from a tag's <c>TagConfig</c> JSON (the Galaxy reference an
|
||||
/// alias surfaces), or <c>null</c> when the config is empty, not a JSON object, lacks a string
|
||||
/// <c>FullName</c>, or is malformed. A small local copy mirrors the composer's own extraction —
|
||||
/// consistent with this codebase, where the composer and validator each keep their own.
|
||||
/// </summary>
|
||||
private static string? ExtractTagConfigFullName(string? tagConfig)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tagConfig))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(tagConfig);
|
||||
return doc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Object
|
||||
&& doc.RootElement.TryGetProperty("FullName", out var fn)
|
||||
&& fn.ValueKind == System.Text.Json.JsonValueKind.String ? fn.GetString() : null;
|
||||
}
|
||||
catch (System.Text.Json.JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves an equipment to its cluster via <c>Equipment.UnsLineId → UnsLine.UnsAreaId →
|
||||
/// UnsArea.ClusterId</c>. Returns <c>null</c> when the equipment, its line, or its area cannot be
|
||||
|
||||
Reference in New Issue
Block a user