diff --git a/pom.xml b/pom.xml
index bbf4779..d8f026f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,11 +80,19 @@
+
+
+ org.springframework.retry
+ spring-retry
+ 1.2.2.RELEASE
+
+
+
com.alibaba
fastjson
- 1.2.78
+ 2.0.21
@@ -201,6 +209,10 @@
influxdb-client-java
6.7.0
+
+ junit
+ junit
+
diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Connector.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Connector.java
index f209099..e7f9413 100644
--- a/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Connector.java
+++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Connector.java
@@ -30,7 +30,7 @@ public interface S7Connector extends Closeable {
* @param offset
* @return
*/
- public byte[] read(DaveArea area, int areaNumber, int bytes, int offset) throws Exception;
+ public byte[] read(DaveArea area, int areaNumber, int bytes, int offset);
/**
* Reads an area 读需要bit 位置的 变量(其实就是 read bool变量的时候调用这个方法。)
@@ -41,7 +41,7 @@ public interface S7Connector extends Closeable {
* @param offset
* @return
*/
- public byte[] read(DaveArea area, int areaNumber, int bytes, int offset, int bitOffset, TransportSize transportSize) throws Exception;
+ public byte[] read(DaveArea area, int areaNumber, int bytes, int offset, int bitOffset, TransportSize transportSize);
/**
* Writes an area
* desc : 只要未抛出异常,都是 操作成功的
@@ -50,9 +50,9 @@ public interface S7Connector extends Closeable {
* @param offset
* @param buffer
*/
- public void write(DaveArea area, int areaNumber, int offset, byte[] buffer) throws Exception;
+ public void write(DaveArea area, int areaNumber, int offset, byte[] buffer);
//如果 bitOffset 没有 那么就填0
- public void write(DaveArea area, int areaNumber, int byteOffset, int bitOffset, byte[] buffer, PlcVar var) throws Exception;
+ public void write(DaveArea area, int areaNumber, int byteOffset, int bitOffset, byte[] buffer, PlcVar var);
}
diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/utils/ByteUtils.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/utils/ByteUtils.java
index 6444008..97c6973 100644
--- a/src/main/java/com/qgs/dc/s7/my/s7connector/api/utils/ByteUtils.java
+++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/utils/ByteUtils.java
@@ -6,6 +6,7 @@ package com.qgs.dc.s7.my.s7connector.api.utils;
* @DATE: 2021/12/16 9:08
*/
+import com.qgs.dc.s7.my.s7connector.exception.S7ParseDataException;
import com.qgs.dc.s7.my.s7connector.utils.CommonFunctions;
import java.io.UnsupportedEncodingException;
@@ -47,9 +48,14 @@ public class ByteUtils {
}
}
- public static String addDate(String timeParam, Long day) throws ParseException {
+ public static String addDate(String timeParam, Long day) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); // 日期格式
- Date date = dateFormat.parse(timeParam); // 指定日期
+ Date date = null;
+ try {
+ date = dateFormat.parse(timeParam); // 指定日期
+ }catch (Exception e){
+ throw new S7ParseDataException("ByteUtils.addDate error:", e);
+ }
long time = date.getTime(); // 得到指定日期的毫秒数
day = day * 24 * 60 * 60 * 1000; // 要加上的天数转换成毫秒数
@@ -175,13 +181,13 @@ public class ByteUtils {
// String ascii = new String(b, Charset.forName("UTF-8"));
// return ascii;
// }
- public static Character toChar(byte[] b) throws UnsupportedEncodingException {
+ public static Character toChar(byte[] b) {
if(b.length==1){
return toChar(b[0]);
}
return byteToChar(b);
}
- public static Character toChar(byte b) throws UnsupportedEncodingException {
+ public static Character toChar(byte b){
return byteToChar(b);
}
// public static String toChar(byte b) throws UnsupportedEncodingException {
@@ -194,7 +200,7 @@ public class ByteUtils {
/**
* return null 代表返回传入参数不正确str 取的length太小
* */
- public static String toStr(byte[] b) throws UnsupportedEncodingException {
+ public static String toStr(byte[] b) {
Integer length = Byte.toUnsignedInt(b[1]);
if(length>(b.length-2)){
return null;
@@ -207,7 +213,7 @@ public class ByteUtils {
// String s = new String(content);
return ascii;
}
- public static String[] toStrArray(byte[] b,Integer length,Integer strSize) throws UnsupportedEncodingException {
+ public static String[] toStrArray(byte[] b,Integer length,Integer strSize) {
String[] res = new String[length];
strSize+=2;
for(int i=0;i toBoolArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toBoolArray(byte[] b) {
List res = new ArrayList<>();
for(int i=0;i queue = new LinkedList();
@@ -347,7 +353,7 @@ public class ByteUtils {
// return res;
// }
- public static List toByteArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toByteArray(byte[] b) {
List res = new ArrayList<>();
for(int i=0;i toCharArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toCharArray(byte[] b) {
List res = new ArrayList<>();
for(int i=0;i 有符号的整形
* */
- public static List toWordArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toWordArray(byte[] b) {
List res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
@@ -383,7 +389,7 @@ public class ByteUtils {
/**
* 默认:dword => 有符号的整形
* */
- public static List toDWordArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toDWordArray(byte[] b) {
List res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
@@ -444,7 +450,7 @@ public class ByteUtils {
/**
* USInt 无符号整形 1个字节 =》 Integer
* */
- public static List toUSIntArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toUSIntArray(byte[] b) {
List res = new ArrayList<>();
for(int i=0;i toUIntArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toUIntArray(byte[] b) {
List res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
@@ -468,7 +474,7 @@ public class ByteUtils {
/**
* UDInt 无符号整形 4个字节 =》 Long
* */
- public static List toUDIntArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toUDIntArray(byte[] b) {
List res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
@@ -483,7 +489,7 @@ public class ByteUtils {
/**
* SInt 无符号整形 1个字节 =》 Integer
* */
- public static List toSIntArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toSIntArray(byte[] b) {
List res = new ArrayList<>();
for(int i=0;i toIntArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toIntArray(byte[] b) {
List res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
@@ -507,7 +513,7 @@ public class ByteUtils {
/**
* DInt 无符号整形 4个字节 =》 Integer
* */
- public static List toDIntArray(byte[] b) throws UnsupportedEncodingException {
+ public static List toDIntArray(byte[] b) {
List res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/S7ClientNew.java b/src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/S7ClientNew.java
new file mode 100644
index 0000000..31fff9d
--- /dev/null
+++ b/src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/S7ClientNew.java
@@ -0,0 +1,355 @@
+package com.qgs.dc.s7.my.s7connector.enmuc;
+
+
+import com.qgs.dc.s7.my.s7connector.api.DaveArea;
+import com.qgs.dc.s7.my.s7connector.api.S7Connector;
+import com.qgs.dc.s7.my.s7connector.api.factory.S7ConnectorFactory;
+import com.qgs.dc.s7.my.s7connector.api.utils.ByteUtils;
+import com.qgs.dc.s7.my.s7connector.exception.S7Exception;
+import com.qgs.dc.s7.my.s7connector.type.PlcVar;
+import com.qgs.dc.s7.my.s7connector.utils.CommonFunctions;
+import com.qgs.dc.s7.retry.S7RetryTemplate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2022/1/15 13:01
+ */
+public enum S7ClientNew {
+ //TODO 步骤1 这里是配置多PLC 的,,,有多个plc 就在这里配置一个枚举类
+ //1500 西门子200smart、1200、1500默认的 机架号=0 槽位号=1; 300/400 默认的 机架-0 插槽-2
+ S7_1200("192.168.0.52",0,1,1,PlcVarActual.HeartBeatFor1200),
+
+ S7_1500("192.168.0.51",0,1,1,PlcVarActual.HeartBeat),
+ //1500 机架-0 插槽-1
+ //后续 在这里扩展 多PLC应用。
+ ;
+ private String host;
+ //默认 0 机架号
+ private Integer rack;
+ //默认 0
+ private Integer slot;
+
+ //心跳变量,如果plc没有让电控的人加一个,这个是必填的
+ private PlcVarActual heartBeat;
+
+ private List connections;
+ //coreSize 是线程池的大小
+ private Integer coreSize;
+ //pickOne 就是一个初始化 的轮询取余值
+ private int pickOne;
+ private static final Logger logger = LoggerFactory.getLogger(S7ClientNew.class);
+
+
+
+
+ //coreSize 是线程池的数量
+ S7ClientNew(String host, Integer rack, Integer slot, Integer coreSize, PlcVarActual heartBeat){
+ this.host = host;
+ this.rack = rack;
+ this.slot = slot;
+ this.pickOne = 0;
+
+ this.coreSize = coreSize;
+
+ connections = new ArrayList<>();
+ connectionPool();
+
+ }
+
+ public String getHost(){
+ return this.host;
+ }
+
+ /**
+ * PlcVar(byte[]) 转 java对象 对照表
+ * 单体变量
+ * Bool ===> Boolean
+ * LREAL ===> Double
+ * REAL ===> Float
+ * DATE ===> String(yyyy-MM-dd 这种形式的类型)
+ * DTL ===> String("年-月-日-工作日-时-分-秒" 这种格式)
+ * TIME ===> Integer(单位 ms)
+ * USINT ===> Integer
+ * SINT ===> Integer
+ * UINT ===> Integer
+ * INT ===> Integer
+ * DINT ===> Integer
+ * UINT ===> Long
+ * Byte ===> Integer(有符号)(默认)
+ * Integer(无符号)(后续扩展)
+ * Char ===> Character
+ * WChar ===> Character
+ * String ===> String (特殊)
+
+
+ * 数组变量
+ * BoolArray ===> List
+ * ByteArray ===> List
+ * WordArray ===> List
+ * DWordArray ===> List
+ * CharArray ===> List
+ * SIntArray ===> List
+ * IntArray ===> List
+ * DIntArray ===> List
+ * UIntArray ===> List
+ * USIntArray ===> List
+ * UDIntArray ===> List
+ * StringArray ===> String[] (特殊)
+ *
+ * 如果返回null,就代表出现了异常,并且尝试了 retryMax 次数,并且尝试重置连接
+ * */
+ public Object read(DaveArea area, Integer areaNumber, Integer byteOffset, Integer bitOffset, Integer length, Integer strSizes, PlcVar type) {
+ S7Connector connector = getConnector();
+ return S7RetryTemplate.getInstance().execute(
+ context -> {
+ //String 类型比较特殊。 String[] 也是同理,Sring数组里面的子项 也是有两个字节的 readBytes。
+ if(type.equals(PlcVar.STRING)){
+ Integer readBytes = 2;
+ byte[] read = connector.read(
+ area,
+ areaNumber,
+ readBytes,
+ byteOffset,
+ bitOffset,
+ type.getTransportSize()
+ );
+
+ Integer allLength = Integer.valueOf(read[1])+2;
+ byte[] readF = connector.read(
+ area,
+ areaNumber,
+ allLength,
+ byteOffset,
+ bitOffset,
+ type.getTransportSize()
+ );
+ return type.toObject(readF);
+ }else if(type.equals(PlcVar.BOOL_Array)){
+
+ byte[] read = connector.read(
+ area,
+ areaNumber,
+ CommonFunctions.exactDivision(length,8),
+ byteOffset,
+ bitOffset,
+ type.getTransportSize()
+ );
+ List booleans = ByteUtils.toBoolArray(read);
+ List res = new ArrayList();
+ for(int i=0;i {
+ logger.info("S7-Retry : 已达到最大重试次数: "+S7RetryTemplate.getMaxRetryTimes()+", 现在尝试重新连接");
+ if(replaceConnector(connector)==1){
+ logger.info("S7-Retry-Read 现在恢复成功,创建新connection成功");
+ return null;
+ }else {
+ logger.info("S7-Retry-Read 现在恢复失败,创建新connection失败");
+ return null;
+ }
+ }
+ );
+ }
+
+
+
+ /**
+ *
+ * eg :
+ * Object newValue = Boolean.FALSE
+ * s7Service.write(PlcVarActual.HeartBeat, newValue, S7Client.S7_1200);
+ *
+ * PlcVar(byte[]) 转 java对象 对照表
+ * 单体变量
+ * Bool ===> Object newValue = Boolean.FALSE
+ * LREAL ===> Object newValue = Boolean.FALSE
+ * REAL ===> Object newValue = Boolean.FALSE
+ * DATE ===> 暂时没需求(有问题找我)
+ * DTL ===> 暂时没需求(有问题找我)
+ * TIME ===> 暂时没需求(有问题找我)
+ * USINT ===> Object newValue = new Integer(1)
+ * SINT ===> Object newValue = new Integer(1)
+ * UINT ===> Object newValue = new Integer(1)
+ * INT ===> Object newValue = new Integer(1)
+ * DINT ===> Object newValue = new Integer(1)
+ * UINT ===> Object newValue = new Integer(1)
+ * Byte ===> Object newValue = 0x11
+ *
+ * Char ===> Object newValue = 'a'
+ * WChar ===> Object newValue = '菜'
+ * String ===> Object newValue = '你好啊' (特殊)
+
+
+ * 数组变量
+ * 注意:在write的时候,你write的数量 一定要和 plc中存在的数量一一对应
+ * BoolArray ===> boolean[] booleanArray = new boolean[2]; .... 赋予值
+ * ByteArray ===> byte[] write_byteArrays = new byte[2];
+ * WordArray ===> short[] shortArrays_content = new short[2];
+ * DWordArray ===> int[] intArrays_content = new int[2];
+ * CharArray ===> char[] charArrays_content = new char[2];
+ * SIntArray ===> int[] sintArrays_content = new int[2];
+ * IntArray ===> int[] iintArrays_content = new int[2];
+ * DIntArray ===> int[] dintArrays_content = new int[2];
+ * UIntArray ===> int[] uintArrays_content = new int[3];
+ * USIntArray ===> int[] usintArrays_content = new int[3];
+ * UDIntArray ===> int[] udintArrays_content = new int[3];
+ * StringArray ===> String[] stringArrays_content = new String[3];
+ * //如果有其他数据类型 这里没有找我扩展
+ *
+ *
+ * */
+ public void write(DaveArea area, Integer areaNumber, Integer byteOffset, Integer bitOffset,Integer strSize, PlcVar type, Object newValue) throws Exception {
+ S7Connector connector = getConnector();
+
+ S7RetryTemplate.getInstance().execute(
+ context -> {
+ //String 类型比较特殊。 String[] 也是同理,Sring数组里面的子项 也是有两个字节的 readBytes。
+ if(type.equals(PlcVar.STRING)){
+ connector.write(
+ area,
+ areaNumber,
+ byteOffset,
+ bitOffset,
+ ByteUtils.strToBytes(newValue.toString(), strSize),
+ type
+ );
+ }else if(type.equals(PlcVar.BOOL_Array)){
+ connector.write(
+ area,
+ areaNumber,
+ byteOffset,
+ bitOffset,
+ ByteUtils.toByteArray((boolean[])newValue),
+ type
+ );
+ }else if(type.equals(PlcVar.STRING_Array)){
+ //todo here 检查 read write service
+ connector.write(
+ area,
+ areaNumber,
+ byteOffset,
+ bitOffset,
+ ByteUtils.strArrayToBytes((String[])newValue, strSize),
+ type
+ );
+ }else {
+ byte[] bytes = type.toBytes(newValue);
+ connector.write(
+ area,
+ areaNumber,
+ byteOffset,
+ bitOffset,
+ bytes,
+ type
+ );
+ }
+ return null;
+ },
+ context -> {
+ logger.info("S7-Retry-Write : 已达到最大重试次数: "+S7RetryTemplate.getMaxRetryTimes()+", 现在尝试重新连接");
+ if( replaceConnector(connector) == 1 ){
+ logger.info("S7-Retry-Write 现在恢复成功,创建新connection成功");
+ return null;
+ }else {
+ logger.info("S7-Retry-Write 现在恢复失败,创建新connection失败");
+ return null;
+ }
+ }
+ );
+
+
+
+
+ }
+
+ /**
+ * desc: 传入的connection 是需要被替换的
+ * return:
+ * 1 代表替换成功
+ * -1 代表替换失败(创建connection失败,原因:出现异常.原来的connection也被舍弃掉了)
+ * */
+ private Integer replaceConnector(S7Connector oldOne){
+ connections.remove(oldOne);
+ S7Connector connect = connect(host, rack, slot);
+ if(connect == null){
+ return -1;
+ }else {
+ connections.add(connect);
+ return 1;
+ }
+
+ }
+
+ public S7Connector getConnector() {
+ int size = connections.size();
+ S7Connector s7Connector = connections.get((pickOne + size) % size);
+ pickOne+=1;
+ pickOne = (pickOne)%size;
+ return s7Connector;
+ }
+
+ private S7Connector connect(String host,Integer rack,Integer slot ){
+ try {
+ S7Connector connector = S7ConnectorFactory
+ .buildTCPConnector()
+ .withHost(host)
+ .withRack(rack) //optional rack 是机架号
+ .withSlot(slot) //optional slot 是插槽号
+ .build();
+ return connector;
+ }catch (S7Exception e){
+// logger.info("创建S7Connector 连接失败,原因:"+e.getMessage());
+ return null;
+ }
+
+ }
+
+ private void resetConnetctions(){
+ this.connections = new ArrayList();
+ connectionPool();
+ }
+
+ private void connectionPool(){
+ for(int i=0;i MAX_SIZE) {
final byte[] ret = new byte[bytes];
//注意这里 嵌套了 递归,让无限递归去解决 MAX_SIZE的问题。
@@ -108,7 +110,7 @@ public abstract class S7BaseConnection implements S7Connector {
}
}
@Override
- public synchronized byte[] read(final DaveArea area, final int areaNumber, final int bytes, final int offset, int bitOffset, TransportSize transportSize) throws Exception {
+ public synchronized byte[] read(final DaveArea area, final int areaNumber, final int bytes, final int offset, int bitOffset, TransportSize transportSize) {
if(bitOffset==0){
return read(area,areaNumber,bytes,offset);
}
@@ -128,6 +130,7 @@ public abstract class S7BaseConnection implements S7Connector {
final byte[] buffer = new byte[bytes];
final int ret = this.dc.readBytes(area, areaNumber, offset,bitOffset, bytes, buffer,transportSize);
+ //thr resultException
checkResult(ret);
return buffer;
}
@@ -135,7 +138,7 @@ public abstract class S7BaseConnection implements S7Connector {
/** {@inheritDoc} */
@Override
- public synchronized void write(final DaveArea area, final int areaNumber, final int offset, final byte[] buffer) throws Exception {
+ public synchronized void write(final DaveArea area, final int areaNumber, final int offset, final byte[] buffer) {
if (buffer.length > MAX_SIZE) {
// Split buffer
final byte[] subBuffer = new byte[MAX_SIZE];
@@ -155,7 +158,7 @@ public abstract class S7BaseConnection implements S7Connector {
}
@Override
- public synchronized void write(final DaveArea area, final int areaNumber, final int byteOffset, final int bitOffset, final byte[] buffer, PlcVar var) throws Exception {
+ public synchronized void write(final DaveArea area, final int areaNumber, final int byteOffset, final int bitOffset, final byte[] buffer, PlcVar var) {
if(bitOffset==0){
write(area,areaNumber,byteOffset,buffer);
return;
diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PLCinterface.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PLCinterface.java
index 8d25ea0..ece684b 100644
--- a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PLCinterface.java
+++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PLCinterface.java
@@ -15,6 +15,7 @@ limitations under the License.
*/
package com.qgs.dc.s7.my.s7connector.impl.nodave;
+import com.qgs.dc.s7.my.s7connector.exception.S7IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,7 +46,7 @@ public final class PLCinterface {
this.protocol = protocol;
}
- public int read(final byte[] b, int start, int len) {
+ public int read(final byte[] b, int start, int len){
int res;
try {
int retry = 0;
@@ -67,18 +68,20 @@ public final class PLCinterface {
}
return res;
} catch (final IOException e) {
- logger.info("Socket read字节流失败: "+e);
- return 0;
+ String errMsg = "Socket read字节流失败: "+e.getMessage();
+ logger.info(errMsg);
+ throw new S7IOException(errMsg);
}
}
- public int write(final byte[] b, final int start, final int len) {
+ public int write(final byte[] b, final int start, final int len){
try {
this.out.write(b, start, len);
return 1;
} catch (final IOException e) {
- logger.info("Socket write字节流失败: "+e);
- return 0;
+ String errMsg = "Socket write字节流失败: "+e.getMessage();
+ logger.info(errMsg);
+ throw new S7IOException(errMsg);
}
}
diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/S7Connection.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/S7Connection.java
index 77502c8..9c4ef64 100644
--- a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/S7Connection.java
+++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/S7Connection.java
@@ -76,7 +76,7 @@ public abstract class S7Connection {
* it's NULL you can get your data from the resultPointer in daveConnection
* long as you do not send further requests.
*/
- public ResultSet execReadRequest(final PDU p) {
+ public ResultSet execReadRequest(final PDU p) throws Exception {
PDU p2;
int errorState;
errorState = this.exchange(p);
@@ -269,7 +269,7 @@ public abstract class S7Connection {
* build the PDU for a PDU length negotiation
* 构建第二次握手
*/
- public int negPDUlengthRequest() {
+ public int negPDUlengthRequest() throws Exception {
int res;
final PDU p = new PDU(this.msgOut, this.PDUstartOut);
//S7-Param 构造
@@ -320,7 +320,7 @@ public abstract class S7Connection {
final PDU p1 = new PDU(this.msgOut, this.PDUstartOut);
p1.initReadRequest();
p1.addVarToReadRequest(area, DBnum, start, len);
- //发送read var request
+ //通过tcp链接 发送read var request,thr S7IOException
res = this.exchange(p1);
if (res != Nodave.RESULT_OK) {
this.semaphore.release();
diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/TCPConnection.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/TCPConnection.java
index e6b1b7d..e1c58ac 100644
--- a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/TCPConnection.java
+++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/TCPConnection.java
@@ -17,6 +17,7 @@ package com.qgs.dc.s7.my.s7connector.impl.nodave;
import com.qgs.dc.s7.my.s7connector.api.utils.ByteUtils;
import com.qgs.dc.s7.my.s7connector.enmuc.S7Client;
+import com.qgs.dc.s7.my.s7connector.exception.S7IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -59,7 +60,7 @@ public final class TCPConnection extends S7Connection {
*
* @return the int
*/
- public int connectPLC() {
+ public int connectPLC() throws Exception {
//COTP包 字节流(是上位机 和plc进行的第一次握手)
final byte[] b4 = { //b4.length = 18
(byte) 0x11, //(16进制)当前字节以后的字节数
@@ -113,7 +114,7 @@ public final class TCPConnection extends S7Connection {
* 0 代表成功
* */
@Override
- public int exchange(final PDU p1) {
+ public int exchange(final PDU p1){
this.msgOut[4] = (byte) 0x02; //当前字节以后的字节数(也就是下面两个字节)
this.msgOut[5] = (byte) 0xf0; //PDU Type
this.msgOut[6] = (byte) 0x80; //TPDU number (如果这个字节首位为1 代表就是最后一个传输单元了 如1000 0000 ;; 如果首字母不为1 就代表这个Request是比较长的要分多个unit传输,后7位就代表传输单元序号)
@@ -123,9 +124,9 @@ public final class TCPConnection extends S7Connection {
if(writeRes!=0 && readRes!=0){
return 0;
}else {
-
- logger.info("exchange 出现了问题:① writeRes:"+(writeRes==0?"writeISOPacket成功":"writeISOPacket失败")+"② readRes:"+(readRes==0?"readISOPacket成功":"readISOPacket失败"));
- return 1 ;
+ String errMsg = "exchange 出现了问题:① writeRes:"+(writeRes==0?"writeISOPacket成功":"writeISOPacket失败")+"② readRes:"+(readRes==0?"readISOPacket成功":"readISOPacket失败");
+ logger.info(errMsg);
+ throw new S7IOException(errMsg);
}
}
@@ -133,15 +134,14 @@ public final class TCPConnection extends S7Connection {
* Read iso packet.
*
* @return the int
- * 0 ==> 不成功
+ * 0 ==> 不成功(异常)
* -1 ==> 读到一个空的字节流 (这种情况很少)
* 其他 ==> 成功
*/
- protected int readISOPacket() {
+ protected int readISOPacket(){
//read return 为0 就是异常
int res = this.iface.read(this.msgIn, 0, 4);
if (res == 4) {
-// final int len = (0x100 * this.msgIn[2]) + this.msgIn[3]; //读取字节数 这串是bug代码
final int len = (0x100 * ByteUtils.toUInt(this.msgIn[2])) + ByteUtils.toUInt(this.msgIn[3]);
res += this.iface.read(this.msgIn, 4, len);
} else {
@@ -159,7 +159,7 @@ public final class TCPConnection extends S7Connection {
* 1 ==> 成功
* 0 ==> 不成功(出现异常了)
*/
- protected int sendISOPacket(int size) {
+ protected int sendISOPacket(int size){
size += 4;
//下面包装的 是TPKT 字节包(第一次握手)
this.msgOut[0] = (byte) 0x03; //Version ,默认版本3
diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/type/PlcVar.java b/src/main/java/com/qgs/dc/s7/my/s7connector/type/PlcVar.java
index de5e305..57ca970 100644
--- a/src/main/java/com/qgs/dc/s7/my/s7connector/type/PlcVar.java
+++ b/src/main/java/com/qgs/dc/s7/my/s7connector/type/PlcVar.java
@@ -330,7 +330,7 @@ public enum PlcVar {
*
*
* */
- public Object toObject(byte[] value) throws ParseException, UnsupportedEncodingException {
+ public Object toObject(byte[] value) {
if(!isArray){
Object res = null;
switch (dataType) {
diff --git a/src/main/java/com/qgs/dc/s7/retry/S7RetryTemplate.java b/src/main/java/com/qgs/dc/s7/retry/S7RetryTemplate.java
new file mode 100644
index 0000000..08d3282
--- /dev/null
+++ b/src/main/java/com/qgs/dc/s7/retry/S7RetryTemplate.java
@@ -0,0 +1,60 @@
+package com.qgs.dc.s7.retry;
+
+import com.qgs.dc.s7.my.s7connector.exception.S7CheckResultException;
+import com.qgs.dc.s7.my.s7connector.exception.S7IOException;
+import org.springframework.remoting.RemoteAccessException;
+import org.springframework.retry.backoff.FixedBackOffPolicy;
+import org.springframework.retry.policy.SimpleRetryPolicy;
+import org.springframework.retry.support.RetryTemplate;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author caixiang
+ * @description 重试模板和重试策略
+ */
+public class S7RetryTemplate extends RetryTemplate {
+
+ private volatile static S7RetryTemplate instance = null;
+ /**
+ * 重试间隔时间ms,默认1000ms
+ * */
+ private static long fixedPeriodTime = 500L;
+ /**
+ * 最大重试次数,默认为3
+ */
+ private static int maxRetryTimes = 3;
+ /**
+ * 表示哪些异常需要重试,key表示异常的字节码,value为true表示需要重试
+ */
+ private static Map, Boolean> exceptionMap = new HashMap<>();
+ private S7RetryTemplate() {
+ // 代表S7IOException 这个异常是需要重试的(true), 如果你想设置这个异常不去重试,那么可以把它设置为false。
+ exceptionMap.put(S7IOException.class,true);
+ exceptionMap.put(S7CheckResultException.class,true);
+
+ }
+ public static int getMaxRetryTimes() {
+ return maxRetryTimes;
+ }
+
+ public static S7RetryTemplate getInstance() {
+ if (instance == null) {
+ synchronized (S7RetryTemplate.class) {
+ if (instance == null) {
+ FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
+ // 定义重试间隔-间隔 fixedPeriodTime ms再重试,总共重试3次
+ backOffPolicy.setBackOffPeriod(fixedPeriodTime);
+ instance = new S7RetryTemplate();
+ // 定义重试次数- maxRetryTimes
+ SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap);
+ instance.setRetryPolicy(retryPolicy);
+
+ instance.setBackOffPolicy(backOffPolicy);
+ }
+ }
+ }
+ return instance;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/s7/retrydemo/RetryDemoTask.java b/src/main/java/com/qgs/dc/s7/retrydemo/RetryDemoTask.java
new file mode 100644
index 0000000..a6896de
--- /dev/null
+++ b/src/main/java/com/qgs/dc/s7/retrydemo/RetryDemoTask.java
@@ -0,0 +1,39 @@
+package com.qgs.dc.s7.retrydemo;
+
+
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.remoting.RemoteAccessException;
+
+/**
+ * @Author: cx
+ * @Description:
+ */
+public class RetryDemoTask {
+ /**
+ * 重试方法
+ * @return
+ */
+ public static boolean retryTask(String param) {
+ System.out.println("retry-task : 收到请求参数:"+param);
+
+ int i = RandomUtils.nextInt(0,11);
+ System.out.println("retry-task : 随机生成的数:"+i);
+ if (i == 0) {
+ System.out.println("retry-task : 为0,抛出参数异常.");
+ //因为Illeague这个异常我们没有在exceptionMap里面配置,所以 抛出这个异常后
+ //spring-retry不会进行重试,而是会直接进入recovery函数
+ throw new IllegalArgumentException("retry-task : 参数异常");
+ }else if (i == 1){
+ System.out.println("retry-task : 为1,返回true.");
+ return true;
+ }else if (i == 2){
+ System.out.println("retry-task : 为2,返回false.");
+ return false;
+ }else{
+ //因为RemoteAccessExcep这个异常我们在exceptionMap里面配置了,所以 抛出这个异常后
+ //spring-retry会进行重试
+ System.out.println("retry-task : 大于2,抛出自定义异常.");
+ throw new RemoteAccessException("retry-task : 大于2,抛出远程访问异常");
+ }
+ }
+}
diff --git a/src/main/java/com/qgs/dc/s7/retrydemo/RetryMain.java b/src/main/java/com/qgs/dc/s7/retrydemo/RetryMain.java
new file mode 100644
index 0000000..5f6cfbc
--- /dev/null
+++ b/src/main/java/com/qgs/dc/s7/retrydemo/RetryMain.java
@@ -0,0 +1,58 @@
+package com.qgs.dc.s7.retrydemo;
+
+import org.springframework.remoting.RemoteAccessException;
+import org.springframework.retry.backoff.FixedBackOffPolicy;
+import org.springframework.retry.policy.SimpleRetryPolicy;
+import org.springframework.retry.support.RetryTemplate;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class RetryMain {
+ public static void main(String[] args) {
+
+ /**
+ * 重试间隔时间ms,默认1000ms
+ * */
+ long fixedPeriodTime = 1000L;
+ /**
+ * 最大重试次数,默认为3
+ */
+ int maxRetryTimes = 3;
+ /**
+ * 表示哪些异常需要重试,key表示异常的字节码,value为true表示需要重试
+ */
+ Map, Boolean> exceptionMap = new HashMap<>();
+
+
+ exceptionMap.put(RemoteAccessException.class,true);
+
+ // 构建重试模板实例
+ RetryTemplate retryTemplate = new RetryTemplate();
+
+ // 设置重试回退操作策略,主要设置重试间隔时间
+ FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
+ backOffPolicy.setBackOffPeriod(fixedPeriodTime);
+
+ // 设置重试策略,主要设置重试次数
+ SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap);
+
+ retryTemplate.setRetryPolicy(retryPolicy);
+ retryTemplate.setBackOffPolicy(backOffPolicy);
+
+ Boolean execute = retryTemplate.execute(
+ //RetryCallback
+ retryContext -> {
+ boolean b = RetryDemoTask.retryTask("abc");
+ System.err.println("retry-main : 调用的结果:"+b+",times:"+retryContext.getRetryCount());
+ return b;
+ },
+ retryContext -> {
+ //RecoveryCallback
+ System.err.println("retry-main : 已达到最大重试次数或抛出了不重试的异常~~~");
+ return false;
+ }
+ );
+ System.err.println("retry-main : 执行结果:"+execute);
+ }
+}
diff --git a/src/main/java/com/qgs/dc/s7/retrydemo/SpringS7RetryTemplateTest.java b/src/main/java/com/qgs/dc/s7/retrydemo/SpringS7RetryTemplateTest.java
new file mode 100644
index 0000000..8c480ca
--- /dev/null
+++ b/src/main/java/com/qgs/dc/s7/retrydemo/SpringS7RetryTemplateTest.java
@@ -0,0 +1,66 @@
+package com.qgs.dc.s7.retrydemo;
+
+import org.junit.Test;
+import org.springframework.remoting.RemoteAccessException;
+import org.springframework.retry.backoff.FixedBackOffPolicy;
+import org.springframework.retry.policy.SimpleRetryPolicy;
+import org.springframework.retry.support.RetryTemplate;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author: zgd
+ * @Description: spring-retry 重试框架
+ */
+public class SpringS7RetryTemplateTest {
+
+ /**
+ * 重试间隔时间ms,默认1000ms
+ * */
+ private long fixedPeriodTime = 1000L;
+ /**
+ * 最大重试次数,默认为3
+ */
+ private int maxRetryTimes = 3;
+ /**
+ * 表示哪些异常需要重试,key表示异常的字节码,value为true表示需要重试
+ */
+ private Map, Boolean> exceptionMap = new HashMap<>();
+
+
+ @Test
+ public void test() {
+
+ //文档:https://blog.csdn.net/minghao0508/article/details/123972703
+ exceptionMap.put(RemoteAccessException.class,true);
+
+ // 构建重试模板实例
+ RetryTemplate retryTemplate = new RetryTemplate();
+
+ // 设置重试回退操作策略,主要设置重试间隔时间
+ FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
+ backOffPolicy.setBackOffPeriod(fixedPeriodTime);
+
+ // 设置重试策略,主要设置重试次数
+ SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap);
+
+ retryTemplate.setRetryPolicy(retryPolicy);
+ retryTemplate.setBackOffPolicy(backOffPolicy);
+
+ Boolean execute = retryTemplate.execute(
+ //RetryCallback
+ retryContext -> {
+ boolean b = RetryDemoTask.retryTask("abc");
+ System.out.println("调用的结果:"+b);
+ return b;
+ },
+ retryContext -> {
+ //RecoveryCallback
+ System.out.println("已达到最大重试次数或抛出了不重试的异常~~~");
+ return false;
+ }
+ );
+ System.out.println("执行结果:"+execute);
+ }
+
+}
diff --git a/src/main/java/com/qgs/dc/s7/retrydemo/TestMain.java b/src/main/java/com/qgs/dc/s7/retrydemo/TestMain.java
new file mode 100644
index 0000000..fd73a10
--- /dev/null
+++ b/src/main/java/com/qgs/dc/s7/retrydemo/TestMain.java
@@ -0,0 +1,20 @@
+package com.qgs.dc.s7.retrydemo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestMain {
+ public static void main(String[] args) {
+ Integer a = 1;
+ Integer a1 = 2;
+ Integer a2 = 3;
+ List list = new ArrayList();
+ list.add(a);
+ list.add(a1);
+ list.add(a2);
+ Integer integer = list.get(0);
+ list.remove(integer);
+ System.out.println();
+
+ }
+}