完成连接OpcUa服务器
This commit is contained in:
@@ -9,25 +9,15 @@ namespace DMS.Infrastructure.Interfaces.Services
|
||||
{
|
||||
public interface IOpcUaService
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建 OPC UA 会话
|
||||
/// </summary>
|
||||
/// <param name="opcUaServerUrl">OPC UA 服务器地址</param>
|
||||
/// <param name="stoppingToken">取消令牌</param>
|
||||
/// <returns></returns>
|
||||
public Task CreateSession(string opcUaServerUrl, CancellationToken stoppingToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 连接到 OPC UA 服务器(异步)
|
||||
/// </summary>
|
||||
/// <param name="stoppingToken">取消令牌</param>
|
||||
/// <returns></returns>
|
||||
public Task ConnectAsync(CancellationToken stoppingToken = default);
|
||||
public Task ConnectAsync(string opcUaServerUrl,CancellationToken stoppingToken = default);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 连接到 OPC UA 服务器(同步)
|
||||
/// </summary>
|
||||
public void Connect();
|
||||
|
||||
/// <summary>
|
||||
/// 断开 OPC UA 服务器连接
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace DMS.Infrastructure.Services
|
||||
public class OpcUaService : IOpcUaService
|
||||
{
|
||||
private Session? _session;
|
||||
private string? _serverUrl;
|
||||
private string _serverUrl;
|
||||
|
||||
/// <summary>
|
||||
/// 创建 OPC UA 会话
|
||||
@@ -22,17 +22,14 @@ namespace DMS.Infrastructure.Services
|
||||
/// <param name="opcUaServerUrl">OPC UA 服务器地址</param>
|
||||
/// <param name="stoppingToken">取消令牌</param>
|
||||
/// <returns></returns>
|
||||
public async Task CreateSession(string opcUaServerUrl, CancellationToken stoppingToken = default)
|
||||
public async Task CreateSession( CancellationToken stoppingToken = default)
|
||||
{
|
||||
if (string.IsNullOrEmpty(opcUaServerUrl))
|
||||
{
|
||||
throw new ArgumentException("OPC UA server URL cannot be null or empty.", nameof(opcUaServerUrl));
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
_session = await OpcUaHelper.CreateOpcUaSessionAsync(opcUaServerUrl, stoppingToken);
|
||||
_serverUrl = opcUaServerUrl;
|
||||
_session = await OpcUaHelper.CreateOpcUaSessionAsync(_serverUrl, stoppingToken);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -43,8 +40,13 @@ namespace DMS.Infrastructure.Services
|
||||
/// <summary>
|
||||
/// 连接到 OPC UA 服务器
|
||||
/// </summary>
|
||||
public async Task ConnectAsync(CancellationToken stoppingToken = default)
|
||||
public async Task ConnectAsync(string opcUaServerUrl, CancellationToken stoppingToken = default)
|
||||
{
|
||||
_serverUrl = opcUaServerUrl;
|
||||
if (string.IsNullOrEmpty(opcUaServerUrl))
|
||||
{
|
||||
throw new ArgumentException("OPC UA server URL cannot be null or empty.", nameof(opcUaServerUrl));
|
||||
}
|
||||
if (string.IsNullOrEmpty(_serverUrl))
|
||||
{
|
||||
throw new InvalidOperationException("Server URL is not set. Please call CreateSession first.");
|
||||
@@ -57,43 +59,10 @@ namespace DMS.Infrastructure.Services
|
||||
}
|
||||
|
||||
// 重新创建会话
|
||||
await CreateSession(_serverUrl, stoppingToken);
|
||||
await CreateSession( stoppingToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连接到 OPC UA 服务器(同步版本,用于向后兼容)
|
||||
/// </summary>
|
||||
public void Connect()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_serverUrl))
|
||||
{
|
||||
throw new InvalidOperationException("Server URL is not set. Please call CreateSession first.");
|
||||
}
|
||||
|
||||
// 如果已经连接,直接返回
|
||||
if (_session?.Connected == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查会话是否存在但未连接
|
||||
if (_session != null)
|
||||
{
|
||||
// 尝试重新激活会话
|
||||
try
|
||||
{
|
||||
_session.Reconnect();
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 如果重新连接失败,继续到重新创建会话的步骤
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有会话或重新连接失败,抛出异常提示需要调用 CreateSession
|
||||
throw new InvalidOperationException("Session is not created or connection lost. Please call CreateSession first.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 断开 OPC UA 服务器连接
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace DMS.WPF.Services
|
||||
{ typeof(ConfirmDialogViewModel), typeof(ConfirmDialog) },
|
||||
{ typeof(VariableTableDialogViewModel), typeof(VariableTableDialog) },
|
||||
{ typeof(ImportExcelDialogViewModel), typeof(ImportExcelDialog) },
|
||||
{ typeof(ImportOpcUaDialogViewModel), typeof(ImportOpcUaDialog) },
|
||||
{ typeof(VariableDialogViewModel), typeof(VariableDialog) },
|
||||
// { typeof(MqttDialogViewModel), typeof(MqttDialog) }, // Add other mappings here
|
||||
// ... other dialogs
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using DMS.Helper;
|
||||
using DMS.Message;
|
||||
using DMS.WPF.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using DMS.Core.Enums;
|
||||
|
||||
namespace DMS.Services;
|
||||
|
||||
public partial class NavgatorServices : ObservableRecipient, IRecipient<NavgatorMessage>
|
||||
{
|
||||
// [ObservableProperty]
|
||||
private ViewModelBase currentViewModel;
|
||||
|
||||
public NavgatorServices()
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
// partial void OnCurrentViewModelChanging(ViewModelBase viewModel)
|
||||
// {
|
||||
// viewModel?.OnLoading();
|
||||
// }
|
||||
//
|
||||
// partial void OnCurrentViewModelChanged(ViewModelBase viewModel)
|
||||
// {
|
||||
// OnViewModelChanged?.Invoke();
|
||||
// viewModel?.OnLoaded();
|
||||
// }
|
||||
|
||||
public ViewModelBase CurrentViewModel
|
||||
{
|
||||
get => currentViewModel;
|
||||
set { currentViewModel = value; }
|
||||
}
|
||||
|
||||
|
||||
public async void Receive(NavgatorMessage message)
|
||||
{
|
||||
try
|
||||
{
|
||||
ViewModelBase nextViewModel = message.Value;
|
||||
//如果OnExit返回False终止跳转
|
||||
|
||||
if (currentViewModel != null)
|
||||
{
|
||||
var isExit = await currentViewModel.OnExitAsync();
|
||||
if (!isExit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nextViewModel?.OnLoading();
|
||||
CurrentViewModel = message.Value;
|
||||
OnViewModelChanged?.Invoke();
|
||||
currentViewModel?.OnLoaded();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
NotificationHelper.ShowError($"切换视图时发生了错误:{e.Message}", e);
|
||||
}
|
||||
}
|
||||
|
||||
public event Action OnViewModelChanged;
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
// 文件: DMS.WPF/Services/NavigationService.cs
|
||||
|
||||
using DMS.Helper;
|
||||
using DMS.ViewModels;
|
||||
using DMS.WPF.ViewModels;
|
||||
using DMS.WPF.ViewModels.Items;
|
||||
using DMS.WPF.Views;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using DMS.ViewModels;
|
||||
using DMS.WPF.ViewModels.Items;
|
||||
using DMS.WPF.Views;
|
||||
|
||||
namespace DMS.WPF.Services;
|
||||
|
||||
@@ -39,6 +40,13 @@ public class NavigationService : INavigationService
|
||||
|
||||
var mainViewModel = App.Current.Services.GetRequiredService<MainViewModel>();
|
||||
var viewModel = GetViewModelByKey(menu.TargetViewKey);
|
||||
if (viewModel == null)
|
||||
{
|
||||
|
||||
NotificationHelper.ShowError($"切换界面失败,没有找到界面:{menu.TargetViewKey}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (viewModel is INavigatable navigatableViewModel)
|
||||
{
|
||||
@@ -70,7 +78,7 @@ public class NavigationService : INavigationService
|
||||
case "SettingView":
|
||||
return App.Current.Services.GetRequiredService<SettingViewModel>();
|
||||
default:
|
||||
throw new KeyNotFoundException($"未找到与键 '{key}' 关联的视图模型类型。请检查 NavigationService 的映射配置。");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ public partial class DevicesViewModel : ViewModelBase, INavigatable
|
||||
{
|
||||
Header = device.Name,
|
||||
Icon = SegoeFluentIcons.Devices2.Glyph,
|
||||
TargetViewKey = "DevicesView"
|
||||
TargetViewKey = "DeviceDetailView"
|
||||
};
|
||||
if (device.IsAddDefVarTable)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,9 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using DMS.Core.Models;
|
||||
using DMS.Helper;
|
||||
using DMS.Infrastructure.Interfaces.Services;
|
||||
using DMS.WPF.ViewModels.Items;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Client;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
@@ -27,14 +29,25 @@ public partial class ImportOpcUaDialogViewModel : DialogViewModelBase<List<Varia
|
||||
[ObservableProperty]
|
||||
private bool _isConnected;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _connectButtonText="连接服务器";
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isConnectButtonEnabled = true;
|
||||
|
||||
private Session _session;
|
||||
|
||||
public ImportOpcUaDialogViewModel()
|
||||
private readonly IOpcUaService _opcUaService;
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public ImportOpcUaDialogViewModel(IOpcUaService opcUaService)
|
||||
{
|
||||
//OpcUaNodes = new ObservableCollection<OpcUaNode>();
|
||||
SelectedNodeVariables = new ObservableCollection<Variable>();
|
||||
// Automatically connect when the ViewModel is created
|
||||
//ConnectC.Execute(null);
|
||||
this._opcUaService = opcUaService;
|
||||
|
||||
_cancellationTokenSource=new CancellationTokenSource();
|
||||
|
||||
}
|
||||
|
||||
@@ -44,27 +57,29 @@ public partial class ImportOpcUaDialogViewModel : DialogViewModelBase<List<Varia
|
||||
try
|
||||
{
|
||||
// 断开现有连接
|
||||
if (_session != null && _session.Connected)
|
||||
if (!_opcUaService.IsConnected())
|
||||
{
|
||||
await _session.CloseAsync();
|
||||
_session.Dispose();
|
||||
_session = null;
|
||||
await _opcUaService.ConnectAsync(EndpointUrl, _cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
IsConnected = false;
|
||||
SelectedNodeVariables.Clear();
|
||||
IsConnected= _opcUaService.IsConnected();
|
||||
if (IsConnected)
|
||||
{
|
||||
ConnectButtonText = "已连接";
|
||||
IsConnectButtonEnabled = false;
|
||||
}
|
||||
|
||||
//_session = await ServiceHelper.CreateOpcUaSessionAsync(EndpointUrl);
|
||||
|
||||
NotificationHelper.ShowSuccess($"已连接到 OPC UA 服务器: {EndpointUrl}");
|
||||
IsConnected = true;
|
||||
|
||||
// 浏览根节点
|
||||
var rootNodeId = new NodeId(ObjectIds.ObjectsFolder);
|
||||
var list=_opcUaService.BrowseNodes(rootNodeId);
|
||||
//await BrowseNodes(OpcUaNodes, ObjectIds.ObjectsFolder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsConnected = false;
|
||||
IsConnectButtonEnabled = false;
|
||||
ConnectButtonText = "连接服务器";
|
||||
NotificationHelper.ShowError($"连接 OPC UA 服务器失败: {EndpointUrl} - {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ public partial class MqttsViewModel : ViewModelBase
|
||||
private readonly DataServices _dataServices;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ILogger<MqttsViewModel> _logger;
|
||||
private readonly NavgatorServices _navgatorServices;
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<MqttServerItemViewModel> _mqtts;
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
|
||||
}
|
||||
|
||||
// 显示OPC UA导入对话框,让用户选择要导入的变量
|
||||
ImportOpcUaDialogViewModel importOpcUaDialogViewModel = new ImportOpcUaDialogViewModel();
|
||||
ImportOpcUaDialogViewModel importOpcUaDialogViewModel = App.Current.Services.GetRequiredService<ImportOpcUaDialogViewModel>() ;
|
||||
var importedVariables = await _dialogService.ShowDialogAsync(importOpcUaDialogViewModel);
|
||||
if (importedVariables == null || !importedVariables.Any())
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -22,9 +23,31 @@
|
||||
<ColumnDefinition Width="2*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 连接区域 -->
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0,0,0,10"
|
||||
Orientation="Horizontal">
|
||||
<TextBox
|
||||
x:Name="EndpointUrlTextBox"
|
||||
Width="300"
|
||||
Margin="0,0,10,0"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="False"
|
||||
Text="{Binding EndpointUrl, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Button
|
||||
x:Name="ConnectButton"
|
||||
Command="{Binding ConnectCommand}"
|
||||
Content="{Binding ConnectButtonText}"
|
||||
IsEnabled="{Binding IsConnectButtonEnabled}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- 节点树 -->
|
||||
<TreeView
|
||||
Grid.Row="0"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="0,0,10,0"
|
||||
ItemsSource="{Binding OpcUaNodes}"
|
||||
@@ -48,7 +71,7 @@
|
||||
|
||||
<!-- 变量列表 -->
|
||||
<DataGrid
|
||||
Grid.Row="0"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
|
||||
@@ -10,16 +10,10 @@ namespace DMS.WPF.Views.Dialogs;
|
||||
/// </summary>
|
||||
public partial class ImportOpcUaDialog : ContentDialog
|
||||
{
|
||||
public ImportOpcUaDialogViewModel ViewModel
|
||||
{
|
||||
get => (ImportOpcUaDialogViewModel)DataContext;
|
||||
set => DataContext = value;
|
||||
}
|
||||
|
||||
public ImportOpcUaDialog(ImportOpcUaDialogViewModel viewModel)
|
||||
public ImportOpcUaDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
ViewModel = viewModel;
|
||||
}
|
||||
|
||||
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
|
||||
Reference in New Issue
Block a user