feat(adminui): wire OpcUaClient picker to live browser
This commit is contained in:
+2
-1
@@ -58,7 +58,8 @@ else
|
|||||||
CurrentAddress="@_pickedAddress"
|
CurrentAddress="@_pickedAddress"
|
||||||
OnPickAddress="@OnAddressPicked">
|
OnPickAddress="@OnAddressPicked">
|
||||||
<OpcUaClientAddressPickerBody CurrentAddress="@_pickedAddress"
|
<OpcUaClientAddressPickerBody CurrentAddress="@_pickedAddress"
|
||||||
CurrentAddressChanged="@((s) => _pickedAddress = s)" />
|
CurrentAddressChanged="@((s) => _pickedAddress = s)"
|
||||||
|
GetConfigJson="@SerializeCurrentConfig" />
|
||||||
</DriverTagPicker>
|
</DriverTagPicker>
|
||||||
|
|
||||||
@* Endpoint *@
|
@* Endpoint *@
|
||||||
|
|||||||
+90
-11
@@ -1,22 +1,57 @@
|
|||||||
@* Static OPC UA Client address builder: NodeId free text → verbatim.
|
@* OPC UA Client address picker:
|
||||||
Live browse deferred to a follow-up phase. *@
|
1. Manual NodeId entry (always available)
|
||||||
|
2. (DriverOperator-gated) Browse remote server with live tree, lazy expand. *@
|
||||||
<div class="alert alert-info py-2 px-3 mb-3 small">
|
@implements IAsyncDisposable
|
||||||
<strong>Note:</strong> Live OPC UA node browse is deferred to a follow-up phase.
|
@using Microsoft.AspNetCore.Authorization
|
||||||
Enter the NodeId string manually below.
|
@using ZB.MOM.WW.OtOpcUa.AdminUI.Browsing
|
||||||
</div>
|
@using ZB.MOM.WW.OtOpcUa.Commons.Browsing
|
||||||
|
@inject IBrowserSessionService BrowserService
|
||||||
|
@inject AuthenticationStateProvider AuthState
|
||||||
|
@inject IAuthorizationService AuthorizationService
|
||||||
|
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<label class="form-label">NodeId</label>
|
<label class="form-label">NodeId</label>
|
||||||
<input type="text" class="form-control form-control-sm mono" placeholder="ns=2;s=Channel.Device.Tag"
|
<input type="text" class="form-control form-control-sm mono"
|
||||||
|
placeholder="ns=2;s=Channel.Device.Tag"
|
||||||
@bind="_nodeId" @bind:after="OnChangedAsync" />
|
@bind="_nodeId" @bind:after="OnChangedAsync" />
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
OPC UA NodeId string, e.g. <code>ns=2;s=Channel.Device.Tag</code> or <code>i=1001</code>.
|
OPC UA NodeId string, e.g. <code>ns=2;s=Channel.Device.Tag</code> or
|
||||||
|
<code>i=1001</code>. Use Browse to navigate the remote server.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (_canOperate)
|
||||||
|
{
|
||||||
|
<div class="mt-3 d-flex align-items-center gap-2">
|
||||||
|
@if (_token == Guid.Empty)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-outline-primary btn-sm"
|
||||||
|
disabled="@_opening" @onclick="OpenBrowseAsync">
|
||||||
|
@if (_opening) { <span class="spinner-border spinner-border-sm me-1"></span> }
|
||||||
|
Browse remote server
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="chip chip-ok">Browser open</span>
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm" @onclick="CloseBrowseAsync">
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
@if (_openError is not null) { <span class="chip chip-bad" title="@_openError">@TruncatedError()</span> }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (_token != Guid.Empty)
|
||||||
|
{
|
||||||
|
<div class="mt-2">
|
||||||
|
<DriverBrowseTree SessionToken="_token" OnNodeSelected="OnTreeSelectAsync"
|
||||||
|
SelectedNodeId="_nodeId" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<span class="text-muted small">Result:</span>
|
<span class="text-muted small">Result:</span>
|
||||||
<code class="mono ms-2">@_built</code>
|
<code class="mono ms-2">@_built</code>
|
||||||
@@ -25,14 +60,47 @@
|
|||||||
@code {
|
@code {
|
||||||
[Parameter] public string CurrentAddress { get; set; } = "";
|
[Parameter] public string CurrentAddress { get; set; } = "";
|
||||||
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
[Parameter] public EventCallback<string> CurrentAddressChanged { get; set; }
|
||||||
|
[Parameter, EditorRequired] public Func<string> GetConfigJson { get; set; } = () => "{}";
|
||||||
|
|
||||||
private string _nodeId = "";
|
private string _nodeId = "";
|
||||||
private string _built = "";
|
private string _built = "";
|
||||||
|
private Guid _token = Guid.Empty;
|
||||||
|
private bool _opening;
|
||||||
|
private bool _canOperate;
|
||||||
|
private string? _openError;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
var auth = await AuthState.GetAuthenticationStateAsync();
|
||||||
|
var authResult = await AuthorizationService.AuthorizeAsync(auth.User, null, "DriverOperator");
|
||||||
|
_canOperate = authResult.Succeeded;
|
||||||
_built = _nodeId;
|
_built = _nodeId;
|
||||||
_ = CurrentAddressChanged.InvokeAsync(_built);
|
await CurrentAddressChanged.InvokeAsync(_built);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenBrowseAsync()
|
||||||
|
{
|
||||||
|
_opening = true; _openError = null; StateHasChanged();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = GetConfigJson() ?? "{}";
|
||||||
|
var result = await BrowserService.OpenAsync("OpcUaClient", json, default);
|
||||||
|
if (result.Ok) _token = result.Token;
|
||||||
|
else _openError = result.Message;
|
||||||
|
}
|
||||||
|
finally { _opening = false; StateHasChanged(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CloseBrowseAsync()
|
||||||
|
{
|
||||||
|
var t = _token; _token = Guid.Empty; StateHasChanged();
|
||||||
|
if (t != Guid.Empty) await BrowserService.CloseAsync(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnTreeSelectAsync(BrowseNode node)
|
||||||
|
{
|
||||||
|
_nodeId = node.NodeId;
|
||||||
|
await OnChangedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnChangedAsync()
|
private async Task OnChangedAsync()
|
||||||
@@ -40,4 +108,15 @@
|
|||||||
_built = _nodeId;
|
_built = _nodeId;
|
||||||
await CurrentAddressChanged.InvokeAsync(_built);
|
await CurrentAddressChanged.InvokeAsync(_built);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string TruncatedError() =>
|
||||||
|
_openError is null ? "" : (_openError.Length > 60 ? _openError[..60] + "…" : _openError);
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
if (_token != Guid.Empty)
|
||||||
|
{
|
||||||
|
try { await BrowserService.CloseAsync(_token); } catch { /* circuit teardown */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user