feat(adminui): wire Galaxy picker to live browser + attribute side-panel
This commit is contained in:
+2
-1
@@ -58,7 +58,8 @@ else
|
|||||||
CurrentAddress="@_pickedAddress"
|
CurrentAddress="@_pickedAddress"
|
||||||
OnPickAddress="@OnAddressPicked">
|
OnPickAddress="@OnAddressPicked">
|
||||||
<GalaxyAddressPickerBody CurrentAddress="@_pickedAddress"
|
<GalaxyAddressPickerBody CurrentAddress="@_pickedAddress"
|
||||||
CurrentAddressChanged="@((s) => _pickedAddress = s)" />
|
CurrentAddressChanged="@((s) => _pickedAddress = s)"
|
||||||
|
GetConfigJson="@SerializeCurrentConfig" />
|
||||||
</DriverTagPicker>
|
</DriverTagPicker>
|
||||||
|
|
||||||
@* mxaccessgw connection *@
|
@* mxaccessgw connection *@
|
||||||
|
|||||||
+151
-9
@@ -1,10 +1,15 @@
|
|||||||
@* Static Galaxy address builder: tag_name.AttributeName free text → verbatim.
|
@* Galaxy address picker:
|
||||||
Live Galaxy browse deferred to a follow-up phase. *@
|
1. Manual tag/attribute entry (always available).
|
||||||
|
2. (DriverOperator-gated) Live browse: object tree on the left,
|
||||||
<div class="alert alert-info py-2 px-3 mb-3 small">
|
attribute side-panel on the right. Clicking an attribute commits
|
||||||
<strong>Note:</strong> Live Galaxy browse is deferred to a follow-up phase.
|
tag_name.AttributeName into the result. *@
|
||||||
Enter the tag and attribute name manually below.
|
@implements IAsyncDisposable
|
||||||
</div>
|
@using Microsoft.AspNetCore.Authorization
|
||||||
|
@using ZB.MOM.WW.OtOpcUa.AdminUI.Browsing
|
||||||
|
@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-5">
|
<div class="col-md-5">
|
||||||
@@ -21,6 +26,70 @@
|
|||||||
</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 galaxy
|
||||||
|
</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="row g-3 mt-1">
|
||||||
|
<div class="col-md-7">
|
||||||
|
<label class="form-label small">Objects</label>
|
||||||
|
<DriverBrowseTree SessionToken="_token" OnNodeSelected="OnObjectSelectAsync"
|
||||||
|
SelectedNodeId="_tagName" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<label class="form-label small">Attributes of @(string.IsNullOrEmpty(_tagName) ? "—" : _tagName)</label>
|
||||||
|
<div class="border rounded p-2" style="max-height:420px; overflow:auto; min-height:240px">
|
||||||
|
@if (_attrsLoading)
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm me-1"></span>
|
||||||
|
}
|
||||||
|
else if (_attrsError is not null)
|
||||||
|
{
|
||||||
|
<span class="text-danger small">@_attrsError</span>
|
||||||
|
}
|
||||||
|
else if (_attrs is null)
|
||||||
|
{
|
||||||
|
<span class="text-muted small">Pick an object.</span>
|
||||||
|
}
|
||||||
|
else if (_attrs.Count == 0)
|
||||||
|
{
|
||||||
|
<span class="text-muted small">No attributes.</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@foreach (var a in _attrs)
|
||||||
|
{
|
||||||
|
var sel = _attributeName == a.Name ? "bg-primary-subtle" : "";
|
||||||
|
<div class="d-flex justify-content-between align-items-center py-1 @sel"
|
||||||
|
style="cursor:pointer" @onclick="@(() => SelectAttributeAsync(a))">
|
||||||
|
<span class="mono small">@a.Name</span>
|
||||||
|
<span class="text-muted small">@a.DriverDataType@(a.IsArray ? "[]" : "") · @a.SecurityClass</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
@@ -29,15 +98,77 @@
|
|||||||
@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 _tagName = "";
|
private string _tagName = "";
|
||||||
private string _attributeName = "";
|
private string _attributeName = "";
|
||||||
private string _built = "";
|
private string _built = "";
|
||||||
|
private Guid _token = Guid.Empty;
|
||||||
|
private bool _opening;
|
||||||
|
private bool _attrsLoading;
|
||||||
|
private bool _canOperate;
|
||||||
|
private string? _openError;
|
||||||
|
private string? _attrsError;
|
||||||
|
private IReadOnlyList<AttributeInfo>? _attrs;
|
||||||
|
|
||||||
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 = Build();
|
_built = Build();
|
||||||
_ = 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("Galaxy", 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;
|
||||||
|
_attrs = null;
|
||||||
|
StateHasChanged();
|
||||||
|
if (t != Guid.Empty) await BrowserService.CloseAsync(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnObjectSelectAsync(BrowseNode node)
|
||||||
|
{
|
||||||
|
_tagName = node.NodeId;
|
||||||
|
_attributeName = "";
|
||||||
|
_attrs = null;
|
||||||
|
_attrsLoading = true;
|
||||||
|
_attrsError = null;
|
||||||
|
StateHasChanged();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_attrs = await BrowserService.AttributesAsync(_token, _tagName, default);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_attrsError = ex.Message;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_attrsLoading = false;
|
||||||
|
await OnChangedAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SelectAttributeAsync(AttributeInfo a)
|
||||||
|
{
|
||||||
|
_attributeName = a.Name;
|
||||||
|
await OnChangedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnChangedAsync()
|
private async Task OnChangedAsync()
|
||||||
@@ -54,4 +185,15 @@
|
|||||||
return _tagName;
|
return _tagName;
|
||||||
return $"{_tagName}.{_attributeName}";
|
return $"{_tagName}.{_attributeName}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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