feat(dcl+ui): rename BrowseOpcUaNode -> ConnectionName-keyed; implement site handler + dialog failure mapping

- BrowseOpcUaNodeCommand: int DataConnectionId -> string ConnectionName
  (site DataConnectionManagerActor indexes children by name; CentralUI
  already has the connection name in scope via the dropdown — no extra
  plumbing across the trust boundary).
- IOpcUaBrowseService / OpcUaBrowseService: parameter renamed accordingly.
- OpcUaBrowserDialog: collapse the duplicate ConnectionName parameters
  (display label and routing key are the same string).
- Task 10: DataConnectionManagerActor forwards BrowseOpcUaNodeCommand to
  its child by name (owns ConnectionNotFound); DataConnectionActor adds
  the receive across all three lifecycle states (Connecting / Connected
  / Reconnecting) and maps adapter outcomes to BrowseFailureKind
  (NotBrowsable / ConnectionNotConnected / Timeout / ServerError).
- Task 17: SetFailure in OpcUaBrowserDialog implements the full
  BrowseFailureKind switch with friendly UI messages.
- Tests: DataConnectionManagerBrowseHandlerTests covers ConnectionNotFound,
  NotBrowsable, success, and ConnectionNotConnectedException paths.
This commit is contained in:
Joseph Doherty
2026-05-28 12:09:43 -04:00
parent 6999aedc60
commit d285174597
7 changed files with 313 additions and 13 deletions
@@ -57,7 +57,12 @@
@code {
[Parameter] public string SiteId { get; set; } = "";
[Parameter] public int DataConnectionId { get; set; }
/// <summary>
/// Name of the site-local data connection. Serves both as the modal-header
/// display label AND as the routing key for the browse round-trip — the
/// site's <c>DataConnectionManagerActor</c> indexes its children by
/// connection name (no id-keyed lookup at the site).
/// </summary>
[Parameter] public string ConnectionName { get; set; } = "";
[Parameter] public string? InitialNodeId { get; set; }
[Parameter] public EventCallback<string> OnSelected { get; set; }
@@ -105,7 +110,7 @@
_rootNodes = new();
StateHasChanged();
var result = await BrowseService.BrowseChildrenAsync(SiteId, DataConnectionId, parentNodeId: null);
var result = await BrowseService.BrowseChildrenAsync(SiteId, ConnectionName, parentNodeId: null);
if (result.Failure is not null)
{
SetFailure(result.Failure);
@@ -130,7 +135,7 @@
{
node.Loading = true;
StateHasChanged();
var result = await BrowseService.BrowseChildrenAsync(SiteId, DataConnectionId, node.NodeId);
var result = await BrowseService.BrowseChildrenAsync(SiteId, ConnectionName, node.NodeId);
node.Loading = false;
if (result.Failure is not null)
@@ -155,12 +160,23 @@
_manualNodeId = node.NodeId;
}
// NOTE: Task 17 will replace this body with the full BrowseFailureKind switch
// that maps each failure kind to a friendly UI message.
// 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.
private void SetFailure(BrowseFailure failure)
{
_failure = failure;
_failureMessage = failure.Message;
_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.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}",
_ => failure.Message
};
StateHasChanged();
}
private Task RetryRootLoad() => LoadRootAsync();