完成从OPCServer导入变量的基础功能
This commit is contained in:
340
ViewModels/Dialogs/OpcUaImportDialogViewModel.cs
Normal file
340
ViewModels/Dialogs/OpcUaImportDialogViewModel.cs
Normal file
@@ -0,0 +1,340 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Client;
|
||||
using Opc.Ua.Configuration;
|
||||
using PMSWPF.Enums;
|
||||
using PMSWPF.Helper;
|
||||
using PMSWPF.Models;
|
||||
|
||||
namespace PMSWPF.ViewModels.Dialogs;
|
||||
|
||||
public partial class OpcUaImportDialogViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
private string _endpointUrl = "opc.tcp://127.0.0.1:4855"; // 默认值
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<OpcUaNode> _opcUaNodes;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<VariableData> _selectedNodeVariables;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _selectAllVariables;
|
||||
|
||||
private Session _session;
|
||||
|
||||
public OpcUaImportDialogViewModel()
|
||||
{
|
||||
OpcUaNodes = new ObservableCollection<OpcUaNode>();
|
||||
SelectedNodeVariables = new ObservableCollection<VariableData>();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 断开现有连接
|
||||
if (_session != null && _session.Connected)
|
||||
{
|
||||
_session.Close();
|
||||
_session.Dispose();
|
||||
_session = null;
|
||||
}
|
||||
|
||||
/*ApplicationInstance application = new ApplicationInstance
|
||||
{
|
||||
ApplicationName = "PMSWPF OPC UA Client",
|
||||
ApplicationType = ApplicationType.Client,
|
||||
ConfigSectionName = "PMSWPF.OpcUaClient"
|
||||
};
|
||||
|
||||
ApplicationConfiguration config = await application.LoadApplicationConfiguration(false);
|
||||
|
||||
// var config = new ApplicationConfiguration()
|
||||
// {
|
||||
// ApplicationName = application.ApplicationName,
|
||||
// ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:OpcUADemoClient",
|
||||
// ApplicationType = application.ApplicationType,
|
||||
// SecurityConfiguration = new SecurityConfiguration
|
||||
// {
|
||||
// ApplicationCertificate = new CertificateIdentifier { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/MachineDefault", SubjectName = application.ApplicationName },
|
||||
// TrustedIssuerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/UA Certificate Authorities" },
|
||||
// TrustedPeerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/UA Applications" },
|
||||
// RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/RejectedCertificates" },
|
||||
// AutoAcceptUntrustedCertificates = true // 自动接受不受信任的证书 (仅用于测试)
|
||||
// },
|
||||
// TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
|
||||
// ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
|
||||
// TraceConfiguration = new TraceConfiguration { OutputFilePath = "./Logs/OpcUaClient.log", DeleteOnLoad = true, TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security }
|
||||
// };
|
||||
//
|
||||
// bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0);
|
||||
// if (!haveAppCertificate)
|
||||
// {
|
||||
// throw new Exception("Application instance certificate invalid!");
|
||||
// }
|
||||
//
|
||||
// EndpointDescription selectedEndpoint
|
||||
// = CoreClientUtils.SelectEndpoint(application.ApplicationConfiguration, EndpointUrl, false);
|
||||
// EndpointConfiguration endpointConfiguration
|
||||
// = EndpointConfiguration.Create(application.ApplicationConfiguration);
|
||||
// ConfiguredEndpoint configuredEndpoint
|
||||
// = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration);
|
||||
// var config = new ApplicationConfiguration()
|
||||
// {
|
||||
// ApplicationName = application.ApplicationName,
|
||||
// ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:OpcUADemoClient",
|
||||
// ApplicationType = application.ApplicationType,
|
||||
// SecurityConfiguration = new SecurityConfiguration
|
||||
// {
|
||||
// ApplicationCertificate = new CertificateIdentifier { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/MachineDefault", SubjectName = application.ApplicationName },
|
||||
// TrustedIssuerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/UA Certificate Authorities" },
|
||||
// TrustedPeerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/UA Applications" },
|
||||
// RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/RejectedCertificates" },
|
||||
// AutoAcceptUntrustedCertificates = true // 自动接受不受信任的证书 (仅用于测试)
|
||||
// },
|
||||
// TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
|
||||
// ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
|
||||
// TraceConfiguration = new TraceConfiguration { OutputFilePath = "./Logs/OpcUaClient.log", DeleteOnLoad = true, TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security }
|
||||
// };
|
||||
|
||||
bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0);
|
||||
if (!haveAppCertificate)
|
||||
{
|
||||
throw new Exception("Application instance certificate invalid!");
|
||||
}
|
||||
|
||||
EndpointDescription selectedEndpoint
|
||||
= CoreClientUtils.SelectEndpoint(application.ApplicationConfiguration, EndpointUrl, false);
|
||||
EndpointConfiguration endpointConfiguration
|
||||
= EndpointConfiguration.Create(application.ApplicationConfiguration);
|
||||
ConfiguredEndpoint configuredEndpoint
|
||||
= new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration);
|
||||
|
||||
_session = await Session.Create(
|
||||
config,
|
||||
configuredEndpoint,
|
||||
false,
|
||||
"PMSWPF OPC UA Session",
|
||||
60000,
|
||||
new UserIdentity(new AnonymousIdentityToken()),
|
||||
null);
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// 1. 创建应用程序配置
|
||||
var application = new ApplicationInstance
|
||||
{
|
||||
ApplicationName = "OpcUADemoClient",
|
||||
ApplicationType = ApplicationType.Client,
|
||||
ConfigSectionName = "Opc.Ua.Client"
|
||||
};
|
||||
|
||||
var config = new ApplicationConfiguration()
|
||||
{
|
||||
ApplicationName = application.ApplicationName,
|
||||
ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:OpcUADemoClient",
|
||||
ApplicationType = application.ApplicationType,
|
||||
SecurityConfiguration = new SecurityConfiguration
|
||||
{
|
||||
ApplicationCertificate = new CertificateIdentifier { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/MachineDefault", SubjectName = application.ApplicationName },
|
||||
TrustedIssuerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/UA Certificate Authorities" },
|
||||
TrustedPeerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/UA Applications" },
|
||||
RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/RejectedCertificates" },
|
||||
AutoAcceptUntrustedCertificates = true // 自动接受不受信任的证书 (仅用于测试)
|
||||
},
|
||||
TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
|
||||
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
|
||||
TraceConfiguration = new TraceConfiguration { OutputFilePath = "./Logs/OpcUaClient.log", DeleteOnLoad = true, TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security }
|
||||
};
|
||||
application.ApplicationConfiguration = config;
|
||||
|
||||
// 验证并检查证书
|
||||
await config.Validate(ApplicationType.Client);
|
||||
await application.CheckApplicationInstanceCertificate(false, 0);
|
||||
|
||||
// 2. 查找并选择端点 (将 useSecurity 设置为 false 以进行诊断)
|
||||
var selectedEndpoint = CoreClientUtils.SelectEndpoint(EndpointUrl, false);
|
||||
|
||||
_session = await Session.Create(
|
||||
config,
|
||||
new ConfiguredEndpoint(null, selectedEndpoint, EndpointConfiguration.Create(config)),
|
||||
false,
|
||||
"PMSWPF OPC UA Session",
|
||||
60000,
|
||||
new UserIdentity(new AnonymousIdentityToken()),
|
||||
null);
|
||||
|
||||
NotificationHelper.ShowSuccess($"已连接到 OPC UA 服务器: {EndpointUrl}");
|
||||
|
||||
// 浏览根节点
|
||||
await BrowseNodes(OpcUaNodes, ObjectIds.ObjectsFolder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NlogHelper.Error($"连接 OPC UA 服务器失败: {EndpointUrl} - {ex.Message}", ex);
|
||||
NotificationHelper.ShowError($"连接 OPC UA 服务器失败: {EndpointUrl} - {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 处理来自服务器的数据变化通知
|
||||
/// </summary>
|
||||
private static void OnNotification(MonitoredItem item, MonitoredItemNotificationEventArgs e)
|
||||
{
|
||||
foreach (var value in item.DequeueValues())
|
||||
{
|
||||
Console.WriteLine($"[通知] {item.DisplayName}: {value.Value} | 时间戳: {value.SourceTimestamp.ToLocalTime()} | 状态: {value.StatusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BrowseNodes(ObservableCollection<OpcUaNode> nodes, NodeId parentNodeId)
|
||||
{
|
||||
try
|
||||
{
|
||||
Opc.Ua.ReferenceDescriptionCollection references;
|
||||
byte[] continuationPoint = null;
|
||||
|
||||
_session.Browse(
|
||||
null, // RequestHeader
|
||||
new ViewDescription(),
|
||||
parentNodeId,
|
||||
0u,
|
||||
BrowseDirection.Forward,
|
||||
Opc.Ua.ReferenceTypeIds.HierarchicalReferences,
|
||||
true,
|
||||
(uint)Opc.Ua.NodeClass.Object | (uint)Opc.Ua.NodeClass.Variable,
|
||||
out continuationPoint,
|
||||
out references
|
||||
);
|
||||
|
||||
foreach (var rd in references)
|
||||
{
|
||||
NodeType nodeType = NodeType.Folder; // 默认是文件夹
|
||||
if ((rd.NodeClass & NodeClass.Variable) != 0)
|
||||
{
|
||||
nodeType = NodeType.Variable;
|
||||
}
|
||||
else if ((rd.NodeClass & NodeClass.Object) != 0)
|
||||
{
|
||||
nodeType = NodeType.Object;
|
||||
}
|
||||
|
||||
var opcUaNode = new OpcUaNode(rd.DisplayName.Text, (NodeId)rd.NodeId, nodeType);
|
||||
nodes.Add(opcUaNode);
|
||||
|
||||
// 如果是文件夹或对象,添加一个虚拟子节点,用于懒加载
|
||||
if (nodeType == NodeType.Folder || nodeType == NodeType.Object)
|
||||
{
|
||||
opcUaNode.Children.Add(new OpcUaNode("Loading...", NodeId.Null, NodeType.Folder)); // 虚拟节点
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NlogHelper.Error($"浏览 OPC UA 节点失败: {parentNodeId} - {ex.Message}", ex);
|
||||
NotificationHelper.ShowError($"浏览 OPC UA 节点失败: {parentNodeId} - {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LoadNodeVariables(OpcUaNode node)
|
||||
{
|
||||
if (node.NodeType == NodeType.Variable)
|
||||
{
|
||||
// 如果是变量节点,直接显示它
|
||||
SelectedNodeVariables.Clear();
|
||||
SelectedNodeVariables.Add(new VariableData
|
||||
{
|
||||
Name = node.DisplayName,
|
||||
NodeId = node.NodeId.ToString(),
|
||||
OpcUaNodeId = node.NodeId.ToString(),
|
||||
ProtocolType = ProtocolType.OpcUA,
|
||||
IsActive = true // 默认选中
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.IsLoaded || node.IsLoading)
|
||||
{
|
||||
return; // 已经加载或正在加载
|
||||
}
|
||||
|
||||
node.IsLoading = true;
|
||||
node.Children.Clear(); // 清除虚拟节点
|
||||
|
||||
try
|
||||
{
|
||||
Opc.Ua.ReferenceDescriptionCollection references;
|
||||
byte[] continuationPoint = null;
|
||||
|
||||
_session.Browse(
|
||||
null, // RequestHeader
|
||||
new ViewDescription(),
|
||||
node.NodeId,
|
||||
0u,
|
||||
BrowseDirection.Forward,
|
||||
Opc.Ua.ReferenceTypeIds.HierarchicalReferences,
|
||||
true,
|
||||
(uint)Opc.Ua.NodeClass.Object | (uint)Opc.Ua.NodeClass.Variable,
|
||||
out continuationPoint,
|
||||
out references
|
||||
);
|
||||
|
||||
foreach (var rd in references)
|
||||
{
|
||||
NodeType nodeType = NodeType.Folder;
|
||||
if ((rd.NodeClass & NodeClass.Variable) != 0)
|
||||
{
|
||||
nodeType = NodeType.Variable;
|
||||
}
|
||||
else if ((rd.NodeClass & NodeClass.Object) != 0)
|
||||
{
|
||||
nodeType = NodeType.Object;
|
||||
}
|
||||
|
||||
var opcUaNode = new OpcUaNode(rd.DisplayName.Text, (NodeId)rd.NodeId, nodeType);
|
||||
node.Children.Add(opcUaNode);
|
||||
|
||||
if (nodeType == NodeType.Folder || nodeType == NodeType.Object)
|
||||
{
|
||||
opcUaNode.Children.Add(new OpcUaNode("Loading...", NodeId.Null, NodeType.Folder)); // 虚拟节点
|
||||
}
|
||||
|
||||
// 如果是变量,添加到右侧列表
|
||||
if (nodeType == NodeType.Variable)
|
||||
{
|
||||
SelectedNodeVariables.Add(new VariableData
|
||||
{
|
||||
Name = opcUaNode.DisplayName,
|
||||
NodeId = opcUaNode.NodeId.ToString(),
|
||||
OpcUaNodeId = opcUaNode.NodeId.ToString(),
|
||||
ProtocolType = ProtocolType.OpcUA,
|
||||
IsActive = true // 默认选中
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
node.IsLoaded = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NlogHelper.Error($"加载 OPC UA 节点变量失败: {node.NodeId} - {ex.Message}", ex);
|
||||
NotificationHelper.ShowError($"加载 OPC UA 节点变量失败: {node.NodeId} - {ex.Message}", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
node.IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<VariableData> GetSelectedVariables()
|
||||
{
|
||||
return new ObservableCollection<VariableData>(SelectedNodeVariables.Where(v => v.IsActive));
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,9 @@ partial class VariableTableViewModel : ViewModelBase
|
||||
[ObservableProperty]
|
||||
private bool _isS7ProtocolSelected;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isOpcUaProtocolSelected;
|
||||
|
||||
public VariableTableViewModel(IDialogService dialogService)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
@@ -85,6 +88,7 @@ partial class VariableTableViewModel : ViewModelBase
|
||||
public override void OnLoaded()
|
||||
{
|
||||
IsS7ProtocolSelected = VariableTable.ProtocolType == ProtocolType.S7;
|
||||
IsOpcUaProtocolSelected = VariableTable.ProtocolType == ProtocolType.OpcUA;
|
||||
|
||||
if (VariableTable.DataVariables != null)
|
||||
{
|
||||
@@ -223,6 +227,44 @@ partial class VariableTableViewModel : ViewModelBase
|
||||
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ImportFromOpcUaServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
var importedVariables = await _dialogService.ShowOpcUaImportDialog();
|
||||
if (importedVariables == null || !importedVariables.Any())
|
||||
{
|
||||
return; // 用户取消或没有选择任何变量
|
||||
}
|
||||
|
||||
ContentDialog processingDialog = _dialogService.ShowProcessingDialog("正在处理...", "正在导入OPC UA变量,请稍等片刻....");
|
||||
|
||||
foreach (var variableData in importedVariables)
|
||||
{
|
||||
variableData.CreateTime = DateTime.Now;
|
||||
variableData.VariableTableId = VariableTable.Id;
|
||||
variableData.ProtocolType = ProtocolType.OpcUA; // 确保协议类型正确
|
||||
}
|
||||
|
||||
// 插入数据库
|
||||
var resVarDataCount = await _varDataRepository.AddAsync(importedVariables);
|
||||
|
||||
// 更新界面
|
||||
variableTable.DataVariables = await _varDataRepository.GetAllAsync();
|
||||
_dataVariables = new ObservableCollection<VariableData>(variableTable.DataVariables);
|
||||
processingDialog?.Hide();
|
||||
|
||||
string msgSuccess = $"成功导入OPC UA变量:{resVarDataCount}个。";
|
||||
NlogHelper.Info(msgSuccess);
|
||||
NotificationHelper.ShowSuccess(msgSuccess);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
NotificationHelper.ShowError($"从OPC UA服务器导入变量的过程中发生了不可预期的错误:{e.Message}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
private async void AddVarData(VariableTable variableTable)
|
||||
|
||||
Reference in New Issue
Block a user