feat(uns): tag + virtual-tag modals wired into the tree
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
|
||||
|
||||
/// <summary>
|
||||
@@ -49,6 +51,44 @@ public sealed record EquipmentEditDto(string EquipmentId, string Name, string Ma
|
||||
string? HardwareRevision, string? SoftwareRevision, short? YearOfConstruction, string? AssetLocation,
|
||||
string? ManufacturerUri, string? DeviceManualUri, bool Enabled, byte[] RowVersion);
|
||||
|
||||
/// <summary>
|
||||
/// An equipment-bound tag projected for editing: its operator-editable fields, the owning equipment
|
||||
/// (so the host can scope the candidate-driver list and refresh the right node), plus the concurrency
|
||||
/// token the edit modal must echo back on save. Tree tags are always equipment-bound (decision #110),
|
||||
/// so <c>FolderPath</c> never surfaces here.
|
||||
/// </summary>
|
||||
/// <param name="TagId">The tag's stable id (read-only on edit).</param>
|
||||
/// <param name="EquipmentId">The owning equipment id.</param>
|
||||
/// <param name="Name">The tag name.</param>
|
||||
/// <param name="DriverInstanceId">The bound driver id.</param>
|
||||
/// <param name="DataType">The OPC UA built-in type name.</param>
|
||||
/// <param name="AccessLevel">The tag-level access baseline.</param>
|
||||
/// <param name="WriteIdempotent">Whether writes are safe to retry.</param>
|
||||
/// <param name="PollGroupId">Optional poll-group key; <c>null</c> when unset.</param>
|
||||
/// <param name="TagConfig">The schemaless per-driver-type JSON config.</param>
|
||||
/// <param name="RowVersion">The optimistic-concurrency token last read.</param>
|
||||
public sealed record TagEditDto(string TagId, string EquipmentId, string Name, string DriverInstanceId, string DataType,
|
||||
TagAccessLevel AccessLevel, bool WriteIdempotent, string? PollGroupId, string TagConfig, byte[] RowVersion);
|
||||
|
||||
/// <summary>
|
||||
/// An equipment-bound virtual tag projected for editing: its operator-editable fields, the owning
|
||||
/// equipment (so the host can refresh the right node), plus the concurrency token the edit modal must
|
||||
/// echo back on save. Virtual tags are always scoped to an equipment (plan decision #2), so the modal
|
||||
/// never offers an equipment-change control.
|
||||
/// </summary>
|
||||
/// <param name="VirtualTagId">The virtual tag's stable id (read-only on edit).</param>
|
||||
/// <param name="EquipmentId">The owning equipment id.</param>
|
||||
/// <param name="Name">The virtual-tag name.</param>
|
||||
/// <param name="DataType">The OPC UA built-in type name.</param>
|
||||
/// <param name="ScriptId">The bound script id.</param>
|
||||
/// <param name="ChangeTriggered">Whether the tag re-evaluates on dependency change.</param>
|
||||
/// <param name="TimerIntervalMs">Optional periodic re-evaluation cadence in ms; <c>null</c> when unset.</param>
|
||||
/// <param name="Historize">Whether the tag's values are historized.</param>
|
||||
/// <param name="Enabled">Whether the tag is spawned in deployments.</param>
|
||||
/// <param name="RowVersion">The optimistic-concurrency token last read.</param>
|
||||
public sealed record VirtualTagEditDto(string VirtualTagId, string EquipmentId, string Name, string DataType, string ScriptId,
|
||||
bool ChangeTriggered, int? TimerIntervalMs, bool Historize, bool Enabled, byte[] RowVersion);
|
||||
|
||||
/// <summary>
|
||||
/// Loads the structural portion of the unified-namespace (UNS) browse tree —
|
||||
/// Enterprise → Cluster → Area → Line → Equipment — from the config database.
|
||||
@@ -103,6 +143,24 @@ public interface IUnsTreeService
|
||||
/// <returns>The equipment's edit projection, or <c>null</c> when missing.</returns>
|
||||
Task<EquipmentEditDto?> LoadEquipmentAsync(string equipmentId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Loads a single equipment-bound tag 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.
|
||||
/// </summary>
|
||||
/// <param name="tagId">The tag to load.</param>
|
||||
/// <param name="ct">A token to cancel the load.</param>
|
||||
/// <returns>The tag's edit projection, or <c>null</c> when missing.</returns>
|
||||
Task<TagEditDto?> LoadTagAsync(string tagId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Loads a single equipment-bound virtual tag 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.
|
||||
/// </summary>
|
||||
/// <param name="virtualTagId">The virtual tag to load.</param>
|
||||
/// <param name="ct">A token to cancel the load.</param>
|
||||
/// <returns>The virtual tag's edit projection, or <c>null</c> when missing.</returns>
|
||||
Task<VirtualTagEditDto?> LoadVirtualTagAsync(string virtualTagId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Loads every driver instance in a cluster (regardless of namespace kind) so the equipment modal
|
||||
/// can offer the full cluster driver list for binding. Ordered by <c>DriverInstanceId</c>. Each is
|
||||
|
||||
@@ -178,6 +178,50 @@ public sealed class UnsTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TagEditDto?> LoadTagAsync(string tagId, CancellationToken ct = default)
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(ct);
|
||||
|
||||
return await db.Tags
|
||||
.AsNoTracking()
|
||||
.Where(t => t.TagId == tagId)
|
||||
.Select(t => new TagEditDto(
|
||||
t.TagId,
|
||||
t.EquipmentId!,
|
||||
t.Name,
|
||||
t.DriverInstanceId,
|
||||
t.DataType,
|
||||
t.AccessLevel,
|
||||
t.WriteIdempotent,
|
||||
t.PollGroupId,
|
||||
t.TagConfig,
|
||||
t.RowVersion))
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<VirtualTagEditDto?> LoadVirtualTagAsync(string virtualTagId, CancellationToken ct = default)
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(ct);
|
||||
|
||||
return await db.VirtualTags
|
||||
.AsNoTracking()
|
||||
.Where(v => v.VirtualTagId == virtualTagId)
|
||||
.Select(v => new VirtualTagEditDto(
|
||||
v.VirtualTagId,
|
||||
v.EquipmentId,
|
||||
v.Name,
|
||||
v.DataType,
|
||||
v.ScriptId,
|
||||
v.ChangeTriggered,
|
||||
v.TimerIntervalMs,
|
||||
v.Historize,
|
||||
v.Enabled,
|
||||
v.RowVersion))
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<(string DriverInstanceId, string Display)>> LoadDriversForClusterAsync(
|
||||
string clusterId,
|
||||
|
||||
Reference in New Issue
Block a user