基于C#的OPC UA客户端SDK与服务端测试程序

基于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();

 

 

专注于matlab/simulink,电子电路,编程