简介:这份资源是面向工业自动化开发者与工控软件工程师的 OPCUA 通信实例源码,聚焦 PLC 与 PC 机之间的数据交换场景,帮助读者理解并落地 OPC 统一架构下的客户端与服务器交互机制。压缩包共 285 个文件,约 926KB,以 C 语言源码(111 个 .c)与头文件(47 个 .h)为核心实现,辅以 21 个 Python 脚本、12 个 CMake 构建文件及若干配置、说明与测试文档,整体结构清晰,便于按模块阅读与二次开发。内容围绕身份验证与授权、信息模型、服务集、网络通信及二进制编解码等关键技术点展开,涵盖服务器端节点建模与监听、客户端连接浏览读写订阅、安全证书配置及示例应用等模块。已有 265 人学习下载,适合希望掌握 OPCUA 通信原理、提升自动化系统集成与扩展能力的开发者参考实践。
1. OPCUA 通信用于 PLC 与 PC 机通信实例源码:从握手到订阅的完整落地
车间里一台西门子 S7-1200 跑着产线逻辑,上位机 PC 需要每 200ms 抓一次温度、压力、节拍,还要在参数越限时立刻收到推送。传统做法是走 Modbus TCP 轮询,点位一多就卡,语义还得自己对齐。这套 OPCUA 通信用于 PLC 与 PC 机通信的实例源码,解决的正是这个场景:它把服务器端、客户端、配置文件和示例应用打包在一起,让你直接看到 PLC 侧信息模型怎么建、PC 侧怎么连、怎么读、怎么写、怎么订阅。适合做工业自动化的开发者、上位机工程师,以及正在把 OPCUA 塞进现有产线通信链路的从业者。源码不是玩具,它把身份验证、数据模型、服务集、网络通信、编码解码这几块都落到了可运行的文件里,下面按「是什么 → 怎么用 → 坑在哪」拆开讲。
2. 源码结构与 OPCUA 通信链路拆解:服务器、客户端、配置各管什么
拿到压缩包先别急着跑,把目录翻一遍,理解每个文件在通信链路里的位置,后面调参和排错才不会抓瞎。这套源码的骨架大致分四块:服务器端代码、客户端端代码、配置文件、示例应用。它们不是孤立的,服务器端负责在 PLC 侧把数据组织成节点树并监听连接,客户端端负责在 PC 侧发起会话、浏览节点、读写和订阅,配置文件承载安全策略与证书信息,示例应用则把客户端调用串成一个能看结果的小程序。
2.1 服务器端:信息模型与节点是怎么建起来的
OPCUA 服务器不是简单开个端口等连接,它要先有一套信息模型。信息模型就是一棵节点树,每个节点有 NodeId、BrowseName、数据类型、访问权限。PLC 里的一个温度变量,在服务器端会被映射成一个变量节点,挂在某个对象节点下面。源码里服务器端的核心动作通常是这几步:初始化服务器、加载证书、注册地址空间、添加变量节点、启动监听。
# 服务器端初始化与节点注册(示意,按源码实际类名替换) from opcua import Server server = Server() server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/") # 监听所有网卡,端口 4840 server.set_server_name("PLC_OPCUA_Server") # 加载安全策略与证书,源码里通常放在配置文件读取 server.load_certificate("cert/server_cert.pem") server.load_private_key("cert/server_key.pem") idx = server.register_namespace("http://plc.example.com") # 自定义命名空间 objects = server.get_objects_node() plc_obj = objects.add_object(idx, "PLC_Line1") # 建一个产线对象节点 temp_node = plc_obj.add_variable(idx, "Temperature", 25.0) # 温度变量节点 temp_node.set_writable() # 允许客户端写入 server.start()这段代码的逻辑说明:set_endpoint决定客户端连哪个地址,0.0.0.0表示监听本机所有网卡,端口 4840 是 OPCUA 常见默认端口,但源码里如果改了端口,客户端必须同步改。register_namespace注册自定义命名空间,避免和标准节点冲突。add_object和add_variable是建信息模型的关键,节点层级决定了客户端浏览时的路径。set_writable不加,客户端写入会直接被拒。参数上,命名空间索引idx后面客户端要用到,写死或动态获取都行,但两边必须一致。
2.2 客户端端:连接、浏览、读写、订阅四件事
客户端在 PC 机上跑,任务就四件:连上服务器、浏览节点找到目标、读写变量、订阅变化。源码里客户端部分一般会把这四步拆成独立函数,方便你替换成自己的业务逻辑。
# 客户端连接与订阅(示意) from opcua import Client client = Client("opc.tcp://192.168.1.10:4840/freeopcua/server/") client.set_user("operator") # 用户名,源码配置文件里定义 client.set_password("opcua123") # 密码 client.connect() root = client.get_root_node() plc_obj = root.get_child(["0:Objects", "2:PLC_Line1"]) # 按命名空间索引找节点 temp_node = plc_obj.get_child(["2:Temperature"]) print("当前温度:", temp_node.get_value()) # 读 temp_node.set_value(30.5) # 写 # 订阅:值变化时回调 class SubHandler: def datachange_notification(self, node, val, data): print("温度变化:", val) handler = SubHandler() sub = client.create_subscription(200, handler) # 200ms 发布间隔 sub.subscribe_data_change(temp_node)逻辑说明:Client的地址必须和服务器set_endpoint完全一致,IP、端口、路径三者错一个就连不上。get_child里的0:Objects是标准节点,2:PLC_Line1里的2就是服务器注册的命名空间索引,这个数字两边对不上是新手最常见的翻车点。create_subscription的第一个参数是发布间隔,单位毫秒,设太小网络压力大,设太大实时性差,200ms 是产线监控里比较稳的值。订阅回调datachange_notification是服务器主动推,不是客户端轮询,这是 OPCUA 相比 Modbus 轮询的核心优势。
2.3 配置文件:安全策略与证书别用默认
配置文件通常管三件事:服务器端点地址、安全策略、证书路径。安全策略常见有 None、Basic128Rsa15、Basic256Sha256 几档,源码为了让你先跑通,可能默认给的是 None,但实际产线不能这么干。证书这块,服务器和客户端要互相交换证书,客户端信任服务器证书,服务器信任客户端证书,任何一边没导入信任列表,握手阶段就会断。配置文件里还会写用户名密码或匿名访问开关,匿名访问调试方便,上线前必须关掉。
提示:先把配置文件里的端点地址、端口、命名空间索引、安全策略四项抄下来,后面客户端和服务器对不上时,第一个查的就是这四项。
3. 从零跑通 PLC 与 PC 机通信:环境、连接、读写、订阅四步实操
上一章把结构拆开了,这一章直接动手。目标很明确:让 PC 机上的客户端连上 PLC 侧的服务器,读到值、写进去、收到订阅推送。整个过程分四步,每步都有容易卡住的地方,我按实际调试顺序写。
3.1 环境准备与依赖安装
先确认 Python 版本,源码一般跑在 3.7 以上。依赖主要是 OPCUA 库,常见的是opcua或asyncua,源码用哪个就装哪个。虚拟环境建议开,避免和系统里其他工业库冲突。
python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install opcua # 按源码 requirements.txt 为准 pip install cryptography # 证书相关,安全策略非 None 时必需逻辑说明:cryptography在安全策略为 None 时可能不报错,但一旦切到 Basic256Sha256,没有它直接起不来。参数上,如果源码带了requirements.txt,优先按文件装,版本号别乱升,工业库版本差异经常导致 API 不兼容。
3.2 启动服务器并确认监听
服务器端代码跑起来后,别急着开客户端,先在 PLC 侧或本机确认端口在听。
# Linux 下确认 4840 端口监听 netstat -tlnp | grep 4840 # Windows 下 netstat -ano | findstr 4840逻辑说明:如果端口没出现,说明服务器没起来或崩了,先看服务器端日志。常见原因是证书路径写错、端口被占、命名空间注册失败。参数上,0.0.0.0:4840表示所有网卡,如果只想本机调试可以改127.0.0.1,但 PC 机远程连就必须是0.0.0.0或实际网卡 IP。
3.3 客户端连接与节点浏览
连接成功后,第一件事不是读写,是浏览节点树,确认你要的变量到底挂在哪。
# 浏览节点,打印层级 def browse(node, depth=0): for child in node.get_children(): print(" " * depth + child.get_browse_name().to_string()) browse(child, depth + 1) browse(client.get_root_node())逻辑说明:get_browse_name返回的是 BrowseName,带命名空间前缀,和 NodeId 不是一回事。浏览的目的是拿到目标节点的准确路径,路径错了get_child就抛异常。参数上,浏览深度别太深,产线服务器节点多的时候全量浏览很慢,可以先定位到 Objects 再往下。
3.4 读写与订阅的完整调用
浏览确认路径后,读写和订阅按第 2 章的客户端代码走。这里补一个实际业务里常用的点:批量读。
# 批量读多个节点,减少往返 nodes = [temp_node, pressure_node, cycle_node] values = [n.get_value() for n in nodes] print(dict(zip(["temp", "pressure", "cycle"], values)))逻辑说明:OPCUA 单次读一个节点会有网络往返开销,点位多的时候批量读能明显降延迟。参数上,订阅间隔和批量读频率要配合,订阅负责变化推送,批量读负责周期快照,两者别设成同一频率互相打架。
注意:读写测试先用一个可写变量,别拿产线正在用的关键变量练手,写错值可能触发联锁。
4. 避坑与排查:OPCUA 连接失败、订阅无推送、证书报错的常见问题
这一章是血泪经验集中区。OPCUA 的报错信息经常很笼统,光看异常栈定位不到根因,下面五条是我在实际项目里反复遇到的。
4.1 连接直接被拒:端点地址或端口不一致
现象:客户端connect()抛连接异常,服务器日志没有连接记录。原因:客户端地址和服务器set_endpoint不一致,IP、端口、路径任意一项不同都会连不上。解决:把两边地址逐字符对比,路径里的freeopcua/server/这种后缀最容易漏。端口被防火墙拦也会这样,先在服务器本机用客户端连一次排除网络因素。
4.2 握手阶段断开:证书未互信
现象:TCP 能连上,但会话建立到一半断,日志提示证书验证失败。原因:服务器证书没导入客户端信任列表,或客户端证书没导入服务器信任列表。解决:找到双方证书目录,把对方证书加入 trusted 列表,重启服务。安全策略从 None 切到加密时,这一步必做,没有后悔药。
4.3 订阅建了但收不到推送
现象:create_subscription没报错,但回调一直不触发。原因:订阅的节点值没变化,或发布间隔设得比变化频率还大,或节点本身不可订阅。解决:先手动改一次节点值确认订阅链路通,再把发布间隔调小测试。参数上,发布间隔要小于你关心的最小变化周期。
4.4 写入被拒:节点不可写或权限不足
现象:set_value抛权限异常。原因:服务器端节点没调set_writable,或当前用户角色没有写权限。解决:服务器端确认节点可写,配置文件里确认用户角色。匿名访问默认通常只读。
4.5 命名空间索引对不上
现象:get_child找不到节点,报 BadNodeIdUnknown。原因:客户端用的命名空间索引和服务器注册的不一致,服务器重启后索引可能变。解决:客户端动态获取命名空间索引,别写死数字。常见做法是用get_namespace_index按 URI 查。
5. 进阶技巧:用订阅+批量读把 PLC 与 PC 机通信延迟压到 100ms 内
跑通之后,真正决定这套源码能不能上产线的,是延迟和稳定性。我一般会做三件事:订阅负责变化、批量读负责快照、心跳负责探活。订阅间隔设 100ms,批量读 500ms 一次,两者错开,避免同一时刻网络拥塞。心跳用一个只读节点,客户端每 2s 读一次,读不到就重连。
# 心跳探活与自动重连 import time def keep_alive(client, heartbeat_node): while True: try: heartbeat_node.get_value() time.sleep(2) except Exception: print("连接断开,尝试重连") client.connect() time.sleep(1)逻辑说明:get_value成功说明会话还在,失败就重连。参数上,心跳间隔别太短,2s 足够,太短反而增加服务器负担。重连后订阅要重新建,因为会话变了,旧订阅失效,这点源码里如果没处理,要自己补。
| 参数 | 建议值 | 说明 |
|---|---|---|
| 订阅发布间隔 | 100~200ms | 越小实时性越好,网络压力越大 |
| 批量读周期 | 500ms | 和订阅错开,避免拥塞 |
| 心跳间隔 | 2s | 探活,失败触发重连 |
| 安全策略 | Basic256Sha256 | 上线必用,调试可临时 None |
从那以后我每次把 OPCUA 往产线推之前,都强制走一遍「订阅+批量读+心跳」三件套,少一件都不敢上线。希望帮到你。
本文还有配套的精品资源,点击获取