
简介面向Java开发与工业自动化从业者这份压缩包借助HslCommunication组件提供了Java与西门子PLC基于S7协议通讯的完整示例代码解决了开发者直接对接PLC时协议细节复杂、上手门槛高的问题。HslCommunication将S7协议底层封装为易用的API通过TCP/IP即可连接S7-200、S7-1200、S7-300、S7-400等常见型号完成数据读取与写入屏蔽了底层数据包组包与网络通信细节适用于远程监控、自动化控制系统集成及工业物联网场景的快速原型搭建。资源包共19个文件以7个Java源码为核心配合6个XML工程配置、1个TXT说明和点位读取演示整体仅15KB结构精简清晰可直接导入IDE研读。目前已有293人学习下载。通过阅读源码可以掌握PLC连接对象的创建、IP与端口配置、读写指令调用等关键流程同时理解HslCommunication对TCP/IP通信的封装思路为后续开发稳定可靠的工业应用提供可复用的代码范式。1. Java连西门子PLC没那么玄S7协议与HslCommunication适配层工业现场最常见的需求之一就是用Java把西门子PLC里的设备状态、产量、温度读出来再把控制字写回去。很多人一听到S7协议就头大以为要从TCP报文开始自己拼TPKT、PDU、TSAP其实有一条现成的路HslCommunication。这个开源通讯库把S7协议封装成了几十个Java方法你只需要告诉它PLC的IP、机型、机架号和槽号就能直接读点位。这份压缩包正好是围绕HslCommunication写好的Java示例工程既有S7-1200的完整连接代码也有PLC200的独立点位读取Demo。这套东西适合两类人一类是做设备数据采集、MES对接、非标自动化上位机的Java开发想快速把PLC接进自己的业务系统另一类是刚入行、被网上零散的S7报文教程劝退的初学者。它的价值在于协议黑匣子不用自己撬但连接参数、地址格式、数据类型的坑一个都绕不开。下面按我拆包、复现的实际顺序来写。2. 拆包看源码PLC200与PLC1200两份工程和一个点位读取Demo2.1 压缩包里的实际文件IDEA工程、源码与补丁残留先看压包里的顶层内容plc-master说明作者是在Git仓库的master分支上打出来的包。下面有README.en.md.rej这是git apply补丁失败时留下的reject文件属于历史残留不影响使用删掉即可。真正干活的是src目录和两个PLC型号子目录。PLC200和PLC1200分别对应西门子S7-200系列和S7-1200系列的示例工程。每个子目录里都有独立的IDEA工程文件untitled.iml、.idea/下的uiDesigner.xml、libraries/、misc.xml、modules.xml、encodings.xml。这套文件是IntelliJ IDEA的标准工程结构libraries/里存的是库引用路径说明作者是通过IDEA界面把HslCommunication的jar挂进工程的。PLCDataReader.java是专门负责点位读取的类PLCConnection.java是连接管理类Main.java是入口另外还有一个单独拆出来的“A点位读取demo”示例。建议导入时直接用IDEA打开untitled.iml让它重建工程索引。如果你用的是Eclipse或VS Code不用管.iml按普通Java工程把src设为源码根目录、把lib下的jar加进classpath也能跑。特别注意encodings.xml里如果写的是GBK说明源码注释可能是中文GBK编码导入后乱码时把IDE的Global Encoding和File Encoding都切成UTF-8再重新打开文件这是我在Windows上踩过的第一个坑。2.2 S7协议TCP连接原理102端口、机架号与槽位HslCommunication虽然把协议细节封装了但连接参数还是要理解否则配错了连日志都看不懂。S7协议跑在TCP 102端口上连接建立后先做TPKT传输层握手再通过S7通信服务协商PDU长度之后才允许发读写请求。这套握手由库内部自动完成你真正要关心的只有三个参数IP地址、机架号Rack、槽号Slot。对S7-1200和S7-1500CPU通常是插在机架0的第0槽或第1槽HslCommunication里默认给的是槽位0或1具体看PLC组态。而对S7-300/400CPU常见槽位是2如果按默认的1去连会出现一种很迷惑的现象TCP连接能建立、端口能通但握手时直接被拒日志里报“协议错误”。这类问题在5.1节里再展开。从代码角度HslCommunication的Java版提供了多个S7Net构造重载常见用法是传入PLC类型、IP地址、端口号和槽位。PLC类型枚举里通常有S200、S300、S400、S1200、S1500。其中S200走的是S7协议里针对200的封装实现和300/400/1200在TSAP计算上有差异这个差异直接决定了你用哪个构造函数。2.3 PLC200与PLC1200的初始化差异PPI老设备和S7-TCPPLC200和PLC1200两个目录分开放不是随便分的。S7-200老款CPU的以太网通讯能力有限原生支持的是PPI协议走串口或特定以太网模块时和标准的S7-TCP并不完全相同。HslCommunication里对S7-200的封装更多是兼容带以太网模块或者S7-200 SMART的场景。如果你手里是早期S7-200 CPU本体只有一个串口没有CP243-1以太网模块那PLC200目录里的示例是跑不通的必须先加硬件模块或者改用串口PPI方案。S7-1200则原生支持S7-TCP这也是为什么PLC1200目录里的PLCConnection.java最简单一个IP、一个槽号基本就能连。从工程命名也能反推作者的使用场景他大概率是用S7-1200做验证同时保留了S7-200的代码给老设备备用。所以你在复用这份代码时先确认现场PLC的具体型号和固件再决定走哪个目录。我看到PLC200下的PLCDataReader.java写得比1200那边更细因为200的地址区划分更受数据类型影响直接读V区变量存储区时byte和bool的偏移处理很容易错位。3. 跑通第一个点位引入HslCommunication、连接S7-1200并读回数据3.1 工程导入与依赖jar放lib还是走Maven坐标先把工程跑起来再谈业务。拿到压缩包后我习惯先把PLC1200目录单独拷出来做成一个干净工程避免PLC200的代码干扰排查。依赖引入有两种方式一种是用包里libraries/引用的本地jar直接把HslCommunication的jar放进lib目录IDEA里右键Add as Library另一种是走Maven按HslCommunication在仓库发布的坐标引用具体版本号以你拿到的Release为准。如果走Maven注意HslCommunication的Java版依赖了少量第三方库最典型的是日志组件。缺日志依赖时连接失败不会报错而是静默超时表现得特别像“PLC没开机”。我建议第一次跑通之前不要动任何日志配置先用默认实现把连接代码跑起来看到日志输出“连接成功”再继续。本地jar方式有一个好处可以直接在IDEA里点进S7Net的源码看到它内部发送的PDU字节。遇到协议层问题时这一步能省大量猜测时间。从命令行用javac编译也可以但classpath要把lib下的所有jar都带全我一般写成一个run.sh把-cp lib/*:src直接写进去。3.2 最小可运行代码连接、检查状态、读MB和DB下面这段是我根据PLCConnection.java和PLCDataReader.java的常见结构整理出的最小可运行代码目标是把S7-1200的MB0字节、I0.0位和DB1.DBW0读出来。import com.github.hslcommunication.S7Net; import com.github.hslcommunication.PLCType; public class S7ReadDemo { public static void main(String[] args) { // 机型选S1200IP按现场PLC实际地址改端口默认102 S7Net plc new S7Net(PLCType.S1200, 192.168.1.10, 102, 0, 1); plc.setConnectTimeOut(3000); plc.setTimeOut(5000); // connect返回操作结果IsSuccess为true才继续 OperateResult connectResult plc.connectServer(); if (!connectResult.IsSuccess) { System.out.println(连接失败 connectResult.Message); return; } // M区按字节偏移M0是第一个字节 OperateResultByte m0 plc.readByte(M0); // I区是输入映像区I0.0是第0字节的第0位 OperateResultboolean i00 plc.readBool(I0.0); // DB块要写块号地址DBW0表示DB1的第0字 OperateResultInteger dbw0 plc.readInt16(DB1.DBW0); System.out.println(M0 m0.Content); System.out.println(I0.0 i00.Content); System.out.println(DB1.DBW0 dbw0.Content); plc.connectClose(); } }这段代码里重点看三个地方。第一是构造函数的五个参数机型、IP、端口、机架号、槽号S7-1200通常机架0槽位1如果是S7-300往往要改成槽位2。第二是readByte和readBool的地址格式“M0”读的是M存储区的第0字节“I0.0”则是位地址。第三是返回值统一包装成OperateResult先判断IsSuccess再取Content不要裸调getContent()否则通讯失败时会拿到默认值数据是错的但你不知道。3.3 连接参数怎么设IP、slot、超时与重连参数配置是整个复现过程里最容易翻车的部分。给出一张我实际使用的参数对照表按现场情况改参数推荐值说明IP地址如192.168.1.10必须和PLC在同一网段先ping通再连端口102S7协议固定端口不能改成其他值机架号Rack0S7-300有些组态是0少数是1槽号Slot1200/1500填0或1300/400填2槽号错的时候TCP能通但握手失败connectTimeOut3000ms现场PLC响应慢时调到5000timeOut5000ms读大块数据时不够批量读建议8000重连是另一个关键。PLC断电重启、网线松动都会让S7连接失效而且Java侧的Socket往往感知不到。常见做法是开启一个定时任务每5秒调用一次connectServer()如果返回的连接状态已经断开就重新建连。HslCommunication本身不提供自动重连开关这个逻辑要自己在业务层做。我习惯在PLCConnection.java里增加一个ensureConnected()方法内部先判断IsConnect再决定是否重连每次读写前都过一遍这个方法能挡掉大部分现场“为什么跑着跑着读不到数据”的投诉。提示连接失败时先别急着怀疑代码。在命令行用telnet 192.168.1.10 102测一下端口通不通。端口通了问题大概率在槽号或机型端口都不通问题在PLC侧防火墙或物理链路。4. 数据读写与类型映射bool、int、float、string的地址与API4.1 S7数据区地址规则I/Q/M/DB与字节位偏移S7协议里的数据区有固定的助记符I是输入映像区Q是输出映像区M是位存储区DB是数据块。地址的最小单位是字节往上组合成字和双字。理解这一点你才不会把“M100”当成一个位地址。以M区为例M100指第100字节如果你要读第100字节的第3位要写成M100.3。同样的规则适用于I区和Q区I0.0到I0.7是输入字节0的8个位。DB块稍微特殊一点地址里要带上块号比如DB1.DBW0表示DB1的第0个字DB1.DBD4表示DB1从第4字节开始的32位双字。注意DB偏移是从0开始算的这和很多触摸屏组态软件里从1显示的习惯不同对接老设备点位表时要先做减法。字节里位的高低顺序也容易踩坑。S7采用大端序高位字节在前。比如一个16位整数存在DBW0里第0字节是高位。Java本身也是大端序理论上直接对齐但HslCommunication的读取方法会帮你做转换所以尽量用封装好的readInt16、readInt32而不是自己拼byte数组。自己拼数组的结果往往是数字对不上最后排查半天发现是高低字节组反了。4.2 读写方法清单从ReadBool到ReadStringHslCommunication常用读写方法按数据类型整理如下每行对应一个典型业务场景方法地址示例读取结果适用场景readBoolM100.3布尔值设备启停、阀门开关readByteMB10单字节原始状态字readInt16DB1.DBW0short温度、压力等模拟量readInt32DB1.DBD4int计数器、产量累计readFloatDB1.DBD8float变频器频率、实际值readStringDB1.DBB10, 20定长字符串设备型号、操作员姓名字符串读取要特别注意第二个参数是字节长度而不是字符串长度。S7里存字符串常见格式是前1个字节存最大长度、第2个字节存当前长度从第3个字节才是内容。如果你用readString(DB1.DBB10, 20)读出来一堆乱码先看点位表里这个字符串的定义格式把偏移往后挪两个字节再试。写操作和读操作一一对应write(M100.3, true)、write(DB1.DBW0, 50)是常态用法。写bool时地址必须带位号直接写write(M100, true)有些版本会当成写整个字节行为不可预期。我在项目里统一规定凡是bool点位地址一律写全位号凡是16位以上数值一律用对应的writeInt16、writeFloat禁止拿整数强转。4.3 批量读写与PDU长度减少通讯次数才是性能关键读单个点位没问题后下一个瓶颈是性能。S7协议一个PDU包能承载的数据量有限HslCommunication内部有最大长度限制通常是240字节左右S7-1200/1500协商后可以到960字节。如果一次read指定的字节数超过这个上限返回的OperateResult会带上“长度错误”之类的消息。批量读的常规做法有两个。一是用数组读readInt32(DB1, 10)直接连续读10个整数前提是地址连续二是不连续点位就分组并发每个组控制在PDU长度内再用CompletableFuture并行发起。注意并发读要配合独立的连接实例或者加锁HslCommunication的单个S7Net实例内部发送接收共用Socket多线程同时读写同一个实例会出现“请求响应对不上”的乱象。我在现场采集30台设备时每台设备有20多个点位就是把点位表按DB块连续地址拼成一个大buffer一次读回再在本地拆字段。实测单次读取50字节和读取10字节的耗时几乎一样但通讯次数从20次降到1次整个轮询周期从1.2秒降到0.3秒。所以写采集逻辑前先花半小时整理点位连续性比换什么高性能库都管用。5. 避坑连不上、读错数、掉线的六类现场问题5.1 TCP能通但握手失败机架号与槽号不匹配现象connectServer()返回失败日志提示协议错误但telnet IP 102是通的。原因S7握手阶段PLC要校验机架号和槽号西门子300/400常见CPU槽位是2而代码里默认按1200的槽位1在连握手自然被拒绝。解决查PLC组态里的实际槽位在构造函数里改最后一个参数S7-300/400改成2S7-1200/1500确认是0还是1后再填。5.2 float读出来是天文数字或位数不对现象读变频器频率PLC监控屏里显示50.00HzJava读回来是1.4E-38或者一个明显不合理的整数。原因数据类型映射错了最常见的是PLC组态里用32位浮点代码里却用了readInt32去读或者反之。另一种可能是字节错位点位表写DBD8你读成了DBD4。解决先确认PLC组态里的数据类型和起始地址float用readFloat、int用readInt32不要混用地址按字节偏移仔细算一遍DBD和DBW之间差两个字节。5.3 PLC200老设备连不上协议根本不兼容现象把PLC200目录的代码原样拿来连接S7-200老CPU一直报握手超时。原因老款S7-200 CPU只带串口走的是PPI协议HslCommunication的S7协议封装需要以太网接口。没有CP243-1以太网模块或者不是S7-200 SMART压根没有TCP 102端口可连。解决现场确认CPU型号和硬件配置老200要么加模块走S7-TCP要么改用PPI串口方案200 SMART直接用S7协议槽号通常填0。5.4 运行半小时后突然读不到数据空闲断线没有重建现象程序启动时正常几十分钟后日志开始连续报超时连接对象还显示在线。原因PLC侧有通讯看门狗长时间没有读写会断开连接而Java侧的Socket对象感知不到底层断开HslCommunication的连接状态还停留在旧值。 解决每次读写前先检查连接状态断开就重连写一个ensureConnected()方法内部判断IsConnect并调用connectServer()把这段逻辑放在所有读写入口形成一个固定前缀。5.5 多线程同时读写导致数据错乱现象用线程池并发读多个点位偶发读到完全错误的数据比如读温度读成了产量而且频率不定。原因同一个S7Net实例被多个线程共享内部Socket的发送和接收没有互斥A线程的请求可能拿到B线程的响应。解决给共享的S7Net实例加synchronized锁或者干脆用连接池每个线程持有一个独立连接。现场点数多时我选择后者省去锁竞争代价是PLC侧连接数会增加一般几十个以内没问题。5.6 bool点位写入不生效现象write(M100, true)返回成功但触摸屏和PLC程序里M100没有任何变化。原因地址M100在S7协议里指向的是整个字节不是位。HslCommunication对这种不带位号的bool写入可能按字节处理或者落到位0取决于具体版本。解决统一写成write(M100.0, true)带全位号。任何bool点位不论读写地址模板都钉死为“字节.位”形成规范后这类问题就根除了。6. 验证与进阶仿真验证、日志开关与封装采集服务6.1 没有实物PLC怎么验证现场不一定有PLC给你联调我通常先用TIA Portal的仿真功能跑一个虚拟S7-1200把虚拟PLC的IP设成和本机同网段再用这份工程去连。注意仿真模式下要把PLC允许远程GET/PUT访问的勾选打开否则握手会失败。连上后往MB0、DB1写入测试值程序读回来的值能对上就说明代码链路没问题。6.2 把连接封装成可复用的采集服务项目上线前我会把PLCConnection.java改造成一个服务类连接、重连、读、写、关闭五个方法外加一个记录最后一次通讯时间的时间戳。采集任务以固定周期调用读回的数据放进一个带时间戳的Map丢给上层MES或数据库。点位表单独配置成JSON避免改点位就改代码。这样换设备、加点位都只动配置文件不动逻辑“从那以后我每次接非标PLC项目都强制先花半小时把点位表、字节序、槽号钉在文档里再写第一行连接代码血泪经验都在这了希望帮到你。”本文还有配套的精品资源点击获取