Przeglądaj źródła

S7 更新了一个大版本,待测试

master
CaiXiang 1 rok temu
rodzic
commit
829c9ce9ca
17 zmienionych plików z 865 dodań i 49 usunięć
  1. +13
    -1
      pom.xml
  2. +4
    -4
      src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Connector.java
  3. +24
    -18
      src/main/java/com/qgs/dc/s7/my/s7connector/api/utils/ByteUtils.java
  4. +355
    -0
      src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/S7ClientNew.java
  5. +64
    -0
      src/main/java/com/qgs/dc/s7/my/s7connector/exception/S7CheckResultException.java
  6. +66
    -0
      src/main/java/com/qgs/dc/s7/my/s7connector/exception/S7IOException.java
  7. +64
    -0
      src/main/java/com/qgs/dc/s7/my/s7connector/exception/S7ParseDataException.java
  8. +10
    -7
      src/main/java/com/qgs/dc/s7/my/s7connector/impl/S7BaseConnection.java
  9. +9
    -6
      src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PLCinterface.java
  10. +3
    -3
      src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/S7Connection.java
  11. +9
    -9
      src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/TCPConnection.java
  12. +1
    -1
      src/main/java/com/qgs/dc/s7/my/s7connector/type/PlcVar.java
  13. +60
    -0
      src/main/java/com/qgs/dc/s7/retry/S7RetryTemplate.java
  14. +39
    -0
      src/main/java/com/qgs/dc/s7/retrydemo/RetryDemoTask.java
  15. +58
    -0
      src/main/java/com/qgs/dc/s7/retrydemo/RetryMain.java
  16. +66
    -0
      src/main/java/com/qgs/dc/s7/retrydemo/SpringS7RetryTemplateTest.java
  17. +20
    -0
      src/main/java/com/qgs/dc/s7/retrydemo/TestMain.java

+ 13
- 1
pom.xml Wyświetl plik

@@ -80,11 +80,19 @@
</dependency>
<!-- websocket 结束 -->

<!-- retry 结束 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
<!-- retry 结束 -->

<!-- fastjson 开始 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
<version>2.0.21</version>
</dependency>
<!-- fastjson 结束 -->
<!-- hutool 开始 -->
@@ -201,6 +209,10 @@
<artifactId>influxdb-client-java</artifactId>
<version>6.7.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>

<!-- influx end -->



+ 4
- 4
src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Connector.java Wyświetl plik

@@ -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);

}

+ 24
- 18
src/main/java/com/qgs/dc/s7/my/s7connector/api/utils/ByteUtils.java Wyświetl plik

@@ -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<length;i++){
@@ -275,7 +281,7 @@ public class ByteUtils {
return b;
}

public static List<Boolean> toBoolArray(byte[] b) throws UnsupportedEncodingException {
public static List<Boolean> toBoolArray(byte[] b) {
List<Boolean> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
boolean[] booleanArray = getBooleanArray(b[i], true);
@@ -301,7 +307,7 @@ public class ByteUtils {


//bool array to byte array
public static byte[] toByteArray(boolean[] b) throws UnsupportedEncodingException {
public static byte[] toByteArray(boolean[] b){
Integer byteLength = CommonFunctions.exactDivision(b.length, 8);
byte[] res = new byte[byteLength];
Queue<Boolean> queue = new LinkedList<Boolean>();
@@ -347,7 +353,7 @@ public class ByteUtils {
// return res;
// }

public static List<Byte> toByteArray(byte[] b) throws UnsupportedEncodingException {
public static List<Byte> toByteArray(byte[] b) {
List<Byte> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add((b[i]));
@@ -355,7 +361,7 @@ public class ByteUtils {
return res;
}

public static List<Character> toCharArray(byte[] b) throws UnsupportedEncodingException {
public static List<Character> toCharArray(byte[] b) {
List<Character> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add(toChar(b[i]));
@@ -367,7 +373,7 @@ public class ByteUtils {
/**
* 默认:word => 有符号的整形
* */
public static List<Integer> toWordArray(byte[] b) throws UnsupportedEncodingException {
public static List<Integer> toWordArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
@@ -383,7 +389,7 @@ public class ByteUtils {
/**
* 默认:dword => 有符号的整形
* */
public static List<Integer> toDWordArray(byte[] b) throws UnsupportedEncodingException {
public static List<Integer> toDWordArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
@@ -444,7 +450,7 @@ public class ByteUtils {
/**
* USInt 无符号整形 1个字节 =》 Integer
* */
public static List<Integer> toUSIntArray(byte[] b) throws UnsupportedEncodingException {
public static List<Integer> toUSIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add(toUInt(b[i]));
@@ -454,7 +460,7 @@ public class ByteUtils {
/**
* UInt 无符号整形 2个字节 =》 Integer
* */
public static List<Integer> toUIntArray(byte[] b) throws UnsupportedEncodingException {
public static List<Integer> toUIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
@@ -468,7 +474,7 @@ public class ByteUtils {
/**
* UDInt 无符号整形 4个字节 =》 Long
* */
public static List<Long> toUDIntArray(byte[] b) throws UnsupportedEncodingException {
public static List<Long> toUDIntArray(byte[] b) {
List<Long> res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
@@ -483,7 +489,7 @@ public class ByteUtils {
/**
* SInt 无符号整形 1个字节 =》 Integer
* */
public static List<Integer> toSIntArray(byte[] b) throws UnsupportedEncodingException {
public static List<Integer> toSIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add(toInt(b[i]));
@@ -493,7 +499,7 @@ public class ByteUtils {
/**
* Int 无符号整形 2个字节 =》 Integer
* */
public static List<Integer> toIntArray(byte[] b) throws UnsupportedEncodingException {
public static List<Integer> toIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
@@ -507,7 +513,7 @@ public class ByteUtils {
/**
* DInt 无符号整形 4个字节 =》 Integer
* */
public static List<Integer> toDIntArray(byte[] b) throws UnsupportedEncodingException {
public static List<Integer> toDIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){


+ 355
- 0
src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/S7ClientNew.java Wyświetl plik

@@ -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<S7Connector> 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<Boolean>
* ByteArray ===> List<Byte>
* WordArray ===> List<Integer>
* DWordArray ===> List<Integer>
* CharArray ===> List<Character>
* SIntArray ===> List<Integer>
* IntArray ===> List<Integer>
* DIntArray ===> List<Integer>
* UIntArray ===> List<Integer>
* USIntArray ===> List<Integer>
* UDIntArray ===> List<Long>
* 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<Boolean> booleans = ByteUtils.toBoolArray(read);
List<Boolean> res = new ArrayList<Boolean>();
for(int i=0;i<length;i++){
res.add(i,booleans.get(i));
}
return res;
}else if(type.equals(PlcVar.STRING_Array)){
Integer arrayLength = length;
Integer strSize = strSizes;

byte[] read = connector.read(
area,
areaNumber,
arrayLength*(strSize+2),
byteOffset,
bitOffset,
type.getTransportSize()
);
return ByteUtils.toStrArray(read, arrayLength, strSize);
}else {
Integer readBytes = type.getTransportSize().getSizeInBytes() * length;
byte[] read = connector.read(
area,
areaNumber,
readBytes,
byteOffset,
bitOffset,
type.getTransportSize()
);
return type.toObject(read);
}
},
context -> {
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<S7Connector>();
connectionPool();
}

private void connectionPool(){
for(int i=0;i<coreSize;i++){
S7Connector connect = connect(host, rack, slot);
if(connect!=null){
connections.add(connect);
}
}
//todo 在plc上新增一个 变量来解决 心跳问题 okok
}
}

+ 64
- 0
src/main/java/com/qgs/dc/s7/my/s7connector/exception/S7CheckResultException.java Wyświetl plik

@@ -0,0 +1,64 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.qgs.dc.s7.my.s7connector.exception;

/**
* The Class S7Exception is an exception related to S7 Communication
*/
public final class S7CheckResultException extends RuntimeException {

/** The Constant serialVersionUID. */
private static final long serialVersionUID = -4761415733559374116L;

/**
* Instantiates a new s7 exception.
*/
public S7CheckResultException() {
}

/**
* Instantiates a new s7 exception.
*
* @param message
* the message
*/
public S7CheckResultException(final String message) {
super(message);
}

/**
* Instantiates a new s7 exception.
*
* @param message
* the message
* @param cause
* the cause
*/
public S7CheckResultException(final String message, final Throwable cause) {
super(message, cause);
}

/**
* Instantiates a new s7 exception.
*
* @param cause
* the cause
*/
public S7CheckResultException(final Throwable cause) {
super(cause);
}

}

+ 66
- 0
src/main/java/com/qgs/dc/s7/my/s7connector/exception/S7IOException.java Wyświetl plik

@@ -0,0 +1,66 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.qgs.dc.s7.my.s7connector.exception;

import java.io.IOException;

/**
* The Class S7IOException is an exception related to S7 Communication
*/
public final class S7IOException extends RuntimeException {

/** The Constant serialVersionUID. */
private static final long serialVersionUID = -4761415733559374116L;

/**
* Instantiates a new s7 exception.
*/
public S7IOException() {
}

/**
* Instantiates a new s7 exception.
*
* @param message
* the message
*/
public S7IOException(final String message) {
super(message);
}

/**
* Instantiates a new s7 exception.
*
* @param message
* the message
* @param cause
* the cause
*/
public S7IOException(final String message, final Throwable cause) {
super(message, cause);
}

/**
* Instantiates a new s7 exception.
*
* @param cause
* the cause
*/
public S7IOException(final Throwable cause) {
super(cause);
}

}

+ 64
- 0
src/main/java/com/qgs/dc/s7/my/s7connector/exception/S7ParseDataException.java Wyświetl plik

@@ -0,0 +1,64 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.qgs.dc.s7.my.s7connector.exception;

/**
* The Class S7Exception is an exception related to S7 Communication
*/
public final class S7ParseDataException extends RuntimeException {

/** The Constant serialVersionUID. */
private static final long serialVersionUID = -4761415733559374116L;

/**
* Instantiates a new s7 exception.
*/
public S7ParseDataException() {
}

/**
* Instantiates a new s7 exception.
*
* @param message
* the message
*/
public S7ParseDataException(final String message) {
super(message);
}

/**
* Instantiates a new s7 exception.
*
* @param message
* the message
* @param cause
* the cause
*/
public S7ParseDataException(final String message, final Throwable cause) {
super(message, cause);
}

/**
* Instantiates a new s7 exception.
*
* @param cause
* the cause
*/
public S7ParseDataException(final Throwable cause) {
super(cause);
}

}

+ 10
- 7
src/main/java/com/qgs/dc/s7/my/s7connector/impl/S7BaseConnection.java Wyświetl plik

@@ -17,6 +17,8 @@ package com.qgs.dc.s7.my.s7connector.impl;

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.exception.S7CheckResultException;
import com.qgs.dc.s7.my.s7connector.exception.S7Exception;
import com.qgs.dc.s7.my.s7connector.impl.nodave.Nodave;
import com.qgs.dc.s7.my.s7connector.impl.nodave.S7Connection;
import com.qgs.dc.s7.my.s7connector.type.PlcVar;
@@ -32,7 +34,7 @@ public abstract class S7BaseConnection implements S7Connector {

/** The Constant MAX_SIZE. */
//private static final int MAX_SIZE = 96; //原版96 ;; ioserver 127 ;;
private static final int MAX_SIZE = 212; //S7-1200-maxReadBytes = 212 ;; S7-1200-maxReadBytes = 452
private static final int MAX_SIZE = 212; //S7-1200-maxReadBytes = 212 ;; S7-1500-maxReadBytes = 452

/** The Constant PROPERTY_AREA. */
public static final String PROPERTY_AREA = "area";
@@ -52,11 +54,11 @@ public abstract class S7BaseConnection implements S7Connector {
* @param libnodaveResult
* the libnodave result
*/
public static void checkResult(final int libnodaveResult) throws Exception {
public static void checkResult(final int libnodaveResult) {
if (libnodaveResult != Nodave.RESULT_OK) {
final String msg = Nodave.strerror(libnodaveResult);
// throw new IllegalArgumentException("Result: " + msg);
throw new Exception(msg);
throw new S7CheckResultException("errMsg : "+msg);
}
}

@@ -87,7 +89,7 @@ public abstract class S7BaseConnection implements S7Connector {

/** {@inheritDoc} */
@Override
public synchronized byte[] read(final DaveArea area, final int areaNumber, final int bytes, final int offset) throws Exception {
public synchronized byte[] read(final DaveArea area, final int areaNumber, final int bytes, final int offset) {
if (bytes > 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;


+ 9
- 6
src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PLCinterface.java Wyświetl plik

@@ -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);
}
}



+ 3
- 3
src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/S7Connection.java Wyświetl plik

@@ -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();


+ 9
- 9
src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/TCPConnection.java Wyświetl plik

@@ -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


+ 1
- 1
src/main/java/com/qgs/dc/s7/my/s7connector/type/PlcVar.java Wyświetl plik

@@ -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) {


+ 60
- 0
src/main/java/com/qgs/dc/s7/retry/S7RetryTemplate.java Wyświetl plik

@@ -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<Class<? extends Throwable>, 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;
}
}

+ 39
- 0
src/main/java/com/qgs/dc/s7/retrydemo/RetryDemoTask.java Wyświetl plik

@@ -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,抛出远程访问异常");
}
}
}

+ 58
- 0
src/main/java/com/qgs/dc/s7/retrydemo/RetryMain.java Wyświetl plik

@@ -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<Class<? extends Throwable>, 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);
}
}

+ 66
- 0
src/main/java/com/qgs/dc/s7/retrydemo/SpringS7RetryTemplateTest.java Wyświetl plik

@@ -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<Class<? extends Throwable>, 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);
}

}

+ 20
- 0
src/main/java/com/qgs/dc/s7/retrydemo/TestMain.java Wyświetl plik

@@ -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<Integer> list = new ArrayList<Integer>();
list.add(a);
list.add(a1);
list.add(a2);
Integer integer = list.get(0);
list.remove(integer);
System.out.println();

}
}

Ładowanie…
Anuluj
Zapisz