64e3fbe035
v2-ci / build (push) Failing after 1m43s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Adds <summary>, <param>, <typeparam>, and <inheritdoc/> tags to public members surfaced by commentchecker — resolves 5,847 of 5,869 issues (99.6%) across three /fixdocs passes.
118 lines
3.7 KiB
C#
118 lines
3.7 KiB
C#
using System.Collections.ObjectModel;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using ZB.MOM.WW.OtOpcUa.Client.Shared;
|
|
using ZB.MOM.WW.OtOpcUa.Client.UI.Services;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Client.UI.ViewModels;
|
|
|
|
/// <summary>
|
|
/// Represents a single node in the OPC UA browse tree with lazy-load support.
|
|
/// </summary>
|
|
public partial class TreeNodeViewModel : ObservableObject
|
|
{
|
|
private static readonly TreeNodeViewModel PlaceholderSentinel = new();
|
|
private readonly IUiDispatcher? _dispatcher;
|
|
|
|
private readonly IOpcUaClientService? _service;
|
|
private bool _hasLoadedChildren;
|
|
|
|
[ObservableProperty] private bool _isExpanded;
|
|
|
|
[ObservableProperty] private bool _isLoading;
|
|
|
|
/// <summary>
|
|
/// Private constructor for the placeholder sentinel only.
|
|
/// </summary>
|
|
private TreeNodeViewModel()
|
|
{
|
|
NodeId = string.Empty;
|
|
DisplayName = "Loading...";
|
|
NodeClass = string.Empty;
|
|
HasChildren = false;
|
|
}
|
|
|
|
/// <summary>Initializes a new tree node view model.</summary>
|
|
/// <param name="nodeId">The OPC UA node identifier.</param>
|
|
/// <param name="displayName">The display name for this node.</param>
|
|
/// <param name="nodeClass">The OPC UA node class.</param>
|
|
/// <param name="hasChildren">Whether this node has child nodes.</param>
|
|
/// <param name="service">The OPC UA client service for browsing.</param>
|
|
/// <param name="dispatcher">The UI dispatcher for thread-safe updates.</param>
|
|
public TreeNodeViewModel(
|
|
string nodeId,
|
|
string displayName,
|
|
string nodeClass,
|
|
bool hasChildren,
|
|
IOpcUaClientService service,
|
|
IUiDispatcher dispatcher)
|
|
{
|
|
NodeId = nodeId;
|
|
DisplayName = displayName;
|
|
NodeClass = nodeClass;
|
|
HasChildren = hasChildren;
|
|
_service = service;
|
|
_dispatcher = dispatcher;
|
|
|
|
if (hasChildren) Children.Add(PlaceholderSentinel);
|
|
}
|
|
|
|
/// <summary>The string NodeId of this node.</summary>
|
|
public string NodeId { get; }
|
|
|
|
/// <summary>The display name shown in the tree.</summary>
|
|
public string DisplayName { get; }
|
|
|
|
/// <summary>The OPC UA node class (Object, Variable, etc.).</summary>
|
|
public string NodeClass { get; }
|
|
|
|
/// <summary>Whether this node has child references.</summary>
|
|
public bool HasChildren { get; }
|
|
|
|
/// <summary>Child nodes (may contain a placeholder sentinel before first expand).</summary>
|
|
public ObservableCollection<TreeNodeViewModel> Children { get; } = [];
|
|
|
|
/// <summary>
|
|
/// Returns whether this node instance is the placeholder sentinel.
|
|
/// </summary>
|
|
internal bool IsPlaceholder => ReferenceEquals(this, PlaceholderSentinel);
|
|
|
|
partial void OnIsExpandedChanged(bool value)
|
|
{
|
|
if (value && !_hasLoadedChildren && HasChildren) _ = LoadChildrenAsync();
|
|
}
|
|
|
|
private async Task LoadChildrenAsync()
|
|
{
|
|
if (_service == null || _dispatcher == null) return;
|
|
|
|
_hasLoadedChildren = true;
|
|
IsLoading = true;
|
|
|
|
try
|
|
{
|
|
var nodeId = Opc.Ua.NodeId.Parse(NodeId);
|
|
var results = await _service.BrowseAsync(nodeId);
|
|
|
|
_dispatcher.Post(() =>
|
|
{
|
|
Children.Clear();
|
|
foreach (var result in results)
|
|
Children.Add(new TreeNodeViewModel(
|
|
result.NodeId,
|
|
result.DisplayName,
|
|
result.NodeClass,
|
|
result.HasChildren,
|
|
_service,
|
|
_dispatcher));
|
|
});
|
|
}
|
|
catch
|
|
{
|
|
_dispatcher.Post(() => Children.Clear());
|
|
}
|
|
finally
|
|
{
|
|
_dispatcher.Post(() => IsLoading = false);
|
|
}
|
|
}
|
|
} |