完成添加OPC变量
This commit is contained in:
@@ -24,6 +24,7 @@ public class DbVariableData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 节点ID,用于标识变量在设备或系统中的唯一路径。
|
/// 节点ID,用于标识变量在设备或系统中的唯一路径。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
public string NodeId { get; set; } = String.Empty;
|
public string NodeId { get; set; } = String.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -78,6 +78,12 @@ public partial class VariableData : ObservableObject
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 指示变量是否被选中
|
||||||
|
/// </summary>
|
||||||
|
[ObservableProperty]
|
||||||
|
public bool isSelect;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 指示是否需要保存变量数据。
|
/// 指示是否需要保存变量数据。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ public partial class OpcUaImportDialogViewModel : ObservableObject
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private ObservableCollection<VariableData> _selectedNodeVariables;
|
private ObservableCollection<VariableData> _selectedNodeVariables;
|
||||||
|
|
||||||
|
public List<VariableData> SelectedVariables { get; set; }=new List<VariableData>();
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _selectAllVariables;
|
private bool _selectAllVariables;
|
||||||
|
|
||||||
@@ -128,7 +130,6 @@ public partial class OpcUaImportDialogViewModel : ObservableObject
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 1. 创建应用程序配置
|
// 1. 创建应用程序配置
|
||||||
var application = new ApplicationInstance
|
var application = new ApplicationInstance
|
||||||
{
|
{
|
||||||
@@ -144,15 +145,43 @@ public partial class OpcUaImportDialogViewModel : ObservableObject
|
|||||||
ApplicationType = application.ApplicationType,
|
ApplicationType = application.ApplicationType,
|
||||||
SecurityConfiguration = new SecurityConfiguration
|
SecurityConfiguration = new SecurityConfiguration
|
||||||
{
|
{
|
||||||
ApplicationCertificate = new CertificateIdentifier { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/MachineDefault", SubjectName = application.ApplicationName },
|
ApplicationCertificate = new CertificateIdentifier
|
||||||
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" },
|
StoreType = "Directory",
|
||||||
RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory", StorePath = "%CommonApplicationData%/OPC Foundation/CertificateStores/RejectedCertificates" },
|
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 // 自动接受不受信任的证书 (仅用于测试)
|
AutoAcceptUntrustedCertificates = true // 自动接受不受信任的证书 (仅用于测试)
|
||||||
},
|
},
|
||||||
TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
|
TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
|
||||||
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
|
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
|
||||||
TraceConfiguration = new TraceConfiguration { OutputFilePath = "./Logs/OpcUaClient.log", DeleteOnLoad = true, TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security }
|
TraceConfiguration = new TraceConfiguration
|
||||||
|
{
|
||||||
|
OutputFilePath = "./Logs/OpcUaClient.log", DeleteOnLoad = true,
|
||||||
|
TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security
|
||||||
|
}
|
||||||
};
|
};
|
||||||
application.ApplicationConfiguration = config;
|
application.ApplicationConfiguration = config;
|
||||||
|
|
||||||
@@ -183,6 +212,7 @@ public partial class OpcUaImportDialogViewModel : ObservableObject
|
|||||||
NotificationHelper.ShowError($"连接 OPC UA 服务器失败: {EndpointUrl} - {ex.Message}", ex);
|
NotificationHelper.ShowError($"连接 OPC UA 服务器失败: {EndpointUrl} - {ex.Message}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 处理来自服务器的数据变化通知
|
/// 处理来自服务器的数据变化通知
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -190,7 +220,8 @@ public partial class OpcUaImportDialogViewModel : ObservableObject
|
|||||||
{
|
{
|
||||||
foreach (var value in item.DequeueValues())
|
foreach (var value in item.DequeueValues())
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[通知] {item.DisplayName}: {value.Value} | 时间戳: {value.SourceTimestamp.ToLocalTime()} | 状态: {value.StatusCode}");
|
Console.WriteLine(
|
||||||
|
$"[通知] {item.DisplayName}: {value.Value} | 时间戳: {value.SourceTimestamp.ToLocalTime()} | 状态: {value.StatusCode}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,13 +340,42 @@ public partial class OpcUaImportDialogViewModel : ObservableObject
|
|||||||
// 如果是变量,添加到右侧列表
|
// 如果是变量,添加到右侧列表
|
||||||
if (nodeType == NodeType.Variable)
|
if (nodeType == NodeType.Variable)
|
||||||
{
|
{
|
||||||
|
// Read the DataType attribute
|
||||||
|
ReadValueId readValueId = new ReadValueId
|
||||||
|
{
|
||||||
|
NodeId = opcUaNode.NodeId,
|
||||||
|
AttributeId = Attributes.DataType,
|
||||||
|
// You might need to specify IndexRange and DataEncoding if dealing with arrays or specific encodings
|
||||||
|
};
|
||||||
|
|
||||||
|
DataValueCollection results;
|
||||||
|
DiagnosticInfoCollection diagnosticInfos;
|
||||||
|
|
||||||
|
_session.Read(
|
||||||
|
null, // RequestHeader
|
||||||
|
0, // MaxAge
|
||||||
|
TimestampsToReturn.Source,
|
||||||
|
new ReadValueIdCollection { readValueId },
|
||||||
|
out results,
|
||||||
|
out diagnosticInfos
|
||||||
|
);
|
||||||
|
|
||||||
|
string dataType = string.Empty;
|
||||||
|
|
||||||
|
if (results != null && results.Count > 0 && results[0].Value != null)
|
||||||
|
{
|
||||||
|
// Convert the NodeId of the DataType to a readable string
|
||||||
|
NodeId dataTypeNodeId = (NodeId)results[0].Value;
|
||||||
|
dataType = _session.NodeCache.GetDisplayText(dataTypeNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
SelectedNodeVariables.Add(new VariableData
|
SelectedNodeVariables.Add(new VariableData
|
||||||
{
|
{
|
||||||
Name = opcUaNode.DisplayName,
|
Name = opcUaNode.DisplayName,
|
||||||
NodeId = opcUaNode.NodeId.ToString(),
|
|
||||||
OpcUaNodeId = opcUaNode.NodeId.ToString(),
|
OpcUaNodeId = opcUaNode.NodeId.ToString(),
|
||||||
ProtocolType = ProtocolType.OpcUA,
|
ProtocolType = ProtocolType.OpcUA,
|
||||||
IsActive = true // 默认选中
|
IsActive = true, // Default selected
|
||||||
|
DataType = dataType // Assign the read DataType
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,6 +395,6 @@ public partial class OpcUaImportDialogViewModel : ObservableObject
|
|||||||
|
|
||||||
public ObservableCollection<VariableData> GetSelectedVariables()
|
public ObservableCollection<VariableData> GetSelectedVariables()
|
||||||
{
|
{
|
||||||
return new ObservableCollection<VariableData>(SelectedNodeVariables.Where(v => v.IsActive));
|
return new ObservableCollection<VariableData>(SelectedVariables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,14 +25,26 @@
|
|||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<!-- 连接设置 -->
|
<!-- 连接设置 -->
|
||||||
<StackPanel Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal" Margin="0,0,0,10">
|
<StackPanel Grid.Row="0"
|
||||||
<TextBlock Text="Endpoint URL:" VerticalAlignment="Center" Margin="0,0,5,0" />
|
Grid.ColumnSpan="2"
|
||||||
<TextBox Text="{Binding EndpointUrl, UpdateSourceTrigger=PropertyChanged}" Width="300" Margin="0,0,10,0" />
|
Orientation="Horizontal"
|
||||||
<Button Content="连接" Command="{Binding ConnectCommand}" />
|
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>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- 节点树 -->
|
<!-- 节点树 -->
|
||||||
<TreeView Grid.Row="1" Grid.Column="0" ItemsSource="{Binding OpcUaNodes}" Margin="0,0,10,0" SelectedItemChanged="TreeView_SelectedItemChanged">
|
<TreeView Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
ItemsSource="{Binding OpcUaNodes}"
|
||||||
|
Margin="0,0,10,0"
|
||||||
|
SelectedItemChanged="TreeView_SelectedItemChanged">
|
||||||
<TreeView.ItemTemplate>
|
<TreeView.ItemTemplate>
|
||||||
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||||
<TextBlock Text="{Binding DisplayName}" />
|
<TextBlock Text="{Binding DisplayName}" />
|
||||||
@@ -41,23 +53,32 @@
|
|||||||
</TreeView>
|
</TreeView>
|
||||||
|
|
||||||
<!-- 变量列表 -->
|
<!-- 变量列表 -->
|
||||||
<DataGrid Grid.Row="1" Grid.Column="1" ItemsSource="{Binding SelectedNodeVariables}" AutoGenerateColumns="False" IsReadOnly="True">
|
<DataGrid Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
ItemsSource="{Binding SelectedNodeVariables}"
|
||||||
|
SelectionChanged="Selector_OnSelectionChanged"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
IsReadOnly="True">
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTemplateColumn>
|
<DataGridTemplateColumn>
|
||||||
<DataGridTemplateColumn.HeaderTemplate>
|
<DataGridTemplateColumn.HeaderTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<CheckBox IsChecked="{Binding DataContext.SelectAllVariables, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
|
<CheckBox
|
||||||
|
IsChecked="{Binding DataContext.SelectAllVariables, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.HeaderTemplate>
|
</DataGridTemplateColumn.HeaderTemplate>
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}" />
|
<CheckBox IsChecked="{Binding IsSelect, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
</DataGridTemplateColumn>
|
</DataGridTemplateColumn>
|
||||||
<DataGridTextColumn Header="名称" Binding="{Binding DisplayName}" />
|
<DataGridTextColumn Header="名称"
|
||||||
<DataGridTextColumn Header="节点ID" Binding="{Binding NodeId}" />
|
Binding="{Binding Name}" />
|
||||||
<DataGridTextColumn Header="数据类型" Binding="{Binding DataType}" />
|
<DataGridTextColumn Header="节点ID"
|
||||||
|
Binding="{Binding OpcUaNodeId}" />
|
||||||
|
<DataGridTextColumn Header="数据类型"
|
||||||
|
Binding="{Binding DataType}" />
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using iNKORE.UI.WPF.Modern.Controls;
|
using iNKORE.UI.WPF.Modern.Controls;
|
||||||
using PMSWPF.Models;
|
using PMSWPF.Models;
|
||||||
@@ -41,4 +42,17 @@ public partial class OpcUaImportDialog : ContentDialog
|
|||||||
await ViewModel.LoadNodeVariables(selectedNode);
|
await ViewModel.LoadNodeVariables(selectedNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.AddedItems!=null && args.AddedItems.Count>0)
|
||||||
|
{
|
||||||
|
foreach (var item in args.AddedItems)
|
||||||
|
{
|
||||||
|
ViewModel.SelectedVariables.Add((VariableData)item);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user