Add cross-platform OPC UA client stack: shared library, CLI tool, and Avalonia UI
Implements Client.Shared (IOpcUaClientService with connection lifecycle, failover, browse, read/write, subscriptions, alarms, history, redundancy), Client.CLI (8 CliFx commands mirroring tools/opcuacli-dotnet), and Client.UI (Avalonia desktop app with tree browser, read/write, subscriptions, alarms, and history tabs). All three target .NET 10 and are covered by 249 unit tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Opc.Ua;
|
||||
using ZB.MOM.WW.LmxOpcUa.Client.Shared;
|
||||
using ZB.MOM.WW.LmxOpcUa.Client.UI.Services;
|
||||
|
||||
namespace ZB.MOM.WW.LmxOpcUa.Client.UI.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel for the read/write panel.
|
||||
/// </summary>
|
||||
public partial class ReadWriteViewModel : ObservableObject
|
||||
{
|
||||
private readonly IOpcUaClientService _service;
|
||||
private readonly IUiDispatcher _dispatcher;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyCanExecuteChangedFor(nameof(ReadCommand))]
|
||||
[NotifyCanExecuteChangedFor(nameof(WriteCommand))]
|
||||
private string? _selectedNodeId;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _currentValue;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _currentStatus;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _sourceTimestamp;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _serverTimestamp;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _writeValue;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _writeStatus;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyCanExecuteChangedFor(nameof(ReadCommand))]
|
||||
[NotifyCanExecuteChangedFor(nameof(WriteCommand))]
|
||||
private bool _isConnected;
|
||||
|
||||
public bool IsNodeSelected => !string.IsNullOrEmpty(SelectedNodeId);
|
||||
|
||||
public ReadWriteViewModel(IOpcUaClientService service, IUiDispatcher dispatcher)
|
||||
{
|
||||
_service = service;
|
||||
_dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
partial void OnSelectedNodeIdChanged(string? value)
|
||||
{
|
||||
OnPropertyChanged(nameof(IsNodeSelected));
|
||||
if (!string.IsNullOrEmpty(value) && IsConnected)
|
||||
{
|
||||
_ = ExecuteReadAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanReadOrWrite() => IsConnected && !string.IsNullOrEmpty(SelectedNodeId);
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanReadOrWrite))]
|
||||
private async Task ReadAsync()
|
||||
{
|
||||
await ExecuteReadAsync();
|
||||
}
|
||||
|
||||
private async Task ExecuteReadAsync()
|
||||
{
|
||||
if (string.IsNullOrEmpty(SelectedNodeId)) return;
|
||||
|
||||
try
|
||||
{
|
||||
var nodeId = NodeId.Parse(SelectedNodeId);
|
||||
var dataValue = await _service.ReadValueAsync(nodeId);
|
||||
|
||||
_dispatcher.Post(() =>
|
||||
{
|
||||
CurrentValue = dataValue.Value?.ToString() ?? "(null)";
|
||||
CurrentStatus = dataValue.StatusCode.ToString();
|
||||
SourceTimestamp = dataValue.SourceTimestamp.ToString("O");
|
||||
ServerTimestamp = dataValue.ServerTimestamp.ToString("O");
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dispatcher.Post(() =>
|
||||
{
|
||||
CurrentValue = null;
|
||||
CurrentStatus = $"Error: {ex.Message}";
|
||||
SourceTimestamp = null;
|
||||
ServerTimestamp = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanReadOrWrite))]
|
||||
private async Task WriteAsync()
|
||||
{
|
||||
if (string.IsNullOrEmpty(SelectedNodeId) || WriteValue == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
var nodeId = NodeId.Parse(SelectedNodeId);
|
||||
var statusCode = await _service.WriteValueAsync(nodeId, WriteValue);
|
||||
|
||||
_dispatcher.Post(() =>
|
||||
{
|
||||
WriteStatus = statusCode.ToString();
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dispatcher.Post(() =>
|
||||
{
|
||||
WriteStatus = $"Error: {ex.Message}";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all displayed values.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
SelectedNodeId = null;
|
||||
CurrentValue = null;
|
||||
CurrentStatus = null;
|
||||
SourceTimestamp = null;
|
||||
ServerTimestamp = null;
|
||||
WriteValue = null;
|
||||
WriteStatus = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user