C#实现的OPC DA转OPC UA服务器软件车间里那台老设备还在用OPC DA协议新上的MES系统却只认OPC UA这种青黄不接的情况搞过工业自动化的都懂。与其等设备升级不如自己动手搭个转换器。今天咱们用C#搞个能打的中间件代码不复杂但绝对实用。先上硬菜整个架构分三层OPC DA数据抓取层用经典的OpcNetApi.Com.dll内存缓存层实时数据中转站OPC UA服务层OPC基金会官方库走起数据抓取的核心代码长这样// 连接到DA服务器 var server new Opc.Da.Server(new OpcCom.Factory(), null); server.Connect(new Opc.URL(opcda://192.168.1.100/Factory.OPC.DA)); // 创建订阅 var subscription (Opc.Da.Subscription)server.CreateSubscription(new Opc.Da.SubscriptionState()); // 添加监控项 var items new Opc.Da.Item[] { new Opc.Da.Item { ItemName Channel1.Device1.Tag001 } }; var results subscription.AddItems(items); // 数据更新回调 subscription.DataChanged (handle, requestHandle, values) { foreach(var item in values) { // 这里把数据塞进缓存队列 DataCache.Update(item.ItemName, item.Value, item.Timestamp); } };这段代码有几个关键点用OpcCom.Factory处理COM组件互操作订阅机制实现准实时数据采集注意这里的回调函数要避免阻塞。遇到过有人在这里直接调UA的写方法结果数据量大时直接崩了所以咱们用内存缓存做缓冲层是明智的。转换到UA这边节点映射是重头戏。UA的复杂数据类型能让新人哭出来咱们先搞定基础类型转换// 创建UA节点 var folderNode server.AddObject( ObjectIds.ObjectsFolder, DA_Proxy, DA_Proxy, NodeClass.Object); var dataNode server.AddVariable( folderNode.NodeId, Tag001, new DataValue(new Variant(0)), DataTypeIds.Double, ValueRanks.Scalar);这里用OPCFoundation的UA核心库建了个对象文件夹注意NodeId的生成策略别瞎搞生产环境建议用持久化存储。数据类型映射是个大坑DA的VT_EMPTY转UA的StatusCode要特别处理别问我怎么知道的...C#实现的OPC DA转OPC UA服务器软件缓存队列和UA服务层的对接用了个骚操作// 定时器每100ms触发一次 var timer new System.Timers.Timer(100); timer.Elapsed (s, e) { var snapshot DataCache.GetSnapshot(); foreach(var item in snapshot) { var node FindNodeByTag(item.Key); node.Value new DataValue(new Variant(item.Value)); node.Timestamp DateTime.UtcNow; node.StatusCode StatusCodes.Good; } };这种批处理模式比实时更新靠谱得多既能缓解UA的写压力又能避免时间戳错乱。注意这里用UTC时间跨时区项目吃过亏的兄弟应该懂。性能优化方面实测单核虚拟机跑5000个标签转换循环周期压到50ms时CPU占用不到15%。秘诀在于用Array.Copy代替LINQ做数据快照节点查找用ConcurrentDictionary缓存避免在回调里做任何阻塞操作最后提一嘴安全配置UA的SecurityPolicy必须配None的话记得在防火墙层面做好隔离。测试时被IT部门追杀过三次之后我现在见到SecurityMode.None就条件反射加白名单。代码扔到Gitee上被老师傅吐槽这不就是个高级点的转接头么但上线半年扛住了产线数据风暴深藏功与名。下次有机会聊聊怎么用SIMD指令集优化数据转换那才是真·硬核玩法。