使用OpcUaHelper在C# WinForms中连接OPC UA服务器并读取数据
下面是一个完整的示例,展示如何使用OpcUaHelper库在C# WinForms应用程序中连接OPC UA服务器并读取数据。
1. 准备工作
首先,确保你已经安装了OpcUaHelper NuGet包。可以通过NuGet包管理器控制台安装:
Install-Package OpcUaHelper
2. 创建WinForms应用程序
2.1 设计界面
创建一个简单的WinForms窗体,包含以下控件:
- 文本框:用于输入服务器URL
- 连接/断开按钮
- 节点ID文本框
- 读取按钮
- 数据显示区域(如DataGridView或ListBox)
2.2 完整代码示例
using OpcUaHelper;
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace OpcUaWinFormsClient
{
public partial class MainForm : Form
{
private OpcUaClient opcUaClient = new OpcUaClient();
private bool isConnected = false;
public MainForm()
{
InitializeComponent();
UpdateUiState();
}
private void btnConnect_Click(object sender, EventArgs e)
{
if (!isConnected)
{
ConnectToServer();
}
else
{
DisconnectFromServer();
}
}
private async void ConnectToServer()
{
string serverUrl = txtServerUrl.Text.Trim();
if (string.IsNullOrEmpty(serverUrl))
{
MessageBox.Show("请输入有效的服务器URL");
return;
}
try
{
btnConnect.Enabled = false;
lblStatus.Text = "正在连接...";
// 连接到服务器
await opcUaClient.ConnectServerAsync(serverUrl);
isConnected = true;
lblStatus.Text = "已连接";
MessageBox.Show("连接成功!");
}
catch (Exception ex)
{
lblStatus.Text = "连接失败";
MessageBox.Show($"连接失败: {ex.Message}");
}
finally
{
btnConnect.Enabled = true;
UpdateUiState();
}
}
private void DisconnectFromServer()
{
try
{
opcUaClient.Disconnect();
isConnected = false;
lblStatus.Text = "已断开";
}
catch (Exception ex)
{
MessageBox.Show($"断开连接时出错: {ex.Message}");
}
finally
{
UpdateUiState();
}
}
private void UpdateUiState()
{
btnConnect.Text = isConnected ? "断开连接" : "连接";
btnRead.Enabled = isConnected;
txtNodeId.Enabled = isConnected;
}
private async void btnRead_Click(object sender, EventArgs e)
{
string nodeId = txtNodeId.Text.Trim();
if (string.IsNullOrEmpty(nodeId))
{
MessageBox.Show("请输入节点ID");
return;
}
try
{
btnRead.Enabled = false;
// 读取单个节点值
var value = await opcUaClient.ReadNodeAsync(nodeId);
// 显示读取结果
lstResults.Items.Add($"节点: {nodeId}, 值: {value}, 类型: {value?.GetType()}");
lstResults.TopIndex = lstResults.Items.Count - 1;
}
catch (Exception ex)
{
MessageBox.Show($"读取数据时出错: {ex.Message}");
}
finally
{
btnRead.Enabled = true;
}
}
private async void btnBrowse_Click(object sender, EventArgs e)
{
try
{
treeNodes.Nodes.Clear();
var rootNode = treeNodes.Nodes.Add("Root");
// 浏览服务器节点
await BrowseNodeAsync("i=84", rootNode); // Objects文件夹通常是i=84
}
catch (Exception ex)
{
MessageBox.Show($"浏览节点时出错: {ex.Message}");
}
}
private async Task BrowseNodeAsync(string nodeId, TreeNode parentTreeNode)
{
var references = await opcUaClient.BrowseNodeReferenceAsync(nodeId);
foreach (var reference in references)
{
var childNode = parentTreeNode.Nodes.Add($"{reference.DisplayName} ({reference.NodeId})");
// 如果是文件夹/对象类型,添加一个虚拟子节点以便展开
if (reference.NodeClass == Opc.Ua.NodeClass.Object ||
reference.NodeClass == Opc.Ua.NodeClass.Variable)
{
childNode.Nodes.Add("加载中...");
}
}
}
private async void treeNodes_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
var node = e.Node;
// 如果只有一个"加载中..."子节点,则实际加载子节点
if (node.Nodes.Count == 1 && node.Nodes[0].Text == "加载中...")
{
try
{
node.Nodes.Clear();
// 从节点文本中提取NodeId
var nodeId = ExtractNodeIdFromTreeNode(node);
if (!string.IsNullOrEmpty(nodeId))
{
await BrowseNodeAsync(nodeId, node);
}
}
catch (Exception ex)
{
MessageBox.Show($"加载子节点时出错: {ex.Message}");
node.Nodes.Add($"错误: {ex.Message}");
}
}
}
private string ExtractNodeIdFromTreeNode(TreeNode node)
{
// 从类似 "DisplayName (ns=2;s=MyVariable)" 的文本中提取 "ns=2;s=MyVariable"
var text = node.Text;
var start = text.IndexOf('(') + 1;
var end = text.IndexOf(')');
if (start > 0 && end > start)
{
return text.Substring(start, end - start);
}
return null;
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (isConnected)
{
DisconnectFromServer();
}
}
}
}
3. 功能说明
连接/断开服务器:
- 使用
ConnectServerAsync
方法异步连接 - 使用
Disconnect
方法断开连接
- 使用
读取节点数据:
- 使用
ReadNodeAsync
方法读取单个节点值 - 支持所有OPC UA数据类型
- 使用
浏览服务器节点:
- 使用
BrowseNodeReferenceAsync
方法浏览节点引用 - 实现树形视图的延迟加载
- 使用
错误处理:
- 对所有OPC UA操作进行适当的错误处理
4. 扩展功能
你可以根据需要扩展此示例,添加以下功能:
写入数据:
await opcUaClient.WriteNodeAsync(nodeId, value);
订阅数据变化:
// 创建订阅 var subscription = new OpcUaHelper.Subscription(opcUaClient); // 添加监控项 subscription.AddItem(nodeId); // 数据变化事件 subscription.DataChangeReceived += (s, e) => { // 处理数据变化 }; // 启动订阅 await subscription.ApplyAsync();
调用方法:
var results = await opcUaClient.CallMethodByNodeIdAsync(objectNodeId, methodNodeId, inputArguments);
5. 注意事项
异步编程:所有OPC UA操作都应使用异步方法,避免阻塞UI线程。
错误处理:网络操作容易出错,确保有适当的错误处理和用户反馈。
安全性:生产环境中可能需要配置安全策略和用户凭据。
资源清理:确保在应用程序退出时正确断开连接。