fix(dcl+centralui): MxGateway tag browse — lazy attributes, frame-size cap, wider scrollable picker
Expanding a Galaxy object in the tag picker hung on "loading…": the browse reply inlined every child's full attribute set (~152 KB), exceeding Akka's 128 KB remote frame, and remoting silently discarded the oversized reply. Browse path (DataConnectionLayer): - RealMxGatewayClient: navigation now uses BrowseChildren(include_attributes= false) — child objects only — and an object's own attributes load lazily via DiscoverHierarchy(root, max_depth=0) when it's expanded. Payload drops from ~152 KB/level to a few KB. Seam contract unchanged. - DataConnectionActor.CapBrowseChildren: protocol-agnostic byte-budget cap (~100 KB) on every BrowseNodeResult before it crosses the site→central frame, OR-ing the adapter's own Truncated flag. Byte budget, not a count — the only bound that holds regardless of NodeId/attribute-name length. - RealOpcUaClient: requestedMaxReferencesPerNode 1000 → 500 to narrow the window before the byte budget applies. - Graceful gRPC Unimplemented handling → NotSupportedException → BrowseFailureKind.NotBrowsable with an actionable message (older gateway builds lacking BrowseChildren). Picker UI (CentralUI): - NodeBrowserDialog: modal-lg → modal-xl; new scoped .razor.css caps the tree at 55vh with its own scrollbar so manual entry + Select/Cancel stay visible. - Protocol-agnostic failure messages (was hardcoded "OPC UA …"); renamed the leftover opcua-browser-tree class to node-browser-tree. Tests: new frame-budget cap test + NotSupported=>NotBrowsable mapping test; DCL suite 88/88. Doc: Component-DataConnectionLayer.md records the lazy attribute-light browse and the frame-size guard.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
@if (_isVisible)
|
||||
{
|
||||
<div class="modal show d-block" tabindex="-1" role="dialog" style="background-color: rgba(0,0,0,0.5);">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-dialog modal-xl" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Browse — @ConnectionName</h5>
|
||||
@@ -21,7 +21,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="opcua-browser-tree">
|
||||
<div class="node-browser-tree">
|
||||
@if (_rootNodes.Count == 0 && _failure is null)
|
||||
{
|
||||
<em class="text-muted">Loading…</em>
|
||||
@@ -167,20 +167,25 @@
|
||||
_manualNodeId = node.NodeId;
|
||||
}
|
||||
|
||||
// Task 17: map each BrowseFailureKind to a friendly UI message. The raw
|
||||
// failure.Message is surfaced verbatim only for ServerError (which carries
|
||||
// the OPC UA SDK's own Bad_* text) and as the default fallback for any
|
||||
// future failure kind added without a UI mapping.
|
||||
// Task 17: map each BrowseFailureKind to a friendly UI message. Messages are
|
||||
// protocol-agnostic (the dialog serves every browsable protocol — OPC UA,
|
||||
// MxGateway, …). The raw failure.Message is surfaced verbatim for ServerError
|
||||
// (which carries the underlying protocol SDK's own error text), for
|
||||
// NotBrowsable when the adapter supplied a reason (e.g. a gateway build that
|
||||
// lacks the browse RPC), and as the default fallback for any future failure
|
||||
// kind added without a UI mapping.
|
||||
private void SetFailure(BrowseFailure failure)
|
||||
{
|
||||
_failure = failure;
|
||||
_failureMessage = failure.Kind switch
|
||||
{
|
||||
BrowseFailureKind.ConnectionNotFound => "Connection no longer exists at the site.",
|
||||
BrowseFailureKind.ConnectionNotConnected => "OPC UA session not connected — retry shortly or use manual entry.",
|
||||
BrowseFailureKind.NotBrowsable => "This connection does not support browsing.",
|
||||
BrowseFailureKind.ConnectionNotConnected => "Connection not connected — retry shortly or use manual entry.",
|
||||
BrowseFailureKind.NotBrowsable => string.IsNullOrWhiteSpace(failure.Message)
|
||||
? "This connection does not support browsing."
|
||||
: failure.Message,
|
||||
BrowseFailureKind.Timeout => "Browse timed out — the server may be slow. Try again or enter the node id manually.",
|
||||
BrowseFailureKind.ServerError => $"OPC UA server error: {failure.Message}",
|
||||
BrowseFailureKind.ServerError => $"Server error: {failure.Message}",
|
||||
_ => failure.Message
|
||||
};
|
||||
StateHasChanged();
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/* Scoped styles for the protocol-agnostic tag browse dialog. */
|
||||
|
||||
/* Cap the tree's height and let it scroll independently so deep hierarchies
|
||||
(e.g. a Galaxy with many objects/attributes) don't push the manual-entry
|
||||
field and Select/Cancel buttons off-screen. Both axes scroll: vertical for
|
||||
long sibling lists, horizontal for deeply-indented nested nodes. */
|
||||
.node-browser-tree {
|
||||
max-height: 55vh;
|
||||
overflow: auto;
|
||||
border: 1px solid var(--bs-border-color, #dee2e6);
|
||||
border-radius: 0.375rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background-color: var(--bs-body-bg, #fff);
|
||||
}
|
||||
Reference in New Issue
Block a user