From 27ee4d8eca980d99d944f3300c4f255b2383035e Mon Sep 17 00:00:00 2001 From: Shuang Date: Wed, 17 May 2023 17:43:46 +0800 Subject: [PATCH] =?UTF-8?q?docs:=201#=20modify=20tutorial=20(=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E7=9B=B8=E5=85=B3=E6=95=99=E7=A8=8B);=202#=20upgrade?= =?UTF-8?q?=20to=20v1.3.0=20(=E7=89=88=E6=9C=AC=E5=8D=87=E7=BA=A7=E5=88=B0?= =?UTF-8?q?v1.3.0);?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README-CN.md | 6 +- README.md | 6 +- pom.xml | 4 +- .../iot/protocol/s7/service/PLCNetwork.java | 6 +- .../protocol/modbus/service/DemoReadTest.java | 3 + .../modbus/service/DemoWriteTest.java | 3 + .../s7/serializer/S7SerializerTest.java | 4 +- .../iot/protocol/s7/service/DemoS7Custom.java | 49 +++++++ tips/changeLog.md | 9 ++ tutorial/README-Modbus-CN.md | 69 ++++++++- tutorial/README-Modbus-EN.md | 69 ++++++++- tutorial/README-S7-CN.md | 134 ++++++++++++++--- tutorial/README-S7-EN.md | 138 +++++++++++++++--- 13 files changed, 439 insertions(+), 61 deletions(-) create mode 100644 src/test/java/com/github/xingshuangs/iot/protocol/s7/service/DemoS7Custom.java diff --git a/README-CN.md b/README-CN.md index c5fdfca2..f66cce6d 100644 --- a/README-CN.md +++ b/README-CN.md @@ -1,6 +1,6 @@ # IOT-COMMUNICATION -![Maven-v1.2.7](https://img.shields.io/badge/Maven-v1.2.7-brightgreen) +[![Maven-v1.3.0](https://img.shields.io/badge/Maven-v1.3.0-brightgreen)](https://mvnrepository.com/artifact/com.github.xingshuangs/iot-communication) ![Language-java8](https://img.shields.io/badge/Language-java8-blue) ![Idea-2022.02.03](https://img.shields.io/badge/Idea-2022.02.03-lightgrey) ![CopyRight-Oscura](https://img.shields.io/badge/CopyRight-Oscura-yellow) @@ -14,7 +14,7 @@ 目前它只是一个物联网通信的工具,包含 -- 西门子S7通信协议(可以访问西门子S1500,S1200,S200smart); +- 西门子S7通信协议(可以访问西门子**S1500**,**S1200**,**S200smart**,**西门子机床828D**); - ModbusTCP通信协议; - 基础字节数组解析转换工具; @@ -28,7 +28,7 @@ com.github.xingshuangs iot-communication - 1.2.7 + 1.3.0 ``` diff --git a/README.md b/README.md index 5650079b..bb22821c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # IOT-COMMUNICATION -![Maven-v1.2.7](https://img.shields.io/badge/Maven-v1.2.7-brightgreen) +[![Maven-v1.3.0](https://img.shields.io/badge/Maven-v1.3.0-brightgreen)](https://mvnrepository.com/artifact/com.github.xingshuangs/iot-communication) ![Language-java8](https://img.shields.io/badge/Language-java8-blue) ![Idea-2022.02.03](https://img.shields.io/badge/Idea-2022.02.03-lightgrey) ![CopyRight-Oscura](https://img.shields.io/badge/CopyRight-Oscura-yellow) @@ -14,7 +14,7 @@ Now, it is a tool for iot communication, it includes -- Siemens S7 protocol, it can access to S1500, S1200, S200smart. +- Siemens S7 protocol, it can access to **S1500**, **S1200**, **S200smart**, **Siemens Machine Tool 828D**. - ModbusTCP protocol. - Parse byte array data. @@ -28,7 +28,7 @@ Add the dependency to pom.xml in the JAVA project com.github.xingshuangs iot-communication - 1.2.7 + 1.3.0 ``` diff --git a/pom.xml b/pom.xml index 080e94e0..585bd8d8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,11 +6,11 @@ com.github.xingshuangs iot-communication - 1.2.7 + 1.3.0 jar iot-communication - iot communication tool 目前支持S7、ModbusTCP通信协议、字节数据转换 + iot-communication is a tool 目前支持S7、ModbusTCP通信协议、字节数据转换 UTF-8 diff --git a/src/main/java/com/github/xingshuangs/iot/protocol/s7/service/PLCNetwork.java b/src/main/java/com/github/xingshuangs/iot/protocol/s7/service/PLCNetwork.java index 9b712e14..6a1d08f0 100644 --- a/src/main/java/com/github/xingshuangs/iot/protocol/s7/service/PLCNetwork.java +++ b/src/main/java/com/github/xingshuangs/iot/protocol/s7/service/PLCNetwork.java @@ -19,13 +19,13 @@ /** * plc的网络通信 - * 最小字节数组大小是240-18=222,480-18=462,960-18=942 + * 最大读取字节数组大小是240-18=222,480-18=462,960-18=942 * 根据测试S1200[CPU 1214C],单次读多字节 - * 发送:最大字节读取长度是 216 = 240 - 24, 24(响应报文的PDU)=10(header)+14(parameter) + * 发送:最大字节读取长度是 216 = 240 - 24, 24(请求报文的PDU)=10(header)+14(parameter) * 接收:最大字节读取长度是 222 = 240 - 18, 18(响应报文的PDU)=12(header)+2(parameter)+4(dataItem) * 根据测试S1200[CPU 1214C],单次写多字节 * 发送:最大字节写入长度是 212 = 240 - 28, 28(请求报文的PDU)=10(header)+14(parameter)+4(dataItem) - * 接收:最大字节写入长度是 225 = 240 - 15, 15(请求报文的PDU)=12(header)+2(parameter)+1(dataItem) + * 接收:最大字节写入长度是 225 = 240 - 15, 15(响应报文的PDU)=12(header)+2(parameter)+1(dataItem) * * @author xingshuang */ diff --git a/src/test/java/com/github/xingshuangs/iot/protocol/modbus/service/DemoReadTest.java b/src/test/java/com/github/xingshuangs/iot/protocol/modbus/service/DemoReadTest.java index 4450d527..5926c957 100644 --- a/src/test/java/com/github/xingshuangs/iot/protocol/modbus/service/DemoReadTest.java +++ b/src/test/java/com/github/xingshuangs/iot/protocol/modbus/service/DemoReadTest.java @@ -1,6 +1,8 @@ package com.github.xingshuangs.iot.protocol.modbus.service; +import com.github.xingshuangs.iot.utils.HexUtil; + import java.util.List; /** @@ -9,6 +11,7 @@ public class DemoReadTest { public static void main(String[] args) { ModbusTcp plc = new ModbusTcp(1, "127.0.0.1"); + plc.setComCallback(x -> System.out.printf("长度[%d]:%s%n", x.length, HexUtil.toHexString(x))); // read coil List readCoil = plc.readCoil(0, 2); diff --git a/src/test/java/com/github/xingshuangs/iot/protocol/modbus/service/DemoWriteTest.java b/src/test/java/com/github/xingshuangs/iot/protocol/modbus/service/DemoWriteTest.java index a7f002d5..f11c5f0a 100644 --- a/src/test/java/com/github/xingshuangs/iot/protocol/modbus/service/DemoWriteTest.java +++ b/src/test/java/com/github/xingshuangs/iot/protocol/modbus/service/DemoWriteTest.java @@ -1,6 +1,8 @@ package com.github.xingshuangs.iot.protocol.modbus.service; +import com.github.xingshuangs.iot.utils.HexUtil; + import java.util.Arrays; import java.util.List; @@ -11,6 +13,7 @@ public class DemoWriteTest { public static void main(String[] args) { ModbusTcp plc = new ModbusTcp(1, "127.0.0.1"); + plc.setComCallback(x -> System.out.printf("长度[%d]:%s%n", x.length, HexUtil.toHexString(x))); // single write coil plc.writeCoil(0, true); diff --git a/src/test/java/com/github/xingshuangs/iot/protocol/s7/serializer/S7SerializerTest.java b/src/test/java/com/github/xingshuangs/iot/protocol/s7/serializer/S7SerializerTest.java index a106f525..0b817db7 100644 --- a/src/test/java/com/github/xingshuangs/iot/protocol/s7/serializer/S7SerializerTest.java +++ b/src/test/java/com/github/xingshuangs/iot/protocol/s7/serializer/S7SerializerTest.java @@ -19,7 +19,8 @@ public class S7SerializerTest { @Test public void read() { - s7PLC.setComCallback(x -> log.debug("长度[{}],内容:{}", x.length, HexUtil.toHexString(x))); + s7PLC.setComCallback(x -> System.out.printf("长度[%d]:%s%n", x.length, HexUtil.toHexString(x))); +// s7PLC.setComCallback(x -> log.debug("长度[{}],内容:{}", x.length, HexUtil.toHexString(x))); S7Serializer s7Serializer = S7Serializer.newInstance(s7PLC); DemoBean bean = s7Serializer.read(DemoBean.class); log.info(bean.toString()); @@ -65,6 +66,7 @@ public void writeLargeData() { S7Serializer s7Serializer = S7Serializer.newInstance(s7PLC); DemoLargeBean bean = s7Serializer.read(DemoLargeBean.class); System.out.println("-------------------------------"); + bean.setBitData(true); bean.getByteData2()[0] = (byte) 0x05; bean.getByteData3()[0] = (byte) 0x05; bean.getByteData4()[0] = (byte) 0x05; diff --git a/src/test/java/com/github/xingshuangs/iot/protocol/s7/service/DemoS7Custom.java b/src/test/java/com/github/xingshuangs/iot/protocol/s7/service/DemoS7Custom.java new file mode 100644 index 00000000..f6872f06 --- /dev/null +++ b/src/test/java/com/github/xingshuangs/iot/protocol/s7/service/DemoS7Custom.java @@ -0,0 +1,49 @@ +package com.github.xingshuangs.iot.protocol.s7.service; + + +import com.github.xingshuangs.iot.protocol.s7.enums.*; +import com.github.xingshuangs.iot.protocol.s7.model.*; + +/** + * @author xingshuang + */ +public class DemoS7Custom { + + public static void main(String[] args) { + S7PLC s7PLC = new S7PLC(EPlcType.S1200, "127.0.0.1"); + + // bit数据读写 + byte[] expect = new byte[]{(byte) 0x00}; + s7PLC.writeRaw(EParamVariableType.BIT, 1, EArea.DATA_BLOCKS, 1, 0, 3, + EDataVariableType.BIT, expect); + byte[] actual = s7PLC.readRaw(EParamVariableType.BIT, 1, EArea.DATA_BLOCKS, 1, 0, 3); + + // byte数据读写 + expect = new byte[]{(byte) 0x02, (byte) 0x03}; + s7PLC.writeRaw(EParamVariableType.BYTE, 2, EArea.DATA_BLOCKS, 1, 1, 0, + EDataVariableType.BYTE_WORD_DWORD, expect); + byte[] actual1 = s7PLC.readRaw(EParamVariableType.BYTE, 2, EArea.DATA_BLOCKS, 1, 1, 0); + + // 对象形式发送 + RequestNckItem item = new RequestNckItem(ENckArea.C_CHANNEL, 1, 23, 1, ENckModule.S, 1); + S7Data s7Data = NckRequestBuilder.creatNckRequest(item); + S7Data ackData = s7PLC.readFromServerByPersistence(s7Data); + + // 裸报文发送 + byte[] sendByteArray = new byte[]{ + // tpkt + (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x1D, + // cotp DT Data + (byte) 0x02, (byte) 0xF0, (byte) 0x80, + // header + (byte) 0x32, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x13, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0x00, + // parameter + (byte) 0x04, (byte) 0x01, + // request item + (byte) 0x12, (byte) 0x08, (byte) 0x82, (byte) 0x41, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x01, (byte) 0x7f, (byte) 0x01 + }; + byte[] recByteArray = s7PLC.readFromServerByPersistence(sendByteArray); + + s7PLC.close(); + } +} diff --git a/tips/changeLog.md b/tips/changeLog.md index 9be9080b..3ca83863 100644 --- a/tips/changeLog.md +++ b/tips/changeLog.md @@ -1,3 +1,12 @@ +## v1.3.0 +- 更新时间:2023.05.17 +- 添加西门子协议中NCK的寻址方式,主要针对西门子机床828D的数据访问 +- S7协议添加对time、date、timeOfDay数据类型的读写 +- S7协议序列化方式中添加对string、time、date、timeOfDay的解析 +- 修复S7协议写string时更改第一个字节的bug(第一个字节为允许最大字节数),现在直接跳过该字节的处理 +- modbus添加回调事件,用于输出交互的报文 +- modbus添加短连接方式,添加readBoolean方法 + ## v1.2.7 - 更新时间:2023.03.16 - 修复下S7序列化读取时,由于实体类字段超过18个且读取数据量较小导致报文大小超过PDU的BUG diff --git a/tutorial/README-Modbus-CN.md b/tutorial/README-Modbus-CN.md index 10643754..ab7b25bc 100644 --- a/tutorial/README-Modbus-CN.md +++ b/tutorial/README-Modbus-CN.md @@ -2,6 +2,73 @@ [返回主页](../README-CN.md) +## 前言 + +> 注意点 + +- **1** 个线圈 = **1** 位 +- **1** 个寄存器地址 = **2** 个字节; +- [ 寄存器起始地址 = **0001** ] 相当于 [ 访问地址 = **0** ],存在 **1** 位地址偏移; +- **4 字节**数据的编码格式 = **BA_DC**; (大端模式 = **DC_BA**,小端模式 = **AB_CD**) + +> 存储区 + +| 区号 | 名称 | 读写 | 地址范围 | 对应方法 | +|:---:|:------|:--------|:-----------:|:-------------------------------------| +| 0区 | 输出线圈 | 可读可写布尔量 | 00001-09999 | readCoil / writeCoil | +| 1区 | 输入线圈 | 只读布尔量 | 10001-19999 | readDiscreteInput | +| 3区 | 输入寄存器 | 只读寄存器 | 30001-39999 | readInputRegister | +| 4区 | 保持寄存器 | 可读可写寄存器 | 40001-49999 | readHoldRegister / writeHoldRegister | + +> 功能码 + +| 功能码 | 功能说明 | 对应方法 | +|:---:|:--------|:------------------| +| 01H | 读取输出线圈 | readCoil | +| 02H | 读取输入线圈 | readDiscreteInput | +| 03H | 读取保持寄存器 | readHoldRegister | +| 04H | 读取输入寄存器 | readInputRegister | +| 05H | 写入单线圈 | writeCoil | +| 06H | 写入单寄存器 | writeHoldRegister | +| 0FH | 写入多线圈 | writeCoil | +| 10H | 写入多寄存器 | writeHoldRegister | + +> 保持寄存器快捷访问 + +| 方法 | 寄存器数量 | 字节大小 | 位大小 | 含义 | 寄存器 | +|:-------------|:-----:|:----:|:---:|:----------|:-----:| +| readBoolean | 1 | 1/8 | 1 | 读取boolean | 保持寄存器 | +| readInt16 | 1 | 2 | 16 | 读取Int16 | 保持寄存器 | +| readUInt16 | 1 | 2 | 16 | 读取UInt16 | 保持寄存器 | +| readInt32 | 2 | 4 | 32 | 读取Int32 | 保持寄存器 | +| readUInt32 | 2 | 4 | 32 | 读取UInt32 | 保持寄存器 | +| readFloat32 | 2 | 4 | 32 | 读取Float32 | 保持寄存器 | +| readFloat64 | 4 | 8 | 64 | 读取Float64 | 保持寄存器 | +| readString | n | 2n | 16n | 读取字符串 | 保持寄存器 | +| writeInt16 | 1 | 2 | 16 | 写入Int16 | 保持寄存器 | +| writeUInt16 | 1 | 2 | 16 | 写入UInt16 | 保持寄存器 | +| writeInt32 | 2 | 4 | 32 | 写入Int32 | 保持寄存器 | +| writeUInt32 | 2 | 4 | 32 | 写入UInt32 | 保持寄存器 | +| writeFloat32 | 2 | 4 | 32 | 写入Float32 | 保持寄存器 | +| writeFloat64 | 4 | 8 | 64 | 写入Float32 | 保持寄存器 | +| writeString | n | 2n | 16n | 写入字符串 | 保持寄存器 | + +## 打印报文 + +如果想知道通信过程中的实际输入输出报文内容,可以添加报文信息打印 + +```java +class Demo { + public static void main(String[] args) { + ModbusTcp plc = new ModbusTcp(1, "127.0.0.1"); + // 报文输出设置 + plc.setComCallback(x -> System.out.printf("长度[%d]:%s%n", x.length, HexUtil.toHexString(x))); + plc.writeInt16(2, (short) 10); + plc.close(); + } +} +``` + ## 通信连接 - 默认采用长连接的方式,不用的时候需要手动关闭; @@ -122,7 +189,7 @@ class Demo { // hold register write float64 plc.writeFloat64(2, 33.21); - // hold register write String + // hold register write String,偶数长度 plc.writeString(2, "1234"); plc.close(); diff --git a/tutorial/README-Modbus-EN.md b/tutorial/README-Modbus-EN.md index 3375ef60..6b7b5825 100644 --- a/tutorial/README-Modbus-EN.md +++ b/tutorial/README-Modbus-EN.md @@ -2,6 +2,73 @@ [HOME BACK](../README.md) +## Foreword + +> Tips + +- **1** coil = **1** bit +- **1** register address = **2** bytes; +- [ register start address = **0001** ] equal to [ access address = **0** ], there is a **1** address offset; +- The encoding format of **4 bytes** data = **BA_DC**; (big-endian mode = **DC_BA**,little-endian mode = **AB_CD**) + +> Area + +| Area Number | Name | Read/Write | Address Range | Method | +|:-----------:|:---------------|:-----------|:-------------:|:-------------------------------------| +| 0 area | output coil | read/write | 00001-09999 | readCoil / writeCoil | +| 1 area | input coil | read | 10001-19999 | readDiscreteInput | +| 3 area | input register | read | 30001-39999 | readInputRegister | +| 4 area | hold register | read/write | 40001-49999 | readHoldRegister / writeHoldRegister | + +> Function Code + +| Function Code | Description | Method | +|:-------------:|:------------------------|:------------------| +| 01H | read output coil | readCoil | +| 02H | read input coil | readDiscreteInput | +| 03H | read hold register | readHoldRegister | +| 04H | read input register | readInputRegister | +| 05H | write single coil | writeCoil | +| 06H | write single register | writeHoldRegister | +| 0FH | write multiple coil | writeCoil | +| 10H | write multiple register | writeHoldRegister | + +> Hold Register Quick Access + +| Method | Register Count | Size in Byte | Size in Bit | Register | +|:-------------|:--------------:|:------------:|:-----------:|:-------------:| +| readBoolean | 1 | 1/8 | 1 | hold register | +| readInt16 | 1 | 2 | 16 | hold register | +| readUInt16 | 1 | 2 | 16 | hold register | +| readInt32 | 2 | 4 | 32 | hold register | +| readUInt32 | 2 | 4 | 32 | hold register | +| readFloat32 | 2 | 4 | 32 | hold register | +| readFloat64 | 4 | 8 | 64 | hold register | +| readString | n | 2n | 16n | hold register | +| writeInt16 | 1 | 2 | 16 | hold register | +| writeUInt16 | 1 | 2 | 16 | hold register | +| writeInt32 | 2 | 4 | 32 | hold register | +| writeUInt32 | 2 | 4 | 32 | hold register | +| writeFloat32 | 2 | 4 | 32 | hold register | +| writeFloat64 | 4 | 8 | 64 | hold register | +| writeString | n | 2n | 16n | hold register | + +## Print Message + +If you want to know the actual input and output of packets during communication, you can print packet information. + +```java +class Demo { + public static void main(String[] args) { + ModbusTcp plc = new ModbusTcp(1, "127.0.0.1"); + // print message + plc.setComCallback(x -> System.out.printf("Length[%d]:%s%n", x.length, HexUtil.toHexString(x))); + plc.writeInt16(2, (short) 10); + plc.close(); + } +} +``` + ## Communication Connection - By default, the long connection mode is adopted. You need to close connection manually when it is not in use. @@ -122,7 +189,7 @@ class Demo { // hold register write float64 plc.writeFloat64(2, 33.21); - // hold register write String + // hold register write String, even length plc.writeString(2, "1234"); plc.close(); diff --git a/tutorial/README-S7-CN.md b/tutorial/README-S7-CN.md index 60524ade..0f22681f 100644 --- a/tutorial/README-S7-CN.md +++ b/tutorial/README-S7-CN.md @@ -4,6 +4,12 @@ ## 前言 +- 支持单数据读写,多数据读写,大数据量自动分包读写 +- 支持序列化批量多地址且地址不连续的读写 +- 支持读写**DB**区,**I**区,**Q**区,**M**区,**V**区 +- 支持读写西门子**S1500**,**S1200**,**S200Smart**,**西门子机床828D** (**S300,S400未测试过,但同S1200**) +- 支持PLC自动重连 + 1. 如果你不熟悉S7协议可以查看这个[地址](https://blog.csdn.net/XS_YOUYOU/article/details/124870209) 2. 对于200smartPLC的V区,就是DB1.X,例如,**V1=DB1.1,V100=DB1.100** @@ -11,20 +17,20 @@ > 知识点1:地址的格式以及对应含义,兼容大小写 -| 简写 | 区域 | 字节索引 | 位索引 | PLC类型 | -|---------|:----:|:----:|:---:|----------| -| DB1.1.2 | DB1区 | 1 | 2 | 1200 | -| DB2 | DB2区 | 0 | 0 | 1200 | -| DB3.3 | DB3区 | 3 | 0 | 1200 | -| D1.1.2 | DB1区 | 1 | 2 | 1200 | -| Q1.6 | Q区 | 1 | 6 | 1200 | -| Q1 | Q区 | 1 | 0 | 1200 | -| I2.5 | I区 | 2 | 5 | 1200 | -| I2 | I区 | 2 | 0 | 1200 | -| M3.2 | M区 | 3 | 2 | 1200 | -| M3 | M区 | 3 | 0 | 1200 | -| V2.1 | V区 | 2 | 1 | 200Smart | -| V2 | V区 | 2 | 0 | 200Smart | +| 简写 | 区域 | 字节索引 | 位索引 | PLC类型 | +|---------|:----:|:----:|:---:|-------------| +| DB1.1.2 | DB1区 | 1 | 2 | S1200/S1500 | +| DB2 | DB2区 | 0 | 0 | S1200/S1500 | +| DB3.3 | DB3区 | 3 | 0 | S1200/S1500 | +| D1.1.2 | DB1区 | 1 | 2 | S1200/S1500 | +| Q1.6 | Q区 | 1 | 6 | S1200/S1500 | +| Q1 | Q区 | 1 | 0 | S1200/S1500 | +| I2.5 | I区 | 2 | 5 | S1200/S1500 | +| I2 | I区 | 2 | 0 | S1200/S1500 | +| M3.2 | M区 | 3 | 2 | S1200/S1500 | +| M3 | M区 | 3 | 0 | S1200/S1500 | +| V2.1 | V区 | 2 | 1 | S200Smart | +| V2 | V区 | 2 | 0 | S200Smart | > 知识点2:访问数据类型与JAVA数据类型和PLC数据类型对应关系 @@ -43,12 +49,45 @@ | date | 日期 | 16 | 2 | LocalDate | Date | 2023-04-03 | | timeOfDay | 一天中的时间 | 32 | 4 | LocalTime | TimeOfDay | 10:22:11 | +> 知识点3:PLC地址与本项目地址和数据类型的对应关系 + +| PLC地址 | 位大小 | 字节大小 | 访问地址 | 访问数据类型 | PLC类型 | +|--------------|:---:|:----:|:----------|:---------------------|:-----------:| +| DB100.DBX0.0 | 1 | 1/8 | DB100.0.0 | boolean | S1200/S1500 | +| DB100.DBB5 | 8 | 1 | DB100.5 | byte | S1200/S1500 | +| DB100.DBW6 | 16 | 2 | DB100.6 | uint16/int16 | S1200/S1500 | +| DB100.DBD3 | 32 | 4 | DB100.3 | uint32/int32/float32 | S1200/S1500 | +| VB100 | 8 | 1 | V100 | byte | S200Smart | +| VW100 | 16 | 2 | V100 | uint16/int16 | S200Smart | +| VD100 | 32 | 4 | V100 | uint32/int32/float32 | S200Smart | +| MB1 | 8 | 1 | M1 | byte | - | +| MW1 | 16 | 2 | M1 | uint16/int16 | - | +| MD1 | 32 | 4 | M1 | uint32/int32/float32 | - | + +![S200Smart](http://www.ad.siemens.com.cn/productportal/Prods/s7-200-smart-portal/200SmartTop/programming/images/4.2.jpg) + +## 打印报文 + +如果想知道通信过程中的实际输入输出报文内容,可以添加报文信息打印 + +```java +class Demo { + public static void main(String[] args) { + S7PLC s7PLC = new S7PLC(EPlcType.S1200, "127.0.0.1"); + // 报文输出设置 + s7PLC.setComCallback(x -> System.out.printf("长度[%d]:%s%n", x.length, HexUtil.toHexString(x))); + s7PLC.readByte("DB2.1"); + s7PLC.close(); + } +} +``` + ## 通信连接 - PLC默认采用长连接的方式,不用的时候需要手动关闭; - 若需要短连接,则需要手动设置; -### 1. 长连接方式 +### 1. 长连接方式(推荐) ```java class Demo { @@ -57,7 +96,7 @@ class Demo { S7PLC s7PLC = new S7PLC(EPlcType.S1200, "127.0.0.1"); s7PLC.writeByte("DB2.1", (byte) 0x11); s7PLC.readByte("DB2.1"); - // 需要手动关闭 + // 需要手动关闭,若一直要使用,则不需要关闭 s7PLC.close(); } } @@ -78,7 +117,7 @@ class Demo { } ``` -## 客户端教程 +## 客户端教程(S7Any寻址) ### 1. 直接方式读写 @@ -225,16 +264,39 @@ class Demo { class Demo { public static void main(String[] args) { S7PLC s7PLC = new S7PLC(EPlcType.S1200, "127.0.0.1"); + // bit数据读写 byte[] expect = new byte[]{(byte) 0x00}; - this.s7PLC.writeRaw(EParamVariableType.BIT, 1, EArea.DATA_BLOCKS, 1, 0, 3, + s7PLC.writeRaw(EParamVariableType.BIT, 1, EArea.DATA_BLOCKS, 1, 0, 3, EDataVariableType.BIT, expect); - byte[] actual = this.s7PLC.readRaw(EParamVariableType.BIT, 1, EArea.DATA_BLOCKS, 1, 0, 3); + byte[] actual = s7PLC.readRaw(EParamVariableType.BIT, 1, EArea.DATA_BLOCKS, 1, 0, 3); + // byte数据读写 expect = new byte[]{(byte) 0x02, (byte) 0x03}; - this.s7PLC.writeRaw(EParamVariableType.BYTE, 2, EArea.DATA_BLOCKS, 1, 1, 0, + s7PLC.writeRaw(EParamVariableType.BYTE, 2, EArea.DATA_BLOCKS, 1, 1, 0, EDataVariableType.BYTE_WORD_DWORD, expect); - actual = this.s7PLC.readRaw(EParamVariableType.BYTE, 2, EArea.DATA_BLOCKS, 1, 1, 0); + byte[] actual1 = s7PLC.readRaw(EParamVariableType.BYTE, 2, EArea.DATA_BLOCKS, 1, 1, 0); + + // 对象形式发送 + RequestNckItem item = new RequestNckItem(ENckArea.C_CHANNEL, 1, 23, 1, ENckModule.S, 1); + S7Data s7Data = NckRequestBuilder.creatNckRequest(item); + S7Data ackData = s7PLC.readFromServerByPersistence(s7Data); + + // 裸报文发送 + byte[] sendByteArray = new byte[]{ + // tpkt + (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x1D, + // cotp DT Data + (byte) 0x02, (byte) 0xF0, (byte) 0x80, + // header + (byte) 0x32, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x13, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0x00, + // parameter + (byte) 0x04, (byte) 0x01, + // request item + (byte) 0x12, (byte) 0x08, (byte) 0x82, (byte) 0x41, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x01, (byte) 0x7f, (byte) 0x01 + }; + byte[] recByteArray = s7PLC.readFromServerByPersistence(sendByteArray); + s7PLC.close(); } } @@ -372,7 +434,7 @@ class Demo { } ``` -## 服务端教程 +## 服务端教程(S7Any寻址) - 服务端支持默认支持I区,Q区,M区,T区,C区以及DB1区,每个区都包含65536个字节; - 服务端可以自定义DB区,随意添加; @@ -417,7 +479,7 @@ class Demo { } ``` -## 西门子机床教程 +## 西门子机床教程(NCK寻址) ```java class Demo { @@ -446,3 +508,29 @@ class Demo { } } ``` + +## 常见问题 + +> 1、为什么PLC能写入数据,但是checkConnected 却是false呢? + +通信采用懒加载,读写的时候才会触发连接,将checkConnected放在write或read后就变成true + +> 2、PLC通信过程中最大的读写数据字节大小? + +PLC的网络通信,根据不同型号PLC的PDULength而定,S1200=240,S1500=960,总之有240,480,960
+最大读取字节数组大小是240-18=222,480-18=462,960-18=942
+ +```text +根据测试S1200[CPU 1214C],单次读多字节 +发送:最大字节读取长度是 216 = 240 - 24, 24(请求报文的PDU)=10(header)+14(parameter) +接收:最大字节读取长度是 222 = 240 - 18, 18(响应报文的PDU)=12(header)+2(parameter)+4(dataItem) + +根据测试S1200[CPU 1214C],单次写多字节 +发送:最大字节写入长度是 212 = 240 - 28, 28(请求报文的PDU)=10(header)+14(parameter)+4(dataItem) +接收:最大字节写入长度是 225 = 240 - 15, 15(响应报文的PDU)=12(header)+2(parameter)+1(dataItem) +``` + +> 3、在PLC关闭之后获取异常,在PLC重启之后自动连入该怎么处理? + +内部支持断线重连,每次触发读写操作的时候,若PLC已经断线,则触发重连操作。 + diff --git a/tutorial/README-S7-EN.md b/tutorial/README-S7-EN.md index e8b59ed5..a31389e7 100644 --- a/tutorial/README-S7-EN.md +++ b/tutorial/README-S7-EN.md @@ -4,6 +4,13 @@ ## Foreword +- Read-write single data and multi data, large data automatic subcontracting. +- Read-write serialized batch multiple addresses and discontinuous address. +- Read-write **DB**, **I**, **Q**, **M**, and **V**. +- Read-write Siemens **S1500**, **S1200**, **S200Smart**, **Siemens Machine Tool 828D**. (**S300, S400 not tested, but + same as S1200**) +- Support automatic PLC reconnection. + 1. You can check this [address](https://blog.csdn.net/XS_YOUYOU/article/details/124870209) if you're not familiar with the S7 protocol.
2. 200smartPLC, V Area == DB1.X. Example: **V1=DB1.1, V100=DB1.100** @@ -12,20 +19,20 @@ > Tips1: Format and meaning of the address, case compatible. -| Abbr | Area | Byte Index | Bit Index | PLC Type | -|---------|:----:|:----------:|:---------:|----------| -| DB1.1.2 | DB1 | 1 | 2 | 1200 | -| DB2 | DB2 | 0 | 0 | 1200 | -| DB3.3 | DB3 | 3 | 0 | 1200 | -| D1.1.2 | DB1 | 1 | 2 | 1200 | -| Q1.6 | Q | 1 | 6 | 1200 | -| Q1 | Q | 1 | 0 | 1200 | -| I2.5 | I | 2 | 5 | 1200 | -| I2 | I | 2 | 0 | 1200 | -| M3.2 | M | 3 | 2 | 1200 | -| M3 | M | 3 | 0 | 1200 | -| V2.1 | V | 2 | 1 | 200Smart | -| V2 | V | 2 | 0 | 200Smart | +| Abbr | Area | Byte Index | Bit Index | PLC Type | +|:--------|:----:|:----------:|:---------:|:------------| +| DB1.1.2 | DB1 | 1 | 2 | S1200/S1500 | +| DB2 | DB2 | 0 | 0 | S1200/S1500 | +| DB3.3 | DB3 | 3 | 0 | S1200/S1500 | +| D1.1.2 | DB1 | 1 | 2 | S1200/S1500 | +| Q1.6 | Q | 1 | 6 | S1200/S1500 | +| Q1 | Q | 1 | 0 | S1200/S1500 | +| I2.5 | I | 2 | 5 | S1200/S1500 | +| I2 | I | 2 | 0 | S1200/S1500 | +| M3.2 | M | 3 | 2 | S1200/S1500 | +| M3 | M | 3 | 0 | S1200/S1500 | +| V2.1 | V | 2 | 1 | S200Smart | +| V2 | V | 2 | 0 | S200Smart | > Tips2: Access data types mapping to JAVA data types and PLC data types. @@ -44,12 +51,45 @@ | date | 16 | 2 | LocalDate | Date | 2023-04-03 | | timeOfDay | 32 | 4 | LocalTime | TimeOfDay | 10:22:11 | +> Tip3: The PLC address mapping to the project address and data type + +| PLC Address | Data Size in Bit | Data Size in Byte | Access Address | Access Data Type | PLC Type | +|--------------|:----------------:|:-----------------:|:---------------|:---------------------|:-----------:| +| DB100.DBX0.0 | 1 | 1/8 | DB100.0.0 | boolean | S1200/S1500 | +| DB100.DBB5 | 8 | 1 | DB100.5 | byte | S1200/S1500 | +| DB100.DBW6 | 16 | 2 | DB100.6 | uint16/int16 | S1200/S1500 | +| DB100.DBD3 | 32 | 4 | DB100.3 | uint32/int32/float32 | S1200/S1500 | +| VB100 | 8 | 1 | V100 | byte | S200Smart | +| VW100 | 16 | 2 | V100 | uint16/int16 | S200Smart | +| VD100 | 32 | 4 | V100 | uint32/int32/float32 | S200Smart | +| MB1 | 8 | 1 | M1 | byte | - | +| MW1 | 16 | 2 | M1 | uint16/int16 | - | +| MD1 | 32 | 4 | M1 | uint32/int32/float32 | - | + +![S200Smart](http://www.ad.siemens.com.cn/productportal/Prods/s7-200-smart-portal/200SmartTop/programming/images/4.2.jpg) + +## Print Message + +If you want to know the actual input and output of packets during communication, you can print packet information. + +```java +class Demo { + public static void main(String[] args) { + S7PLC s7PLC = new S7PLC(EPlcType.S1200, "127.0.0.1"); + // print message + s7PLC.setComCallback(x -> System.out.printf("Length[%d]:%s%n", x.length, HexUtil.toHexString(x))); + s7PLC.readByte("DB2.1"); + s7PLC.close(); + } +} +``` + ## Communication Connection - By default, the long connection mode is adopted. You need to close connection manually when it is not in use. - If a short connection is required, you need to set it manually. -### 1. Long Connection Mode +### 1. Long Connection Mode (**Recommend**) ```java class Demo { @@ -58,7 +98,7 @@ class Demo { S7PLC s7PLC = new S7PLC(EPlcType.S1200, "127.0.0.1"); s7PLC.writeByte("DB2.1", (byte) 0x11); s7PLC.readByte("DB2.1"); - // close it manually + // close it manually, if you want to use it all the time, you do not need to close it s7PLC.close(); } } @@ -79,7 +119,7 @@ class Demo { } ``` -## Client Tutorial +## Client Tutorial (S7Any address) ### 1. Direct Mode Read-write @@ -226,16 +266,38 @@ class Demo { class Demo { public static void main(String[] args) { S7PLC s7PLC = new S7PLC(EPlcType.S1200, "127.0.0.1"); + // bit data read-write byte[] expect = new byte[]{(byte) 0x00}; - this.s7PLC.writeRaw(EParamVariableType.BIT, 1, EArea.DATA_BLOCKS, 1, 0, 3, + s7PLC.writeRaw(EParamVariableType.BIT, 1, EArea.DATA_BLOCKS, 1, 0, 3, EDataVariableType.BIT, expect); - byte[] actual = this.s7PLC.readRaw(EParamVariableType.BIT, 1, EArea.DATA_BLOCKS, 1, 0, 3); + byte[] actual = s7PLC.readRaw(EParamVariableType.BIT, 1, EArea.DATA_BLOCKS, 1, 0, 3); + // byte data read-write expect = new byte[]{(byte) 0x02, (byte) 0x03}; - this.s7PLC.writeRaw(EParamVariableType.BYTE, 2, EArea.DATA_BLOCKS, 1, 1, 0, + s7PLC.writeRaw(EParamVariableType.BYTE, 2, EArea.DATA_BLOCKS, 1, 1, 0, EDataVariableType.BYTE_WORD_DWORD, expect); - actual = this.s7PLC.readRaw(EParamVariableType.BYTE, 2, EArea.DATA_BLOCKS, 1, 1, 0); + byte[] actual1 = s7PLC.readRaw(EParamVariableType.BYTE, 2, EArea.DATA_BLOCKS, 1, 1, 0); + + // send with object + RequestNckItem item = new RequestNckItem(ENckArea.C_CHANNEL, 1, 23, 1, ENckModule.S, 1); + S7Data s7Data = NckRequestBuilder.creatNckRequest(item); + S7Data ackData = s7PLC.readFromServerByPersistence(s7Data); + + // send with raw message + byte[] sendByteArray = new byte[]{ + // tpkt + (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x1D, + // cotp DT Data + (byte) 0x02, (byte) 0xF0, (byte) 0x80, + // header + (byte) 0x32, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x13, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0x00, + // parameter + (byte) 0x04, (byte) 0x01, + // request item + (byte) 0x12, (byte) 0x08, (byte) 0x82, (byte) 0x41, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x01, (byte) 0x7f, (byte) 0x01 + }; + byte[] recByteArray = s7PLC.readFromServerByPersistence(sendByteArray); s7PLC.close(); } @@ -374,7 +436,7 @@ class Demo { } ``` -## Server Tutorial +## Server Tutorial (S7Any address) - By default, the server supports area I, Q, M, T, C and DB1, each includes 65536 bytes. - The server can customize the DB area and add it at will. @@ -419,7 +481,7 @@ class Demo { } ``` -## Siemens Machine Tool Tutorial +## Siemens Machine Tool Tutorial(NCK address) ```java class Demo { @@ -447,4 +509,32 @@ class Demo { s7PLC.close(); } } -``` \ No newline at end of file +``` + +## Q&A + +> 1、Why can PLC write data but checkConnected return always false? + +Communication uses lazy loading. The connection is triggered only when reading or writing.
CheckConnected return +true +after reading or writing. + +> 2、Maximum read/write data byte size during PLC communication? + +According to different types of PLC PDULength, S1200=240, S1500=960. In a word there are 240, 480, 960
+The maximum read byte array size is 240-18=222, 480-18=462, 960-18=942
+ +```text +According to the test S1200[CPU 1214C], read multiple bytes in a single time +Send:The maximum byte read length is 216 = 240 - 24, 24(request PDU)=10(header)+14(parameter) +Receive:The maximum byte read length is 222 = 240 - 18, 18(response PDU)=12(header)+2(parameter)+4(dataItem) + +According to the test S1200[CPU 1214C], write multiple bytes in a single time +Send:The maximum byte write length is 212 = 240 - 28, 28(request PDU)=10(header)+14(parameter)+4(dataItem) +Receive:The maximum byte write length is 225 = 240 - 15, 15(response PDU)=12(header)+2(parameter)+1(dataItem) +``` + +> 3、What about getting exceptions after PLC shutdown and automatically connecting after PLC restart? + +Internal support for disconnection reconnects.
If the PLC has been disconnected, the reconnection is +triggered in each time of reading and writing operation. \ No newline at end of file