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

This commit is contained in:
CaiXiang 2022-12-22 12:25:27 +08:00
parent 5b2c38770d
commit 829c9ce9ca
17 changed files with 865 additions and 49 deletions

14
pom.xml
View File

@ -80,11 +80,19 @@
</dependency> </dependency>
<!-- websocket 结束 --> <!-- websocket 结束 -->
<!-- retry 结束 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
<!-- retry 结束 -->
<!-- fastjson 开始 --> <!-- fastjson 开始 -->
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId> <artifactId>fastjson</artifactId>
<version>1.2.78</version> <version>2.0.21</version>
</dependency> </dependency>
<!-- fastjson 结束 --> <!-- fastjson 结束 -->
<!-- hutool 开始 --> <!-- hutool 开始 -->
@ -201,6 +209,10 @@
<artifactId>influxdb-client-java</artifactId> <artifactId>influxdb-client-java</artifactId>
<version>6.7.0</version> <version>6.7.0</version>
</dependency> </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- influx end --> <!-- influx end -->

View File

@ -30,7 +30,7 @@ public interface S7Connector extends Closeable {
* @param offset * @param offset
* @return * @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变量的时候调用这个方法 * Reads an area 读需要bit 位置的 变量其实就是 read bool变量的时候调用这个方法
@ -41,7 +41,7 @@ public interface S7Connector extends Closeable {
* @param offset * @param offset
* @return * @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 * Writes an area
* desc : 只要未抛出异常都是 操作成功的 * desc : 只要未抛出异常都是 操作成功的
@ -50,9 +50,9 @@ public interface S7Connector extends Closeable {
* @param offset * @param offset
* @param buffer * @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 //如果 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);
} }

View File

@ -6,6 +6,7 @@ package com.qgs.dc.s7.my.s7connector.api.utils;
* @DATE: 2021/12/16 9:08 * @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 com.qgs.dc.s7.my.s7connector.utils.CommonFunctions;
import java.io.UnsupportedEncodingException; 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"); // 日期格式 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(); // 得到指定日期的毫秒数 long time = date.getTime(); // 得到指定日期的毫秒数
day = day * 24 * 60 * 60 * 1000; // 要加上的天数转换成毫秒数 day = day * 24 * 60 * 60 * 1000; // 要加上的天数转换成毫秒数
@ -175,13 +181,13 @@ public class ByteUtils {
// String ascii = new String(b, Charset.forName("UTF-8")); // String ascii = new String(b, Charset.forName("UTF-8"));
// return ascii; // return ascii;
// } // }
public static Character toChar(byte[] b) throws UnsupportedEncodingException { public static Character toChar(byte[] b) {
if(b.length==1){ if(b.length==1){
return toChar(b[0]); return toChar(b[0]);
} }
return byteToChar(b); return byteToChar(b);
} }
public static Character toChar(byte b) throws UnsupportedEncodingException { public static Character toChar(byte b){
return byteToChar(b); return byteToChar(b);
} }
// public static String toChar(byte b) throws UnsupportedEncodingException { // public static String toChar(byte b) throws UnsupportedEncodingException {
@ -194,7 +200,7 @@ public class ByteUtils {
/** /**
* return null 代表返回传入参数不正确str 取的length太小 * return null 代表返回传入参数不正确str 取的length太小
* */ * */
public static String toStr(byte[] b) throws UnsupportedEncodingException { public static String toStr(byte[] b) {
Integer length = Byte.toUnsignedInt(b[1]); Integer length = Byte.toUnsignedInt(b[1]);
if(length>(b.length-2)){ if(length>(b.length-2)){
return null; return null;
@ -207,7 +213,7 @@ public class ByteUtils {
// String s = new String(content); // String s = new String(content);
return ascii; 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]; String[] res = new String[length];
strSize+=2; strSize+=2;
for(int i=0;i<length;i++){ for(int i=0;i<length;i++){
@ -275,7 +281,7 @@ public class ByteUtils {
return b; return b;
} }
public static List<Boolean> toBoolArray(byte[] b) throws UnsupportedEncodingException { public static List<Boolean> toBoolArray(byte[] b) {
List<Boolean> res = new ArrayList<>(); List<Boolean> res = new ArrayList<>();
for(int i=0;i<b.length;i++){ for(int i=0;i<b.length;i++){
boolean[] booleanArray = getBooleanArray(b[i], true); boolean[] booleanArray = getBooleanArray(b[i], true);
@ -301,7 +307,7 @@ public class ByteUtils {
//bool array to byte array //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); Integer byteLength = CommonFunctions.exactDivision(b.length, 8);
byte[] res = new byte[byteLength]; byte[] res = new byte[byteLength];
Queue<Boolean> queue = new LinkedList<Boolean>(); Queue<Boolean> queue = new LinkedList<Boolean>();
@ -347,7 +353,7 @@ public class ByteUtils {
// return res; // return res;
// } // }
public static List<Byte> toByteArray(byte[] b) throws UnsupportedEncodingException { public static List<Byte> toByteArray(byte[] b) {
List<Byte> res = new ArrayList<>(); List<Byte> res = new ArrayList<>();
for(int i=0;i<b.length;i++){ for(int i=0;i<b.length;i++){
res.add((b[i])); res.add((b[i]));
@ -355,7 +361,7 @@ public class ByteUtils {
return res; return res;
} }
public static List<Character> toCharArray(byte[] b) throws UnsupportedEncodingException { public static List<Character> toCharArray(byte[] b) {
List<Character> res = new ArrayList<>(); List<Character> res = new ArrayList<>();
for(int i=0;i<b.length;i++){ for(int i=0;i<b.length;i++){
res.add(toChar(b[i])); res.add(toChar(b[i]));
@ -367,7 +373,7 @@ public class ByteUtils {
/** /**
* 默认word => 有符号的整形 * 默认word => 有符号的整形
* */ * */
public static List<Integer> toWordArray(byte[] b) throws UnsupportedEncodingException { public static List<Integer> toWordArray(byte[] b) {
List<Integer> res = new ArrayList<>(); List<Integer> res = new ArrayList<>();
int i=0; int i=0;
while ((i+2)<=b.length){ while ((i+2)<=b.length){
@ -383,7 +389,7 @@ public class ByteUtils {
/** /**
* 默认dword => 有符号的整形 * 默认dword => 有符号的整形
* */ * */
public static List<Integer> toDWordArray(byte[] b) throws UnsupportedEncodingException { public static List<Integer> toDWordArray(byte[] b) {
List<Integer> res = new ArrayList<>(); List<Integer> res = new ArrayList<>();
int i=0; int i=0;
while ((i+4)<=b.length){ while ((i+4)<=b.length){
@ -444,7 +450,7 @@ public class ByteUtils {
/** /**
* USInt 无符号整形 1个字节 = Integer * USInt 无符号整形 1个字节 = Integer
* */ * */
public static List<Integer> toUSIntArray(byte[] b) throws UnsupportedEncodingException { public static List<Integer> toUSIntArray(byte[] b) {
List<Integer> res = new ArrayList<>(); List<Integer> res = new ArrayList<>();
for(int i=0;i<b.length;i++){ for(int i=0;i<b.length;i++){
res.add(toUInt(b[i])); res.add(toUInt(b[i]));
@ -454,7 +460,7 @@ public class ByteUtils {
/** /**
* UInt 无符号整形 2个字节 = Integer * UInt 无符号整形 2个字节 = Integer
* */ * */
public static List<Integer> toUIntArray(byte[] b) throws UnsupportedEncodingException { public static List<Integer> toUIntArray(byte[] b) {
List<Integer> res = new ArrayList<>(); List<Integer> res = new ArrayList<>();
int i=0; int i=0;
while ((i+2)<=b.length){ while ((i+2)<=b.length){
@ -468,7 +474,7 @@ public class ByteUtils {
/** /**
* UDInt 无符号整形 4个字节 = Long * UDInt 无符号整形 4个字节 = Long
* */ * */
public static List<Long> toUDIntArray(byte[] b) throws UnsupportedEncodingException { public static List<Long> toUDIntArray(byte[] b) {
List<Long> res = new ArrayList<>(); List<Long> res = new ArrayList<>();
int i=0; int i=0;
while ((i+4)<=b.length){ while ((i+4)<=b.length){
@ -483,7 +489,7 @@ public class ByteUtils {
/** /**
* SInt 无符号整形 1个字节 = Integer * SInt 无符号整形 1个字节 = Integer
* */ * */
public static List<Integer> toSIntArray(byte[] b) throws UnsupportedEncodingException { public static List<Integer> toSIntArray(byte[] b) {
List<Integer> res = new ArrayList<>(); List<Integer> res = new ArrayList<>();
for(int i=0;i<b.length;i++){ for(int i=0;i<b.length;i++){
res.add(toInt(b[i])); res.add(toInt(b[i]));
@ -493,7 +499,7 @@ public class ByteUtils {
/** /**
* Int 无符号整形 2个字节 = Integer * Int 无符号整形 2个字节 = Integer
* */ * */
public static List<Integer> toIntArray(byte[] b) throws UnsupportedEncodingException { public static List<Integer> toIntArray(byte[] b) {
List<Integer> res = new ArrayList<>(); List<Integer> res = new ArrayList<>();
int i=0; int i=0;
while ((i+2)<=b.length){ while ((i+2)<=b.length){
@ -507,7 +513,7 @@ public class ByteUtils {
/** /**
* DInt 无符号整形 4个字节 = Integer * DInt 无符号整形 4个字节 = Integer
* */ * */
public static List<Integer> toDIntArray(byte[] b) throws UnsupportedEncodingException { public static List<Integer> toDIntArray(byte[] b) {
List<Integer> res = new ArrayList<>(); List<Integer> res = new ArrayList<>();
int i=0; int i=0;
while ((i+4)<=b.length){ while ((i+4)<=b.length){

View File

@ -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 西门子200smart12001500默认的 机架号=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
}
}

View File

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

View File

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

View File

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

View File

@ -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.DaveArea;
import com.qgs.dc.s7.my.s7connector.api.S7Connector; 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.Nodave;
import com.qgs.dc.s7.my.s7connector.impl.nodave.S7Connection; import com.qgs.dc.s7.my.s7connector.impl.nodave.S7Connection;
import com.qgs.dc.s7.my.s7connector.type.PlcVar; import com.qgs.dc.s7.my.s7connector.type.PlcVar;
@ -32,7 +34,7 @@ public abstract class S7BaseConnection implements S7Connector {
/** The Constant MAX_SIZE. */ /** The Constant MAX_SIZE. */
//private static final int MAX_SIZE = 96; //原版96 ;; ioserver 127 ;; //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. */ /** The Constant PROPERTY_AREA. */
public static final String PROPERTY_AREA = "area"; public static final String PROPERTY_AREA = "area";
@ -52,11 +54,11 @@ public abstract class S7BaseConnection implements S7Connector {
* @param libnodaveResult * @param libnodaveResult
* the libnodave result * the libnodave result
*/ */
public static void checkResult(final int libnodaveResult) throws Exception { public static void checkResult(final int libnodaveResult) {
if (libnodaveResult != Nodave.RESULT_OK) { if (libnodaveResult != Nodave.RESULT_OK) {
final String msg = Nodave.strerror(libnodaveResult); final String msg = Nodave.strerror(libnodaveResult);
// throw new IllegalArgumentException("Result: " + msg); // 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} */ /** {@inheritDoc} */
@Override @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) { if (bytes > MAX_SIZE) {
final byte[] ret = new byte[bytes]; final byte[] ret = new byte[bytes];
//注意这里 嵌套了 递归让无限递归去解决 MAX_SIZE的问题 //注意这里 嵌套了 递归让无限递归去解决 MAX_SIZE的问题
@ -108,7 +110,7 @@ public abstract class S7BaseConnection implements S7Connector {
} }
} }
@Override @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){ if(bitOffset==0){
return read(area,areaNumber,bytes,offset); return read(area,areaNumber,bytes,offset);
} }
@ -128,6 +130,7 @@ public abstract class S7BaseConnection implements S7Connector {
final byte[] buffer = new byte[bytes]; final byte[] buffer = new byte[bytes];
final int ret = this.dc.readBytes(area, areaNumber, offset,bitOffset, bytes, buffer,transportSize); final int ret = this.dc.readBytes(area, areaNumber, offset,bitOffset, bytes, buffer,transportSize);
//thr resultException
checkResult(ret); checkResult(ret);
return buffer; return buffer;
} }
@ -135,7 +138,7 @@ public abstract class S7BaseConnection implements S7Connector {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @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) { if (buffer.length > MAX_SIZE) {
// Split buffer // Split buffer
final byte[] subBuffer = new byte[MAX_SIZE]; final byte[] subBuffer = new byte[MAX_SIZE];
@ -155,7 +158,7 @@ public abstract class S7BaseConnection implements S7Connector {
} }
@Override @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){ if(bitOffset==0){
write(area,areaNumber,byteOffset,buffer); write(area,areaNumber,byteOffset,buffer);
return; return;

View File

@ -15,6 +15,7 @@ limitations under the License.
*/ */
package com.qgs.dc.s7.my.s7connector.impl.nodave; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -67,8 +68,9 @@ public final class PLCinterface {
} }
return res; return res;
} catch (final IOException e) { } catch (final IOException e) {
logger.info("Socket read字节流失败: "+e); String errMsg = "Socket read字节流失败: "+e.getMessage();
return 0; logger.info(errMsg);
throw new S7IOException(errMsg);
} }
} }
@ -77,8 +79,9 @@ public final class PLCinterface {
this.out.write(b, start, len); this.out.write(b, start, len);
return 1; return 1;
} catch (final IOException e) { } catch (final IOException e) {
logger.info("Socket write字节流失败: "+e); String errMsg = "Socket write字节流失败: "+e.getMessage();
return 0; logger.info(errMsg);
throw new S7IOException(errMsg);
} }
} }

View File

@ -76,7 +76,7 @@ public abstract class S7Connection {
* it's NULL you can get your data from the resultPointer in daveConnection * it's NULL you can get your data from the resultPointer in daveConnection
* long as you do not send further requests. * long as you do not send further requests.
*/ */
public ResultSet execReadRequest(final PDU p) { public ResultSet execReadRequest(final PDU p) throws Exception {
PDU p2; PDU p2;
int errorState; int errorState;
errorState = this.exchange(p); errorState = this.exchange(p);
@ -269,7 +269,7 @@ public abstract class S7Connection {
* build the PDU for a PDU length negotiation * build the PDU for a PDU length negotiation
* 构建第二次握手 * 构建第二次握手
*/ */
public int negPDUlengthRequest() { public int negPDUlengthRequest() throws Exception {
int res; int res;
final PDU p = new PDU(this.msgOut, this.PDUstartOut); final PDU p = new PDU(this.msgOut, this.PDUstartOut);
//S7-Param 构造 //S7-Param 构造
@ -320,7 +320,7 @@ public abstract class S7Connection {
final PDU p1 = new PDU(this.msgOut, this.PDUstartOut); final PDU p1 = new PDU(this.msgOut, this.PDUstartOut);
p1.initReadRequest(); p1.initReadRequest();
p1.addVarToReadRequest(area, DBnum, start, len); p1.addVarToReadRequest(area, DBnum, start, len);
//发送read var request //通过tcp链接 发送read var requestthr S7IOException
res = this.exchange(p1); res = this.exchange(p1);
if (res != Nodave.RESULT_OK) { if (res != Nodave.RESULT_OK) {
this.semaphore.release(); this.semaphore.release();

View File

@ -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.api.utils.ByteUtils;
import com.qgs.dc.s7.my.s7connector.enmuc.S7Client; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -59,7 +60,7 @@ public final class TCPConnection extends S7Connection {
* *
* @return the int * @return the int
*/ */
public int connectPLC() { public int connectPLC() throws Exception {
//COTP包 字节流是上位机 和plc进行的第一次握手 //COTP包 字节流是上位机 和plc进行的第一次握手
final byte[] b4 = { //b4.length = 18 final byte[] b4 = { //b4.length = 18
(byte) 0x11, //16进制当前字节以后的字节数 (byte) 0x11, //16进制当前字节以后的字节数
@ -123,9 +124,9 @@ public final class TCPConnection extends S7Connection {
if(writeRes!=0 && readRes!=0){ if(writeRes!=0 && readRes!=0){
return 0; return 0;
}else { }else {
String errMsg = "exchange 出现了问题:① writeRes:"+(writeRes==0?"writeISOPacket成功":"writeISOPacket失败")+"② readRes:"+(readRes==0?"readISOPacket成功":"readISOPacket失败");
logger.info("exchange 出现了问题:① writeRes:"+(writeRes==0?"writeISOPacket成功":"writeISOPacket失败")+"② readRes:"+(readRes==0?"readISOPacket成功":"readISOPacket失败")); logger.info(errMsg);
return 1 ; throw new S7IOException(errMsg);
} }
} }
@ -133,7 +134,7 @@ public final class TCPConnection extends S7Connection {
* Read iso packet. * Read iso packet.
* *
* @return the int * @return the int
* 0 ==> 不成功 * 0 ==> 不成功异常
* -1 ==> 读到一个空的字节流 这种情况很少 * -1 ==> 读到一个空的字节流 这种情况很少
* 其他 ==> 成功 * 其他 ==> 成功
*/ */
@ -141,7 +142,6 @@ public final class TCPConnection extends S7Connection {
//read return 为0 就是异常 //read return 为0 就是异常
int res = this.iface.read(this.msgIn, 0, 4); int res = this.iface.read(this.msgIn, 0, 4);
if (res == 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]); final int len = (0x100 * ByteUtils.toUInt(this.msgIn[2])) + ByteUtils.toUInt(this.msgIn[3]);
res += this.iface.read(this.msgIn, 4, len); res += this.iface.read(this.msgIn, 4, len);
} else { } else {

View File

@ -330,7 +330,7 @@ public enum PlcVar {
* *
* *
* */ * */
public Object toObject(byte[] value) throws ParseException, UnsupportedEncodingException { public Object toObject(byte[] value) {
if(!isArray){ if(!isArray){
Object res = null; Object res = null;
switch (dataType) { switch (dataType) {

View File

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

View File

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

View File

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

View File

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

View File

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