完成打开导入OPC变量对话框自动连接服务器
This commit is contained in:
@@ -180,9 +180,10 @@ public class DialogService :IDialogService
|
|||||||
return result == ContentDialogResult.Primary ? vm.SelectedMqtt : null;
|
return result == ContentDialogResult.Primary ? vm.SelectedMqtt : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<VariableData>> ShowOpcUaImportDialog()
|
public async Task<List<VariableData>> ShowOpcUaImportDialog(string endpointUrl)
|
||||||
{
|
{
|
||||||
var vm= new OpcUaImportDialogViewModel();
|
var vm= new OpcUaImportDialogViewModel();
|
||||||
|
vm.EndpointUrl = endpointUrl;
|
||||||
var dialog = new OpcUaImportDialog(vm);
|
var dialog = new OpcUaImportDialog(vm);
|
||||||
var result = await dialog.ShowAsync();
|
var result = await dialog.ShowAsync();
|
||||||
return result == ContentDialogResult.Primary ? vm.GetSelectedVariables().ToList() : null;
|
return result == ContentDialogResult.Primary ? vm.GetSelectedVariables().ToList() : null;
|
||||||
|
|||||||
@@ -22,5 +22,5 @@ public interface IDialogService
|
|||||||
ContentDialog ShowProcessingDialog(string title, string message);
|
ContentDialog ShowProcessingDialog(string title, string message);
|
||||||
Task<PollLevelType?> ShowPollLevelDialog(PollLevelType pollLevelType);
|
Task<PollLevelType?> ShowPollLevelDialog(PollLevelType pollLevelType);
|
||||||
Task<Mqtt?> ShowMqttSelectionDialog();
|
Task<Mqtt?> ShowMqttSelectionDialog();
|
||||||
Task<List<VariableData>> ShowOpcUaImportDialog();
|
Task<List<VariableData>> ShowOpcUaImportDialog(string endpointUrl);
|
||||||
}
|
}
|
||||||
@@ -216,43 +216,76 @@ namespace PMSWPF.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 1. 创建应用程序实例。
|
// 1. 创建应用程序配置
|
||||||
ApplicationInstance application = new ApplicationInstance
|
var application = new ApplicationInstance
|
||||||
{
|
{
|
||||||
ApplicationName = "PMSWPF OPC UA Client",
|
ApplicationName = "OpcUADemoClient",
|
||||||
ApplicationType = ApplicationType.Client,
|
ApplicationType = ApplicationType.Client,
|
||||||
ConfigSectionName = "PMSWPF.OpcUaClient"
|
ConfigSectionName = "Opc.Ua.Client"
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. 加载应用程序配置。
|
var config = new ApplicationConfiguration()
|
||||||
ApplicationConfiguration config = await application.LoadApplicationConfiguration(false);
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
// 3. 检查应用程序实例证书。
|
// 验证并检查证书
|
||||||
bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, 0);
|
await config.Validate(ApplicationType.Client);
|
||||||
if (!haveAppCertificate)
|
await application.CheckApplicationInstanceCertificate(false, 0);
|
||||||
{
|
|
||||||
throw new Exception("Application instance certificate invalid!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 发现服务器提供的终结点。
|
// 2. 查找并选择端点 (将 useSecurity 设置为 false 以进行诊断)
|
||||||
DiscoveryClient discoveryClient = DiscoveryClient.Create(new Uri(device.OpcUaEndpointUrl));
|
var selectedEndpoint = CoreClientUtils.SelectEndpoint(device.OpcUaEndpointUrl, false);
|
||||||
EndpointDescriptionCollection endpoints = discoveryClient.GetEndpoints(new Opc.Ua.StringCollection { device.OpcUaEndpointUrl });
|
|
||||||
|
|
||||||
// 简化处理:选择第一个无安全策略的终结点。在生产环境中应选择合适的安全策略。
|
session = await Session.Create(
|
||||||
// ConfiguredEndpoint configuredEndpoint = new ConfiguredEndpoint(null, endpoints.First(e => e.SecurityMode == MessageSecurityMode.None), config);
|
config,
|
||||||
EndpointDescription selectedEndpoint = CoreClientUtils.SelectEndpoint(application.ApplicationConfiguration, device.OpcUaEndpointUrl, false);
|
new ConfiguredEndpoint(null, selectedEndpoint, EndpointConfiguration.Create(config)),
|
||||||
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(application.ApplicationConfiguration);
|
false,
|
||||||
ConfiguredEndpoint configuredEndpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration);
|
"PMSWPF OPC UA Session",
|
||||||
|
60000,
|
||||||
// 5. 创建会话。
|
new UserIdentity(new AnonymousIdentityToken()),
|
||||||
session = await Session.Create(
|
null);
|
||||||
config,
|
|
||||||
configuredEndpoint,
|
|
||||||
false, // 不更新证书
|
|
||||||
"PMSWPF OPC UA Session", // 会话名称
|
|
||||||
60000, // 会话超时时间(毫秒)
|
|
||||||
new UserIdentity(new AnonymousIdentityToken()), // 使用匿名用户身份
|
|
||||||
null);
|
|
||||||
|
|
||||||
_opcUaSessions[device.OpcUaEndpointUrl] = session;
|
_opcUaSessions[device.OpcUaEndpointUrl] = session;
|
||||||
NlogHelper.Info($"Connected to OPC UA server: {device.OpcUaEndpointUrl}");
|
NlogHelper.Info($"Connected to OPC UA server: {device.OpcUaEndpointUrl}");
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public partial class OpcUaImportDialogViewModel : ObservableObject
|
|||||||
{
|
{
|
||||||
OpcUaNodes = new ObservableCollection<OpcUaNode>();
|
OpcUaNodes = new ObservableCollection<OpcUaNode>();
|
||||||
SelectedNodeVariables = new ObservableCollection<VariableData>();
|
SelectedNodeVariables = new ObservableCollection<VariableData>();
|
||||||
|
// Automatically connect when the ViewModel is created
|
||||||
|
_ = Connect().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
|
|||||||
@@ -232,7 +232,13 @@ partial class VariableTableViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var importedVariables = await _dialogService.ShowOpcUaImportDialog();
|
string opcUaEndpointUrl = VariableTable?.Device?.OpcUaEndpointUrl;
|
||||||
|
if (string.IsNullOrEmpty(opcUaEndpointUrl))
|
||||||
|
{
|
||||||
|
NotificationHelper.ShowError("OPC UA Endpoint URL 未设置。请在设备详情中配置。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var importedVariables = await _dialogService.ShowOpcUaImportDialog(opcUaEndpointUrl);
|
||||||
if (importedVariables == null || !importedVariables.Any())
|
if (importedVariables == null || !importedVariables.Any())
|
||||||
{
|
{
|
||||||
return; // 用户取消或没有选择任何变量
|
return; // 用户取消或没有选择任何变量
|
||||||
|
|||||||
@@ -12,11 +12,9 @@
|
|||||||
SecondaryButtonText="取消"
|
SecondaryButtonText="取消"
|
||||||
PrimaryButtonClick="ContentDialog_PrimaryButtonClick"
|
PrimaryButtonClick="ContentDialog_PrimaryButtonClick"
|
||||||
SecondaryButtonClick="ContentDialog_SecondaryButtonClick"
|
SecondaryButtonClick="ContentDialog_SecondaryButtonClick"
|
||||||
Width="800"
|
>
|
||||||
Height="600">
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
@@ -24,23 +22,8 @@
|
|||||||
<ColumnDefinition Width="2*" />
|
<ColumnDefinition Width="2*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<!-- 连接设置 -->
|
|
||||||
<StackPanel Grid.Row="0"
|
|
||||||
Grid.ColumnSpan="2"
|
|
||||||
Orientation="Horizontal"
|
|
||||||
Margin="0,0,0,10">
|
|
||||||
<TextBlock Text="Endpoint URL:"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="0,0,5,0" />
|
|
||||||
<TextBox Text="{Binding EndpointUrl, UpdateSourceTrigger=PropertyChanged}"
|
|
||||||
Width="300"
|
|
||||||
Margin="0,0,10,0" />
|
|
||||||
<Button Content="连接"
|
|
||||||
Command="{Binding ConnectCommand}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- 节点树 -->
|
<!-- 节点树 -->
|
||||||
<TreeView Grid.Row="1"
|
<TreeView Grid.Row="0"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
ItemsSource="{Binding OpcUaNodes}"
|
ItemsSource="{Binding OpcUaNodes}"
|
||||||
Margin="0,0,10,0"
|
Margin="0,0,10,0"
|
||||||
@@ -67,7 +50,7 @@
|
|||||||
</TreeView>
|
</TreeView>
|
||||||
|
|
||||||
<!-- 变量列表 -->
|
<!-- 变量列表 -->
|
||||||
<DataGrid Grid.Row="1"
|
<DataGrid Grid.Row="0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
ItemsSource="{Binding SelectedNodeVariables}"
|
ItemsSource="{Binding SelectedNodeVariables}"
|
||||||
SelectionChanged="Selector_OnSelectionChanged"
|
SelectionChanged="Selector_OnSelectionChanged"
|
||||||
|
|||||||
Reference in New Issue
Block a user