简介:这份资源是一套基于HslCommunication实现远程读写PLC的多语言示例代码,覆盖C#、Java、Python三种技术栈,适合正在做毕设、课程设计或工程实训的开发者参考。示例包含C#控制台服务端与Windows窗体客户端、Java工程及Python脚本,可以对照学习不同语言下调用HslCommunication库、建立通信并读写PLC数据的基本流程。资源共27个文件,压缩包仅1.54MB,以C#源码(.cs)、配置文件(.config)、Java依赖包(.jar)及.csproj工程文件为主,同时包含.py与.java示例脚本,目录按语言和工程模块划分,便于检索。目前已有549人学习下载。需要留意的是,代码定位为参考资料而非开箱即用的成品,读者需具备一定基础,能自行调试报错并根据实际PLC型号修改通信参数。
1. 远程读写PLC,先从通信链路说起
工业现场有个很常见的需求:设备在三楼车间,数据要进二楼的服务器,人在办公室喝着咖啡看产线状况。传统做法是站到触摸屏前抄数,稍微现代一点是在工控机上装组态软件,但这些都绕不开一个事实——你得先和PLC把话通上。HslCommunication这类通信库解决的就是这个“通上”的问题:它把三菱、西门子、欧姆龙、台达、Modbus TCP等一二线PLC的上位机通信协议封装成了统一的读写接口,让JAVA、Python这类非工控语言也能像读本地变量一样操作PLC的软元件地址。对IT从业者来说,这意味着不再需要懂梯形图,也不需要买专用网关,只要网线能到PLC,就能用熟悉的语言把数据读出来、写进去。这篇文章我会按照自己做过的路径,先把通信模型讲清楚,再给出JAVA和Python的最小可运行示例,最后把远程部署时最容易翻车的网络问题和坑点逐个说透。
2. HslCommunication的通信模型与JAVA端最小实现
2.1 它做了什么:从字节流到软元件地址
PLC本身并不认识HTTP或者JSON,它只认特定格式的二进制帧。三菱MC协议有Qna-3E、Qna-1E之分,西门子S7协议基于COTP和S7-Comm,欧姆龙Fins又是另一套包头结构。HslCommunication做得最核心的一件事,是把这些帧的组包、校验、超时重发、拆包解包全部封装成统一的方法调用,读哪个站、从哪个地址读、读出来是short还是int还是string,都是调用方传入参数。
常见做法是选JAVA版本作为上位机主服务,因为它天然跨平台,部署在Linux服务器上很稳。HslCommunication的JAVA版包名是com.github.hslcommunication,旧版本在Maven中央仓库可以搜到,新版本一般直接引JAR包。我这里用Maven坐标引入依赖:
<dependency> <groupId>com.github.hslcommunication</groupId> <artifactId>hslcommunication</artifactId> <version>1.0.0</version> </dependency>如果你用的是自己构建的JAR,记得把依赖一起引进去,因为HslCommunication底层会用到一些commons-logging之类的库。版本不同,包路径可能从com.hslcommunication变成com.github.hslcommunication,引包报错时先检查这一处。
2.2 用JAVA读取三菱PLC的完整代码
下面这段是连接三菱Q系列或FX5U、读取D100寄存器并写入M100线圈的最小示例:
import com.github.hslcommunication.melsec.MelsecTcpNet; public class MitsubishiReadWriteExample { public static void main(String[] args) throws Exception { // 1. 创建三菱MC协议TCP连接对象,参数是PLC的IP地址和端口号 MelsecTcpNet plc = new MelsecTcpNet("192.168.1.10", 6000); plc.setConnectTimeOut(3000); // 连接超时3秒 plc.setTimeOut(5000); // 读写超时5秒 // 2. 打开连接 try { plc.open(); if (!plc.isOpen()) { System.out.println("连接失败:" + plc.getErrorCode()); return; } // 3. 读取D100开始的5个int值(32位) int[] dValues = plc.readInt32("D100", 5); for (int i = 0; i < dValues.length; i++) { System.out.println("D" + (100 + i) + " = " + dValues[i]); } // 4. 写单个int值到D200 plc.write("D200", 4567); // 5. 置位M100线圈为ON plc.write("M100", true); } finally { plc.close(); } } }逻辑说明:MelsecTcpNet构造参数第一个是PLC的IP地址,第二个是端口号,三菱默认是6000,不要随意改成其他值。readInt32读取的是32位整数,对应PLC里的DINT或32位D寄存器组合;如果你PLC里存的是16位整数,要改用readInt16。写线圈时布尔值true对应ON,false对应OFF。getErrorCode()返回的数字不是HTTP状态码,是HslCommunication内部定义的错误编号,比如60表示远程设备无响应。
2.3 参数怎么调:端口、超时和掉线重连
JAVA实测中必调的参数有三个。第一个是端口号,三菱MC协议一般走6000,西门子S7-1200走102,Modbus TCP走502。第二个是超时时间,connectTimeOut和timeOut都不能设成0,否则连接线程会无限阻塞,我一般放3到5秒,产线网络抖动严重时放到8秒。第三个是重连策略,HslCommunication自带内置重连逻辑,异地PLC按轮询周期调一次读写方法时,如果连接断开,下一次调用会自动尝试重新连接。
提示:不要在每个线程里都new一个连接对象。一台PLC维持一个长连接即可,多线程共享同一个实例时,HslCommunication内部对读写方法做了锁处理,但同一时间只有一个线程能执行读写,高并发场景要加队列或者用
Task异步提交。
3. 用Python远程读写PLC的三种落地方式
3.1 JAVA版做服务,Python走HTTP,这是最稳的组合
HslCommunication官方没有纯Python的三菱/西门子通信库(Modbus库除外),常见的做法是让JAVA上位机把HslCommunication包装一个HTTP接口,Python作为业务端通过requests或httpx调用。这个模式在Python自动化脚本、AI数据采样场景中非常实用,Python只负责业务逻辑,不碰底层协议。
下面用Spring Boot风格的Controller片段说明这个桥接层怎么写:
@RestController @RequestMapping("/plc") public class PlcController { @GetMapping("/read") public Map<String, Object> read(@RequestParam String address, @RequestParam int length) { Map<String, Object> result = new HashMap<>(); try { int[] values = plcInstance.readInt32(address, length); result.put("code", 0); result.put("data", values); } catch (Exception e) { result.put("code", -1); result.put("error", e.getMessage()); } return result; } }Python端的调用就是三行代码:
import requests resp = requests.get("http://192.168.1.100:8080/plc/read", params={"address": "D100", "length": 10}, timeout=5) data = resp.json() if data["code"] == 0: print("D100~D109:", data["data"]) else: print("读取失败:", data["error"])逻辑说明:HTTP桥接的好处是让Python端完全没有通信依赖,你换了PLC品牌也只用改JAVA端。requests.get的timeout字段一定要设,否则JAVA服务端线程池被拖垮时,Python端会一直挂住。
3.2 Python直接连西门子S7系列,pyl Beckhoff的替代路线
如果你的PLC是西门子S7-200SMART或S7-1200,有另一个选择是用python-snap7。这个库不走HslCommunication,是专门针对S7协议的Python绑定。
import snap7 from snap7.util import get_int, set_int plc = snap7.client.Client() plc.connect("192.168.1.10", 0, 1, timeout=3) if plc.get_connected(): # 读取DB1.DBW0开始的2个字节,解释为int area = 0x84 # DB块标识 db_number = 1 start_offset = 0 size = 2 data = plc.read_area(area, db_number, start_offset, size) value = get_int(data, 0) # 字节序按西门子高位在前 print("DB1.DBW0 =", value) # 写入DB1.DBW4为999 new_data = bytearray(2) set_int(new_data, 0, 999) plc.write_area(area, db_number, 4, new_data) else: print("连接失败")参数说明:plc.connect(ip, rack, slot, timeout)第三个参数slot很关键,S7-300/400的CPU一般在槽位2,S7-1200/1500是槽位1,不同PLC型号这俩参数组合不一样。read_area第一个参数0x84代表DB块,如果是输入区则用0x81,输出区用0x82,M区用0x83。字节序默认是大端(西门子风格),这和Modbus TCP默认大端一致,但和JAVA的HslCommunication默认小端处理有差异,两边对同一地址读出来是short还是int时要重点检查字节序。
3.3 直接给Python装HslCommunication的Modbus TCP通道
很多国产PLC(台达、汇川、信捷)和部分欧系PLC都支持Modbus TCP,这类设备可以直接用Python的pymodbus库读写,不经过任何桥接。下面是一个读保持寄存器、写线圈的完整示例:
from pymodbus.client import ModbusTcpClient client = ModbusTcpClient("192.168.1.20", port=502, timeout=3) if client.connect(): # 读从站1的保持寄存器,地址40001映射为0,读5个寄存器 rr = client.read_holding_registers(address=0, count=5, slave=1) if not rr.isError(): print("寄存器值:", rr.registers) # 写线圈地址0为ON wr = client.write_coil(address=0, value=True, slave=1) if not wr.isError(): print("线圈写入成功") client.close()逻辑说明:Modbus地址有“零基地址”和“一基地址”的区别,HslCommunication里写热词"40001"表示第一路保持寄存器,而pymodbus的address=0对应同一路,中间减1的偏移是新手最常踩的坑。slave参数在TCP模式下通常忽略,但很多网关设备挂着多个从站时要用它区分。
4. 远程访问的组网方式:VMware、TIA和防火墙的配合
4.1 本地跑虚拟机连PLC,网络模式怎么选
很多人第一次在VMware里装好博途(TIA Portal)和HslCommunication测试程序后,发现虚拟机和PLC物理设备之间ping不通。这个问题的根源在网络连接模式上。VMware有三种模式:仅主机模式(Host-Only)只与宿主机通信,NAT模式通过宿主机共享IP出网,桥接模式(Bridged)会直接占用宿主机物理网卡的同网段IP。连接真正的PLC,必须选桥接模式,并且要卡在正确的物理网卡上。
VMware桥接模式的具体配置路径是:虚拟机设置 -> 网络适配器 -> 选择“桥接模式” -> 复制物理网络连接状态。如果宿主机有无线网卡和有线网卡两块,还需要在“编辑”->“虚拟网络编辑器”里把桥接目标明确指定到连着PLC的那块物理网卡上。PLC一般接在有线网卡,Wi-Fi经常搜不到设备信号。
4.2 跨网段访问PLC时,路由和防火墙要一起看
生产网络的PLC IP往往是192.168.0.x或10.0.0.x,服务器在另一个网段,JAVA程序访问PLC必须经过三层路由。我建议先把连接链路拆成两段来排查:服务器同网段的交换机能不能ping通PLC的网关,服务器本身能不能ping通PLC的IP。
防火墙是最常见的拦路虎。Linux服务器上通常开着firewalld或ufw,即使你确认应用层代码没问题,单个UDP或TCP端口被屏蔽会让所有超时白白浪费。三菱MC协议需要放行TCP 6000,西门子S7需要放行TCP 102,Modbus TCP需要放行TCP 502:
firewall-cmd --permanent --add-port=6000/tcp firewall-cmd --permanent --add-port=102/tcp firewall-cmd --permanent --add-port=502/tcp firewall-cmd --reload如果是Windows服务器,在“高级安全Windows防火墙”里添加入站规则时要注意“作用域”选项卡,尽量限定远程IP地址为PLC所在的子网,不要直接放行所有来源。这里有个容易被忽略的点:PLC的CPU模块本身也常带“允许来自远程的PUT/GET通信访问”这类开关,尤其是西门子S7-1200,默认情况下DB块优化访问开启,HslCommunication或snap7直接按绝对地址读写会报“无法访问DB块”,需要在博途里关闭DB块的优化块访问属性。
4.3 远程PLC报警和端到端连通性验证
HslCommunication读不到数据时,先不要怀疑代码,按照下面三步验证链路。第一步,宿主机命令telnet 192.168.1.10 6000,能通说明端口开放。第二步,下载一个Modbus Poll或Packet Sender做裸通信测试,直接看响应帧。第三步,检查PLC程序里的通信设置,三菱需要开启MC协议,西门子需要配置连接机制为“允许来自远程对象的通信访问”。
用脚本定时做端到端验证是保证远程读写可靠的手段,我常用下面这段Python:
import subprocess import json result = subprocess.run( ["curl", "-s", "-m", "3", "http://127.0.0.1:8080/plc/health"], capture_output=True, text=True ) if result.returncode == 0: data = json.loads(result.stdout) if data.get("connected"): print(f"PLC连接正常, 当前RSSI: {data.get('signal', 'N/A')}") else: print("PLC连接异常") else: print("HTTP桥接服务不可达")注意:这里RSSI字段不是所有PLC都返回,仅当你的JAVA封装的健康检查接口里有这个字段才打印。端到端验证的频率不用太高,每30秒到1分钟一次就够了,频繁的连接断开会占满PLC的通信资源,严重时触发PLC报警。日志里出现Link-100一类编号时,不是PLC程序故障,而是通信模块断线重连的提示,排查重点应放在物理链路而不是逻辑代码。
5. 一个能落地的技巧:读点位分组打包,写点位统一提交
远程读写的性能瓶颈往往不在通信库,而在调用模型。假设你有一个监控页面需要同时显示D100、D200、D300共三组数据,分开三次readInt32调用会产生三次网络往返。PLC通信是按“一帧一请求一响应”走的,每次调用都要等待响应超时后释放连接资源,三次调用就是三倍延迟和三倍失败概率。
更好的做法是按块读取。三菱PLC的D区是连续的,西门子系统DB块内部也是连续的,所以推荐每次都把请求封装成“起始地址+长度”的形式,一次拉取一大块数据到本地缓存,然后由JAVA或Python解析,读多组数据时只发起一轮请求:
// 一次读D100~D199共100个int,避免100次网络往返 int[] bigChunk = plc.readInt32("D100", 100); // 本地解析:需要D105就取bigChunk[5],需要D130就取bigChunk[30]写入也一样,能连续写的不要拆开。HslCommunication支持write批量写数组,例如一次写入D200到D204五个float值。
float[] fiveValues = {1.1f, 2.2f, 3.3f, 4.4f, 5.5f}; plc.write("D200", fiveValues);参数说明:write的第二个参数传入数组时,内部会自动计算寄存器个数,不需要额外传长度。注意float在HslCommunication里默认对应32位寄存器,如果你的PLC组态用的是16位字存两个float,要使用writeFloat并注意起始地址对齐偶数。
分块打包的第二个好处是异常定位简单。如果整块读成功,你就知道链路是通的,问题只在数据内容;如果整块读失败,日志里记录的起始地址和长度已经足够判断是地址越界还是通信中断,不需要对每个点位单独打点排查。
最后说一个验证读写正确性的自检方式。临时在PLC程序里加一个计数器位,每收到一次写入请求就累加一次,然后在JAVA或Python端监听返回值的增量。如果写入次数远大于计数器增量,说明你的上位机程序有重复提交逻辑;如果写入次数小于计数器增量,说明有其它上位机软件在跟你抢同一批地址。这个验证手段在远程读写场景下帮人省过大量排查时间,比单看通信库的返回值更接近真相。
本文还有配套的精品资源,点击获取