v3(b1-wp4b): make AdminUI compile against greenfield schema behind Batch-2/3 stubs

Namespace entity + NamespaceKind retired; DriverInstance dropped NamespaceId;
Tag is raw-only (no EquipmentId/DriverInstanceId/FullName/FolderPath); driver
options replaced pre-declared Tags with RawTags. Stub the retired surfaces so
the AdminUI + Host build green; real Raw/UNS authoring arrives in v3 Batch 2/3.

- ClusterNamespaces/NamespaceEdit: bodies replaced with 'Namespaces retired' banner
- DriverIdentitySection: dropped Namespace dropdown + NamespaceId model field
- 8 driver pages: dropped Namespace binding; retired the pre-declared Tags editor
  (replaced with a '/raw Batch 2' note); multi-device pages keep their Devices editor
- UnsTreeService: equipment-tag counts/list -> empty; Create/UpdateTag -> failure
  result ('Tag authoring moved to the Raw tree (/raw) in v3 Batch 2'); Namespace/
  driver-binding queries removed
- IScriptTagCatalog: project surviving Tag columns only (path from TagConfig FullName, else Name)
- OpcUaClientTagConfigEditor: rebind stale FullName -> model's NodeId
- ClusterDrivers: drop retired NamespaceId column

AdminUI + Host build 0/0. AdminUI.Tests / Host.IntegrationTests remain red (WP6).
This commit is contained in:
Joseph Doherty
2026-07-15 21:09:04 -04:00
parent 604928b29d
commit 11576460a7
15 changed files with 155 additions and 1147 deletions
@@ -140,12 +140,13 @@ public sealed class ScriptTagCatalog(IDbContextFactory<OtOpcUaConfigDbContext> d
{
await using var db = await dbFactory.CreateDbContextAsync(ct);
// Only the resolvable keys are projected, so no UNS join is needed: the equipment-tag key is
// the FullName from TagConfig, the FolderPath-scoped tag key is FolderPath/Name, and the
// virtual-tag key is its own Name. Pull just those columns, untracked.
// v3: Tag is Raw-only — EquipmentId / FolderPath / DriverInstanceId were retired (the
// equipment↔Tag + driver bindings are gone). The resolvable key is the driver FullName carried
// in TagConfig; project only the surviving columns. (The Raw/UNS tag catalog is rebuilt in
// Batch 2/3.)
var tagRows = await db.Tags
.AsNoTracking()
.Select(t => new { t.EquipmentId, t.Name, t.FolderPath, t.DataType, t.DriverInstanceId, t.TagConfig })
.Select(t => new { t.Name, t.DataType, t.TagConfig })
.ToListAsync(ct);
var vtagRows = await db.VirtualTags
@@ -157,13 +158,10 @@ public sealed class ScriptTagCatalog(IDbContextFactory<OtOpcUaConfigDbContext> d
foreach (var t in tagRows)
{
var path = t.EquipmentId is null
// FolderPath-scoped tag (EquipmentId null) — subscribes by the MXAccess dot-ref, which
// is "FolderPath.Name" when a folder is set, else just "Name".
? (string.IsNullOrWhiteSpace(t.FolderPath) ? t.Name : $"{t.FolderPath}.{t.Name}")
// Equipment driver tag — the runtime GetTag key is the driver FullName from TagConfig.
: ExtractFullNameFromTagConfig(t.TagConfig);
entries.Add(new ScriptTagInfo(path, "Tag", t.DataType, t.DriverInstanceId));
// The runtime GetTag key is the driver FullName from TagConfig; fall back to Name if absent.
var full = ExtractFullNameFromTagConfig(t.TagConfig);
var path = string.IsNullOrWhiteSpace(full) ? t.Name : full;
entries.Add(new ScriptTagInfo(path, "Tag", t.DataType, null));
}
foreach (var v in vtagRows)
@@ -185,7 +183,10 @@ public sealed class ScriptTagCatalog(IDbContextFactory<OtOpcUaConfigDbContext> d
/// </summary>
private static string ExtractFullNameFromTagConfig(string tagConfig)
{
if (string.IsNullOrWhiteSpace(tagConfig)) return tagConfig;
// Best-effort: pull the driver FullName out of the TagConfig JSON blob if present.
// Returns "" when absent/unparseable so the caller falls back to the tag Name — never
// the raw blob (which would surface as a garbage completion path).
if (string.IsNullOrWhiteSpace(tagConfig)) return "";
try
{
using var doc = System.Text.Json.JsonDocument.Parse(tagConfig);
@@ -193,10 +194,10 @@ public sealed class ScriptTagCatalog(IDbContextFactory<OtOpcUaConfigDbContext> d
&& doc.RootElement.TryGetProperty("FullName", out var fullName)
&& fullName.ValueKind == System.Text.Json.JsonValueKind.String)
{
return fullName.GetString() ?? tagConfig;
return fullName.GetString() ?? "";
}
}
catch (System.Text.Json.JsonException) { /* fall through to raw blob */ }
return tagConfig;
catch (System.Text.Json.JsonException) { /* fall through to tag Name */ }
return "";
}
}