以下是针对前文《用C#实现工业现场数据的实时采集与存储》的OPC UA 集成扩展无缝融入原有架构。内容基于 .NET 8 / .NET 92025–2026 年主流工业实践推荐使用OPC Foundation 官方 UA-.NETStandard库开源、跨平台、社区活跃度最高、持续更新至 2025。为什么选择 OPC UA .NET Standard StackOPC UA 是工业 4.0 / IIoT 事实标准支持复杂信息模型、安全加密、Pub/Sub未来扩展。官方库OPCFoundation.NetStandard.Opc.Ua纯 .NET Standard 2.0兼容 .NET 8/9无需商业授权Redistributable License。支持异步读写、订阅Monitored Items、断线重连、证书管理。比商业 SDK如 Unified Automation、Prosys更轻量、更新快适合大多数 PLC如 Siemens S7-1500/1200、Beckhoff、Codesys、KEPServerEX 等。NuGet 安装核心包PackageReferenceIncludeOPCFoundation.NetStandard.Opc.UaVersion1.5.*/!-- 2025–2026 最新稳定版 --!-- 可选客户端示例与工具 --PackageReferenceIncludeOPCFoundation.NetStandard.Opc.Ua.SymbolsVersion1.5.*PrivateAssetsall/1. OPC UA Collector 封装异步、带重连usingOpc.Ua;usingOpc.Ua.Client;usingOpc.Ua.Configuration;usingSystem.Threading.Tasks;publicclassOpcUaCollector:IAsyncDisposable{privateSession?_session;privatereadonlystring_endpointUrl;privatereadonlyILogger_logger;privatereadonlyApplicationConfiguration_appConfig;publicOpcUaCollector(stringendpointUrl,ILoggerlogger){_endpointUrlendpointUrl;_loggerlogger;// 简单配置生产环境需证书 用户认证_appConfignewApplicationConfiguration{ApplicationNameIndustrialDataCollector,ApplicationTypeApplicationType.Client,SecurityConfigurationnewSecurityConfiguration{/* ... */},TransportConfigurationsnewTransportConfigurationCollection(),ClientConfigurationnewClientConfiguration{DefaultSessionTimeout60000},// 更多配置...};_appConfig.Validate(ApplicationType.Client).GetAwaiter().GetResult();}publicasyncTaskboolConnectAsync(){try{varendpointnewConfiguredEndpoint(null,newEndpointDescription(_endpointUrl));_sessionawaitSession.Create(_appConfig,endpoint,false,// 不更新端点IndustrialCollectorSession,60000,newUserIdentity(),// 匿名生产用 UserNameIdentityToken 或证书null);_logger.LogInformation(OPC UA 连接成功: {Endpoint},_endpointUrl);returntrue;}catch(Exceptionex){_logger.LogError(ex,OPC UA 连接失败);returnfalse;}}/// summary/// 批量读取多个 Node异步/// /summarypublicasyncTaskDictionarystring,objectReadNodesAsync(IEnumerablestringnodeIds){if(_sessionnull||!_session.Connected)thrownewInvalidOperationException(Session not connected);varreadValuesnewReadValueIdCollection();varnodeIdMapnewDictionarystring,int();// 记住原始顺序intindex0;foreach(varnidStrinnodeIds){varnodeIdnewNodeId(nidStr);readValues.Add(newReadValueId{NodeIdnodeId,AttributeIdAttributes.Value});nodeIdMap[nidStr]index;}varresponseawait_session.ReadAsync(null,0,TimestampsToReturn.Both,readValues,CancellationToken.None);varresultsnewDictionarystring,object();for(inti0;iresponse.Results.Count;i){varresultresponse.Results[i];if(result.StatusCodeStatusCodes.Good){varkvnodeIdMap.First(kvkv.Valuei);results[kv.Key]result.Value;// Variant → object}else{_logger.LogWarning(读取失败 Node {NodeId}: {Status},nodeIdMap.First(kvkv.Valuei).Key,result.StatusCode);}}returnresults;}publicasyncValueTaskDisposeAsync(){if(_session!null){await_session.CloseAsync();_session.Dispose();}}}2. 集成到原有采集架构修改 IndustrialDataCollector// 在 IndustrialDataCollector 中添加 OPC UA 支持publicclassIndustrialDataCollector:BackgroundService{// ... 原有 Modbus 等privatereadonlyOpcUaCollector?_opcUa;// 可选注入publicIndustrialDataCollector(ModbusTcpCollectormodbus,OpcUaCollector?opcUanull,// 通过 DI 注入// ...){_modbusmodbus;_opcUaopcUa;// ...}privateasyncTaskCollectedDataPointCollectDataAsync(){varpointnewCollectedDataPoint{TimestampDateTimeOffset.UtcNow};// Modbus 采集原有if(_modbus!null){try{varregsawait_modbus.ReadHoldingRegistersAsync(1,100,20);point.Tags[Modbus_Temp]ModbusUtility.ConvertToFloat(regs.Span.Slice(0,2));}catch{/* 容错 */}}// OPC UA 采集if(_opcUa!null){try{varnodesnew[]{ns2;sPLC1.Tag.Temperature,ns2;sPLC1.Tag.Pressure};varvaluesawait_opcUa.ReadNodesAsync(nodes);if(values.TryGetValue(ns2;sPLC1.Tag.Temperature,outvartemp))point.Tags[OpcUa_Temp]temp;// ...}catch(Exceptionex){_logger.LogWarning(OPC UA 读取异常: {Message},ex.Message);}}returnpoint;}// 重连逻辑扩展privateasyncTaskboolTryEnsureConnected(CancellationTokenct){booloktrue;if(_modbus!null)okawait_modbus.ConnectAsync();if(_opcUa!null)okawait_opcUa.ConnectAsync();returnok;}}3. 订阅模式Monitored Items – 更高效的实时采集OPC UA 的订阅远优于轮询事件驱动、低延迟。简单订阅示例添加到 OpcUaCollectorpublicasyncTaskSubscribeAsync(IEnumerablestringnodeIds,Actionstring,objectvalueChangedCallback){if(_sessionnull)return;varitemsnewMonitoredItemCollection();intclientHandle1;foreach(varnidStrinnodeIds){varitemnewMonitoredItem{StartNodeIdnewNodeId(nidStr),AttributeIdAttributes.Value,SamplingInterval500,// 采样间隔 msQueueSize10,DiscardOldesttrue,ClientHandleclientHandle};items.Add(item);}varsubscriptionnewSubscription{PublishingInterval1000,KeepAliveCount30,LifetimeCount60,Priority0};subscription.AddItems(items);await_session.AddSubscriptionAsync(subscription);awaitsubscription.CreateAsync();subscription.Publish(sender,e){foreach(varnotificationine.NotificationMessage.NotificationData){if(notificationisMonitoredItemNotificationmin){varitemsubscription.MonitoredItems.First(ii.ClientHandlemin.ClientHandle);valueChangedCallback(item.StartNodeId.ToString(),min.Value.Value);}}};}使用方式在 BackgroundService 中await_opcUa.SubscribeAsync(nodes,(nodeId,value){// 直接推入 Channel_channel.Writer.TryWrite(newCollectedDataPoint{TimestampDateTimeOffset.UtcNow,Tags{[nodeId]value}});});4. DI 注册Program.csbuilder.Services.AddSingletonOpcUaCollector(spnewOpcUaCollector(opc.tcp://192.168.1.50:4840,sp.GetRequiredServiceILoggerOpcUaCollector()));// 可选多 Collector 共存Modbus OPC UA MQTT 等5. 工业级注意事项2025–2026 实践证书与安全生产环境强制 Sign EncryptX509 证书用_appConfig.SecurityConfiguration配置。重连策略Session.ReconnectPeriod 5000; KeepAlive 检测。批量订阅单个 Subscription 限 1000–5000 items根据服务器能力分多个。性能订阅优于轮询延迟 100ms网络负载低。诊断用 UaExpert / Prosys OPC UA Browser 验证节点 订阅。异常处理BadSessionIdInvalid / BadTimeout → 自动重连。如果需要更详细的证书配置、订阅 批量读取混合、UA Pub/Sub 扩展MQTT over OPC UA、多服务器聚合或与 WPF/MAUI 实时曲线联动示例告诉我你的具体 OPC UA 服务器类型Siemens、KEPServer、Codesys 等或节点结构我可以进一步细化代码。以下是针对前文OpcUaCollector类中 OPC UA 客户端的证书配置完整示例生产级、安全推荐方式。基于 OPC Foundation 官方 UA-.NETStandard 栈2025–2026 年最新实践包括自签名证书自动创建开发/测试常用加载已有证书生产推荐CA 签发证书 信任列表管理加密模式Sign Encrypt证书存储路径自定义跨平台兼容1. 推荐配置原则工业场景开发/测试允许栈自动创建自签名证书最简单。生产环境使用 CA 签发的 X.509 证书推荐 RSA 2048/3072 或 ECDSA。强制 SecurityMode.SignAndEncrypt SecurityPolicy.Basic256Sha256或更高。证书存储用目录DirectoryStore便于容器/边缘部署。信任服务器证书TrustedPeer 信任 CATrustedIssuer。拒绝列表RejectedStore自动管理。2. 完整 ApplicationConfiguration 示例XML 或代码方式一代码中硬编码推荐用于容器/无配置文件场景privateasyncTaskApplicationConfigurationCreateApplicationConfigurationAsync(){varconfignewApplicationConfiguration{ApplicationNameIndustrialDataCollector,ApplicationUriUtils.Format(urn:{0}:IndustrialDataCollector,System.Net.Dns.GetHostName()),ApplicationTypeApplicationType.Client,// 证书相关核心配置SecurityConfigurationnewSecurityConfiguration{// 应用实例证书ownApplicationCertificatenewCertificateIdentifier{StoreTypeCertificateStoreType.Directory,StorePath./pki/own,// 相对路径或绝对路径 %LocalAppData%/OPC Foundation/pki/ownSubjectNameCNIndustrialDataCollector, DCSystem.Net.Dns.GetHostName()},// 信任的发行者证书CA 根证书TrustedIssuerCertificatesnewCertificateTrustList{StoreTypeCertificateStoreType.Directory,StorePath./pki/issuer},// 信任的 peer 证书服务器证书TrustedPeerCertificatesnewCertificateTrustList{StoreTypeCertificateStoreType.Directory,StorePath./pki/trusted},// 被拒绝的证书自动填充RejectedCertificateStorenewCertificateTrustList{StoreTypeCertificateStoreType.Directory,StorePath./pki/rejected},// 支持的安全策略生产只留强策略SupportedSecurityPoliciesnewStringCollection{SecurityPolicies.Basic256Sha256,SecurityPolicies.Basic256,SecurityPolicies.Basic128Rsa15// 可选视服务器支持},// 自动接受未签名证书测试用生产关闭AutoAcceptUntrustedCertificatesfalse,// 最小密钥长度推荐 2048MinimumCertificateKeySize2048},TransportConfigurationsnewTransportConfigurationCollection(),ClientConfigurationnewClientConfiguration{DefaultSessionTimeout60000,MinPublishRequestCount5,MaxPublishRequestCount20},// 其他日志、Trace 等TraceConfigurationnewTraceConfiguration{OutputFilePath./opcua.log}};// 验证并创建缺失的证书存储目录awaitconfig.Validate(ApplicationType.Client);// 自动创建或检查应用证书最重要一步boolhaveCertawaitconfig.CheckApplicationInstanceCertificate(false,// 不强制创建新证书CertificateFactory.DefaultKeySize,// 2048CertificateFactory.DefaultLifeTime// 120 个月);if(!haveCert){_logger.LogError(应用证书无效或创建失败);thrownewException(Application instance certificate invalid!);}returnconfig;}使用方式在 OpcUaCollector 构造函数中调用publicOpcUaCollector(stringendpointUrl,ILoggerlogger){_endpointUrlendpointUrl;_loggerlogger;_appConfigCreateApplicationConfigurationAsync().GetAwaiter().GetResult();}3. 加载已有 CA 签发证书生产推荐如果已有.pfx含私钥或.der.key文件// 在 SecurityConfiguration.ApplicationCertificate 设置后手动加载varcertIdentifierconfig.SecurityConfiguration.ApplicationCertificate;// 从 PFX 文件加载含私钥byte[]pfxDataFile.ReadAllBytes(C:/certs/myapp.pfx);varcertnewX509Certificate2(pfxData,pfx密码,X509KeyStorageFlags.Exportable);awaitcertIdentifier.SetCertificateAsync(cert);// 或从 DER PEM 私钥加载更安全varpublicCertnewX509Certificate2(myapp.der);varprivateKeyawaitCertificateFactory.LoadPrivateKey(myapp.key,key密码);awaitcertIdentifier.SetCertificateAsync(publicCert,privateKey);4. 信任服务器证书手动或自动// 手动信任服务器证书生产前必须执行一次publicasyncTaskTrustServerCertificateAsync(byte[]serverCertDer){varcertnewX509Certificate2(serverCertDer);config.SecurityConfiguration.AddTrustedPeer(serverCertDer);// 或通过代码信任所有未签名仅测试config.SecurityConfiguration.AutoAcceptUntrustedCertificatestrue;// 危险勿生产用}5. 连接时指定 SecurityMode PolicyvarendpointnewConfiguredEndpoint(null,newEndpointDescription(_endpointUrl){SecurityModeMessageSecurityMode.SignAndEncrypt,SecurityPolicyUriSecurityPolicies.Basic256Sha256});6. 常见目录结构推荐./pki/ ├── own/ # 本应用证书 私钥自动创建或手动放置 │ ├── cert.der │ └── privatekey.pem ├── trusted/ # 信任的服务器证书.der ├── issuer/ # 信任的 CA 根/中间证书 └── rejected/ # 被拒绝的证书自动7. 生产落地 Checklist使用./pki/相对路径或容器卷挂载Kubernetes / Docker 友好关闭AutoAcceptUntrustedCertificates false最小密钥 2048 bit优先 Basic256Sha256证书过期监控定期检查 NotAfter备份 pki/own 私钥丢失无法恢复会话用 UaExpert 测试连接 证书信任如果需要完整带证书的 Session 创建代码、证书自动续期、用户身份证书User Certificate Token示例或与现有 Modbus 混合采集的完整 BackgroundService告诉我你的具体安全要求匿名 / 用户名密码 / 证书用户 / CA 链我可以继续扩展。