feat(centralui): add OPC UA browse button + override column to InstanceConfigure
This commit is contained in:
+120
-3
@@ -9,6 +9,7 @@
|
|||||||
@using ZB.MOM.WW.ScadaBridge.TemplateEngine.Flattening
|
@using ZB.MOM.WW.ScadaBridge.TemplateEngine.Flattening
|
||||||
@using ZB.MOM.WW.ScadaBridge.TemplateEngine.Services
|
@using ZB.MOM.WW.ScadaBridge.TemplateEngine.Services
|
||||||
@using ZB.MOM.WW.ScadaBridge.DeploymentManager
|
@using ZB.MOM.WW.ScadaBridge.DeploymentManager
|
||||||
|
@using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Dialogs
|
||||||
@attribute [Authorize(Policy = AuthorizationPolicies.RequireDeployment)]
|
@attribute [Authorize(Policy = AuthorizationPolicies.RequireDeployment)]
|
||||||
@inject ITemplateEngineRepository TemplateEngineRepository
|
@inject ITemplateEngineRepository TemplateEngineRepository
|
||||||
@inject ISiteRepository SiteRepository
|
@inject ISiteRepository SiteRepository
|
||||||
@@ -108,17 +109,22 @@
|
|||||||
<th>Attribute</th>
|
<th>Attribute</th>
|
||||||
<th>Tag Path</th>
|
<th>Tag Path</th>
|
||||||
<th style="width: 280px;">Connection</th>
|
<th style="width: 280px;">Connection</th>
|
||||||
|
<th>Override</th>
|
||||||
|
<th style="width: 110px;"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach (var attr in _bindingDataSourceAttrs)
|
@foreach (var attr in _bindingDataSourceAttrs)
|
||||||
{
|
{
|
||||||
|
var connId = GetBindingConnectionId(attr.Name);
|
||||||
|
var canBrowse = connId > 0;
|
||||||
|
var isOpcUa = IsOpcUa(connId);
|
||||||
<tr>
|
<tr>
|
||||||
<td class="small">@attr.Name</td>
|
<td class="small">@attr.Name</td>
|
||||||
<td class="small text-muted font-monospace">@attr.DataSourceReference</td>
|
<td class="small text-muted font-monospace">@attr.DataSourceReference</td>
|
||||||
<td>
|
<td>
|
||||||
<select class="form-select form-select-sm"
|
<select class="form-select form-select-sm"
|
||||||
value="@GetBindingConnectionId(attr.Name)"
|
value="@connId"
|
||||||
@onchange="(e) => OnBindingChanged(attr.Name, e)">
|
@onchange="(e) => OnBindingChanged(attr.Name, e)">
|
||||||
<option value="0">— none —</option>
|
<option value="0">— none —</option>
|
||||||
@foreach (var c in _siteConnections)
|
@foreach (var c in _siteConnections)
|
||||||
@@ -127,6 +133,23 @@
|
|||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<input class="form-control form-control-sm"
|
||||||
|
value="@GetOverrideForAttr(attr.Name)"
|
||||||
|
@onchange="(e) => OnOverrideForAttrChanged(attr.Name, e)"
|
||||||
|
placeholder="@(attr.DataSourceReference ?? "(no default)")" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@if (isOpcUa)
|
||||||
|
{
|
||||||
|
<button class="btn btn-sm btn-outline-primary"
|
||||||
|
disabled="@(!canBrowse)"
|
||||||
|
title="@(canBrowse ? "Browse OPC UA address space" : "Pick a connection first")"
|
||||||
|
@onclick="() => OpenBrowser(attr.Name)">
|
||||||
|
Browse…
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -331,6 +354,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@* OPC UA Tag Browser dialog (Task 18) — rendered once; OpenBrowser
|
||||||
|
tracks which binding row's override input receives the picked node id. *@
|
||||||
|
<OpcUaBrowserDialog @ref="_browserRef"
|
||||||
|
SiteId="@_browserSiteIdentifier"
|
||||||
|
ConnectionName="@_browserConnectionName"
|
||||||
|
InitialNodeId="@_browserInitial"
|
||||||
|
OnSelected="OnBrowserSelected" />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -350,8 +381,24 @@
|
|||||||
private List<TemplateAttribute> _bindingDataSourceAttrs = new();
|
private List<TemplateAttribute> _bindingDataSourceAttrs = new();
|
||||||
private List<DataConnection> _siteConnections = new();
|
private List<DataConnection> _siteConnections = new();
|
||||||
private Dictionary<string, int> _bindingSelections = new();
|
private Dictionary<string, int> _bindingSelections = new();
|
||||||
|
/// <summary>
|
||||||
|
/// Per-attribute <c>DataSourceReferenceOverride</c> values (Task 18). Mirrors
|
||||||
|
/// <see cref="_bindingSelections"/> by attribute name. Loaded from the
|
||||||
|
/// existing <see cref="InstanceConnectionBinding"/> rows on init; round-tripped
|
||||||
|
/// through <see cref="ConnectionBinding"/> on <c>SaveBindings</c>.
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<string, string?> _bindingOverrides = new();
|
||||||
private int _bulkConnectionId;
|
private int _bulkConnectionId;
|
||||||
|
|
||||||
|
// OPC UA tag browser (Task 18) — single dialog rendered at page bottom;
|
||||||
|
// _browserAttrInEdit tracks which row gets the picked node id on Select.
|
||||||
|
private OpcUaBrowserDialog? _browserRef;
|
||||||
|
private string? _browserAttrInEdit;
|
||||||
|
private string _browserSiteIdentifier = "";
|
||||||
|
private string _browserConnectionName = "";
|
||||||
|
private string? _browserInitial;
|
||||||
|
private string _siteIdentifier = "";
|
||||||
|
|
||||||
// Overrides
|
// Overrides
|
||||||
private List<TemplateAttribute> _overrideAttrs = new();
|
private List<TemplateAttribute> _overrideAttrs = new();
|
||||||
private Dictionary<string, string?> _overrideValues = new();
|
private Dictionary<string, string?> _overrideValues = new();
|
||||||
@@ -407,7 +454,11 @@
|
|||||||
_templateName = template?.Name ?? $"#{_instance.TemplateId}";
|
_templateName = template?.Name ?? $"#{_instance.TemplateId}";
|
||||||
|
|
||||||
var sites = await SiteRepository.GetAllSitesAsync();
|
var sites = await SiteRepository.GetAllSitesAsync();
|
||||||
_siteName = sites.FirstOrDefault(s => s.Id == _instance.SiteId)?.Name ?? $"#{_instance.SiteId}";
|
var site = sites.FirstOrDefault(s => s.Id == _instance.SiteId);
|
||||||
|
_siteName = site?.Name ?? $"#{_instance.SiteId}";
|
||||||
|
// Task 18: cache the site's machine identifier — the OPC UA browse
|
||||||
|
// dialog routes by SiteIdentifier (string), not the numeric site id.
|
||||||
|
_siteIdentifier = site?.SiteIdentifier ?? "";
|
||||||
|
|
||||||
// Areas
|
// Areas
|
||||||
_siteAreas = (await TemplateEngineRepository.GetAreasBySiteIdAsync(_instance.SiteId)).ToList();
|
_siteAreas = (await TemplateEngineRepository.GetAreasBySiteIdAsync(_instance.SiteId)).ToList();
|
||||||
@@ -420,7 +471,11 @@
|
|||||||
_siteConnections = (await SiteRepository.GetDataConnectionsBySiteIdAsync(_instance.SiteId)).ToList();
|
_siteConnections = (await SiteRepository.GetDataConnectionsBySiteIdAsync(_instance.SiteId)).ToList();
|
||||||
var existingBindings = await TemplateEngineRepository.GetBindingsByInstanceIdAsync(Id);
|
var existingBindings = await TemplateEngineRepository.GetBindingsByInstanceIdAsync(Id);
|
||||||
foreach (var b in existingBindings)
|
foreach (var b in existingBindings)
|
||||||
|
{
|
||||||
_bindingSelections[b.AttributeName] = b.DataConnectionId;
|
_bindingSelections[b.AttributeName] = b.DataConnectionId;
|
||||||
|
if (!string.IsNullOrEmpty(b.DataSourceReferenceOverride))
|
||||||
|
_bindingOverrides[b.AttributeName] = b.DataSourceReferenceOverride;
|
||||||
|
}
|
||||||
|
|
||||||
// Overrides
|
// Overrides
|
||||||
_overrideAttrs = attrs.Where(a => !a.IsLocked).ToList();
|
_overrideAttrs = attrs.Where(a => !a.IsLocked).ToList();
|
||||||
@@ -474,12 +529,74 @@
|
|||||||
_bindingSelections[attr.Name] = _bulkConnectionId;
|
_bindingSelections[attr.Name] = _bulkConnectionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Task 18: per-attribute override input + OPC UA tag browser ──────────
|
||||||
|
|
||||||
|
private string? GetOverrideForAttr(string attrName)
|
||||||
|
=> _bindingOverrides.GetValueOrDefault(attrName);
|
||||||
|
|
||||||
|
private void OnOverrideForAttrChanged(string attrName, ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
var val = e.Value?.ToString();
|
||||||
|
if (string.IsNullOrWhiteSpace(val))
|
||||||
|
_bindingOverrides.Remove(attrName);
|
||||||
|
else
|
||||||
|
_bindingOverrides[attrName] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Looks up the template default <c>DataSourceReference</c> for an attribute.</summary>
|
||||||
|
private string? GetTemplateDefault(string attrName)
|
||||||
|
=> _bindingDataSourceAttrs.FirstOrDefault(a => a.Name == attrName)?.DataSourceReference;
|
||||||
|
|
||||||
|
/// <summary>True when the row's selected data connection is OPC UA.</summary>
|
||||||
|
private bool IsOpcUa(int connectionId)
|
||||||
|
=> connectionId > 0
|
||||||
|
&& string.Equals(
|
||||||
|
_siteConnections.FirstOrDefault(c => c.Id == connectionId)?.Protocol,
|
||||||
|
"OpcUa",
|
||||||
|
StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the OPC UA tag browser dialog for the given attribute row. Remembers
|
||||||
|
/// which attribute is being edited so <see cref="OnBrowserSelected"/> can
|
||||||
|
/// write the picked node id back to the right override input.
|
||||||
|
/// </summary>
|
||||||
|
private async Task OpenBrowser(string attrName)
|
||||||
|
{
|
||||||
|
var connId = GetBindingConnectionId(attrName);
|
||||||
|
var conn = _siteConnections.FirstOrDefault(c => c.Id == connId);
|
||||||
|
if (conn is null) return;
|
||||||
|
|
||||||
|
_browserAttrInEdit = attrName;
|
||||||
|
_browserConnectionName = conn.Name;
|
||||||
|
_browserSiteIdentifier = _siteIdentifier;
|
||||||
|
_browserInitial = _bindingOverrides.GetValueOrDefault(attrName)
|
||||||
|
?? GetTemplateDefault(attrName);
|
||||||
|
|
||||||
|
if (_browserRef is not null)
|
||||||
|
await _browserRef.ShowAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBrowserSelected(string nodeId)
|
||||||
|
{
|
||||||
|
if (_browserAttrInEdit is null) return;
|
||||||
|
_bindingOverrides[_browserAttrInEdit] = nodeId;
|
||||||
|
_browserAttrInEdit = null;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveBindings()
|
private async Task SaveBindings()
|
||||||
{
|
{
|
||||||
_saving = true;
|
_saving = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var bindings = _bindingSelections.Select(kv => new ConnectionBinding(kv.Key, kv.Value)).ToList();
|
// Task 18: include the per-attribute DataSourceReferenceOverride on
|
||||||
|
// the wire record so it round-trips through SetConnectionBindingsAsync
|
||||||
|
// into the InstanceConnectionBinding entity.
|
||||||
|
var bindings = _bindingSelections
|
||||||
|
.Select(kv => new ConnectionBinding(
|
||||||
|
kv.Key,
|
||||||
|
kv.Value,
|
||||||
|
_bindingOverrides.GetValueOrDefault(kv.Key)))
|
||||||
|
.ToList();
|
||||||
var user = await GetCurrentUserAsync();
|
var user = await GetCurrentUserAsync();
|
||||||
var result = await InstanceService.SetConnectionBindingsAsync(Id, bindings, user);
|
var result = await InstanceService.SetConnectionBindingsAsync(Id, bindings, user);
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
|
|||||||
Reference in New Issue
Block a user