ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入OPC Workshop源码:读懂OPC Server实现与二次开发实践

深入OPC Workshop源码:读懂OPC Server实现与二次开发实践 简介opcworkshop是一套完全开源的OPC服务器与客户端实现源码面向工控上位机开发者及对OPC协议感兴趣的C/C程序员可用来理解OPC DA/AE数据访问机制、事件订阅与通报流程也可作为二次开发基座。压缩包共114个文件约205KB以h头文件和cpp源文件为主辅以c文件、vcproj工程文件及sln解决方案头文件划定了OPC接口、数据结构和GUID定义源码则覆盖服务端启动、客户端调用、测试工程等关键环节。浏览学习人数已超1200人整体结构紧凑、依赖较少适合从源码层面快速上手。通过研读这份代码可掌握OPC服务器如何暴露数据、客户端如何读写订阅以及COM/DCOM环境下接口调用与事件处理的实现细节对排查工业通信问题或自研轻量OPC模块都很有帮助。 工业上位机开发这几年绕不开的一个东西就是OPC。很多项目里PLC数据要往MES、SCADA里送甲方动不动就甩一句你们提供OPC Server就行可真到自己从头开始写一个OPC Server时大部分人都懵了COM那套接口看着像天书文档全是英文样例代码又老又绕。我前前后后啃过好几份开源的OPC Server实现包括网上经常被提到的lightOPC和另一个更完整的项目OPC Workshop。如果让我给想入门OPC Server源码的人一个建议我的答案很直接直接看OPC Workshop它比lightOPC更适合用来学习代码结构清楚工程组织也接近真实产品。这篇文章我不打算讲OPC是什么这种基础概念而是直接站在我要把一个开源的OPC Server源码读明白、跑起来、改着用的角度把OPC Workshop这个项目的学习方法、源码组织逻辑、编译踩坑和扩展方向一次说清楚。1. 入手OPC Server源码之前的三个认知准备1.1 OPC DA不是协议而是一组COM接口约定很多刚接触OPC的人会有个错觉以为OPC和Modbus一样是一种基于报文的通信协议比如通过TCP发一帧数据过去设备返回一帧。其实OPC DAData Access数据访问本质上不是协议而是微软COM/DCOM技术之上的一组接口规范。OPC Server是一个COM组件OPC Client通过COM接口调用Server的方法比如给我创建一个Group往Group里添加一个Item把这个Item的值读出来。为什么要先搞懂这一点因为你看源码时所有代码都会围绕COM展开类工厂、接口实现、IUnknown、IDispatch、注册表、GUID、类型库。如果你不理解COM的基本套路打开OPC Workshop源码第一眼就会被ATL的宏吓退。反过来只要你知道COM接口就是一张函数表Client拿到的其实是一个接口指针调用接口方法最终会执行到Server里你实现的那个函数那整个代码就能拆开看。我在实际带人看代码时会先让对方在项目里搜STDMETHODIMP这个宏把所有带这个宏的函数都列出来这就是一个OPC Server对外提供的所有能力的入口。比对着规范文档一页一页看高效得多。1.2 三层对象模型Server、Group、ItemOPC DA的地址空间是一个标准的三层模型OPCServer、OPCGroup、OPCItem。OPC Server是顶层对象负责管理Group的生命周期也是Client连接时最先拿到的对象。一个Server可以包含多个Group。OPC Group是Item的容器它的一个重要职责是设定采集/回调周期。同一个Group里的Item共享一个更新频率这在做性能优化时非常关键。OPC Item对应具体的一个数据点比如1号锅炉的温度3号电机的转速。它本身不存数据它只是指向Server内部数据源的一个句柄。这个模型是整个源码的骨架。OPC Workshop的类设计也基本沿用了这个结构你会在代码里看到类似COpcServer、COpcGroup、COpcItem这样的类名。理解这个三层关系之后再去看接口实现就不会迷路。1.3 数据流客户端轮询 vs 订阅回调OPC DA的数据读取有两种模式源码里对应着两套不同的接口实现。一是同步读写IOPCSyncIOClient发起Read操作Server直接返回当前值。这种模式简单但Client要不停地主动去问网络开销大适合点位少的场景。二是异步订阅IOPCAsyncIO2、IOPCGroupStateMgt配合连接点Client告诉Server你每隔XX毫秒把数据推给我值变了就通知我。这才是OPC真正好用、高效的地方也是现场SCADA系统最常用的方式。读OPC Workshop源码时我建议先看同步读写那条链路因为代码路径短、没有回调逻辑容易建立信心。等同步跑通了再去看异步的实现此时你会接触到IConnectionPointContainer、IConnectionPoint、客户端接收回调的IOPCDataCallback接口理解难度会陡增但一旦啃下来对COM事件模型的理解也会上一个台阶。2. OPC Workshop源码结构导览先看哪个项目再看哪个文件2.1 项目整体划分OPC Workshop虽然是开源项目但它的工程组织比一般Demo要认真很多这也是我说它比lightOPC容易看的核心原因之一。整个解决方案里通常分成这样几类项目核心Server实现工程这是最关键的工程里面实现了OPC DA规范要求的COM接口编译出来是一个DLL/EXE。模拟设备/数据源工程提供后台数据比如用定时器随机生成一批模拟数值模拟PLC的寄存器变化。配置/注册工具用于在Windows上注册COM组件、生成类型库有的版本还带一个简单的配置界面。测试客户端工程一个自己写的OPC Client用来连接本机Server做验证。拿到源码第一步不要急着打开某个.cpp文件通读先在解决方案资源管理器里把所有工程过一遍搞清楚哪个是Server、哪个是Client、哪个是工具。我见过不少人一上来就在一个上千行的文件里死磕磕完发现那只是个工具类这种挫败感非常打击学习热情。2.2 从注册表到内存里的Tag表一个OPC Server启动后Client要找到它靠的是Windows注册表和COM的类工厂机制。OPC Workshop作为一个标准COM组件它的Server端有一个关键函数DllRegisterServer。这个函数做的最重要一件事就是把这个组件的CLSID、ProgID写入注册表。看代码时你可以这么定位先找到CComModule或_AtlModule相关的注册逻辑再看类工厂和UpdateRegistry宏。这些代码会告诉你这个OPC Server的ProgID叫什么、CLSID是多少。以后你在自己的客户端程序里new Guid(...)或指定ProgID去连接时底层就是去注册表里找这个CLSID。Server内部还有一个非常重要的数据结构我把它叫做Tag表。OPC Workshop内部会维护一张表记录当前有多少个Item、每个Item叫什么名字、当前值是多少、质量戳Quality是什么、时间戳是什么。你无论从哪个接口进来最终都是在这张表上做增删改查。找到这张表的定义基本就找到了Server的核心数据模型。2.3 关键接口的实现逻辑我把OPC Server源码里值得重点跟读的接口按建议顺序列一张表每个接口对应Client端的一个核心能力接口/函数作用学习建议IOPCServer::AddGroup创建Group是整个会话的起点先跟读理解Group是如何持有Item集合的IOPCItemMgt::AddItems往Group里添加Item重点看它如何处理无效Item名、客户端句柄与服务端句柄的映射IOPCSyncIO::Read/Write同步读写Item的值最简单适合第一个跟读完整调用链IOPCGroupStateMgt修改更新周期、激活/停止理解回调的数据源周期控制IConnectionPoint相关异步通知机制最难放到最后啃有一类坑会反复出现句柄映射。Client和Server各自维护一套句柄Client传进来的hClient是它自己用来标识Item的服务端必须原样保存并在回调时原样返回给Client否则客户端就没办法知道这条数据属于哪个标签。OPC Workshop源码里对这套映射处理得很规范值得专门花时间看一遍——很多自己写的OPC Server出诡异问题都是在这上面栽了跟头。3. 把OPC Workshop编译跑通在客户端里完成一次真实读写3.1 环境准备与编译顺序OPC Workshop是基于传统Windows COM/ATL技术写的编译环境选择上没有太多花活Visual Studio基本都行。需要注意的点是项目里可能用到了旧版字符集编译前要把项目的字符集改成使用多字节字符集否则很多带char*的代码会报错。另外因为涉及COM和类型库生成建议关闭预编译头的项目单独检查一下如果用VS打开后发现大量文件报找不到头文件先看下是不是Windows SDK版本选得太新。编译顺序上我习惯先把核心Server工程单独编译一遍确认代码本身能编过再去编测试客户端。如果一起编译报错你很难判断问题出在代码还是工具链。3.2 DLL注册与DCOM配置编译成功后接下来是注册。以管理员身份打开命令行执行regsvr32注册生成的DLL。如果看到弹窗提示注册成功那么可以继续如果报模块加载失败多半是缺少运行时依赖比如没装对应的VC运行库。注册完别急着测强烈建议先检查一下DCOM配置。OPC DA基于DCOM在Windows 10及以后的系统上默认DCOM安全策略经常会让Client连不上Server。你需要打开dcomcnfg组件服务找到这个OPC Server的组件在属性里设置身份标识如果只是本机测试选交互式用户最省事。启动和激活权限把当前登录用户加进去给本地启动本地激活权限。访问权限同样把用户加入给允许访问。顺带一提如果后面你打算在另一台机器上跑Client远程连这台Server那要额外处理防火墙和DCOM端口这也是OPC DA最烦人的地方之一。OPC Workshop的学习阶段建议先在本机跑通远程问题等完全理解代码后再去折腾。3.3 用客户端验证模拟一次完整会话编译好测试客户端并连接本机Server后我们需要完整走一遍OPC DA会话的标准流程这段流程无论在哪套OPC系统里都通用创建OPCServer对象实例调用Connect。这一步底层就是COM的CoCreateInstance去注册表找到Server的CLSID并创建对象。调用AddGroup创建一个Group。此时需要传入更新周期比如100ms和客户端组句柄。调用AddItems往Group里添加若干Item比如Random.Int1和Random.Real1这组模拟标签。返回后每个Item会有一个服务端句柄hServer。调用SyncRead读取这几个Item的值。此时如果能拿到非零的数值、质量戳为192质量好/正常说明Server的同步读已经完全打通。再调用AsyncRead或开启订阅回调观察Server端打印出的日志确认数据是按设定周期在推送。我在现场调试时遇到过很多次AddItems成功但Read出来质量戳是0的情况这通常不是通信的问题而是Server内部的Item和真实数据源没绑定上。OPC Workshop因为是模拟数据源理论上不会出现这种问题但你将来替换成真实PLC数据时一定会遇到。到时记住一个排查口诀先看Item名在Tag表里是否存在再看数据源有没有在刷新最后回来看质量戳。4. 为什么说它比lightOPC更容易看4.1 项目组织方式上的差距lightOPC在开源社区里名气不小很多人第一次接触OPC Server源码就是看它。它的优点是代码量少能把一个OPC Server最精简的骨架在几千行里讲完。但精简也是一把双刃剑你会发现很多接口只是给了一个空实现业务逻辑和COM基础机制混在一个文件里看到后面很难分清这是OPC规范要求的东西和这是具体业务逻辑。OPC Workshop的思路不一样。它把COM机制、OPC接口实现、数据源模拟、客户端演示拆成了不同的模块每个模块的职责边界比较清楚。你有任何想研究的问题都能快速定位到对应的工程里去而不是在一堆文件里大海捞针。我自己的体会是读lightOPC像是看一个简笔画能一眼看到轮廓但看不到肌肉和骨骼读OPC Workshop更像看一具教学用的完整骨架模型结构清清楚楚每个部件都能拆下来单独研究。4.2 学习路线的差异骨架 vs 完整工程如果你的目标只是搞清楚COM组件的创建和Release流程那lightOPC完全够用甚至更快。但如果目标是能独立开发一个可维护的OPC Server或者在公司项目里基于开源代码二次开发我强烈建议从OPC Workshop入手。原因很简单真实工程项目里OPC Server不只是实现接口那么简单。它要有配置管理、数据源的接入抽象、日志、异常处理、性能优化这一大堆周边。OPC Workshop在这些方面虽然比不上商业产品但它至少提供了一种正规军的工程写法Copy它的模块切分思路比从零开始组织代码要靠谱得多。另外OPC Workshop带了测试客户端这一点我很看重。学习通信类代码有一个能直接上手的调试客户端比看一百页文档都管用。你可以随时修改Server端的代码然后跑到客户点里验证结果这种改代码-编译-验证的闭环是学习速度的保证。4.3 各自的适用场景我把这两个项目的定位总结一下方便你按需选择维度OPC WorkshoplightOPC代码量中等偏大很小工程完整度接近小型产品最小可行原型COM机制讲解有清晰的模块划分混杂在一起调试体验自带客户端方便验证需要自己另写客户端适合场景二次开发、系统学习快速理解COM/OPC最小骨架如果你只有一晚上想看明白OPC到底是怎么回事看lightOPC如果你有一个月想真正把OPC Server吃透并改造成自己的东西看OPC Workshop这个投入非常值。5. 读完之后往真实项目扩展的三个方向5.1 把模拟数据源替换成真实设备采集OPC Workshop自带的数据源是模拟的读懂了之后第一件值得做的事就是把它替换成真实数据。比如通过Modbus TCP去采集PLC里的寄存器再把寄存器值和OPC Item一一对应起来。这一步做下来你会真正理解OPC Server在工业系统中的位置它本质上是个协议转换数据网关。一边对接各种工业协议Modbus、Profinet、IEC61850另一边对外提供统一的OPC接口。OPC Workshop的价值在于把对外提供OPC接口这半边已经写好了你只需要把对接工业协议那半边填进去。我个人建议你在改数据源时把Tag表的读写接口提取出来定义成一组统一的抽象接口。这样以后换协议、加设备都只是新增一个数据源驱动的事情不用动OPC接口那层代码。这个架构上的好处会随着接入设备变多越来越明显。5.2 从OPC DA向OPC UA迁移的思路现在工业现场越来越倾向于用OPC UA因为UA跨平台、不用DCOM、安全性好。OPC Workshop这一套基于COM的DA技术将来大概率会被UA取代。但学DA源码对理解UA仍然很有帮助——UA虽然实现机制完全不同但信息模型中的Server、对象、变量、订阅这些核心概念和DA是承袭演进的。如果你想从这套源码往UA方向走我建议不要直接改造它而是先移植思路再移植代码。UA有更成熟的第三方开源库比如open62541和UA-.NETStandard你可以用OPC Workshop的模块划分方式来重新组织一个UA Server工程。之后想让DA和UA共存就做一个网关或桥接层把DA Server的数据转发到UA Server里。我在实际项目中就是先用DA把老设备数据统一收进来再用UA把数据模型暴露给上层系统这个过渡方案非常稳。5.3 工程化改造日志、安全与性能OPC Workshop作为教学项目日志和安全这两块相对基础。如果你要在生产环境二次开发建议优先补这三点日志每一条Client连接、Group创建、Item读写、回调失败都要留痕迹。COM接口常见死锁和句柄泄漏很难查很多时候只能靠日志反推时序。安全OPC DA的DCOM安全配置很繁琐生产环境至少要做到按用户授权不同用户只能访问指定Group。这个逻辑可以在AddGroup之前的身份验证环节插入。性能如果点位多上千个Item要关注数据快照的锁粒度。OPC Workshop这种教学代码往往全局一把锁点位多了以后性能会崩。改造方向是分组加锁按Tag表分区管理减少锁竞争。我见过不少二次开发项目死在性能和日志上反而是功能接口很快就调通了。所以我的建议是读懂OPC Workshop只算第一步把它的工程化短板补上才算真正具备上线交付的能力。最后再分享一个我用了很久的学习方法拿到一份开源代码第一遍不要追求读懂每一行而是跟着一条用户请求的路径把关键代码串起来。对OPC Workshop来说就是Connect - AddGroup - AddItems - Read - 下线这条路走一遍给它起个名字叫主链路。主链路通了你再往两边扩去看异常分支、看异步回调、看配置加载。这个思路对看任何通信框架都适用希望你也能在用这套方法啃源码时少走弯路。本文还有配套的精品资源点击获取
返回列表