1. 为什么要在VM虚拟机上做欧姆龙PLC通讯
1.1 搞清VM在通讯中的真实角色
先说个我经常遇到的场景:现场用了博途或者CX-Programmer这些老牌PLC软件,但电脑系统太新,软件装不上;或者公司信息安全规定必须用虚拟机隔离环境;再或者是调试团队人手不够,需要在一台电脑上同时开好几个版本的编程软件。这时候,VMware Workstation这类虚拟机就成了刚需。
很多朋友一听到"VM与欧姆龙PLC通讯"就觉得是玄学,其实虚拟机在通讯链路里扮演的角色非常简单——它就是一台普通的Windows上位机。PLC不关心对面是物理机还是虚拟机,它只认IP地址、端口号和协议格式。所以你的虚拟机只要能正常访问物理网络,FINS通讯这事儿就成了一半。
但麻烦也恰恰出在这里。虚拟机的网络是虚拟出来的,默认的NAT模式、仅主机模式,甚至桥接模式没配对,都会导致虚拟机里的上位机软件找不到PLC。我在实际项目里见过太多人卡在"明明物理机能ping通PLC,虚拟机里就是不行"这个坎上,最后折腾半天发现是VMware的网络适配器绑错了物理网卡。
1.2 三种虚拟网络模式怎么取舍
VMware Workstation默认提供三种网络模式:桥接模式、NAT模式、仅主机模式。做PLC通讯时,选型逻辑很简单,记住一个结论:能用桥接就用桥接,别碰NAT,仅主机模式直接放弃。
| 网络模式 | 虚拟机IP与物理网络关系 | PLC能否访问虚拟机 | 是否推荐 |
|---|---|---|---|
| 桥接模式 | 与物理机在同一局域网,类似另一台独立电脑 | 能,PLC可直接通讯 | 强烈推荐 |
| NAT模式 | 虚拟机在私有网段,通过宿主机转发 | 不能直接访问,需端口映射 | 不推荐 |
| 仅主机模式 | 虚拟机与宿主机组成隔离网络 | PLC无法访问 | 不推荐 |
为什么NAT模式不推荐?因为NAT模式下,虚拟机的流量要经过宿主机做地址转换,等于在PLC和上位机之间插了一道"翻译官"。欧姆龙的FINS协议走UDP时是无连接的,NAT的超时机制、地址映射刷新,都可能让通讯断断续续,你查半天都找不到原因。
桥接模式就直白多了。虚拟机网卡直接桥接到物理网卡上,相当于在交换机上多插了一台电脑,IP地址设置成和PLC同一网段,协议栈走的完全是标准的以太网通讯,没有任何中间层干扰。选桥接模式时,VMware设置里有个"复制物理网络连接状态"的选项,建议勾上,这个选项会让虚拟机在物理网络切换时快速拿到IP,省去很多等待时间。
还有个常见误区:如果电脑有无线网卡和有线网卡,VMware桥接时默认可能是"自动",这时候就必须手动指定桥接到哪个网卡。举个例子,PLC接在网线口上,但VMware把桥接绑定到了Wi-Fi,那虚拟机怎么ping都ping不通PLC。这个坑我踩过不止一次,后面排查章节会细说。
2. FINS TCP/UDP协议的核心原理
2.1 FINS协议到底是什么
FINS(Factory Interface Network Service)是欧姆龙专有的工业以太网协议,它跟西门子的S7COMM、三菱的MC Protocol属于同一层级的应用层协议。欧姆龙从早期的C200H系列开始就用这套协议体系,一直延续到现在的NJ/NX系列,兼容性做得相当好。
FINS支持两种以太网传输方式:FINS/UDP和FINS/TCP。核心区别就两条:
- UDP方式:无连接,发完不管,速度快但不保证到达。FINS指令直接作为UDP数据报内容发送,目的端口默认9600。适合周期性高速读写,比如HMI画面刷新、数据采集。
- TCP方式:有连接,要建立会话,但FINS报文前必须加4字节长度前缀,因为TCP是流式协议,接收方要知道一帧数据的边界。目的端口同样是9600。适合数据可靠性要求高的场景,比如配方下载。
端口号都是9600,但帧格式不同,这是初学者最容易混淆的点。用生活化类比的话:UDP是明信片,写上收件人就扔邮筒了;TCP是挂号信,要先约定信封格式,还要签收回执。
还有一个单位要理解:FINS协议里有个"节点号"(Node Number)的概念,它在报文中用1个字节表示(0~254)。在以太网环境下,FINS节点地址默认跟IP地址最后一段绑定,比如192.168.1.10这个PLC,FINS节点号通常是10(十六进制0A)。这个映射关系在现场配PLC时就要先确认好,后面协议调试全靠它。
2.2 FINS报文的10字节命令头
FINS报文最核心的是前面的10字节命令头,每个字段都有固定含义,调协议时抓包看的就是这一段:
| 字节位置 | 字段名 | 含义 | 典型值 |
|---|---|---|---|
| 0 | ICF | 指令/响应标识 | 0x80表示指令,0x00表示响应 |
| 1 | RSV | 保留字段 | 0x00 |
| 2 | GCT | 网关允许重复次数 | 0x02 |
| 3 | DNA | 目标网络号 | 0x00(本地网络) |
| 4 | DA1 | 目标节点号 | PLC的FINS节点号 |
| 5 | DA2 | 目标单元号 | 0x00(CPU内置以太网口) |
| 6 | SNA | 源网络号 | 0x00 |
| 7 | SA1 | 源节点号 | 上位机的FINS节点号 |
| 8 | SA2 | 源单元号 | 0x00 |
| 9 | SID | 服务标识 | 任意值,用于匹配请求和响应 |
命令头后面跟命令码和参数区。以最常用的"读内存"命令为例,命令码是0x0101,参数区是内存区代码(DM区是0x82)、起始地址(2字节)、读取数量(2字节)。一条完整的FINS/UDP读DM区D100的数据帧,长度就是10字节头加7字节参数,总共17字节。
TCP方式就在这个17字节前面再加4字节长度值,表示后面数据的字节数(包含命令头),即0x00000011。这地方我曾经见过有人把长度算错,结果接收方一直解析不到完整帧,通讯怎么也建立不起来。
2.3 节点号和单元号千万别搞混
现场调试时,我经常被问到"节点号设多少""单元号是不是填1",这里必须掰扯清楚。
节点号(Node Number):在以太网中,FINS协议用它来标识一台设备。默认情况下,它跟IP最后一段匹配,但可以手动改。比如PLC的IP是192.168.1.10,节点号默认是10,你把它改成20也没问题,只要上位机报文的DA1跟上就行。很多老工程师习惯固定用IP末段做节点号,就是为了减少记忆负担。
单元号(Unit Number):这是用来区分PLC里不同通信端口的。CPU上自带以太网口时,单元号默认0;如果是单独的以太网单元模块(比如CJ1W-EIP21),单元号要看模块在机架上的位置,拨码或者软件设置。通讯报文的DA2字段填的就是这个值。
还有一个"网络号"的概念。单机直连时,源和目标网络号都填0,不需要改。如果组了多网段路由,才需要配置网络号,现场90%的场景都用不到,知道有这回事就行。
3. 实操配置全程详解:从PLC到VM再到上位机
3.1 PLC侧网络参数设置
在做任何线上通讯之前,先把PLC的网络参数配好。以欧姆龙CP1H系列为例,用USB线连接PLC和电脑,打开CX-Programmer,新建工程选择对应CPU型号,然后走下面几步:
- 在工程树里双击"设置",打开PLC设置界面,切换到"内置以太网端口"选项卡(CP1H带以太网口才有)。
- 设置IP地址为192.168.1.10,子网掩码255.255.255.0,默认网关留空即可。
- 设置FINS节点号,默认10,与IP末段一致,可以保持默认。
- 确认"UDP/TCP端口"为9600,不要随意改,除非你对通讯链路有绝对把控。
- 把设置传送到PLC,断电重启使参数生效。
CJ2、NJ/NX系列的设置方式大同小异,只是界面入口不一样。NJ系列是在Sysmac Studio里配置,但核心参数还是IP、掩码、节点号这三个。
这里有个关键操作习惯:在电脑端设置好PLC的IP后,先不要急着连FINS,先测试网络通不通。在VM虚拟机里ping一下192.168.1.10,如果通了,说明网络层没问题,接着排查协议层。
3.2 VM侧网络配置要点
VMware里配置桥接网络,没有你想的那么复杂,但有几个关键点必须按顺序做:
第一步,设置虚拟机网络适配器为桥接模式。关机状态下,在虚拟机设置里找到"网络适配器",选择"桥接模式",勾选"复制物理网络连接状态"。如果宿主机有多个网卡,点"配置适配器",只勾选PLC实际连接的那块物理网卡。
第二步,确认虚拟机里Windows的IP地址。开机进入Windows后,打开网络适配器设置,手动指定静态IP:192.168.1.20,子网掩码255.255.255.0。不要用DHCP,工业现场最好固定IP,否则哪天PLC通讯突然断了,排查半天发现是虚拟机IP被路由器重新分配了,太冤了。
第三步,检查Windows防火墙。这是虚拟机里最容易被忽视的问题。Windows防火墙默认会拦截外部的UDP/TCP入站连接,你要么在防火墙入站规则里放行9600端口,要么在调试阶段直接关闭防火墙(仅限内网环境)。我一般会新建一条入站规则,允许TCP和UDP的9600端口,这样既安全又省心。
物理机上如果装了360、腾讯管家之类的第三方安全软件,也要检查有没有拦截虚拟机进程的对外通讯,这类问题不好排查,因为系统防火墙放了也不行。
3.3 CX-Programmer的FINS连接设置
虚拟机网络准备好后,打开CX-Programmer,新建一个和PLC同型号的工程,按照下面的方式设置连接:
- 菜单栏:PLC -> 更改PLC(或者"在线工作"之前的设置入口)。
- 设备类型选择对应的PLC型号,比如CP1H-XA40DR。
- 网络类型选择Ethernet,注意这里会弹出驱动选择,有FINS/TCP和FINS/UDP两个选项。
- 点击"设置",填写目标IP地址(PLC的IP),目标节点号填10,源节点号可以随便填,比如1,单元号填0。
- 点击"测试",等待连接结果。
这里有个细节:CX-Programmer的"测试"按钮实际会发送一条FINS指令去探测PLC,如果网络通、协议对,几秒钟内会返回成功。如果测试失败,不要急着点确定,先记录报错信息,对照后面排查章节找原因。
FINS/TCP和FINS/UDP用哪个?单机直连、数据量不大的调试场景,两者都行。我习惯用FINS/UDP,因为UDP调试起来更简单,不涉及连接状态管理;如果通讯环境有丢包风险或数据量大,用FINS/TCP更稳。
3.4 第三方通讯工具和脚本验证
除了CX-Programmer自带的通讯功能,很多时候我们需要自己写软件或用第三方库和PLC通讯。这里用一个Python脚本做示例,演示怎么发FINS/TCP帧读取DM区数据,这对理解协议本质非常有帮助。
import socket import struct def fins_tcp_read_dm(ip, port=9600, dst_node=10, src_node=1, dm_address=0, count=1): # 10字节FINS命令头 fins_header = bytes([ 0x80, # ICF:命令帧 0x00, # RSV 0x02, # GCT 0x00, # DNA dst_node, # DA1 目标节点号 0x00, # DA2 目标单元号 0x00, # SNA src_node, # SA1 源节点号 0x00, # SA2 源单元号 0x01 # SID 服务ID ]) # 读内存命令:0x0101,DM区代码0x82,起始地址2字节,读取数量2字节 params = bytes([0x01, 0x01, 0x82]) + struct.pack('>H', dm_address) + struct.pack('>H', count) fins_frame = fins_header + params # TCP要在FINS帧前面加4字节长度 frame_len = struct.pack('>I', len(fins_frame)) tcp_data = frame_len + fins_frame sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(3) sock.connect((ip, port)) sock.send(tcp_data) # 接收响应:前4字节是长度,后面是FINS帧 resp_len = struct.unpack('>I', sock.recv(4))[0] resp = b'' while len(resp) < resp_len: chunk = sock.recv(resp_len - len(resp)) if not chunk: break resp += chunk sock.close() if resp[0] == 0x00: # 响应帧ICF为0x00 # 命令码后第一个字是完成码,0x0000表示成功 if resp[11:13] == b'\x00\x00': values = struct.unpack('>' + 'H' * count, resp[13:13 + count * 2]) return values return None if __name__ == '__main__': result = fins_tcp_read_dm('192.168.1.10', dm_address=100, count=1) print('D100 value:', result)这个脚本虽然短,但把FINS/TCP的关键帧格式全部体现出来了。调协议时遇到响应异常,可以打印resp看完成码,比如0101表示内存区代码错误、0105表示目标节点不存在、1101表示命令格式错误,对照欧姆龙手册就能快速定位。
4. 常见问题与排查技巧实录
4.1 网络层排查:ping不通怎么办
VM里ping不同PLC,90%是网络层问题,按顺序排查:
第一步,确认物理机能不能ping通PLC。如果物理机都ping不通,那问题在PLC侧或者网线、交换机,跟虚拟机没关系。PLC和工作站之间的网线要用交叉线还是直通线?现代交换机和网卡都支持自动翻转,用直通线就行,但在老设备上要确认。
第二步,确认虚拟机网络适配器设置。打开VMware的"编辑 -> 虚拟网络编辑器",查看桥接模式绑定了哪块物理网卡。如果绑定的是无线网卡,而PLC接在有线网口上,必须手动改为有线网卡。
第三步,确认虚拟机的IP是不是在PLC同一网段。打开Windows的命令行,运行ipconfig,如果显示的是192.168.1.20/24,PLC是192.168.1.10/24,那网段一致。如果是169.254.x.x,说明自动获取IP失败,马上改静态IP。
第四步,确认虚拟网络驱动正常。热词里提到的"vmnet1有感叹号""虚拟机没有网络适配器",基本都是VMware的网络驱动重新安装或修复了,要做的不是去设备管理器折腾,而是在VMware里重新安装VMware Tools,它自带网络驱动。实在不行,用管理员权限运行VMware安装包,选择"修复",会把虚拟网卡驱动重装一遍。
4.2 协议层排查:通了但连不上
ping通了,但CX-Programmer报错,或者自己发FINS指令得不到正确响应,这时候看协议层。
报错"无法连接目标节点",首先检查PLC侧的FINS节点号是否和报文里的DA1一致。很多PLC在修改IP后,节点号没有跟着变,或者你上位机填的源节点号跟PLC表里的不同。更常见的是PLC的以太网单元号填错了,参数里DA2填了1,但实际PLC内置端口单元号是0。
报错"通讯超时",看看走的是FINS/TCP还是FINS/UDP。TCP方式需要先建立连接,如果PLC侧防火墙或路由器阻塞了TCP握手,客户端会一直卡在连接阶段。UDP方式没有连接过程,但要注意UDP是无应答的,如果PLC没有回复,检查一下上位机防火墙是否拦截了PLC发回的UDP包。
一个很经典的操作上的坑:有人在CX-Programmer里设了PLC的IP,但没有传送到PLC内部保存,PLC一重启就恢复成出厂IP了。改完IP后,务必在CX-Programmer里执行"传送到PLC",并重启PLC。而且要注意,PLC的TCP/IP设置和FINS设置是两套参数,可能存在同一个界面里,别只改了IP就以为完事了。
4.3 VM相关坑位速查表
把调试中遇到的高频问题整理成表格,方便直接对照:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 虚拟机没有网络适配器 | VMware Tools未安装或驱动异常 | 重装VMware Tools,或修复VMware安装 |
| VMnet1/VMnet8网卡有感叹号 | 虚拟网络驱动冲突 | 在虚拟网络编辑器中重置,或重装VMware网络驱动 |
| 桥接模式ping不通 | 桥接绑定了错误的物理网卡 | 手动指定桥接到PLC所在网卡 |
| 虚拟机自动获取到169.254.x.x | DHCP服务异常 | 直接设静态IP,别依赖自动获取 |
| ping通但FINS连不上 | 防火墙拦截9600端口 | 入站规则放行TCP/UDP 9600 |
| PLC重启后IP恢复 | 设置没有传送到PLC | 在线下设置传送到PLC并断电重启 |
还有一个小众但很恶心的坑:VMware在挂起(Suspend)后恢复,虚拟机的网卡可能会出现假死状态,表现为虚拟机里有网卡图标但实际收发数据包全是0。解决方法是关闭虚拟机后再开机,或者禁用/启用虚拟网卡,别用挂起恢复来做长时间在线调试。
个人经验谈
从我做欧姆龙项目这么多年的体会来看,VM虚拟机通讯调试最大的敌人不是协议本身,而是"不确定性"。FINS协议是公开且稳定的,坑往往出在网络拓扑、防火墙、虚拟网卡驱动这些外围因素上。所以我的调试顺序永远是固定的:先物理机直连确认PLC没问题,再开虚拟机桥接,最后才动协议层。每一步都确认清楚了再往下走,能省一半的排查时间。
最后再分享一个小技巧:在VMware里装好欧姆龙软件后,建议做一个快照,把所有驱动、IP设置、防火墙规则都配好之后拍个快照。以后任何一台电脑需要做PLC调试,直接克隆这个虚拟机,省去重新配置的半小时。工业调试现场时间就是金钱,这种能复用的基础设施,越早搭建越划算。