基于C#的OPC UA客户端SDK与服务端测试程序
一、客户端SDK核心实现(基于OPC Foundation官方库)
1. 项目配置(.csproj)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net48</TargetFrameworks>
<PackageReference Include="OPCFoundation.NETStandard.Opc.Ua" Version="1.6.4" />
</PropertyGroup>
</Project>
2. 核心类实现(OpcUaClient.cs)
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class OpcUaClient : IDisposable
{
private Session _session;
private ApplicationConfiguration _config;
public OpcUaClient(string endpointUrl, string username = null, string password = null)
{
InitializeConfig(endpointUrl, username, password);
}
private void InitializeConfig(string endpointUrl, string username, string password)
{
_config = new ApplicationConfiguration
{
ApplicationName = "OPCClient",
ApplicationType = ApplicationType.Client,
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier
{
StoreType = "Directory",
StorePath = "./PKI/own",
SubjectName = "CN=OPCClient"
}
}
};
var endpoint = CoreClientUtils.SelectEndpoint(endpointUrl,
useSecurity: username != null || password != null);
_session = Session.Create(
_config,
endpoint,
new UserIdentity(new UserNameIdentityToken(username, password)),
60000);
}
public async Task<DataValue> ReadNodeAsync(NodeId nodeId)
{
var request = new ReadRequest
{
NodesToRead = new[] { new ReadValueId { NodeId = nodeId, AttributeId = Attributes.Value } }
};
var response = await _session.ReadAsync(request);
return response.Results[0].Value;
}
public async Task WriteNodeAsync(NodeId nodeId, object value)
{
var writeValue = new WriteValue
{
NodeId = nodeId,
AttributeId = Attributes.Value,
Value = new Variant(value)
};
var request = new WriteRequest
{
NodesToWrite = new[] { writeValue }
};
await _session.WriteAsync(request);
}
public void Dispose()
{
_session?.Close();
_session?.Dispose();
}
}
二、服务端测试程序(模拟服务器)
1. 使用Prosys Simulation Server(推荐测试方案)
# 下载地址:https://www.prosysopc.com/tools/opc-ua-server-simulator/
# 安装后配置:
1. 启动Prosys Simulation Server
2. 设置端点地址:opc.tcp://localhost:53530
3. 添加测试变量:
- 名称:Temperature
- 数据类型:Double
- 初始值:25.5
- 启用模拟信号(Sine Wave)
2. 自建测试服务器(代码实现)
using Opc.Ua;
using Opc.Ua.Server;
using System;
public class TestServer : IDisposable
{
private Server _server;
public TestServer()
{
InitializeServer();
}
private void InitializeServer()
{
_server = new Server();
_server.EndpointConfiguration = new EndpointConfiguration
{
Address = "opc.tcp://localhost:4840",
SecurityPolicies = { new SecurityPolicyConfiguration(SecurityPolicy.Basic256Sha256) }
};
_server.Start();
CreateAddressSpace();
}
private void CreateAddressSpace()
{
var root = new FolderNode(
new NodeId(0, "Root"),
"Test Server Root",
"Root folder for test nodes");
var device = new FolderNode(
new NodeId(0, "Device"),
"Test Device",
"Simulated PLC device");
var tempNode = new VariableNode(
new NodeId(0, "Temperature"),
"Temperature",
"Simulated temperature value",
DataType.Double);
tempNode.Value = 25.0;
device.AddChild(tempNode);
root.AddChild(device);
_server.AddressSpace.AddRootNode(root);
}
public void Dispose()
{
_server.Stop();
_server.Dispose();
}
}
三、完整测试流程
1. 客户端连接测试
using var client = new OpcUaClient("opc.tcp://localhost:4840");
await client.ConnectAsync();
// 读取测试
var tempValue = await client.ReadNodeAsync(new NodeId(0, "Temperature"));
Console.WriteLine($"Temperature: {tempValue}");
// 写入测试
await client.WriteNodeAsync(new NodeId(0, "Temperature"), 30.5);
2. 性能测试(压力测试)
public async Task StressTestAsync()
{
var tasks = new Task[1000];
for(int i=0; i<1000; i++)
{
tasks[i] = Task.Run(async () =>
{
var client = new OpcUaClient("opc.tcp://localhost:4840");
await client.ConnectAsync();
await client.WriteNodeAsync(new NodeId(0, "Pressure"), i);
await client.DisconnectAsync();
});
}
await Task.WhenAll(tasks);
}
四、安全配置方案
1. 证书生成(自签名)
# 使用OpenSSL生成证书
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes
2. 客户端证书配置
_config.SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier
{
StoreType = "Directory",
StorePath = "./PKI/own",
SubjectName = "CN=SecureClient"
},
TrustedPeerCertificates = new CertificateTrustList
{
StoreType = "Directory",
StorePath = "./PKI/trusted"
}
};
五、调试与日志
1. 日志配置
using (var logger = new FileLogger("opc_client.log"))
{
SessionManager.Logger = logger;
SessionManager.TraceMask = TraceMasks.All;
}
2. 常见问题排查
| 现象 | 解决方案 |
|---|---|
| 连接超时 | 检查防火墙设置,确认端口4840开放 |
| 证书验证失败 | 确保证书指纹匹配,或切换为None策略 |
| 数据类型不匹配 | 使用TypeCast.Convert转换数据类型 |
| 订阅无响应 | 检查KeepAliveInterval参数设置 |
参考代码 OPC客户端源码SDK+服务端测试程序 www.youwenfan.com/contentcsr/112686.html
六、项目结构
OPCClientDemo/
├── src/
│ ├── OpcUaClient/ // 客户端SDK
│ ├── TestServer/ // 测试服务端
│ └── Models/ // 数据模型
├── tests/
│ ├── IntegrationTests/ // 集成测试
│ └── PerformanceTests/ // 性能测试
└── docs/
└── 开发指南.md
七、扩展功能实现
1. 历史数据访问
var historyRequest = new HistoryReadRequest
{
NodesToRead = new[] { new HistoryReadValueId { NodeId = tempNode } },
HistoryReadDetails = new ReadRawModifiedDetails
{
StartTime = DateTime.UtcNow.AddHours(-1),
EndTime = DateTime.UtcNow
}
};
var historyResponse = await _session.HistoryReadAsync(historyRequest);
2. 事件订阅
var eventFilter = new EventFilter
{
EventType = ObjectIds.ServerStatusType,
SelectClauses = new[] { new SimpleAttributeOperand { AttributeId = Attributes.EventId } }
};
var subscription = new Subscription(_session.DefaultSubscription)
{
PublishingInterval = 1000,
Filter = eventFilter
};
subscription.Create();