更新 S7 部分

This commit is contained in:
caixiang 2022-01-19 15:53:04 +08:00
parent 9b6cd65daf
commit 0d326d95a7
50 changed files with 7162 additions and 121 deletions

View File

@ -1,21 +1,23 @@
package com.qgs.dc.s7.controller; package com.qgs.dc.s7.controller;
import com.qgs.dc.opcua.arg.*;
import com.qgs.dc.opcua.controller.R; import com.qgs.dc.opcua.controller.R;
import com.qgs.dc.s7.service.S7Service; import com.qgs.dc.s7.my.s7connector.api.DaveArea;
import org.apache.plc4x.java.PlcDriverManager; import com.qgs.dc.s7.my.s7connector.api.S7Connector;
import org.apache.plc4x.java.api.PlcConnection; import com.qgs.dc.s7.my.s7connector.api.utils.ByteUtils;
import com.qgs.dc.s7.my.s7connector.enmuc.PlcVarActual;
import com.qgs.dc.s7.my.s7connector.enmuc.S7Client;
import com.qgs.dc.s7.my.s7connector.service.S7Service;
import org.apache.plc4x.java.api.exceptions.PlcConnectionException; import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
import org.apache.plc4x.java.api.messages.PlcReadRequest;
import org.apache.plc4x.java.api.messages.PlcReadResponse;
import org.apache.plc4x.java.api.types.PlcResponseCode;
import org.apache.plc4x.java.api.value.PlcValue;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.concurrent.CompletableFuture; import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
@RestController @RestController
@RequestMapping("/s7") @RequestMapping("/s7")
@ -25,18 +27,47 @@ public class S7Controller {
@Autowired @Autowired
S7Service s7Service; S7Service s7Service;
//demo1
@PostMapping("/addThisPlc") @PostMapping("/testReadAll")
public R addThisPlc() throws PlcConnectionException { public R testReadAll() throws UnsupportedEncodingException, ParseException {
//s7://192.168.1.51?remote-rack=0&remote-slot=3&controller-type=S7_400,如果参数不是默认的 要向这样往url 后面加 for(PlcVarActual actual:PlcVarActual.values()){
return R.ok().put("res",s7Service.addPlc("s7://192.168.0.100")); System.out.println(s7Service.read(actual,S7Client.S7_1200));;
}
return R.ok();
} }
@PostMapping("/getValue")
public R getValue() throws PlcConnectionException {
//demo2
@PostMapping("/readTest")
public R getTestForS7() throws UnsupportedEncodingException, ParseException {
Boolean heartBeat = (Boolean)s7Service.read(PlcVarActual.HeartBeat,S7Client.S7_1200);
String ddtl = (String)s7Service.read(PlcVarActual.DTL,S7Client.S7_1200);
List<Character> characters = (List<Character>)s7Service.read(PlcVarActual.CharArrays,S7Client.S7_1200);
List<Boolean> booleans = (List<Boolean>)s7Service.read(PlcVarActual.BooleanArrays,S7Client.S7_1200);
String stri = (String)s7Service.read(PlcVarActual.STRING1,S7Client.S7_1200);
return R.ok().put("res",1); return R.ok().put("res",heartBeat).put("characters",characters).put("ddtl",ddtl).put("bools",booleans).put("str",stri);
}
//demo3
@PostMapping("/writeTest")
public R writeTest() throws PlcConnectionException, UnsupportedEncodingException {
s7Service.write(PlcVarActual.HeartBeat, false, S7Client.S7_1200);
char[] charArrays_content = new char[2];
charArrays_content[0] = '1';
charArrays_content[1] = 'c';
s7Service.write(PlcVarActual.CharArrays, charArrays_content, S7Client.S7_1200);
boolean[] boolArrays_content = new boolean[2];
boolArrays_content[0] = true;
boolArrays_content[1] = false;
s7Service.write(PlcVarActual.BooleanArrays, boolArrays_content, S7Client.S7_1200);
String str = "你好啊aa";
//todo string 的读写有问题 待会看看
s7Service.write(PlcVarActual.STRING1, str, S7Client.S7_1200);
return R.ok().put("res",true);
} }
} }

View File

@ -0,0 +1,16 @@
package com.qgs.dc.s7.entity;
import lombok.Data;
import java.util.List;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2021/12/6 13:42
*/
@Data
public class ReadedBack {
private Integer size;
private List<Object> content;
}

View File

@ -0,0 +1,17 @@
package com.qgs.dc.s7.my.s7connector.Constant;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2022/1/17 15:16
*/
public class S7ClientArg {
public static final Integer S7ClientPoolCore = 3;
private static final Integer pickOne = 0;
public synchronized Integer getPickOne(){
return pickOne;
}
}

View File

@ -0,0 +1,233 @@
package com.qgs.dc.s7.my.s7connector;
import cn.hutool.core.util.ByteUtil;
import java.math.BigInteger;
import java.util.Stack;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2021/12/10 11:22
*/
public class Main2 {
public static byte[] double2Bytes(double d) {
long value = Double.doubleToRawLongBits(d);
byte[] byteRet = new byte[8];
for (int i = 0; i < 8; i++) {
byteRet[i] = (byte) ((value >> 8 * i) & 0xff);
}
return byteRet;
}
/**
* 1个字节byte[] 转成有符号的short
* */
public static Integer toInt(byte bytes) {
return Integer.valueOf(Byte.toString(bytes));
}
public static Integer toUInt(byte bytes) {
return Integer.valueOf(Byte.toUnsignedInt(bytes));
}
public static void main(String[] args) {
byte[] bytes = new byte[2];
bytes[0] = -1; //内部是 补码形式存在 //todo 搞清楚 补码和原码什么时候转化
bytes[1] = -111;
//-1 原码 1000 0001 -1 补码 1111 1111
//1000 0001 1110 1111 //原码形式
//1111 1111 1001 0001 //补码形式在计算机内部是以补码形式存储的当赋值给2个字节后是无符号整形的 要强制转化成short类型后,如果之前首位是1 就会被java强制转化成补码
//1000 0000 0110 1111 //
//1+16+128+256+512+1024+2048+4096+4096*2+4096*3+4096*4
//1024*(63)
//1000 0000 0110 1111 //-111
System.out.println(toInt(bytes[0]));
System.out.println(toUInt(bytes[0]));
System.out.println(toInt(bytes[1]));
System.out.println(toUInt(bytes[1]));
System.out.println("无符号 "+(bytes[1] & 0xff | (bytes[0] & 0xff) << Byte.SIZE));
System.out.println("有符号 "+(short)(bytes[1] & 0xff | (bytes[0] & 0xff) << Byte.SIZE));
// //byte类型的数字要&0xff再赋值给int类型其本质原因就是想保持二进制补码的一致性 0xff 其实就是0000 0000 0000 0000 0000 0000 1111 0100
short l = 0;
l<<=8; //<<=和我们的 +=是一样的意思就是 l = l << 8
//注意 移位并不会增加原来字节长度被挤掉的数据就没了而且是以补码的形式移动的
l |= (bytes[0] & 0xff); //和上面也是一样的 l = l | (b[i]&0xff) // 255 补码 1111 1111
l<<=7;
l<<=1;
//l<<=8; //<<=和我们的 +=是一样的意思就是 l = l << 8
l |= (bytes[1] & 0xff); //和上面也是一样的 l = l | (b[i]&0xff)
short c = 0;
c<<=8; //<<=和我们的 +=是一样的意思就是 l = l << 8
//注意 移位并不会增加原来字节长度被挤掉的数据就没了而且是以补码的形式移动的
c |= ((byte)1 & 0xff); //和上面也是一样的 l = l | (b[i]&0xff) // 255 补码 1111 1111
c<<=7;
c<<=1;
//l<<=8; //<<=和我们的 +=是一样的意思就是 l = l << 8
c |= ((byte)2 & 0xff); //和上面也是一样的 l = l | (b[i]&0xff)
byte bb = -1;
bb<<=8;
System.out.println(bb);
System.out.println(l);
System.out.println(Byte.valueOf((byte) 0x81)); //有符号 因为0x81 会自动被java 转成byte而byte会以补码 有符号的形式存在
System.out.println(0x81); //无符号
byte[] lrealBuffer = new byte[8];
Number lrealNumber = new Double(-12.1);
byte[] bytesss = ByteUtil.numberToBytes(lrealNumber);
Number intNumber = new Integer(-12);
byte[] bytessss = ByteUtil.numberToBytes(intNumber);
Integer s = new Integer(-999);
byte b = s.byteValue();
System.out.println();
}
public static double bytes2Double(byte[] arr) {
long value = 0;
for (int i = 0; i < 8; i++) {
value |= ((long) (arr[i] & 0xff)) << (8 * i);
}
return Double.longBitsToDouble(value);
}
public static byte[] short2byte(short s){
byte[] b = new byte[2];
for(int i = 0; i < 2; i++){
int offset = 16 - (i+1)*8; //因为byte占4个字节所以要计算偏移量
b[i] = (byte)((s >> offset)&0xff); //把16位分为2个8位进行分别存储
}
return b;
}
/**
* 将byte[]转为各种进制的字符串
* @param bytes byte[]
* @param radix 基数可以转换进制的范围从Character.MIN_RADIX到Character.MAX_RADIX超出范围后变为10进制
* @return 转换后的字符串
*/
public static String binary(byte[] bytes, int radix){
return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数
}
public static String byteToBit(byte b) {
return ""
+ (byte) ((b >> 7) & 0x1) + (byte) ((b >> 6) & 0x1)
+ (byte) ((b >> 5) & 0x1) + (byte) ((b >> 4) & 0x1) +","
+ (byte) ((b >> 3) & 0x1) + (byte) ((b >> 2) & 0x1)
+ (byte) ((b >> 1) & 0x1) + (byte) ((b >> 0) & 0x1);
}
public static short byte2short(byte[] b){
short l = 0;
for (int i = 0; i < 2; i++) {
l<<=8; //<<=和我们的 +=是一样的意思就是 l = l << 8
l |= (b[i] & 0xff); //和上面也是一样的 l = l | (b[i]&0xff)
}
return l;
}
public static byte[] int2byte(int s){
byte[] b = new byte[2];
for(int i = 0; i < 4; i++){
int offset = 16 - (i+1)*8; //因为byte占4个字节所以要计算偏移量
b[i] = (byte)((s >> offset)&0xff); //把32位分为4个8位进行分别存储
}
return b;
}
public static int byte2int(byte[] b){
int l = 0;
for (int i = 0; i < 4; i++) {
l<<=8; //<<=和我们的 +=是一样的意思就是 l = l << 8
l |= (b[i] & 0xff); //和上面也是一样的 l = l | (b[i]&0xff)
}
return l;
}
/**
* int到byte[](byte数组4个字节)
* @param i
* @return
*/
public static byte[] intToByteArray(int i) {
byte[] result = new byte[4];
//由高位到低位
result[0] = (byte)((i >> 24) & 0xFF);
result[1] = (byte)((i >> 16) & 0xFF); //如果查过128 那么会被java自动转成负数
result[2] = (byte)((i >> 8) & 0xFF);
result[3] = (byte)(i & 0xFF);
return result;
}
/**
* 4个字节的byte[]转int
* @param
* @return
*/
public static Integer byteArrayToInt(byte[] in) {
byte[] bytes = new byte[4];
//如果传进来数组不满足4个字节,自动填充
if(in.length>4){
return null;
}else if(in.length!=4){
Stack<Byte> st = new Stack<Byte>();
int c = 4-in.length;
in = invert(in);
for(int i=0;i<in.length;i++){
st.push(in[i]);
}
for(int i=0;i<c;i++){
st.push(Byte.valueOf((byte) 0));
}
for(int i=0;i<4;i++){
bytes[i] = st.pop();
}
}else {
bytes = in;
}
int value=0;
//由高位到低位
for(int i = 0; i < 4; i++) {
int shift= (4-1-i) * 8;
value +=(bytes[i] & 0x000000FF) << shift;//往高位游
}
return value;
}
public static Integer byteToUSInt(byte bytes) {
int value=0;
value +=(bytes & 0xFF);
return value;
}
public static Integer byteToSInt(byte bytes) {
return Byte.valueOf(bytes).intValue();
}
public static byte[] invert(byte[] a){
byte[] b = new byte[a.length];
Stack<Byte> st = new Stack<Byte>();
for(int i=0;i<a.length;i++){
st.push(a[i]);
}
for(int i=0;i<a.length;i++){
b[i] = st.pop();
}
return b;
}
}

View File

@ -0,0 +1,248 @@
package com.qgs.dc.s7.my.s7connector;
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 java.util.List;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2021/12/10 10:17
*/
public class MainForRead {
public static void main(String[] args) {
//前言
//DB3.1.1 中间那个1是byte区后面那个1 是bit
//缺陷 不支持 DB3.1.1
//Create connection
S7Connector connector =
S7ConnectorFactory
.buildTCPConnector()
.withHost("192.168.0.51")
.withRack(0) //optional rack 是机架号
.withSlot(0) //optional slot 是插槽号
.build();
// // [0]
// byte[] bool = connector.read(DaveArea.DB, 3, 1, 3266,0);
// byte[] bool2 = connector.read(DaveArea.DB, 3, 1, 3266,1);
// System.out.println("DB3.0-bool : " + ByteUtils.toBoolean(bool));
// System.out.println("DB3.0-bool2 : " + ByteUtils.toBoolean(bool2));
//bool3 => [1] 0000 0001 ==> 1000 0000 10.......
//bool3 => [3] 0000 0011 ==> 1100 0000 11.......
//bool3 => [10] 0000 1010 ==> 0101 0000 0101.....
//bool3 => [25] 0001 1001 ==> 1001 1000 10011....
byte[] bool3 = connector.read(DaveArea.DB, 3, 1, 3267);
System.out.println("DB3.0-bool3 : " + ByteUtils.toBoolean(bool3));
//
// byte[] bool4 = connector.read(DaveArea.DB, 3, 1, 0);
// System.out.println("DB3.0-bool3 : " + ByteUtils.toBoolean(bool3));
//
//
// //非常规
// {
// //注意1:
// //lreal 要小端拿到字节流要翻转一下
// //参数一读取方式一般默认是DB区域块
// //参数二区域块编号
// //参数三区域数据类型大小int 2字节real 4字节
// //参数四区域偏移量
// //[-64, 40, 51, 51, 51, 51, 51, 51] -64==0xc0(要翻转一下才能用)
// //1100 0000 0010 1000 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011
//
// byte[] lreal = connector.read(DaveArea.DB, 3, 8, 26,0);
// System.out.println("DB3.26-lreal : "+ ByteUtils.forLReal(lreal));
//
// byte[] real = connector.read(DaveArea.DB, 3, 4, 22,0);
// System.out.println("DB3.22-real : "+ ByteUtils.forReal(real));
//
// }
// {
// //data 是有符号的双字节
// //注意2
// //[2, -38] 就就代表1990-1-1 因为这就是其实位置 后续位置要1990+n的 D#1992-01-01
// //[4, 72] 1993-1-1
// //0000 0100 0100 1000 72+1024=1096 1990-1-1 + 1096() = 1993-1-1
// byte[] date = connector.read(DaveArea.DB, 3, 2, 42);
//// System.out.println("DB3.42-DATE : "+addDate("1990-01-01",byte2short(date)));
// Long aLong = Long.valueOf(ByteUtils.toInt(date[0], date[1]).toString());
// System.out.println("DB3.42-DATE : "+ByteUtils.addDate("1990-01-01",aLong));
// }
//
{
//[7, -78, 1, 1, 5, 0, 0, 0, 0, 0, 0, 0] DTL#1970-01-01-00:00:00 1998-1-1 星期五 0.0.0.0
//0000 0111 (1100 1110)=>(1011 0010 178) 178+256+512+1024=1970
//(注意第二个字节是负数要转成补码形式表示(因为在通行传输中 字节是原码形式传输的但是java中long int byte... 都是以补码形式保存的 java帮你自动保存了其实这是不对的所以你要转换一下) 参考https://blog.csdn.net/csdn_ds/article/details/79106006)
//[7, -68, 1, 21, 2, 0, 0, 0, 0, 0, 0, 0]
//0000 0111 (1100 0100)=>(1011 1100 188) 188+256+512+1024=1980
byte[] dtl = connector.read(DaveArea.DB, 3, 12, 44);
byte[] year = new byte[2];
year[0] = dtl[0];
year[1] = dtl[1];
byte[] month = new byte[1];
month[0] = dtl[2];
// System.out.println("DB3.12-DTL : " + byteArrayByteUtils.toUInt(year)+"-"+byteArrayByteUtils.toUInt(month));
System.out.println("DB3.44-DTL : " + ByteUtils.toInt(year[0],year[1])+"-"+ByteUtils.toInt(month[0]));
}
//
// {
// //[59, -102, -55, -1] T#11D_13H_46M_39S_999MS
// //0011 1011
// byte[] time = connector.read(DaveArea.DB, 3, 4, 34);
// System.out.println("DB3.34-time : " + ByteUtils.toInt(time[0],time[1],time[2],time[3]) +" ms");
// }
//
// //常规
// {
// // [0]
// byte[] bool = connector.read(DaveArea.DB, 3, 1, 0);
// System.out.println("DB3.0-bool : " + ByteUtils.toBoolean(bool));
//
// //todo 就像这种情况的话这个工具就无法读取了 这个后续可以改他的源码 就是addreesByte/Bit那里 后续再说好了
// byte[] bool2 = connector.read(DaveArea.DB, 3, 1, 3266);
// System.out.println("DB3.3266-bool : " + ByteUtils.toBoolean(bool2));
//
//
// //byte 目前读取以 有符号的十进制
// //[8]
// //[-63]
// //1011 1111
// byte[] byteOne = connector.read(DaveArea.DB, 3, 1, 1);
// System.out.println("DB3.1-byteOne-有符号 : " + ByteUtils.toInt(byteOne[0])); //有符号 整形
//
// System.out.println("DB3.1-byteOne-无符号 : " + ByteUtils.toUInt(byteOne[0])); //无符号 整形
//
//
// // [0, 5] word 2/8/16进制 可无符号 可有符号就看你真没用
// byte[] word = connector.read(DaveArea.DB, 3, 2, 2);
// System.out.println("DB3.2-word : " + ByteUtils.toInt(word[0],word[1]));
//
// // [-1, -1, -1, -4] 把java自动转换成byte类型 的补码形式 ,如果电控变量是这个要问下他是无符号还是有符号的
// // [FF, FF, FF, FC] 原码
// byte[] dword = connector.read(DaveArea.DB, 3, 4, 4);
// System.out.println("DB3.4-dword : " + ByteUtils.toInt(dword[0],dword[1],dword[2],dword[3])); //带符号的 整形
//
// //[11]
// byte[] usint = connector.read(DaveArea.DB, 3, 1, 8);
// System.out.println("DB3.8-usint : " + ByteUtils.toUInt(usint[0]));
//
// byte[] sint = connector.read(DaveArea.DB, 3, 1, 9);
// System.out.println("DB3.9-sint : " + ByteUtils.toInt(sint[0]));
//
// byte[] uint = connector.read(DaveArea.DB, 3, 2, 10);
// System.out.println("DB3.10-uint : " + ByteUtils.toUInt(uint[0],uint[1]));
//
// //[-1, -111] -111
// //[-1, -64] -64
// //[-4, 25] -999
// byte[] ints = connector.read(DaveArea.DB, 3, 2, 12);
// System.out.println("DB3.12-ints : " + ByteUtils.toInt(ints[0],ints[1]));
//
// byte[] dint = connector.read(DaveArea.DB, 3, 4, 14);
// System.out.println("DB3.14-dint : " + ByteUtils.toInt(dint[0],dint[1],dint[2],dint[3]));
//
// byte[] udint = connector.read(DaveArea.DB, 3, 4, 18);
// System.out.println("DB3.18-udint : " + ByteUtils.toUInt(udint[0],udint[1],udint[2],udint[3]));
//
// //plc 中char 是1个字节 注意char wchar 都是ascii码格式的
// byte[] chars = connector.read(DaveArea.DB, 3, 1, 58);
// Character charss = ByteUtils.toChar(chars);
// System.out.println("DB3.58-char : " + charss);
// //plc 中wchar 是2个字节
// byte[] wchar = connector.read(DaveArea.DB, 3, 2, 60);
// Character wchars =ByteUtils.toChar(wchar);
// System.out.println("DB3.60-wchar : " + wchars);
//
// //n+2 个字节 这里的n 就是实际content的字节数 "123"=> n=3 ;; "@123" ==> n=4
// //[-2, 4, 64, 65, 83, 68] @ASD 前面-2 4两个字节就是上面的2字节 是无用的
// //-2 代表 字符串中存储最大的总字节数 4代表字符数 后面跟着的 是内容最多256个字节
// byte[] str = connector.read(DaveArea.DB, 3, 6, 62);
// String string = ByteUtils.toStr(str);
// System.out.println("DB3.62-str : " +string );
//
// }
//
{
try {
//byte 占用一个字节如果是数组的话就读取2个要事先知道点表规定数组长度实际就是读取 DB3.830
//bytes = byteLength * arrayLength 举例1byteLength * 2arrayLength =2bytes
byte[] boolArrays = connector.read(DaveArea.DB, 3, 2, 830);
List<Boolean> booleans = ByteUtils.toBoolArray(boolArrays);
System.out.println("DB3.830-boolArrays : " +booleans );
byte[] byteArrays = connector.read(DaveArea.DB, 3, 2, 832);
List<Byte> bytes = ByteUtils.toByteArray(byteArrays);
System.out.println("DB3.832-byteArrays : " +bytes );
byte[] charArrays = connector.read(DaveArea.DB, 3, 2, 834);
List<Character> chars = ByteUtils.toCharArray(charArrays);
System.out.println("DB3.834-charArrays : " +chars );
byte[] wordArrays = connector.read(DaveArea.DB, 3, 4, 836);
List<Integer> words = ByteUtils.toWordArray(wordArrays);
System.out.println("DB3.836-wordArrays : " +words );
byte[] dwordArrays = connector.read(DaveArea.DB, 3, 8, 840);
List<Integer> dwords = ByteUtils.toDWordArray(dwordArrays);
System.out.println("DB3.840-dwordArrays : " +dwords );
byte[] sintArrays = connector.read(DaveArea.DB, 3, 2, 852);
List<Integer> sints = ByteUtils.toSIntArray(sintArrays);
System.out.println("DB3.852-sintArrays : " +sints );
byte[] intArrays = connector.read(DaveArea.DB, 3, 4, 848);
List<Integer> ints = ByteUtils.toIntArray(intArrays);
System.out.println("DB3.848-intArrays : " +ints );
byte[] dintArrays = connector.read(DaveArea.DB, 3, 8, 854);
List<Integer> dints = ByteUtils.toDIntArray(dintArrays);
System.out.println("DB3.852-dintArrays : " +dints);
byte[] usintArrays = connector.read(DaveArea.DB, 3, 3, 3240);
List<Integer> usints = ByteUtils.toUSIntArray(usintArrays);
System.out.println("DB3.3240-usintArrays : " +usints );
byte[] uintArrays = connector.read(DaveArea.DB, 3, 6, 3256);
List<Integer> uints = ByteUtils.toUIntArray(uintArrays);
System.out.println("DB3.3256-uintArrays : " +uints );
byte[] udintArrays = connector.read(DaveArea.DB, 3, 12, 3244);
List<Long> udints = ByteUtils.toUDIntArray(udintArrays);
System.out.println("DB3.852-udintArrays : " +udints);
}catch (Exception e){
e.printStackTrace();
}
};
//Write to DB100 10 bytes
//connector.write(DaveArea.DB, 3, 830, byteArrays);
//Close connection
//connector.close();
}
}

View File

@ -0,0 +1,391 @@
package com.qgs.dc.s7.my.s7connector;
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.type.PlcVar;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2021/12/10 10:17
*/
public class MainForWrite {
public static void main(String[] args) throws IOException, ParseException {
//Create connection
S7Connector connector =
S7ConnectorFactory
.buildTCPConnector()
.withHost("192.168.0.51")
.withRack(0) //optional rack 是机架号
.withSlot(0) //optional slot 是插槽号
.build();
{
connector.write(DaveArea.DB, 3, 3266,1,ByteUtils.boolToBytes(true), PlcVar.BOOL);
// byte[] bool = connector.read(DaveArea.DB, 3, 1, 3266,1);
// Boolean b = (Boolean)PlcVar.BOOL.toObject(bool);
// System.out.println("DB3.3266.1-boolean : " + b);
}
//非常规
{
//注意1:
//lreal 要小端拿到字节流要翻转一下
//参数一读取方式一般默认是DB区域块
//参数二区域块编号
//参数三区域数据类型大小int 2字节real 4字节
//参数四区域偏移量
//[-64, 40, 51, 51, 51, 51, 51, 51] -64==0xc0(要翻转一下才能用)
//1100 0000 0010 1000 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011
// byte[] lreal_content = ByteUtils.lrealToBytes(Double.parseDouble("-111.1"));
byte[] lreal_content = PlcVar.LREAL.toBytes(-121.1);
connector.write(DaveArea.DB, 3, 26,lreal_content);
byte[] lreal = connector.read(DaveArea.DB, 3, 8, 26);
// Double aDouble = ByteUtils.lrealbytesToDouble(lreal);
Double ad = (Double)PlcVar.LREAL.toObject(lreal);
System.out.println("DB3.26-lreal : "+ ad);
//byte[] real_content = ByteUtils.realToBytes(Float.valueOf("-11.2"));
byte[] real_content = PlcVar.REAL.toBytes(-12.1);
connector.write(DaveArea.DB, 3, 22,real_content);
byte[] real = connector.read(DaveArea.DB, 3, 4, 22);
//ByteUtils.realbytesToFloat(real)
Float o = (Float)PlcVar.REAL.toObject(real);
System.out.println("DB3.22-real : "+ o);
}
{
//data 是有符号的双字节
//注意2
//[2, -38] 就就代表1990-1-1 因为这就是其实位置 后续位置要1990+n的 D#1992-01-01
//[4, 72] 1993-1-1
//0000 0100 0100 1000 72+1024=1096 1990-1-1 + 1096() = 1993-1-1
byte[] date = connector.read(DaveArea.DB, 3, 2, 42);
// System.out.println("DB3.42-DATE : "+addDate("1990-01-01",byte2short(date)));
// Long aLong = Long.valueOf(ByteUtils.toInt(date[0], date[1]).toString());
// String s = ByteUtils.addDate("1990-01-01", aLong);
String s = (String) PlcVar.DATE.toObject(date);
System.out.println("DB3.42-DATE : "+s);
}
{
//[7, -78, 1, 1, 5, 0, 0, 0, 0, 0, 0, 0] DTL#1970-01-01-00:00:00 1998-1-1 星期五 0.0.0.0
//0000 0111 (1100 1110)=>(1011 0010 178) 178+256+512+1024=1970
//(注意第二个字节是负数要转成补码形式表示(因为在通行传输中 字节是原码形式传输的但是java中long int byte... 都是以补码形式保存的 java帮你自动保存了其实这是不对的所以你要转换一下) 参考https://blog.csdn.net/csdn_ds/article/details/79106006)
//[7, -68, 1, 21, 2, 0, 0, 0, 0, 0, 0, 0]
//0000 0111 (1100 0100)=>(1011 1100 188) 188+256+512+1024=1980
byte[] dtl = connector.read(DaveArea.DB, 3, 12, 44);
// byte[] year = new byte[2];
// year[0] = dtl[0];
// year[1] = dtl[1];
// Integer yearInt = ByteUtils.toInt(year[0], year[1]);
// Integer monthInt = ByteUtils.toInt(dtl[2]);
// Integer dayInt = ByteUtils.toInt(dtl[3]);
// Integer worddayInt = ByteUtils.toInt(dtl[4]);
// Integer hourInt = ByteUtils.toInt(dtl[5]);
// Integer minuInt = ByteUtils.toInt(dtl[6]);
// Integer secondInt = ByteUtils.toInt(dtl[7]);
// System.out.println("DB3.12-DTL : " + byteArrayByteUtils.toUInt(year)+"-"+byteArrayByteUtils.toUInt(month));
String o = (String)PlcVar.DTL.toObject(dtl);
System.out.println("DB3.44-DTL : " + o );
}
{
//[59, -102, -55, -1] T#11D_13H_46M_39S_999MS
//0011 1011
byte[] time = connector.read(DaveArea.DB, 3, 4, 34);
// Integer integer = ByteUtils.toInt(time[0], time[1], time[2], time[3]);
Integer i = (Integer) PlcVar.TIME.toObject(time);
System.out.println("DB3.34-time : " + i +" ms");
}
//常规
//int 相关sintintdint usintuintudint
{
//[11] //[9] ==>2304
// byte[] usint_content = ByteUtils.usintToBytes(9);
byte[] usint_content = PlcVar.USINT.toBytes(9);
connector.write(DaveArea.DB, 3, 8,usint_content);
byte[] usint = connector.read(DaveArea.DB, 3, 1, 8);
// Integer integer = ByteUtils.toUInt(usint[0]);
Integer integer = (Integer) PlcVar.USINT.toObject(usint);
System.out.println("DB3.8-usint : " + integer);
// byte[] bytes = ByteUtils.sintToBytes(-9);
byte[] bytes = PlcVar.SINT.toBytes(-9);
connector.write(DaveArea.DB, 3, 9,bytes);
byte[] sint = connector.read(DaveArea.DB, 3, 1, 9);
// Integer integer1 = ByteUtils.toInt(sint[0]);
Integer integer1 = (Integer) PlcVar.SINT.toObject(usint);
System.out.println("DB3.9-sint : " + integer1);
// byte[] unit_content = ByteUtils.uintToBytes(9);
byte[] unit_content = PlcVar.UINT.toBytes(9);
connector.write(DaveArea.DB, 3, 10,unit_content);
byte[] uint = connector.read(DaveArea.DB, 3, 2, 10);
System.out.println("DB3.10-uint : " + ByteUtils.toUInt(uint[0],uint[1]));
//[-1, -111] -111
//[-1, -64] -64
//[-4, 25] -999
//byte[] bytes1 = ByteUtils.intToBytes(-99);
byte[] bytes1 = PlcVar.INT.toBytes(-99);
connector.write(DaveArea.DB, 3, 12,bytes1);
byte[] ints = connector.read(DaveArea.DB, 3, 2, 12);
System.out.println("DB3.12-ints : " + ByteUtils.toInt(ints[0],ints[1]));
// byte[] bytes2 = ByteUtils.dintToBytes(-999);
byte[] bytes2 = PlcVar.DINT.toBytes(-999);
connector.write(DaveArea.DB, 3, 14,bytes2);
byte[] dint = connector.read(DaveArea.DB, 3, 4, 14);
System.out.println("DB3.14-dint : " + ByteUtils.toInt(dint[0],dint[1],dint[2],dint[3]));
// byte[] bytes3 = ByteUtils.udintToBytes(99);
byte[] bytes3 = PlcVar.UDINT.toBytes(99);
connector.write(DaveArea.DB, 3, 18,bytes3);
byte[] udint = connector.read(DaveArea.DB, 3, 4, 18);
System.out.println("DB3.18-udint : " + ByteUtils.toUInt(udint[0],udint[1],udint[2],udint[3]));
}
{
// [0]
// byte[] bytes = ByteUtils.boolToBytes(false);
byte[] bytes = PlcVar.BOOL.toBytes(false);
connector.write(DaveArea.DB, 3, 0,bytes);
byte[] bool = connector.read(DaveArea.DB, 3, 1, 0);
System.out.println("DB3.0-boolean : " + ByteUtils.toBoolean(bool));
//byte 目前读取以 有符号的十进制
//[8]
//[-63]
//1011 1111
// byte[] setbytes = ByteUtils.setbytes(Byte.valueOf((byte) 0xFF));
byte[] setbytes = PlcVar.BYTE.toBytes((byte)0xFF);
connector.write(DaveArea.DB, 3, 1,setbytes);
byte[] byteOne = connector.read(DaveArea.DB, 3, 1, 1);
System.out.println("DB3.1-byteOne-有符号 : " + ByteUtils.toInt(byteOne[0])); //有符号 整形
System.out.println("DB3.1-byteOne-无符号 : " + ByteUtils.toUInt(byteOne[0])); //无符号 整形
// [0, 5] word 2/8/16进制 可无符号 可有符号就看你真没用
// byte[] bytes1 = ByteUtils.wordToBytes(Short.valueOf("-55"));
byte[] bytes1 = PlcVar.WORD.toBytes(-55);
connector.write(DaveArea.DB, 3, 2,bytes1);
byte[] word = connector.read(DaveArea.DB, 3, 2, 2);
System.out.println("DB3.2-word : " + ByteUtils.toInt(word[0],word[1]));
// [-1, -1, -1, -4] 把java自动转换成byte类型 的补码形式 ,如果电控变量是这个要问下他是无符号还是有符号的
// [FF, FF, FF, FC] 原码
byte[] bytes2 = PlcVar.DWORD.toBytes(-99);
connector.write(DaveArea.DB, 3, 4,bytes2);
byte[] dword = connector.read(DaveArea.DB, 3, 4, 4);
System.out.println("DB3.4-dword : " + ByteUtils.toInt(dword[0],dword[1],dword[2],dword[3])); //带符号的 整形
//plc 中char 是1个字节 注意char wchar 都是ascii码格式的
// byte[] bytes3 = ByteUtils.charToBytes('b');
byte[] bytes3 = PlcVar.CHAR.toBytes('b');
connector.write(DaveArea.DB, 3, 58,bytes3);
byte[] chars = connector.read(DaveArea.DB, 3, 1, 58);
System.out.println("DB3.58-char : " + ByteUtils.toChar(chars));
//plc 中wchar 是2个字节 '菜' =[-125, -36]
// byte[] bytes4 = ByteUtils.wcharToBytes('菜');
byte[] bytes4 = PlcVar.WCHAR.toBytes('翔');
connector.write(DaveArea.DB, 3, 60,bytes4);
byte[] wchar = connector.read(DaveArea.DB, 3, 2, 60);
System.out.println("DB3.60-wchar : " + ByteUtils.toChar(wchar));
//n+2 个字节 这里的n 就是实际content的字节数 "123"=> n=3 ;; "@123" ==> n=4
//[-2, 4, 64, 65, 83, 68] @ASD 前面-2 4两个字节就是上面的2字节 是无用的
//-2 代表 字符串中存储最大的总字节数 4代表字符数 后面跟着的 是内容最多256个字节
//[-2, 4, 64, 65, 83, 68] @ASD
//[-2, 4, 64, 64, 64, 64] @ASD
//读取字符串 要事先知道长度否则可能读取的字符串长度不完整
String s = "你好啊呢";
// byte[] str_content = ByteUtils.strToBytes(s);
// byte[] str_content = PlcVar.STRING.toBytes(s);
// connector.write(DaveArea.DB, 3, 62 , str_content);
//byte[] str = connector.read(DaveArea.DB, 3, str_content.length, 62);
//[-2, 6, -60, -29, -70, -61]
//[-2, 8, -53, -58, -75, -60]
byte[] str = connector.read(DaveArea.DB, 3, 10, 62);
String string = ByteUtils.toStr(str);
System.out.println("DB3.62-str : " +string );
//下面这种方式读取字符串是指定长度的
//byte[] str2 = connector.read(DaveArea.DB, 3, 7, 62);
}
{
//byte 占用一个字节如果是数组的话就读取2个要事先知道点表规定数组长度实际就是读取 DB3.830
//bytes = byteLength * arrayLength 举例1byteLength * 2arrayLength =2bytes
boolean[] booleanArray = new boolean[2];
booleanArray[0] = true;
booleanArray[1] = true;
// byte[] bytes1 = ByteUtils.booleanArrayToBytes(booleanArray);
byte[] bytes1 = PlcVar.BOOL_Array.toBytes(booleanArray);
connector.write(DaveArea.DB, 3, 830 , bytes1);
byte[] boolArrays = connector.read(DaveArea.DB, 3, 2, 830);
// List<Boolean> booleans = ByteUtils.toBoolArray(boolArrays);
List<Boolean> booleans = (List<Boolean>) PlcVar.BOOL_Array.toObject(boolArrays);
System.out.println("DB3.830-boolArrays : " +booleans );
//注意 write的长度要和plc中定义的长度要一致
byte[] write_byteArrays = new byte[2];
write_byteArrays[0] = 1;
write_byteArrays[1] = 2;
byte[] bytes2 = PlcVar.BYTE_Array.toBytes(write_byteArrays);
connector.write(DaveArea.DB, 3, 832 , bytes2);
byte[] byteArrays = connector.read(DaveArea.DB, 3, 2, 832);
// List<Byte> bytes = ByteUtils.toByteArray(byteArrays);
List<Byte> bytes = (List<Byte>) PlcVar.BYTE_Array.toObject(boolArrays);
System.out.println("DB3.832-byteArrays : " +bytes );
short[] shortArrays_content = new short[2];
shortArrays_content[0] = 1;
shortArrays_content[1] = -1;
// byte[] bytes4 = ByteUtils.wordArrayToBytes(shortArrays_content);
byte[] bytes4 = PlcVar.WORD_Array.toBytes(shortArrays_content);
connector.write(DaveArea.DB, 3, 836 ,bytes4);
byte[] wordArrays = connector.read(DaveArea.DB, 3, 4, 836);
List<Integer> words = ByteUtils.toWordArray(wordArrays);
System.out.println("DB3.836-wordArrays : " +words );
int[] intArrays_content = new int[2];
intArrays_content[0] = 1;
intArrays_content[1] = -1;
// byte[] bytes5 = ByteUtils.dwordArrayToBytes(intArrays_content);
byte[] bytes5 = PlcVar.DWORD_Array.toBytes(intArrays_content);
connector.write(DaveArea.DB, 3, 840 ,bytes5);
byte[] dwordArrays = connector.read(DaveArea.DB, 3, 8, 840);
List<Integer> dwords = ByteUtils.toDWordArray(dwordArrays);
System.out.println("DB3.840-dwordArrays : " +dwords );
char[] charArrays_content = new char[2];
charArrays_content[0] = '1';
charArrays_content[1] = 'b';
// byte[] bytes3 = ByteUtils.charArrayToBytes(charArrays_content);
byte[] bytes3 = PlcVar.CHAR_Array.toBytes(charArrays_content);
connector.write(DaveArea.DB, 3, 834 ,bytes3);
byte[] charArrays = connector.read(DaveArea.DB, 3, 2, 834);
List<Character> chars = ByteUtils.toCharArray(charArrays);
System.out.println("DB3.834-charArrays : " +chars );
int[] sintArrays_content = new int[2];
sintArrays_content[0] = 1;
sintArrays_content[1] = -1;
// byte[] bytes6 = ByteUtils.sintArrayToBytes(sintArrays_content);
byte[] bytes6 = PlcVar.SINT_Array.toBytes(sintArrays_content);
connector.write(DaveArea.DB, 3, 852 ,bytes6);
byte[] sintArrays = connector.read(DaveArea.DB, 3, 2, 852);
List<Integer> sints = ByteUtils.toSIntArray(sintArrays);
System.out.println("DB3.852-sintArrays : " +sints );
int[] iintArrays_content = new int[2];
iintArrays_content[0] = 12;
iintArrays_content[1] = -21;
// byte[] bytes7 = ByteUtils.intArrayToBytes(iintArrays_content);
byte[] bytes7 = PlcVar.INT_Array.toBytes(iintArrays_content);
connector.write(DaveArea.DB, 3, 848 ,bytes7);
byte[] intArrays = connector.read(DaveArea.DB, 3, 4, 848);
List<Integer> ints = ByteUtils.toIntArray(intArrays);
System.out.println("DB3.848-intArrays : " +ints );
//todo here
int[] dintArrays_content = new int[2];
dintArrays_content[0] = 12;
dintArrays_content[1] = -21;
//ByteUtils.dintArrayToBytes(dintArrays_content)
byte[] bytes11 =PlcVar.DINT_Array.toBytes(dintArrays_content);
connector.write(DaveArea.DB, 3, 854 ,bytes11);
byte[] dintArrays = connector.read(DaveArea.DB, 3, 8, 854);
List<Integer> dints = ByteUtils.toDIntArray(dintArrays);
System.out.println("DB3.852-dintArrays : " +dints);
int[] uintArrays_content = new int[3];
uintArrays_content[0] = 12;
uintArrays_content[1] = 99;
uintArrays_content[2] = 1;
//byte[] bytes9 = ByteUtils.uintArrayToBytes(uintArrays_content);
byte[] bytes9 = PlcVar.UINT_Array.toBytes(uintArrays_content);
connector.write(DaveArea.DB, 3, 3256 ,bytes9);
byte[] uintArrays = connector.read(DaveArea.DB, 3, 6, 3256);
List<Integer> uints = ByteUtils.toUIntArray(uintArrays);
System.out.println("DB3.3256-uintArrays : " +uints );
int[] usintArrays_content = new int[3];
usintArrays_content[0] = 12;
usintArrays_content[1] = 99;
usintArrays_content[2] = 1;
// byte[] bytes8 = ByteUtils.usintArrayToBytes(usintArrays_content);
byte[] bytes8 = PlcVar.USINT_Array.toBytes(usintArrays_content);
connector.write(DaveArea.DB, 3, 3240 ,bytes8);
byte[] usintArrays = connector.read(DaveArea.DB, 3, 3, 3240);
List<Integer> usints = ByteUtils.toUSIntArray(usintArrays);
System.out.println("DB3.3240-usintArrays : " +usints );
int[] udintArrays_content = new int[3];
udintArrays_content[0] = 12;
udintArrays_content[1] = 99;
udintArrays_content[2] = 1;
// byte[] bytes10 = ByteUtils.udintArrayToBytes(udintArrays_content);
byte[] bytes10 = PlcVar.UDINT_Array.toBytes(udintArrays_content);
connector.write(DaveArea.DB, 3, 3244 ,bytes10);
byte[] udintArrays = connector.read(DaveArea.DB, 3, 12, 3244);
List<Long> udints = ByteUtils.toUDIntArray(udintArrays);
System.out.println("DB3.852-udintArrays : " +udints);
}
//Write to DB100 10 bytes
//connector.write(DaveArea.DB, 3, 830, byteArrays);
//Close connection
connector.close();
}
}

View File

@ -0,0 +1,47 @@
/*
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.api;
public enum DaveArea {
ANALOGINPUTS200(6), // System info of 200 family
ANALOGOUTPUTS200(7), // System flags of 200 family
COUNTER(28), // analog inputs of 200 family
COUNTER200(30), // analog outputs of 200 family
DB(0x84), // Peripheral I/O //这个是plc中的不同区有V区input区output区
DI(0x85), FLAGS(0x83), INPUTS(0x81), LOCAL(0x86), // data blocks
OUTPUTS(0x82), // instance data blocks
P(0x80), // not tested
SYSINFO(3), // local of caller
SYSTEMFLAGS(5), // S7 counters
TIMER(29), // S7 timers
TIMER200(31), // IEC counters (200 family)
V(0x87); // IEC timers (200 family)
/** Function Code */
int code;
/** Constructor */
DaveArea(final int code) {
this.code = code;
}
/**
* Returns the function code as associated
*/
public int getCode() {
return this.code;
}
}

View File

@ -0,0 +1,58 @@
/*
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.api;
import com.qgs.dc.s7.my.s7connector.type.PlcVar;
import com.qgs.dc.s7.my.s7connector.type.TransportSize;
import java.io.Closeable;
public interface S7Connector extends Closeable {
/**
* Reads an area
*
* @param area
* @param areaNumber
* @param bytes
* @param offset
* @return
*/
public byte[] read(DaveArea area, int areaNumber, int bytes, int offset);
/**
* Reads an area 读需要bit 位置的 变量其实就是 read bool变量的时候调用这个方法
*
* @param area
* @param areaNumber
* @param bytes
* @param offset
* @return
*/
public byte[] read(DaveArea area, int areaNumber, int bytes, int offset, int bitOffset, TransportSize transportSize);
/**
* Writes an area
*
* @param area
* @param areaNumber
* @param offset
* @param buffer
*/
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);
}

View File

@ -0,0 +1,78 @@
/*
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.api;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
/**
* The Interface S7Serializable API
*/
public interface S7Serializable {
/**
* Extracts a java type from a byte buffer.
*
* @param <T>
* the generic type
* @param targetClass
* the target class
* @param buffer
* the buffer
* @param byteOffset
* the byte offset
* @param bitOffset
* the bit offset
* @return the t
*/
public <T> T extract(Class<T> targetClass, byte[] buffer, int byteOffset, int bitOffset);
/**
* Returns the S7-Type.
*
* @return the s7 type
*/
public S7Type getS7Type();
/**
* Returns the size of the s7 type bytes.
*
* @return the size in bits
*/
public int getSizeInBits();
/**
* Returns the size of the s7 type bytes.
*
* @return the size in bytes
*/
public int getSizeInBytes();
/**
* Inserts a Java Object to the byte buffer.
*
* @param javaType
* the java type
* @param buffer
* the buffer
* @param byteOffset
* the byte offset
* @param bitOffset
* the bit offset
* @param size
* the size
*/
public void insert(Object javaType, byte[] buffer, int byteOffset, int bitOffset, int size);
}

View File

@ -0,0 +1,70 @@
/*
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.api;
import com.qgs.dc.s7.my.s7connector.exception.S7Exception;
public interface S7Serializer {
/**
* Dispenses an Object from the mapping of the Datablock.
*
* @param <T>
* the generic type
* @param beanClass
* the bean class
* @param dbNum
* the db num
* @param byteOffset
* the byte offset
* @return the t
* @throws S7Exception
* the s7 exception
*/
<T> T dispense(Class<T> beanClass, int dbNum, int byteOffset) throws S7Exception;
/**
* Dispense.
*
* @param <T>
* the generic type
* @param beanClass
* the bean class
* @param dbNum
* the db num
* @param byteOffset
* the byte offset
* @param blockSize
* the block size
* @return the t
* @throws S7Exception
* the s7 exception
*/
<T> T dispense(Class<T> beanClass, int dbNum, int byteOffset, int blockSize) throws S7Exception;
/**
* Stores an Object to the Datablock.
*
* @param bean
* the bean
* @param dbNum
* the db num
* @param byteOffset
* the byte offset
*/
void store(Object bean, int dbNum, int byteOffset);
}

View File

@ -0,0 +1,30 @@
/*
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.api.annotation;
import java.lang.annotation.*;
/**
* Annotation for array-declaration
*
* @author Thomas Rudin
*/
@Target(value = { ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Array {
int size();
}

View File

@ -0,0 +1,29 @@
/*
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.api.annotation;
import java.lang.annotation.*;
/**
* Annotation for a datablock
*
* @author Thomas Rudin
*/
@Target(value = { ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Datablock {
}

View File

@ -0,0 +1,56 @@
/*
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.api.annotation;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
import java.lang.annotation.*;
/**
* Defines an Offset in a DB
*
* @author Thomas Rudin
*/
@Target(value = { ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface S7Variable {
/**
* The size of the array
*/
int arraySize() default 1;
/**
* The bit offset, if any
*/
int bitOffset() default 0;
/**
* The Byte Offset
*/
int byteOffset();
/**
* The specified size (for String)
*/
int size() default 0;
/**
* The corresponding S7 Type
*/
S7Type type();
}

View File

@ -0,0 +1,90 @@
/*
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.api.factory;
import com.qgs.dc.s7.my.s7connector.api.S7Connector;
import com.qgs.dc.s7.my.s7connector.impl.S7TCPConnection;
/**
* S7 connector factory, currently only for TCP connections
*
* @author Thomas Rudin
*
*/
public class S7ConnectorFactory {
/**
* TCP Connection builder
*
*/
public static class TCPConnectionBuilder {
private String host;
//rack slot 都是指的是 remote-rack remote-slot
private int rack = 0, slot = 0, port = 102;
/**
* Builds a connection with given params
*/
public S7Connector build() {
return new S7TCPConnection(this.host, this.rack, this.slot, this.port);
}
/**
* use hostname/ip
*/
public TCPConnectionBuilder withHost(final String host) {
this.host = host;
return this;
}
/**
* use port, default is 102
*/
public TCPConnectionBuilder withPort(final int port) {
this.port = port;
return this;
}
/**
* use rack, default is 0
*/
public TCPConnectionBuilder withRack(final int rack) {
this.rack = rack;
return this;
}
/**
* use slot, default is 2
*/
public TCPConnectionBuilder withSlot(final int slot) {
this.slot = slot;
return this;
}
}
/**
* returns a new TCP connection builder
*
* @return
*/
public static TCPConnectionBuilder buildTCPConnector() {
return new TCPConnectionBuilder();
}
}

View File

@ -0,0 +1,41 @@
/*
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.api.factory;
import com.qgs.dc.s7.my.s7connector.api.S7Connector;
import com.qgs.dc.s7.my.s7connector.api.S7Serializer;
import com.qgs.dc.s7.my.s7connector.impl.serializer.S7SerializerImpl;
/**
* S7 Serializer factory
*
* @author Thomas Rudin
*
*/
public class S7SerializerFactory {
/**
* Builds a new serializer with given connector
*
* @param connector
* the connector to use
* @return a serializer instance
*/
public static S7Serializer buildSerializer(final S7Connector connector) {
return new S7SerializerImpl(connector);
}
}

View File

@ -0,0 +1,689 @@
package com.qgs.dc.s7.my.s7connector.api.utils;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2021/12/16 9:08
*/
import cn.hutool.core.util.ByteUtil;
import java.io.UnsupportedEncodingException;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Stack;
public class ByteUtils {
public static double bytes2Double(byte[] arr) {
long value = 0;
for (int i = 0; i < 8; i++) {
value |= ((long) (arr[i] & 0xff)) << (8 * i);
}
return Double.longBitsToDouble(value);
}
public static Boolean toBoolean(byte[] bytes){
if(bytes.length ==0){
return null;
}
if(bytes[0] == 1){
return true;
}else {
return false;
}
}
public static Boolean toBoolean(byte bytes){
if(bytes == 1){
return true;
}else {
return false;
}
}
public static String addDate(String timeParam, Long day) throws ParseException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); // 日期格式
Date date = dateFormat.parse(timeParam); // 指定日期
long time = date.getTime(); // 得到指定日期的毫秒数
day = day * 24 * 60 * 60 * 1000; // 要加上的天数转换成毫秒数
time += day; // 相加得到新的毫秒数
Date newDate = new Date(time);
return dateFormat.format(newDate); // 将毫秒数转换成日期
}
public static byte[] invert(byte[] a){
byte[] b = new byte[a.length];
Stack<Byte> st = new Stack<Byte>();
for(int i=0;i<a.length;i++){
st.push(a[i]);
}
for(int i=0;i<a.length;i++){
b[i] = st.pop();
}
return b;
}
/**
* 1个字节byte[] 转成有符号的Integer
* */
public static Integer toInt(byte bytes) {
return Integer.valueOf(Byte.toString(bytes));
}
/**
* 1个字节byte[] 转成无符号的Integer
* */
public static Integer toUInt(byte bytes) {
int i = Byte.toUnsignedInt(bytes);
return Integer.valueOf(i);
}
/**
* 2个字节byte[] 转成有符号的Integer
* 默认大端
* */
public static Integer toInt(byte byte1,byte byte2) {
byte[] bytes = new byte[2];
bytes[0] = byte1;
bytes[1] = byte2;
return Integer.valueOf(Short.toString(ByteUtil.bytesToShort(bytes, ByteOrder.BIG_ENDIAN)));
}
/**
* 2个字节byte[] 转成无符号的Integer
* 默认大端
* */
public static Integer toUInt(byte byte1,byte byte2) {
byte[] bytes = new byte[2];
bytes[0] = byte1;
bytes[1] = byte2;
short i = ByteUtil.bytesToShort(bytes, ByteOrder.BIG_ENDIAN);
return Short.toUnsignedInt(i);
}
/**
* 4个字节byte[] 转成有符号的Integer
* 默认大端
* */
public static Integer toInt(byte byte1,byte byte2,byte byte3,byte byte4) {
byte[] bytes = new byte[4];
bytes[0] = byte1;
bytes[1] = byte2;
bytes[2] = byte3;
bytes[3] = byte4;
return ByteUtil.bytesToInt(bytes, ByteOrder.BIG_ENDIAN);
}
/**
* 4个字节byte[] 转成无符号的Long因为如果首位为1 Integer就不满足长度了
* 默认大端
* */
public static Long toUInt(byte byte1,byte byte2,byte byte3,byte byte4) {
byte[] bytes = new byte[8];
bytes[0] = 0;
bytes[1] = 0;
bytes[2] = 0;
bytes[3] = 0;
bytes[4] = byte1;
bytes[5] = byte2;
bytes[6] = byte3;
bytes[7] = byte4;
long l = ByteUtil.bytesToLong(bytes, ByteOrder.BIG_ENDIAN);
return l;
}
/**
*
* 4个字节byte[] 转成有符号的Double
* 默认大端
* */
public static Float forReal(byte[] bytes) {
return Float.intBitsToFloat(toInt(bytes[0],bytes[1],bytes[2],bytes[3]));
}
/**
*
* 8个字节byte[] 转成有符号的Double
* 默认大端
* */
public static Double forLReal(byte[] bytes) {
return ByteUtil.bytesToDouble(bytes, ByteOrder.BIG_ENDIAN);
}
/**
*
* 8个字节byte[] 转成有符号的Double
* 默认大端
* */
public static Double lrealbytesToDouble(byte[] bytes) {
return ByteUtil.bytesToDouble(bytes, ByteOrder.BIG_ENDIAN);
}
public static byte[] lrealToBytes(double doubleValue) {
return ByteUtil.doubleToBytes(doubleValue, ByteOrder.BIG_ENDIAN);
//return double2Bytes(doubleValue);
}
// public static String toChar(byte[] b) throws UnsupportedEncodingException {
// String ascii = new String(b, Charset.forName("UTF-8"));
// return ascii;
// }
public static Character toChar(byte[] b) throws UnsupportedEncodingException {
if(b.length==1){
return toChar(b[0]);
}
return byteToChar(b);
}
public static Character toChar(byte b) throws UnsupportedEncodingException {
return byteToChar(b);
}
// public static String toChar(byte b) throws UnsupportedEncodingException {
// byte[] bs = new byte[1];
// bs[0] = b;
// String ascii = new String(bs, "ascii");
// return ascii;
// }
/**
* return null 代表返回传入参数不正确str 取的length太小
* */
public static String toStr(byte[] b) throws UnsupportedEncodingException {
Integer length = Byte.toUnsignedInt(b[1]);
if(length>(b.length-2)){
return null;
}
byte[] content = new byte[b.length-2];
for(int i=0;i<length;i++){
content[i] = b[i+2];
}
String ascii = new String(content, StandardCharsets.UTF_8);
// String s = new String(content);
return ascii;
}
public static List<Boolean> toBoolArray(byte[] b) throws UnsupportedEncodingException {
List<Boolean> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add(toBoolean(b[i]));
}
return res;
}
public static List<Byte> toByteArray(byte[] b) throws UnsupportedEncodingException {
List<Byte> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add((b[i]));
}
return res;
}
public static List<Character> toCharArray(byte[] b) throws UnsupportedEncodingException {
List<Character> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add(toChar(b[i]));
}
return res;
}
//toWord
/**
* 默认word => 有符号的整形
* */
public static List<Integer> toWordArray(byte[] b) throws UnsupportedEncodingException {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
res.add(
toInt(b[i],b[i+1])
);
i+=2;
}
return res;
}
//toDWord
/**
* 默认dword => 有符号的整形
* */
public static List<Integer> toDWordArray(byte[] b) throws UnsupportedEncodingException {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
res.add(
toInt(b[i],b[i+1],b[i+2],b[i+3])
);
i+=4;
}
return res;
}
/**
*
* 4个字节byte[] 转成有符号的Double
* 默认大端
* */
public static Float realbytesToFloat(byte[] bytes) {
return Float.intBitsToFloat(toInt(bytes[0],bytes[1],bytes[2],bytes[3]));
}
public static byte[] realToBytes(Float f) {
return invert(float2byte(f));
}
/**
* 浮点转换为字节
*
* @param f
* @return
*/
private static byte[] float2byte(float f) {
// 把float转换为byte[]
int fbit = Float.floatToIntBits(f);
byte[] b = new byte[4];
for (int i = 0; i < 4; i++) {
b[i] = (byte) (fbit >> (24 - i * 8));
}
// 翻转数组
int len = b.length;
// 建立一个与源数组元素类型相同的数组
byte[] dest = new byte[len];
// 为了防止修改源数组将源数组拷贝一份副本
System.arraycopy(b, 0, dest, 0, len);
byte temp;
// 将顺位第i个与倒数第i个交换
for (int i = 0; i < len / 2; ++i) {
temp = dest[i];
dest[i] = dest[len - i - 1];
dest[len - i - 1] = temp;
}
return dest;
}
//toUInt
/**
* USInt 无符号整形 1个字节 = Integer
* */
public static List<Integer> toUSIntArray(byte[] b) throws UnsupportedEncodingException {
List<Integer> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add(toUInt(b[i]));
}
return res;
}
/**
* UInt 无符号整形 2个字节 = Integer
* */
public static List<Integer> toUIntArray(byte[] b) throws UnsupportedEncodingException {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
res.add(
toUInt(b[i],b[i+1])
);
i+=2;
}
return res;
}
/**
* UDInt 无符号整形 4个字节 = Long
* */
public static List<Long> toUDIntArray(byte[] b) throws UnsupportedEncodingException {
List<Long> res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
res.add(
toUInt(b[i],b[i+1],b[i+2],b[i+3])
);
i+=4;
}
return res;
}
/**
* SInt 无符号整形 1个字节 = Integer
* */
public static List<Integer> toSIntArray(byte[] b) throws UnsupportedEncodingException {
List<Integer> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add(toInt(b[i]));
}
return res;
}
/**
* Int 无符号整形 2个字节 = Integer
* */
public static List<Integer> toIntArray(byte[] b) throws UnsupportedEncodingException {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
res.add(
toInt(b[i],b[i+1])
);
i+=2;
}
return res;
}
/**
* DInt 无符号整形 4个字节 = Integer
* */
public static List<Integer> toDIntArray(byte[] b) throws UnsupportedEncodingException {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
res.add(
toInt(b[i],b[i+1],b[i+2],b[i+3])
);
i+=4;
}
return res;
}
/**
* sint(1个字节) = byte[]
* 默认大端模式
* */
public static byte[] sintToBytes(Integer i){
byte[] res = new byte[1];
res[0] = Byte.valueOf(i.toString());
return res;
}
/**
* sintArray(1个字节) = byte[]
*
* */
public static byte[] sintArrayToBytes(int[] sintArray) {
byte[] res = new byte[sintArray.length];
for(int i=0;i<sintArray.length;i++){
int i1 = sintArray[i];
res[i] = sintToBytes(i1)[0];
}
return res;
}
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
public static byte[] intToBytes(Integer i) {
Number shortNumber = Short.valueOf(i.toString());
return ByteUtil.numberToBytes(shortNumber,ByteOrder.BIG_ENDIAN);
}
/**
* intArray(2个字节) = byte[]
*
* */
public static byte[] intArrayToBytes(int[] intArray) {
byte[] res = new byte[intArray.length*2];
for(int i=0 ; i< intArray.length ; i++){
byte[] bytes = intToBytes(intArray[i]);
res[i*2] = bytes[0];
res[i*2+1] = bytes[1];
}
return res;
}
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
public static byte[] dintToBytes(Integer i) {
Number intNumber = i;
return ByteUtil.numberToBytes(intNumber,ByteOrder.BIG_ENDIAN);
}
/**
* intArray(2个字节) = byte[]
*
* */
public static byte[] dintArrayToBytes(int[] dintArray) {
byte[] res = new byte[dintArray.length*4];
for(int i=0 ; i< dintArray.length ; i++){
byte[] bytes = dintToBytes(dintArray[i]);
res[i*4] = bytes[0];
res[i*4+1] = bytes[1];
res[i*4+2] = bytes[2];
res[i*4+3] = bytes[3];
}
return res;
}
/**
* sint(1个字节) = byte[]
* 默认大端模式
* */
public static byte[] usintToBytes(Integer i){
if(i<0){
return null;
}
byte[] res = new byte[1];
res[0] = Byte.valueOf(i.toString());
return res;
}
/**
* usintArrayToBytes = byte[]
* 默认大端模式
* */
public static byte[] usintArrayToBytes(int[] usintArray) {
byte[] res = new byte[usintArray.length];
for(int i=0 ; i< usintArray.length ; i++){
byte[] bytes = usintToBytes(usintArray[i]);
if(bytes == null){
return null;
}
res[i] = bytes[0];
}
return res;
}
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
public static byte[] uintToBytes(Integer i) {
if(i<0){
return null;
}
Number shortNumber = Short.valueOf(i.toString());
return ByteUtil.numberToBytes(shortNumber,ByteOrder.BIG_ENDIAN);
}
public static byte[] uintArrayToBytes(int[] uintArray) {
byte[] res = new byte[uintArray.length*2];
for(int i=0 ; i< uintArray.length ; i++){
byte[] bytes = uintToBytes(uintArray[i]);
if(bytes == null){
return null;
}
res[i*2] = bytes[0];
res[i*2+1] = bytes[1];
}
return res;
}
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
public static byte[] udintToBytes(Integer i) {
if(i<0){
return null;
}
Number intNumber = i;
return ByteUtil.numberToBytes(intNumber,ByteOrder.BIG_ENDIAN);
}
public static byte[] udintArrayToBytes(int[] udintArray) {
byte[] res = new byte[udintArray.length*4];
for(int i=0 ; i< udintArray.length ; i++){
byte[] bytes = udintToBytes(udintArray[i]);
if(bytes == null){
return null;
}
res[i*4] = bytes[0];
res[i*4+1] = bytes[1];
res[i*4+2] = bytes[2];
res[i*4+3] = bytes[3];
}
return res;
}
/**
* boolean = byte[]
* 默认大端模式
* */
public static byte[] boolToBytes(Boolean bool) {
byte[] res = new byte[1];
if(bool){
res[0] = 1;
return res;
}else {
res[0] = 0;
return res;
}
}
/**
* booleanArray = byte[]
* */
public static byte[] booleanArrayToBytes(boolean[] boolArray) {
byte[] res = new byte[boolArray.length];
for(int i=0 ; i<boolArray.length ; i++){
boolean b = boolArray[i];
if(b){
res[i] = 1;
}else {
res[i] = 0;
}
}
return res;
}
/**
* charArray = byte[]
* */
public static byte[] charArrayToBytes(char[] charArray) {
byte[] res = new byte[charArray.length];
for(int i=0 ; i< charArray.length ; i++){
byte[] bytes = charToByte(charArray[i]);
if(bytes[0] != 0){
return null;
}else {
res[i] = bytes[1];
}
}
return res;
}
/**
* byte = byte[]
* 默认大端模式
* */
public static byte[] setbytes(Byte bytes) {
byte[] res = new byte[1];
res[0] = bytes;
return res;
}
/**
* word = byte[]
* 默认大端模式
* tip: 目前只支持 带符号的十进制 == word(2个字节)
* */
public static byte[] wordToBytes(Short word) {
return ByteUtil.shortToBytes(word,ByteOrder.BIG_ENDIAN);
}
/**
* wordArray = byte[]
* */
public static byte[] wordArrayToBytes(short[] shortArray) {
byte[] res = new byte[shortArray.length*2];
for(int i=0 ; i< shortArray.length ; i++){
byte[] bytes = wordToBytes(shortArray[i]);
res[i*2] = bytes[0];
res[i*2+1] = bytes[1];
}
return res;
}
/**
* dword = byte[]
* 默认大端模式
* tip: 目前只支持 带符号的十进制 == word(4个字节)
* */
public static byte[] dwordToBytes(Integer dword) {
return ByteUtil.intToBytes(dword,ByteOrder.BIG_ENDIAN);
}
/**
* dwordArray = byte[]
* */
public static byte[] dwordArrayToBytes(int[] intArray) {
byte[] res = new byte[intArray.length*4];
for(int i=0 ; i< intArray.length ; i++){
byte[] bytes = dwordToBytes(intArray[i]);
res[i*4] = bytes[0];
res[i*4+1] = bytes[1];
res[i*4+2] = bytes[2];
res[i*4+3] = bytes[3];
}
return res;
}
/**
* char(1字节) = byte[]
*
* */
public static byte[] charToBytes(Character c) {
return String.valueOf(c).getBytes();
}
public static byte[] wcharToBytes(Character c) {
return charToByte(c);
}
public static byte[] strToBytes(String s) {
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
byte[] res = new byte[bytes.length+2];
res[0] = -2;
res[1] = Integer.valueOf(bytes.length).byteValue();
for(int i=0;i<bytes.length;i++){
res[i+2] = bytes[i];
}
return res;
}
//for private
private static byte[] charToByte(char c) {
byte[] b = new byte[2];
b[0] = (byte) ((c & 0xFF00) >> 8);
b[1] = (byte) (c & 0xFF);
return b;
}
private static char byteToChar(byte[] b) {
char c = (char) (((b[0] & 0xFF) << 8) | (b[1] & 0xFF));
return c;
}
private static char byteToChar(byte b) {
char c = (char) (b & 0xFF);
return c;
}
}

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.blocks;
import com.qgs.dc.s7.my.s7connector.api.annotation.S7Variable;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
/**
* PID Control block representation
*
* @author Thomas Rudin (thomas@rudin-informatik.ch)
*
*/
public class CONT_C {
@S7Variable(type = S7Type.BOOL, byteOffset = 0, bitOffset = 6)
public boolean D_SEL;
@S7Variable(type = S7Type.REAL, byteOffset = 20)
public double GAIN;
@S7Variable(type = S7Type.BOOL, byteOffset = 0, bitOffset = 3)
public boolean I_SEL;
@S7Variable(type = S7Type.REAL, byteOffset = 72)
public double LMN;
@S7Variable(type = S7Type.BOOL, byteOffset = 0, bitOffset = 0)
public boolean MAN_ON;
@S7Variable(type = S7Type.BOOL, byteOffset = 0, bitOffset = 2)
public boolean P_SEL;
@S7Variable(type = S7Type.REAL, byteOffset = 10)
public double PV_IN;
@S7Variable(type = S7Type.REAL, byteOffset = 6)
public double SP_INT;
@S7Variable(type = S7Type.TIME, byteOffset = 24)
public long TN;
@S7Variable(type = S7Type.TIME, byteOffset = 28)
public long TV;
@Override
public String toString() {
return "CONT_C [MAN_ON=" + this.MAN_ON + ", P_SEL=" + this.P_SEL + ", I_SEL=" + this.I_SEL + ", D_SEL="
+ this.D_SEL + ", SP_INT=" + this.SP_INT + ", PV_IN=" + this.PV_IN + ", GAIN=" + this.GAIN + ", TN="
+ this.TN + ", TV=" + this.TV + ", LMN=" + this.LMN + "]";
}
}

View File

@ -0,0 +1,71 @@
package com.qgs.dc.s7.my.s7connector.enmuc;
import com.qgs.dc.s7.my.s7connector.api.DaveArea;
import com.qgs.dc.s7.my.s7connector.type.PlcVar;
import com.qgs.dc.s7.my.s7connector.type.TransportSize;
import org.omg.CORBA.PRIVATE_MEMBER;
//实际 Plc中的变量 要实现录入到这个枚举类中 为后续做准备
public enum PlcVarActual {
HeartBeat("HeartBeat",PlcVar.BOOL,1,DaveArea.DB,3,3267,5),
//bitOffset 是针对Bool 来说的
DB54("DB54",PlcVar.BYTE,1,DaveArea.DB,3,3268,0),
DTL("DTL",PlcVar.DTL,1,DaveArea.DB,3,44,0),
STRING1("STRING",PlcVar.STRING,1,DaveArea.DB,3,62,0),
CharArrays("CharArrays",PlcVar.CHAR_Array,2,DaveArea.DB,3,834,0),
BooleanArrays("BooleanArrays",PlcVar.BOOL_Array,2,DaveArea.DB,3,830,0)
;
private String name;
private DaveArea area;
private Integer areaNumber;
private Integer byteOffset;
private Integer bitOffset;
private PlcVar type;
//length = 1代表 非数组;;; length > 1 代表数组 ;; 注意 length这个参数 是实际plc中 数据的长度和read操作相关
//如果是String 类型不用填length 只需要填string类型的起始位置就行了我会自己去取数据长度也就是说这里的length并不是string 的长度
private Integer length;
PlcVarActual(String name, PlcVar type,Integer length, DaveArea area, Integer areaNumber, Integer byteOffset, Integer bitOffset){
this.name = name;
this.type = type;
this.length = length;
this.area = area;
this.areaNumber = areaNumber;
this.byteOffset = byteOffset;
this.bitOffset = bitOffset;
}
public String getName() {
return name;
}
public DaveArea getArea() {
return area;
}
public Integer getAreaNumber() {
return areaNumber;
}
public Integer getBitOffset() {
return bitOffset;
}
public Integer getByteOffset() {
return byteOffset;
}
public PlcVar getType() {
return type;
}
public Integer getLength() {
return length;
}
}

View File

@ -0,0 +1,181 @@
package com.qgs.dc.s7.my.s7connector.enmuc;
import com.qgs.dc.s7.my.s7connector.api.S7Connector;
import com.qgs.dc.s7.my.s7connector.api.factory.S7ConnectorFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2022/1/15 13:01
*/
public enum S7Client {
//TODO 步骤1 这里是配置多PLC 有多个plc 就在这里配置一个枚举类
S7_1200("192.168.0.51",0,0,3,PlcVarActual.HeartBeat)
//后续 在这里扩展 多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(S7Client.class);
//coreSize 是线程池的数量
S7Client(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;
this.heartBeat = heartBeat;
connections = new ArrayList<>();
connectionPool();
ping();
check_ping();
}
private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
private ScheduledExecutorService ping_fail_check = Executors.newScheduledThreadPool(1);
public S7Connector getConnector() {
int size = connections.size();
S7Connector s7Connector = connections.get((pickOne + size) % size);
pickOne+=1;
pickOne = (pickOne)%size;
return s7Connector;
}
public 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 (Exception e){
logger.info("创建S7Connector 连接失败");
return null;
}
}
private void connectionPool(){
for(int i=0;i<coreSize;i++){
connections.add(connect(host,rack,slot));
}
//todo 在plc上新增一个 变量来解决 心跳问题 okok
}
private void check_ping(){
ping_fail_check.execute(new Runnable() {
@Override
public void run() {
while (true){
if(connections.size()!=coreSize){
int c = coreSize-connections.size();
for(int z=0;z<c;z++){
S7Connector connect = connect(host, rack, slot);
if(connect!=null){
connections.add(connect);
}else {
logger.info("check_ping 断线重连出现异常。。");
}
}
}
try {
Thread.sleep(10000);
}catch (Exception e){
logger.info(e.getMessage());
}
}
}
});
}
private void ping(){
executor.execute(new Runnable() {
@Override
public void run() {
while (true){
for(int i=0;i<coreSize;i++){
S7Connector connector = getConnector();
try {
//只要没有报异常 都是通讯正常的
byte[] read = connector.read(
heartBeat.getArea(),
heartBeat.getAreaNumber(),
heartBeat.getType().getTransportSize().getSizeInBytes(),
heartBeat.getByteOffset(),
heartBeat.getBitOffset(),
heartBeat.getType().getTransportSize());
System.out.println(connector.hashCode()+" : ping");
Thread.sleep(100);
}catch (Exception e){
System.out.println(connector.hashCode()+" : connection error");
logger.info(e.getMessage());
//先把 socket close掉
try {
connector.close();
connections.remove(connector);
//如果是网络波动照成的socket断开 等个1S 再重连试试
Thread.sleep(100);
}catch (Exception ee){
logger.info(ee.getMessage());
}
//todo 把之前的连接close 然后新增一个连接到connections
if(connections.size()!=coreSize){
int c = coreSize-connections.size();
for(int z=0;z<c;z++){
S7Connector connect = connect(host, rack, slot);
if(connect!=null){
connections.add(connect);
}else {
logger.info("断线重连出现异常。。");
}
}
}
}
}
try {
Thread.sleep(30000);
}catch (Exception e){
e.printStackTrace();
logger.info(e.getMessage());
}
}
}
});
}
}

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 S7Exception extends RuntimeException {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = -4761415733559374116L;
/**
* Instantiates a new s7 exception.
*/
public S7Exception() {
}
/**
* Instantiates a new s7 exception.
*
* @param message
* the message
*/
public S7Exception(final String message) {
super(message);
}
/**
* Instantiates a new s7 exception.
*
* @param message
* the message
* @param cause
* the cause
*/
public S7Exception(final String message, final Throwable cause) {
super(message, cause);
}
/**
* Instantiates a new s7 exception.
*
* @param cause
* the cause
*/
public S7Exception(final Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,180 @@
/*
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.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.impl.nodave.Nodave;
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.TransportSize;
/**
* Base-Connection for the S7-PLC Connection Libnodave:
* http://libnodave.sourceforge.net/
*
* @author Thomas Rudin
*/
public abstract class S7BaseConnection implements S7Connector {
/** The Constant MAX_SIZE. */
private static final int MAX_SIZE = 96;
/** The Constant PROPERTY_AREA. */
public static final String PROPERTY_AREA = "area";
/** The Constant PROPERTY_AREANUMBER. */
public static final String PROPERTY_AREANUMBER = "areanumber";
/** The Constant PROPERTY_BYTES. */
public static final String PROPERTY_BYTES = "bytes";
/** The Constant PROPERTY_OFFSET. */
public static final String PROPERTY_OFFSET = "offset";
/**
* Checks the Result.
*
* @param libnodaveResult
* the libnodave result
*/
public static void checkResult(final int libnodaveResult) {
if (libnodaveResult != Nodave.RESULT_OK) {
final String msg = Nodave.strerror(libnodaveResult);
throw new IllegalArgumentException("Result: " + msg);
}
}
/**
* Dump data
*
* @param b
* the byte stream
*/
protected static void dump(final byte[] b) {
for (final byte element : b) {
System.out.print(Integer.toHexString(element & 0xFF) + ",");
}
}
/** The dc. */
private S7Connection dc;
/**
* Initialize the connection
*
* @param dc
* the connection instance
*/
protected void init(final S7Connection dc) {
this.dc = dc;
}
/** {@inheritDoc} */
@Override
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的问题
final byte[] currentBuffer = this.read(area, areaNumber, MAX_SIZE, offset);
System.arraycopy(currentBuffer, 0, ret, 0, currentBuffer.length);
final byte[] nextBuffer = this.read(area, areaNumber, bytes - MAX_SIZE, offset + MAX_SIZE);
System.arraycopy(nextBuffer, 0, ret, currentBuffer.length, nextBuffer.length);
return ret;
} else {
//接收结果的 容器传进去指针
final byte[] buffer = new byte[bytes];
final int ret = this.dc.readBytes(area, areaNumber, offset, bytes, buffer);
checkResult(ret);
return buffer;
}
}
@Override
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);
}
if (bytes > MAX_SIZE) {
final byte[] ret = new byte[bytes];
//注意这里 嵌套了 递归让无限递归去解决 MAX_SIZE的问题
final byte[] currentBuffer = this.read(area, areaNumber, MAX_SIZE, offset,bitOffset,transportSize);
System.arraycopy(currentBuffer, 0, ret, 0, currentBuffer.length);
final byte[] nextBuffer = this.read(area, areaNumber, bytes - MAX_SIZE, offset + MAX_SIZE,bitOffset,transportSize);
System.arraycopy(nextBuffer, 0, ret, currentBuffer.length, nextBuffer.length);
return ret;
} else {
//接收结果的 容器传进去指针
final byte[] buffer = new byte[bytes];
final int ret = this.dc.readBytes(area, areaNumber, offset,bitOffset, bytes, buffer,transportSize);
checkResult(ret);
return buffer;
}
}
/** {@inheritDoc} */
@Override
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];
final byte[] nextBuffer = new byte[buffer.length - subBuffer.length];
System.arraycopy(buffer, 0, subBuffer, 0, subBuffer.length);
System.arraycopy(buffer, MAX_SIZE, nextBuffer, 0, nextBuffer.length);
this.write(area, areaNumber, offset, subBuffer);
this.write(area, areaNumber, offset + subBuffer.length, nextBuffer);
} else {
// Size fits
final int ret = this.dc.writeBytes(area, areaNumber, offset, buffer.length, buffer);
// Check return-value
checkResult(ret);
}
}
@Override
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;
}
if (buffer.length > MAX_SIZE) {
// Split buffer
final byte[] subBuffer = new byte[MAX_SIZE];
final byte[] nextBuffer = new byte[buffer.length - subBuffer.length];
System.arraycopy(buffer, 0, subBuffer, 0, subBuffer.length);
System.arraycopy(buffer, MAX_SIZE, nextBuffer, 0, nextBuffer.length);
this.write(area, areaNumber, byteOffset,bitOffset, subBuffer,var);
this.write(area, areaNumber, byteOffset + subBuffer.length,bitOffset, nextBuffer,var);
} else {
// Size fits
final int ret = this.dc.writeBytes(area, areaNumber, byteOffset,bitOffset, buffer.length, buffer,var);
// Check return-value
checkResult(ret);
}
}
}

View File

@ -0,0 +1,122 @@
/*
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.impl;
import com.qgs.dc.s7.my.s7connector.api.DaveArea;
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.PLCinterface;
import com.qgs.dc.s7.my.s7connector.impl.nodave.TCPConnection;
import java.net.InetSocketAddress;
import java.net.Socket;
/**
* TCP_Connection to a S7 PLC
*
* @author Thomas Rudin
* @href http://libnodave.sourceforge.net/
*
*/
public final class S7TCPConnection extends S7BaseConnection {
/**
* The Connection
*/
private TCPConnection dc;
/**
* The Interface
*/
private PLCinterface di;
/**
* The Host to connect to
*/
private final String host;
/**
* The port to connect to
*/
private final int port;
/**
* Rack and slot number
*/
private final int rack, slot;
/**
* The Socket
*/
private Socket socket;
/**
* Creates a new Instance to the given host, rack, slot and port
*
* @param host
* @throws
*/
public S7TCPConnection(final String host, final int rack, final int slot, final int port) throws S7Exception {
this.host = host;
this.rack = rack;
this.slot = slot;
this.port = port;
this.setupSocket();
}
@Override
public void close() {
try {
this.socket.close();
} catch (final Exception e) {
e.printStackTrace();
}
}
/** {@inheritDoc} */
@Override
protected void finalize() throws Throwable {
this.close();
}
/**
* Sets up the socket
*/
private void setupSocket() {
try {
this.socket = new Socket();
this.socket.setSoTimeout(2000);
//先socket 建立好连接
this.socket.connect(new InetSocketAddress(this.host, this.port));
this.di = new PLCinterface(this.socket.getOutputStream(), this.socket.getInputStream(), "IF1",
DaveArea.LOCAL.getCode(), // TODO Local MPI-Address?
Nodave.PROTOCOL_ISOTCP);
this.dc = new TCPConnection(this.di, this.rack, this.slot);
final int res = this.dc.connectPLC();
checkResult(res);
super.init(this.dc);
} catch (final Exception e) {
throw new S7Exception("constructor", e);
}
}
}

View File

@ -0,0 +1,354 @@
/*
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.impl.nodave;
public final class Nodave {
public final static int MAX_RAW_LEN = 2048;
public final static int MPIReachable = 0x30;
public final static int MPIunused = 0x10;
public final static int OrderCodeSize = 21;
public final static int PartnerListSize = 126;
public final static int PROTOCOL_ISOTCP = 4;
public final static int PROTOCOL_ISOTCP243 = 5;
public final static int PROTOCOL_MPI_IBH = 223; // MPI with IBH NetLink MPI
// to ethernet gateway
public final static int PROTOCOL_MPI_NLPRO = 230; // MPI with IBH NetLink
// MPI to ethernet
// gateway
public final static int PROTOCOL_NLPRO = 230; // MPI with IBH NetLink MPI to
// ethernet gateway
// to ethernet gateway
public final static int PROTOCOL_PPI_IBH = 224; // PPI with IBH NetLink MPI
public final static int RESULT_ADDRESS_OUT_OF_RANGE = 5;
/* means the write data size doesn't fit item size */
public final static int RESULT_CANNOT_EVALUATE_PDU = -123;
public final static int RESULT_CPU_RETURNED_NO_DATA = -124;
public final static int RESULT_EMPTY_RESULT_ERROR = -126;
public final static int RESULT_EMPTY_RESULT_SET_ERROR = -127;
public final static int RESULT_ITEM_NOT_AVAILABLE = 10;
/* means a a piece of data is not available in the CPU, e.g. */
/* when trying to read a non existing DB */
/* CPU tells it does not support to read a bit block with a */
/* length other than 1 bit. */
public final static int RESULT_ITEM_NOT_AVAILABLE200 = 3;
/* means a a piece of data is not available in the CPU, e.g. */
/* when trying to read a non existing DB or bit bloc of length<>1 */
/* This code seems to be specific to 200 family. */
/* CPU tells there is no peripheral at address */
public final static int RESULT_MULTIPLE_BITS_NOT_SUPPORTED = 6;
public final static int RESULT_NO_PERIPHERAL_AT_ADDRESS = 1;
public final static int RESULT_OK = 0; /* means all ok */
public final static int RESULT_SHORT_PACKET = -1024;
public final static int RESULT_TIMEOUT = -1025;
public final static int RESULT_UNEXPECTED_FUNC = -128;
public final static int RESULT_UNKNOWN_DATA_UNIT_SIZE = -129;
public final static int RESULT_UNKNOWN_ERROR = -125;
/* means the data address is beyond the CPUs address range */
public final static int RESULT_WRITE_DATA_SIZE_MISMATCH = 7;
public static float BEFloat(final byte[] b, final int pos) {
int i = 0;
// System.out.println("pos" + pos);
i |= Nodave.USByte(b, pos);
i <<= 8;
i |= Nodave.USByte(b, pos + 1);
i <<= 8;
i |= Nodave.USByte(b, pos + 2);
i <<= 8;
i |= Nodave.USByte(b, pos + 3);
final float f = Float.intBitsToFloat(i);
return (f);
}
public static byte[] bswap_16(int a) {
final byte[] b = new byte[2];
b[1] = (byte) (a & 0xff);
a = a >> 8;
b[0] = (byte) (a & 0xff);
return b;
}
public static byte[] bswap_32(int a) {
final byte[] b = new byte[4];
b[3] = (byte) (a & 0xff);
a = a >> 8;
b[2] = (byte) (a & 0xff);
a = a >> 8;
b[1] = (byte) (a & 0xff);
a = a >> 8;
b[0] = (byte) (a & 0xff);
return b;
}
public static byte[] bswap_32(long a) {
final byte[] b = new byte[4];
b[3] = (byte) (a & 0xff);
a = a >> 8;
b[2] = (byte) (a & 0xff);
a = a >> 8;
b[1] = (byte) (a & 0xff);
a = a >> 8;
b[0] = (byte) (a & 0xff);
return b;
}
/**
* This doesn't swap anything, but the name fits into the series
*
* @param a
* @return
*/
public static byte[] bswap_8(final int a) {
final byte[] b = new byte[1];
b[0] = (byte) (a & 0xff);
return b;
}
/**
* Dumps len hex codes from byte array mem beginning at index start.
*
*/
public static void dump(final String text, final byte[] mem, final int start, final int len) {
System.out.print(text + " ");
for (int i = start; i < (start + len); i++) {
int j = mem[i];
if (j < 0) {
j += 256;
}
String s = Integer.toHexString(j);
if (s.length() < 2) {
s = "0" + s;
}
System.out.print(s + ",");
}
System.out.println(" ");
}
public static long SBELong(final byte[] b, final int pos) {
final int i = b[pos];
int j = b[pos + 1];
int k = b[pos + 2];
int l = b[pos + 3];
// if (i < 0)
// i += 256;
if (j < 0) {
j += 256;
}
if (k < 0) {
k += 256;
}
if (l < 0) {
l += 256;
}
return ((256 * k) + l) + (65536L * ((256 * i) + j));
}
public static int SBEWord(final byte[] b, final int pos) {
final int i = b[pos];
int k = b[pos + 1];
// if (i < 0)
// i += 256;
if (k < 0) {
k += 256;
}
return ((256 * i) + k);
}
public static int SByte(final byte[] b, final int pos) {
final int i = b[pos];
return (i);
}
public static void setBEFloat(final byte[] b, final int pos, final float f) {
int a = Float.floatToIntBits(f);
b[pos + 3] = (byte) (a & 0xff);
a = a >> 8;
b[pos + 2] = (byte) (a & 0xff);
a = a >> 8;
b[pos + 1] = (byte) (a & 0xff);
a = a >> 8;
b[pos] = (byte) (a & 0xff);
}
public static void setUSBELong(final byte[] b, final int pos, long a) {
b[pos + 3] = (byte) (a & 0xff);
a = a >> 8;
b[pos + 2] = (byte) (a & 0xff);
a = a >> 8;
b[pos + 1] = (byte) (a & 0xff);
a = a >> 8;
b[pos] = (byte) (a & 0xff);
}
public static void setUSBEWord(final byte[] b, final int pos, final int val) {
b[pos] = ((byte) (val / 0x100));
b[pos + 1] = ((byte) (val % 0x100));
}
public static void setUSByte(final byte[] b, final int pos, final int val) {
b[pos] = ((byte) (val & 0xff));
}
public static String strerror(final int code) {
switch (code) {
case RESULT_OK:
return "ok";
case RESULT_MULTIPLE_BITS_NOT_SUPPORTED:
return "the CPU does not support reading a bit block of length<>1";
case RESULT_ITEM_NOT_AVAILABLE:
return "the desired item is not available in the PLC";
case RESULT_ITEM_NOT_AVAILABLE200:
return "the desired item is not available in the PLC (200 family)";
case RESULT_ADDRESS_OUT_OF_RANGE:
return "the desired address is beyond limit for this PLC";
case RESULT_CPU_RETURNED_NO_DATA:
return "the PLC returned a packet with no result data";
case Nodave.RESULT_UNKNOWN_ERROR:
return "the PLC returned an error code not understood by this library";
case Nodave.RESULT_EMPTY_RESULT_ERROR:
return "this result contains no data";
case Nodave.RESULT_EMPTY_RESULT_SET_ERROR:
return "cannot work with an undefined result set";
case Nodave.RESULT_CANNOT_EVALUATE_PDU:
return "cannot evaluate the received PDU";
case Nodave.RESULT_WRITE_DATA_SIZE_MISMATCH:
return "Write data size error";
case Nodave.RESULT_NO_PERIPHERAL_AT_ADDRESS:
return "No data from I/O module";
case Nodave.RESULT_UNEXPECTED_FUNC:
return "Unexpected function code in answer";
case Nodave.RESULT_UNKNOWN_DATA_UNIT_SIZE:
return "PLC responds wit an unknown data type";
case Nodave.RESULT_SHORT_PACKET:
return "Short packet from PLC";
case Nodave.RESULT_TIMEOUT:
return "Timeout when waiting for PLC response";
case 0x8000:
return "function already occupied.";
case 0x8001:
return "not allowed in current operating status.";
case 0x8101:
return "hardware fault.";
case 0x8103:
return "object access not allowed.";
case 0x8104:
return "context is not supported.";
case 0x8105:
return "invalid address.";
case 0x8106:
return "data type not supported.";
case 0x8107:
return "data type not consistent.";
case 0x810A:
return "object does not exist.";
case 0x8500:
return "incorrect PDU size.";
case 0x8702:
return "address invalid.";
case 0xd201:
return "block name syntax error.";
case 0xd202:
return "syntax error function parameter.";
case 0xd203:
return "syntax error block type.";
case 0xd204:
return "no linked block in storage medium.";
case 0xd205:
return "object already exists.";
case 0xd206:
return "object already exists.";
case 0xd207:
return "block exists in EPROM.";
case 0xd209:
return "block does not exist.";
case 0xd20e:
return "no block does not exist.";
case 0xd210:
return "block number too big.";
case 0xd240:
return "unfinished block transfer in progress?";
case 0xd241:
return "protected by password.";
default:
return "no message defined for code: " + code + "!";
}
}
public static byte[] toPLCfloat(final double d) {
final float f = (float) d;
return toPLCfloat(f);
}
public static byte[] toPLCfloat(final float f) {
final int i = Float.floatToIntBits(f);
return bswap_32(i);
}
public static long USBELong(final byte[] b, final int pos) {
int i = b[pos];
int j = b[pos + 1];
int k = b[pos + 2];
int l = b[pos + 3];
// System.out.println(
// pos + " 0:" + i + " 1:" + j + " 2:" + k + " 3:" + l);
if (i < 0) {
i += 256;
}
if (j < 0) {
j += 256;
}
if (k < 0) {
k += 256;
}
if (l < 0) {
l += 256;
}
return ((256 * k) + l) + (65536L * ((256 * i) + j));
}
public static int USBEWord(final byte[] b, final int pos) {
int i = b[pos];
int k = b[pos + 1];
if (i < 0) {
i += 256;
}
if (k < 0) {
k += 256;
}
return ((256 * i) + k);
}
public static int USByte(final byte[] b, final int pos) {
int i = b[pos];
if (i < 0) {
i += 256;
}
return (i);
}
private Nodave() {
// Not needed because of utility class
}
}

View File

@ -0,0 +1,621 @@
/*
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.impl.nodave;
import com.qgs.dc.s7.my.s7connector.api.DaveArea;
import com.qgs.dc.s7.my.s7connector.type.PlcVar;
import com.qgs.dc.s7.my.s7connector.type.TransportSize;
public final class PDU {
/**
* known function codes
*/
public final static byte FUNC_READ = 4;
public final static byte FUNC_WRITE = 5;
public int data;
int dlen;
int error;
int header; // the position of the header;
int hlen;
byte[] mem; //msgOut 也就是 上位机==plc 的channel
public int param; // the position of the parameters;
public int plen;
public int udata;
public int udlen;
/**
* set up the PDU information
*
* mem = msgOutmsgIn
* pos = pdu在整个request请求 的起始位置起始都是7第7个字节
*/
public PDU(final byte[] mem, final int pos) {
this.mem = mem;
this.header = pos;
}
public int addBitVarToReadRequest(final int area, final int DBnum, final int start, final int len) {
final byte pa[] = { 0x12, 0x0a, 0x10, 0x01, /* single bits */
0x00, 0x1A, /* insert length in bytes here */
0x00, 0x0B, /* insert DB number here */
(byte) 0x84, /* change this to real area code */
0x00, 0x00, (byte) 0xC0 /* insert start address in bits */
};
Nodave.setUSBEWord(pa, 4, len);
Nodave.setUSBEWord(pa, 6, DBnum);
Nodave.setUSBELong(pa, 8, start);
Nodave.setUSByte(pa, 8, area);
this.mem[this.param + 1]++;
System.arraycopy(pa, 0, this.mem, this.param + this.plen, pa.length);
this.plen += pa.length;
Nodave.setUSBEWord(this.mem, this.header + 6, this.plen);
return 0;
}
public void addBitVarToWriteRequest(final DaveArea area, final int DBnum, final int start, final int byteCount,
final byte[] buffer) {
final byte da[] = { 0, 3, 0, 0, };
final byte pa[] = { 0x12, 0x0a, 0x10, 0x01, /* single bit */
0, 0, /* insert length in bytes here */
0, 0, /* insert DB number here */
0, /* change this to real area code */
0, 0, 0 /* insert start address in bits */
};
if ((area == DaveArea.TIMER) || (area == DaveArea.COUNTER) || (area == DaveArea.TIMER200)
|| (area == DaveArea.COUNTER200)) {
pa[3] = (byte) area.getCode();
pa[4] = (byte) (((byteCount + 1) / 2) / 0x100);
pa[5] = (byte) (((byteCount + 1) / 2) & 0xff);
} else if ((area == DaveArea.ANALOGINPUTS200) || (area == DaveArea.ANALOGOUTPUTS200)) {
pa[3] = 4;
pa[4] = (byte) (((byteCount + 1) / 2) / 0x100);
pa[5] = (byte) (((byteCount + 1) / 2) & 0xff);
} else {
pa[4] = (byte) (byteCount / 0x100);
pa[5] = (byte) (byteCount & 0xff);
}
pa[6] = (byte) (DBnum / 256);
pa[7] = (byte) (DBnum & 0xff);
pa[8] = (byte) area.getCode();
pa[11] = (byte) (start & 0xff);
pa[10] = (byte) ((start / 0x100) & 0xff);
pa[9] = (byte) (start / 0x10000);
if ((this.dlen % 2) != 0) {
this.addData(da, 1);
}
this.mem[this.param + 1]++;
if (this.dlen > 0) {
final byte[] saveData = new byte[this.dlen];
System.arraycopy(this.mem, this.data, saveData, 0, this.dlen);
System.arraycopy(saveData, 0, this.mem, this.data + pa.length, this.dlen);
}
System.arraycopy(pa, 0, this.mem, this.param + this.plen, pa.length);
this.plen += pa.length;
Nodave.setUSBEWord(this.mem, this.header + 6, this.plen);
this.data = this.param + this.plen;
this.addData(da);
this.addValue(buffer);
}
/**
* Add data after parameters, set dlen as needed. Needs valid header and
* parameters
*/
void addData(final byte[] newData) {
final int appPos = this.data + this.dlen; // append to this position
this.dlen += newData.length;
System.arraycopy(newData, 0, this.mem, appPos, newData.length);
Nodave.setUSBEWord(this.mem, this.header + 8, this.dlen);
}
/**
* Add len bytes of len after parameters from a maybe longer block of bytes.
* Set dlen as needed. Needs valid header and parameters
*/
public void addData(final byte[] newData, final int len) {
final int appPos = this.data + this.dlen; // append to this position
this.dlen += len;
System.arraycopy(newData, 0, this.mem, appPos, len);
Nodave.setUSBEWord(this.mem, this.header + 8, this.dlen);
}
public void addParam(final byte[] pa) {
this.plen = pa.length;
System.arraycopy(pa, 0, this.mem, this.param, this.plen);
//设置S7-Header 里的Parameter Length
Nodave.setUSBEWord(this.mem, this.header + 6, this.plen);
// mem[header + 6] = (byte) (pa.length / 256);
// mem[header + 7] = (byte) (pa.length % 256);
//this.data = 17+2 = 19 ( data 其实就是item项 )
this.data = this.param + this.plen;
this.dlen = 0;
}
/*
* add data in user data. Add a user data header, if not yet present.
*/
public void addUserData(final byte[] da) {
final byte udh[] = { (byte) 0xff, 9, 0, 0 };
if (this.dlen == 0) {
this.addData(udh);
}
this.addValue(da);
}
/**
* Add values after value header in data, adjust dlen and data count. Needs
* valid header,parameters,data,dlen
*/
void addValue(final byte[] values) {
int valCount = (0x100 * this.mem[this.data + 2]) + this.mem[this.data + 3];
if (this.mem[this.data + 1] == 4) { // bit data, length is in bits
valCount += 8 * values.length;
} else if (this.mem[this.data + 1] == 9) { // byte data, length is in
// bytes
valCount += values.length;
} else if(this.mem[this.data+1] == 3){
// for bool
valCount += values.length;
}else {
// for other
}
if (this.udata == 0) {
this.udata = this.data + 4;
}
this.udlen += values.length;
Nodave.setUSBEWord(this.mem, this.data + 2, valCount);
this.addData(values);
}
public int addVarToReadRequest(final DaveArea area, final int DBnum, int start, final int len) {
final byte[] pa = {
0x12, //结构表示一般默认 0x12
0x0a, //此字节往后的字节数
0x10, //Syntax id:S7ANY (0X10) 决定寻址方式0x10表示any-type
0x02, //Transport size 数据类型 ,,看附录7 //todo 这里是有点问题的 不同的数据类型这里的参数是不同的后续自己修改吧
0x00, 0x1A, /* length of data in bytes ( 2 bytes ),就是读几个长度 如有些变量是数组 这里就是数组长度如果是非数组变量那么这里都是1 */
0x00, 0x0B, /* DB块编号 */
(byte) 0x84, // * area code 也就是 DaveArea.DB数据区 */
0x00, 0x00, (byte) 0xC0 /* 0-2bit 是Bit Address3-18bit 是Byte Address */
};
if ((area == DaveArea.ANALOGINPUTS200) || (area == DaveArea.ANALOGOUTPUTS200)) {
pa[3] = 4;
start *= 8; /* bits */
} else if ((area == DaveArea.TIMER) || (area == DaveArea.COUNTER) || (area == DaveArea.TIMER200)
|| (area == DaveArea.COUNTER200)) {
pa[3] = (byte) area.getCode();
} else {
start *= 8; /* 乘以8 相当于把这个偏移量向左移动了 3位 */
}
//把len = 2个字节 并且填充到 pa数组 对应位置pa[4]pa[5]
Nodave.setUSBEWord(pa, 4, len);
Nodave.setUSBEWord(pa, 6, DBnum);
//start 是这个db块的偏移量 //todo 其实这里简单处理了 是分Byte address Bit address的
Nodave.setUSBELong(pa, 8, start);
Nodave.setUSByte(pa, 8, area.getCode());
//因为add了 Var 所以要 ,item count ++ this.param + 1 位置就是Item count位置
this.mem[this.param + 1]++;
// 构建好的 param(Var Item) 加入到 Read Request 字节流里面去
System.arraycopy(pa, 0, this.mem, this.param + this.plen, pa.length);
this.plen += pa.length;
//要读的 var item 也算 参数的 所以也算在param length里面
Nodave.setUSBEWord(this.mem, this.header + 6, this.plen);
/**
* TODO calc length of result. Do not add variable if it would exceed
* max. result length.
*/
return 0;
}
public int addVarToReadRequest(final DaveArea area, final int DBnum, int byteAddress,int bitAddress, final int len,final TransportSize transportSize) {
final byte[] pa = {
0x12, //结构表示一般默认 0x12
0x0a, //此字节往后的字节数
0x10, //Syntax id:S7ANY (0X10) 决定寻址方式0x10表示any-type(把这个认为是固定就行了)
0x02, //Transport size 数据类型 ,,看附录7 //todo 这里是有点问题的 不同的数据类型这里的参数是不同的后续自己修改吧
0x00, 0x1A, /* length of data in bytes ( 2 bytes ),就是读几个长度 如有些变量是数组 这里就是数组长度如果是非数组变量那么这里都是1 */
0x00, 0x0B, /* DB块编号 */
(byte) 0x84, // * area code 也就是 DaveArea.DB数据区 */
0x00, 0x00, (byte) 0xC0 /* 0-2bit 是Bit Address3-18bit 是Byte Address */
};
if ((area == DaveArea.ANALOGINPUTS200) || (area == DaveArea.ANALOGOUTPUTS200)) {
pa[3] = 4;
byteAddress *= 8; /* bits */
} else if ((area == DaveArea.TIMER) || (area == DaveArea.COUNTER) || (area == DaveArea.TIMER200)
|| (area == DaveArea.COUNTER200)) {
pa[3] = (byte) area.getCode();
} else {
///* 乘以8 相当于把这个偏移量向左移动了 3位,,向左移动3位的原因是为了 给bitAddress 让路 */
byteAddress *= 8;
// bitAddress 赋予后三位
byteAddress += bitAddress;
}
pa[3] = Byte.valueOf(Short.toString(transportSize.getCode()));
//把len = 2个字节 并且填充到 pa数组 对应位置pa[4]pa[5]
Nodave.setUSBEWord(pa, 4, len);
Nodave.setUSBEWord(pa, 6, DBnum);
//start 是这个db块的偏移量 //todo 其实这里简单处理了 是分Byte address Bit address的
Nodave.setUSBELong(pa, 8, byteAddress);
Nodave.setUSByte(pa, 8, area.getCode());
//因为add了 Var 所以要 ,item count ++ this.param + 1 位置就是Item count位置
this.mem[this.param + 1]++;
// 构建好的 param(Var Item) 加入到 Read Request 字节流里面去
System.arraycopy(pa, 0, this.mem, this.param + this.plen, pa.length);
this.plen += pa.length;
//要读的 var item 也算 参数的 所以也算在param length里面
Nodave.setUSBEWord(this.mem, this.header + 6, this.plen);
/**
* TODO calc length of result. Do not add variable if it would exceed
* max. result length.
*/
return 0;
}
public void addVarToWriteRequest(final DaveArea area, final int DBnum, int byteOffset, int bitOffset, final int byteCount,
final byte[] buffer, PlcVar var) {
final byte da[] = { 0, 4, 0, 0, };
final byte pa[] = {
0x12,
0x0a,
0x10,
0x02, //Transport size 数据类型 ,,看附录7 //todo 这里是有点问题的 不同的数据类型这里的参数是不同的后续自己修改吧
/* unit (for count?, for consistency?) byte */
0, 0, /* length in bytes */
0, 0, /* DB number */
0, /* area code */
0, 0, 0 /* start address in bits */
};
if ((area == DaveArea.TIMER) || (area == DaveArea.COUNTER) || (area == DaveArea.TIMER200)
|| (area == DaveArea.COUNTER200)) {
pa[3] = (byte) area.getCode();
pa[4] = (byte) (((byteCount + 1) / 2) / 0x100);
pa[5] = (byte) (((byteCount + 1) / 2) & 0xff);
} else if ((area == DaveArea.ANALOGINPUTS200) || (area == DaveArea.ANALOGOUTPUTS200)) {
pa[3] = 4;
pa[4] = (byte) (((byteCount + 1) / 2) / 0x100);
pa[5] = (byte) (((byteCount + 1) / 2) & 0xff);
} else {
pa[4] = (byte) (byteCount / 0x100);
pa[5] = (byte) (byteCount & 0xff);
}
pa[6] = (byte) (DBnum / 256);
pa[7] = (byte) (DBnum & 0xff);
pa[8] = (byte) (area.getCode());
//设置parameter item transportsize
pa[3] = (byte) var.getTransportSize().getCode();
///* 乘以8 相当于把这个偏移量向左移动了 3位,,向左移动3位的原因是为了 给bitAddress 让路 */
byteOffset *= 8;
// bitAddress 赋予后三位
byteOffset += bitOffset;
pa[11] = (byte) (byteOffset & 0xff);
pa[10] = (byte) ((byteOffset / 0x100) & 0xff);
pa[9] = (byte) (byteOffset / 0x10000);
if ((this.dlen % 2) != 0) {
this.addData(da, 1);
}
this.mem[this.param + 1]++;
if (this.dlen > 0) {
final byte[] saveData = new byte[this.dlen];
System.arraycopy(this.mem, this.data, saveData, 0, this.dlen);
System.arraycopy(saveData, 0, this.mem, this.data + pa.length, this.dlen);
}
System.arraycopy(pa, 0, this.mem, this.param + this.plen, pa.length);
this.plen += pa.length;
Nodave.setUSBEWord(this.mem, this.header + 6, this.plen);
this.data = this.param + this.plen;
this.addData(da);
// keyItem 的transportsize 赋值
this.mem[this.data + 1] = (byte) var.getTransportSize().getDataTransportSize().getValue();
this.addValue(buffer);
}
public void addVarToWriteRequest(final DaveArea area, final int DBnum, int start, final int byteCount,
final byte[] buffer) {
final byte da[] = { 0, 4, 0, 0, };
final byte pa[] = { 0x12, 0x0a, 0x10, 0x02,
/* unit (for count?, for consistency?) byte */
0, 0, /* length in bytes */
0, 0, /* DB number */
0, /* area code */
0, 0, 0 /* start address in bits */
};
if ((area == DaveArea.TIMER) || (area == DaveArea.COUNTER) || (area == DaveArea.TIMER200)
|| (area == DaveArea.COUNTER200)) {
pa[3] = (byte) area.getCode();
pa[4] = (byte) (((byteCount + 1) / 2) / 0x100);
pa[5] = (byte) (((byteCount + 1) / 2) & 0xff);
} else if ((area == DaveArea.ANALOGINPUTS200) || (area == DaveArea.ANALOGOUTPUTS200)) {
pa[3] = 4;
pa[4] = (byte) (((byteCount + 1) / 2) / 0x100);
pa[5] = (byte) (((byteCount + 1) / 2) & 0xff);
} else {
pa[4] = (byte) (byteCount / 0x100);
pa[5] = (byte) (byteCount & 0xff);
}
pa[6] = (byte) (DBnum / 256);
pa[7] = (byte) (DBnum & 0xff);
pa[8] = (byte) (area.getCode());
start *= 8; /* number of bits */
pa[11] = (byte) (start & 0xff);
pa[10] = (byte) ((start / 0x100) & 0xff);
pa[9] = (byte) (start / 0x10000);
if ((this.dlen % 2) != 0) {
this.addData(da, 1);
}
this.mem[this.param + 1]++;
if (this.dlen > 0) {
final byte[] saveData = new byte[this.dlen];
System.arraycopy(this.mem, this.data, saveData, 0, this.dlen);
System.arraycopy(saveData, 0, this.mem, this.data + pa.length, this.dlen);
}
System.arraycopy(pa, 0, this.mem, this.param + this.plen, pa.length);
this.plen += pa.length;
Nodave.setUSBEWord(this.mem, this.header + 6, this.plen);
this.data = this.param + this.plen;
//下面两个方法是拼接 keyItemvalueItem
this.addData(da);
this.addValue(buffer);
}
/**
* construct a write request for a single item in PLC memory.
*/
/*
* void constructWriteRequest( int area, int DBnum, int start, int len,
* byte[] buffer) { byte pa[] = new byte[14]; byte da[] = { 0, 4, 0, 0 };
* pa[0] = PDU.FUNC_WRITE; pa[1] = (byte) 0x01; pa[2] = (byte) 0x12; pa[3] =
* (byte) 0x0a; pa[4] = (byte) 0x10; pa[5] = (byte) 0x02;
*
* Nodave.setUSBEWord(pa, 6, len); Nodave.setUSBEWord(pa, 8, DBnum);
* Nodave.setUSBELong(pa, 10, 8 * start); // the bit address
* Nodave.setUSByte(pa, 10, area); initHeader(1); addParam(pa); addData(da);
* addValue(buffer); if ((Nodave.Debug & Nodave.DEBUG_PDU) != 0) { dump(); }
* }
*/
/**
* display information about a PDU
*/
public void dump() {
Nodave.dump("PDU header ", this.mem, this.header, this.hlen);
System.out.println("plen: " + this.plen + " dlen: " + this.dlen);
Nodave.dump("Parameter", this.mem, this.param, this.plen);
if (this.dlen > 0) {
Nodave.dump("Data ", this.mem, this.data, this.dlen);
}
if (this.udlen > 0) {
Nodave.dump("result Data ", this.mem, this.udata, this.udlen);
}
}
public int getError() {
return this.error;
}
/**
* return the function code of the PDU
*/
public int getFunc() {
return Nodave.USByte(this.mem, this.param + 0);
}
/*
* typedef struct { uc P; // allways 0x32 uc type; // a type? type 2 and 3
* headers are two bytes longer. uc a,b; // currently unknown us number; //
* Number, can be used to identify answers corresponding to requests us
* plen; // length of parameters which follow this header us dlen; // length
* of data which follows the parameters uc x[2]; // only present in type 2
* and 3 headers. This may contain error information. } PDUHeader;
*/
/**
* return the number of the PDU
*/
public int getNumber() {
return Nodave.USBEWord(this.mem, this.header + 4);
}
/**
* reserve space for the header of a new PDU
*/
public void initHeader(final int type) {
//注意 这里的type 就是下面的 MSG Type消息类型
if ((type == 2) || (type == 3)) {
this.hlen = 12;
} else {
this.hlen = 10;
}
//给S7-Header 初始化 全部初始化为0
for (int i = 0; i < this.hlen; i++) {
//mem 代表msgOut
this.mem[this.header + i] = 0;
}
//this.param param的起始位置是在 initHeader 被初始化的7+10 = 17= this.param (因为数组从0开始所以17这个位置刚好是param的起始位置)
this.param = this.header + this.hlen;
//protocol id 协议id 默认0x32
this.mem[this.header] = (byte) 0x32;
//MSG Type1byte: 消息类型 :
//0x01工作请求Job Request;
//0x02确认Ack主要是由设备请求不携带数据;
//0x03响应数据Ack-Data响应0x01的请求;
//0x07自定义数据Userdata扩展协议类型
this.mem[this.header + 1] = (byte) type;
//dlen = data length ( 报文中存在两个字节 )
this.dlen = 0;
//plen = param length ( 报文中存在两个字节 )
this.plen = 0;
//udlen = protocol data unit reference length
this.udlen = 0;
this.data = 0;
//udata = protocol data unit reference
this.udata = 0;
}
public void initReadRequest() {
final byte pa[] = new byte[2];
pa[0] = PDU.FUNC_READ;
pa[1] = (byte) 0x00;
this.initHeader(1);
this.addParam(pa);
}
/**
* prepare a read request with no item.
*/
public void prepareReadRequest() {
final byte pa[] = new byte[2];
pa[0] = PDU.FUNC_READ;
pa[1] = (byte) 0x00;
this.initHeader(1);
this.addParam(pa);
}
/**
* prepare a write request with no item.
*/
public void prepareWriteRequest() {
final byte pa[] = new byte[2];
pa[0] = PDU.FUNC_WRITE;
pa[1] = (byte) 0x00;
this.initHeader(1);
this.addParam(pa);
}
/**
* set the number of the PDU
*/
public void setNumber(final int n) {
Nodave.setUSBEWord(this.mem, this.header + 4, n);
}
/**
* Setup a PDU instance to reflect the structure of data present in the
* memory area given to initHeader. Needs valid header.
*/
public int setupReceivedPDU() {
int res = Nodave.RESULT_CANNOT_EVALUATE_PDU; // just assume the worst
//this.mem[this.header + 1]( ROSCTR ) 就是MSG TYPE =2 (确认Ack主要是由设备请求不携带数据) ;;MSG TYPE =3 (响应数据Ack-Data响应0x01的请求)
if ((this.mem[this.header + 1] == 2) || (this.mem[this.header + 1] == 3)) {
this.hlen = 12;
//this.header + 10 就是 Error class看附录3/code看附录4
//todo this.header + 10 其实是一个字节一个字节的 不能像下面这样读两个字节其实这种也行的因为这样就能够 同时判断两个信息 是否同时为0如果同时为0 有一个不为0 就代表都不ok
res = Nodave.USBEWord(this.mem, this.header + 10);
} else {
this.error = 0;
this.hlen = 10;
res = 0;
}
//外部初始化的 header == 74字节TPKT + 3个字节COTP
//this.param = 7+12 = 19;
this.param = this.header + this.hlen;
//读取S7-Header 里面的Param-length2个字节转为word ok
this.plen = Nodave.USBEWord(this.mem, this.header + 6);
//this.param(19 固定的如果是ACK) + this.plen(2 是固定的应为就两个参数)
this.data = this.param + this.plen;
//读取S7-Header 里面的Data-length2个字节转为word ok
this.dlen = Nodave.USBEWord(this.mem, this.header + 8);
this.udlen = 0;
this.udata = 0;
return res;
}
int testResultData() {
int res = Nodave.RESULT_CANNOT_EVALUATE_PDU; // just assume the worst
//当响应成功的情况 //todo here read
if ((this.mem[this.data] == (byte) 255) && (this.dlen > 4)) {
res = Nodave.RESULT_OK;
//udata 代表的是read response 里面 Data=>Item=>n个字节的实际数据bytes
this.udata = this.data + 4;
// udlen=data[2]*0x100+data[3];
//udlen = udata length 就是实际承载value 的byte[] 长度
this.udlen = Nodave.USBEWord(this.mem, this.data + 2);
// ==4 是走byte流通过转义字节来 来解码真是value 看附录8
if (this.mem[this.data + 1] == 4) {
//注意 这里的数据响应长度 单位 是bit 不是 byte因为 用bit 表示才准确比如像bit类型 他并不需要一个字节
//往右移3位就相当于 除以8 bit 转成 byte //todo 这个需要和read request关联的后续如果要开发点位的功能 再说
this.udlen >>= 3; /* len is in bits, adjust */
} else if (this.mem[this.data + 1] == 9) {
/* len is already in bytes, ok */
} else if (this.mem[this.data + 1] == 3) {
/* len is in bits, but there is a byte per result bit, ok */
} else {
res = Nodave.RESULT_UNKNOWN_DATA_UNIT_SIZE;
}
} else {
//当响应不成功的情况
//this.mem[this.data] 是return code 成功或者失败
res = this.mem[this.data];
}
return res;
}
public int testPGReadResult() {
if (this.mem[this.param] != 0) {
return Nodave.RESULT_UNEXPECTED_FUNC;
}
return this.testResultData();
};
int testReadResult() {
if (this.mem[this.param] != FUNC_READ) {
return Nodave.RESULT_UNEXPECTED_FUNC;
}
return this.testResultData();
}
int testWriteResult() {
int res = Nodave.RESULT_CANNOT_EVALUATE_PDU;
if (this.mem[this.param] != FUNC_WRITE) {
return Nodave.RESULT_UNEXPECTED_FUNC;
}
if ((this.mem[this.data] == 255)) {
res = Nodave.RESULT_OK;
} else {
res = this.mem[this.data];
}
return res;
}
}

View File

@ -0,0 +1,82 @@
/*
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.impl.nodave;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public final class PLCinterface {
InputStream in;
int localMPI; // the adapter's MPI address
String name;
OutputStream out;
int protocol; // The kind of transport used on this interface.
int wp, rp;
public PLCinterface(final OutputStream out, final InputStream in, final String name, final int localMPI,
final int protocol) {
this.init(out, in, name, localMPI, protocol);
}
public void init(final OutputStream oStream, final InputStream iStream, final String name, final int localMPI,
final int protocol) {
this.out = oStream;
this.in = iStream;
this.name = name;
this.localMPI = localMPI;
this.protocol = protocol;
}
public int read(final byte[] b, int start, int len) {
int res;
try {
int retry = 0;
while ((this.in.available() <= 0) && (retry < 500)) {
try {
if (retry > 0) {
Thread.sleep(1);
}
retry++;
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
res = 0;
while ((this.in.available() > 0) && (len > 0)) {
res = this.in.read(b, start, len);
start += res;
len -= res;
}
return res;
} catch (final IOException e) {
e.printStackTrace();
return 0;
}
}
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) {
System.err.println("Interface.write: " + e);
return 0;
}
}
}

View File

@ -0,0 +1,28 @@
/*
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.impl.nodave;
/**
* @author Thomas Hergenhahn
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public final class Result {
public int bufferStart;
public int error;
public int length;
}

View File

@ -0,0 +1,41 @@
/*
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.impl.nodave;
/**
* @author Thomas Hergenhahn
*
*/
public final class ResultSet {
private int errorState, numResults;
public Result[] results;
public int getErrorState() {
return this.errorState;
}
public int getNumResults() {
return this.numResults;
};
public void setErrorState(final int error) {
this.errorState = error;
}
public void setNumResults(final int nr) {
this.numResults = nr;
};
}

View File

@ -0,0 +1,498 @@
/*
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.impl.nodave;
import com.qgs.dc.s7.my.s7connector.api.DaveArea;
import com.qgs.dc.s7.my.s7connector.type.PlcVar;
import com.qgs.dc.s7.my.s7connector.type.TransportSize;
import java.util.concurrent.Semaphore;
/**
* This class comprises the variables and methods common to connections to an S7
* PLC regardless of the type of transport.
*
* @author Thomas Hergenhahn
*/
public abstract class S7Connection {
static int tmo_normal = 150;
int answLen; // length of last message
/**
* position in result data, incremented when variables are extracted without
* position
*/
int dataPointer;
PLCinterface iface; // pointer to used interface
public int maxPDUlength; //第二次握手里 PDU Length(2个字节)
public byte messageNumber = 0;
//msgIn msgOut 同理
public byte[] msgIn;
//msgOut 确实是当前TcpConnection共用的但不影响每次write因为每次发送都会修改msgOut
public byte[] msgOut;
public int packetNumber = 0; // packetNumber in transport layer
public int PDUstartIn;
public int PDUstartOut;
PDU rcvdPDU;
public Semaphore semaphore;
/**
* absolute begin of result data
*/
int udata;
public S7Connection(final PLCinterface ifa) {
this.iface = ifa;
this.msgIn = new byte[Nodave.MAX_RAW_LEN];
this.msgOut = new byte[Nodave.MAX_RAW_LEN];
this.PDUstartIn = 0;
this.PDUstartOut = 0;
this.semaphore = new Semaphore(1);
}
abstract public int exchange(PDU p1);
// int numResults;
/*
* class Result { int error; byte[] data; }
*/
/*
* Read a predefined set of values from the PLC. Return ok or an error state
* If a buffer pointer is provided, data will be copied into this buffer. If
* 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) {
PDU p2;
int errorState;
errorState = this.exchange(p);
p2 = new PDU(this.msgIn, this.PDUstartIn);
p2.setupReceivedPDU();
/*
* if (p2.udlen == 0) { dataPointer = 0; answLen = 0; return
* Nodave.RESULT_CPU_RETURNED_NO_DATA; }
*/
final ResultSet rs = new ResultSet();
if (p2.mem[p2.param + 0] == PDU.FUNC_READ) {
int numResults = p2.mem[p2.param + 1];
// System.out.println("Results " + numResults);
rs.results = new Result[numResults];
int pos = p2.data;
for (int i = 0; i < numResults; i++) {
final Result r = new Result();
r.error = Nodave.USByte(p2.mem, pos);
if (r.error == 255) {
final int type = Nodave.USByte(p2.mem, pos + 1);
int len = Nodave.USBEWord(p2.mem, pos + 2);
r.error = 0;
// System.out.println("Raw length " + len);
if (type == 4) {
len /= 8;
} else if (type == 3) {
; // length is ok
}
// System.out.println("Byte length " + len);
// r.data = new byte[len];
// System.arraycopy(p2.mem, pos + 4, r.data, 0, len);
// Nodave.dump("Result " + i + ":", r.data, 0, len);
r.bufferStart = pos + 4;
pos += len;
if ((len % 2) == 1) {
pos++;
}
} else {
System.out.println("Error " + r.error);
}
pos += 4;
rs.results[i] = r;
}
numResults = p2.mem[p2.param + 1];
rs.setNumResults(numResults);
this.dataPointer = p2.udata;
this.answLen = p2.udlen;
// }
} else {
errorState |= 2048;
}
this.semaphore.release();
rs.setErrorState(errorState);
return rs;
}
public int getBYTE() {
this.dataPointer += 1;
return Nodave.SByte(this.msgIn, this.dataPointer - 1);
}
public int getBYTE(final int pos) {
return Nodave.SByte(this.msgIn, this.udata + pos);
}
public int getCHAR() {
this.dataPointer += 1;
return Nodave.SByte(this.msgIn, this.dataPointer - 1);
}
public int getCHAR(final int pos) {
return Nodave.SByte(this.msgIn, this.udata + pos);
}
/**
* get an signed 32bit value from the current position in result bytes
*/
public long getDINT() {
this.dataPointer += 4;
return Nodave.SBELong(this.msgIn, this.dataPointer - 4);
}
/**
* get an signed 32bit value from the specified position in result bytes
*/
public long getDINT(final int pos) {
return Nodave.SBELong(this.msgIn, this.udata + pos);
}
/**
* get an unsigned 32bit value from the specified position in result bytes
*/
public long getDWORD(final int pos) {
// System.out.println("getDWORD pos " + pos);
return Nodave.USBELong(this.msgIn, this.udata + pos);
}
/**
* get a float value from the current position in result bytes
*/
public float getFloat() {
this.dataPointer += 4;
return Nodave.BEFloat(this.msgIn, this.dataPointer - 4);
}
/*
* The following methods are here to give Siemens users their usual data
* types:
*/
/**
* get a float value from the specified position in result bytes
*/
public float getFloat(final int pos) {
// System.out.println("getFloat pos " + pos);
return Nodave.BEFloat(this.msgIn, this.udata + pos);
}
public int getINT() {
this.dataPointer += 2;
return Nodave.SBEWord(this.msgIn, this.dataPointer - 2);
}
public int getINT(final int pos) {
return Nodave.SBEWord(this.msgIn, this.udata + pos);
}
public int getPPIresponse() {
return 0;
}
/*
* public void sendYOURTURN() { }
*/
public int getResponse() {
return 0;
}
public int getS16(final int pos) {
return Nodave.SBEWord(this.msgIn, this.udata + pos);
}
public long getS32(final int pos) {
return Nodave.SBELong(this.msgIn, this.udata + pos);
}
public int getS8(final int pos) {
return Nodave.SByte(this.msgIn, this.udata + pos);
}
/**
* get an unsigned 32bit value from the current position in result bytes
*/
public long getU32() {
this.dataPointer += 4;
return Nodave.USBELong(this.msgIn, this.dataPointer - 4);
}
public int getUS16(final int pos) {
return Nodave.USBEWord(this.msgIn, this.udata + pos);
}
public long getUS32(final int pos) {
return Nodave.USBELong(this.msgIn, this.udata + pos);
}
public int getUS8(final int pos) {
return Nodave.USByte(this.msgIn, this.udata + pos);
}
/**
* get an unsigned 16bit value from the current position in result bytes
*/
public int getWORD() {
this.dataPointer += 2;
return Nodave.USBEWord(this.msgIn, this.dataPointer - 2);
}
/**
* get an unsigned 16bit value from the specified position in result bytes
*/
public int getWORD(final int pos) {
return Nodave.USBEWord(this.msgIn, this.udata + pos);
}
/*
* build the PDU for a PDU length negotiation
* 构建第二次握手
*/
public int negPDUlengthRequest() {
int res;
final PDU p = new PDU(this.msgOut, this.PDUstartOut);
//S7-Param 构造
final byte pa[] = {
(byte) 0xF0, //Functionstep Communication
0, //Reserved
0x00, //Max AmQ (parallel jobs with ack) calling也就是说这个connection每次只能调用1次 ;;2个字节
0x01,
0x00, //Max AmQ (parallel jobs with ack) called也就是说这个connection每次只能被调用1次 ;; 2个字节
0x01,
0x03, //PDU Length (2个字节) //这里是960 一般是240/480/960 主要是和CPU型号有关的
(byte) 0xC0,
};
//初始化 S7 Header长度
p.initHeader(1);
//加载 param 参数到 请求里面去
p.addParam(pa);
//构建第二次握手cotp 部分
res = this.exchange(p);
if (res != 0) {
return res;
}
//构建第二次握手 S7COMM 的接收容器
final PDU p2 = new PDU(this.msgIn, this.PDUstartIn);
res = p2.setupReceivedPDU();
if (res != 0) {
return res;
}
this.maxPDUlength = Nodave.USBEWord(this.msgIn, p2.param +6);
return res;
}
//reutrn
// 0 代表成功
// 1 代表失败
// 异常 就会抛出异常
public int readBytes(final DaveArea area, final int DBnum, final int start, final int len, final byte[] buffer) {
int res = 0;
try {
//信号量 加锁防止多处并发访问
this.semaphore.acquire();
} catch (final InterruptedException e) {
e.printStackTrace();
}
//构建 request PDU
final PDU p1 = new PDU(this.msgOut, this.PDUstartOut);
p1.initReadRequest();
p1.addVarToReadRequest(area, DBnum, start, len);
//发送read var request
res = this.exchange(p1);
if (res != Nodave.RESULT_OK) {
this.semaphore.release();
return res;
}
//构建 response PDU 注意这里的msgIn 就是接收到的 plc=上位机 的字节流
final PDU p2 = new PDU(this.msgIn, this.PDUstartIn);
// 这里配置received PUD参数
res = p2.setupReceivedPDU();
if (res != Nodave.RESULT_OK) {
this.semaphore.release();
return res;
}
res = p2.testReadResult();
if (res != Nodave.RESULT_OK) {
this.semaphore.release();
return res;
}
if (p2.udlen == 0) {
this.semaphore.release();
return Nodave.RESULT_CPU_RETURNED_NO_DATA;
}
/*
* copy to user buffer and setup internal buffer pointers:
* 判断外部确实 外部是否真实传 buffer 这个字节容器进来
*/
if (buffer != null) {
System.arraycopy(p2.mem, p2.udata, buffer, 0, p2.udlen);
}
this.dataPointer = p2.udata;
this.udata = p2.udata;
this.answLen = p2.udlen;
this.semaphore.release();
return res;
}
//reutrn
// 0 代表成功
// 1 代表失败
// 异常 就会抛出异常
//start == byteAddress bitStart ==
public int readBytes(final DaveArea area, final int DBnum, final int byteAddress, final int bitAddress, final int len, final byte[] buffer, TransportSize transportSize) {
int res = 0;
try {
//信号量 加锁防止多处并发访问
this.semaphore.acquire();
} catch (final InterruptedException e) {
e.printStackTrace();
}
//构建 request PDU
final PDU p1 = new PDU(this.msgOut, this.PDUstartOut);
p1.initReadRequest();
p1.addVarToReadRequest(area, DBnum, byteAddress,bitAddress, len,transportSize);
//发送read var request
res = this.exchange(p1);
if (res != Nodave.RESULT_OK) {
this.semaphore.release();
return res;
}
//构建 response PDU 注意这里的msgIn 就是接收到的 plc=上位机 的字节流
final PDU p2 = new PDU(this.msgIn, this.PDUstartIn);
// 这里配置received PUD参数
res = p2.setupReceivedPDU();
if (res != Nodave.RESULT_OK) {
this.semaphore.release();
return res;
}
res = p2.testReadResult();
if (res != Nodave.RESULT_OK) {
this.semaphore.release();
return res;
}
if (p2.udlen == 0) {
this.semaphore.release();
return Nodave.RESULT_CPU_RETURNED_NO_DATA;
}
/*
* copy to user buffer and setup internal buffer pointers:
* 判断外部确实 外部是否真实传 buffer 这个字节容器进来
*/
if (buffer != null) {
System.arraycopy(p2.mem, p2.udata, buffer, 0, p2.udlen);
}
this.dataPointer = p2.udata;
this.udata = p2.udata;
this.answLen = p2.udlen;
this.semaphore.release();
return res;
}
public int sendMsg(final PDU p) {
return 0;
}
public void sendRequestData(final int alt) {
}
public int useResult(final ResultSet rs, final int number) {
System.out.println("rs.getNumResults: " + rs.getNumResults() + " number: " + number);
if (rs.getNumResults() > number) {
this.dataPointer = rs.results[number].bufferStart;
return 0;
// udata=rs.results[number].bufferStart;
}
return -33;
};
/*
* Write len bytes to PLC memory area "area", data block DBnum.
*/
public int writeBytes(final DaveArea area, final int DBnum, final int start, final int len, final byte[] buffer) {
int errorState = 0;
this.semaphore.release();
final PDU p1 = new PDU(this.msgOut, this.PDUstartOut);
// p1.constructWriteRequest(area, DBnum, start, len, buffer);
p1.prepareWriteRequest();
p1.addVarToWriteRequest(area, DBnum, start, len, buffer);
errorState = this.exchange(p1);
if (errorState == 0) {
final PDU p2 = new PDU(this.msgIn, this.PDUstartIn);
p2.setupReceivedPDU();
if (p2.mem[p2.param + 0] == PDU.FUNC_WRITE) {
if (p2.mem[p2.data + 0] == (byte) 0xFF) {
this.semaphore.release();
return 0;
}
} else {
errorState |= 4096;
}
}
this.semaphore.release();
return errorState;
}
public int writeBytes(final DaveArea area, final int DBnum, final int byteOffset, final int bitOffset, final int len, final byte[] buffer,final PlcVar var) {
int errorState = 0;
this.semaphore.release();
final PDU p1 = new PDU(this.msgOut, this.PDUstartOut);
// p1.constructWriteRequest(area, DBnum, start, len, buffer);
p1.prepareWriteRequest();
p1.addVarToWriteRequest(area, DBnum, byteOffset,bitOffset, len, buffer,var);
errorState = this.exchange(p1);
if (errorState == 0) {
final PDU p2 = new PDU(this.msgIn, this.PDUstartIn);
p2.setupReceivedPDU();
if (p2.mem[p2.param + 0] == PDU.FUNC_WRITE) {
if (p2.mem[p2.data + 0] == (byte) 0xFF) {
this.semaphore.release();
return 0;
}
} else {
errorState |= 4096;
}
}
this.semaphore.release();
return errorState;
}
}

View File

@ -0,0 +1,190 @@
/*
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.impl.nodave;
import com.qgs.dc.s7.my.s7connector.enmuc.S7Client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Class TCPConnection.
*/
public final class TCPConnection extends S7Connection {
/** The rack. */
int rack;
/** The slot. */
int slot;
private static final Logger logger = LoggerFactory.getLogger(TCPConnection.class);
/**
* Instantiates a new TCP connection.
*
* @param ifa
* the plc interface
* @param rack
* the rack
* @param slot
* the slot
*/
public TCPConnection(final PLCinterface ifa, final int rack, final int slot) {
super(ifa);
this.rack = rack;
this.slot = slot;
this.PDUstartIn = 7;
//PDUstartOut = 7 是因为在Header部分之前 还有TPKT+COTP报文并且这两个报文长度加起来一共7个字节
this.PDUstartOut = 7;
}
/**
* We have our own connectPLC(), but no disconnect() Open connection to a
* PLC. This assumes that dc is initialized by daveNewConnection and is not
* yet used. (or reused for the same PLC ?)
*
* @return the int
*/
public int connectPLC() {
//COTP包 字节流是上位机 和plc进行的第一次握手
final byte[] b4 = { //b4.length = 18
(byte) 0x11, //16进制当前字节以后的字节数
(byte) 0xE0, //PDU type,连接请求附录一
(byte) 0x00, (byte) 0x00, //DST reference
(byte) 0x00, (byte) 0x01, //SRC reference
(byte) 0x00, //固定Class Extended formats No explilcti flow control
(byte) 0xC1, //parameter-codedst-tsap 上位机
(byte) 0x02, //parameter-length
(byte) 0x01, //Source rack 机架
(byte) 0x00, //Source slot 槽位号
(byte) 0xC2, //parameter-codedst-tsap PLC
(byte) 0x02, //parameter-length
(byte) 0x01, //Destination rack 机架 (Des rack 指的就是 plc rack) (msgOut[17])
(byte) 0x02, //Destination slot 槽位号 (Des slot 指的就是 plc slot) (msgOut[18])
(byte) 0xC0, //parameter-codetpdu-size
(byte) 0x01, //parameter-length
(byte) 0x09 //TPDU size
};
// b4 这个数组从0开始位置 放到 this.msgOut 这个数组 第四个位置后面 b4.length 个长度
//之所以目的位置4 是因为前面4个字节是TPKT的内容(第一次握手)
// msgOut 是这个socketChannel 往外写的 bytes上位机 => plc msgIn 是指这个socketChanne 外部往里写的bytes上位机 <= plc
System.arraycopy(b4, 0, this.msgOut, 4, b4.length);
this.msgOut[17] = (byte) (this.rack + 1);
this.msgOut[18] = (byte) this.slot;
this.sendISOPacket(b4.length);
this.readISOPacket();
/*
* PDU p = new PDU(msgOut, 7); p.initHeader(1); p.addParam(b61);
* exchange(p); return (0);
*/
return this.negPDUlengthRequest();
}
/**
* 构建S7COMM 的COTP部分 3个字节第二次握手
*
* return
* 1 代表失败
* 0 代表成功
* */
@Override
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位就代表传输单元序号)
int writeRes = this.sendISOPacket(3 + p1.hlen + p1.plen + p1.dlen); //这里 TPKT COTP字节一起发送的
//这里读到的 ISO_Back_Packet 是暂存在msgIn 这暂存区的后面可以直接拿里面的数据来构建 ISO_Back_Packet 的PDU
int readRes = this.readISOPacket();
if(writeRes!=0 && readRes!=0){
return 0;
}else {
logger.info("exchange 出现了问题:① writeRes:"+writeRes+"② readRes:"+readRes);
return 1 ;
}
}
/**
* Read iso packet.
*
* @return the int
* 0 ==> 不成功
* 其他 ==> 成功
*/
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]; //读取字节数
res += this.iface.read(this.msgIn, 4, len);
} else {
return 0;
}
return res;
}
// public static void main(String[] args) {
// byte[] s = new byte[2];
// List<Byte> a = new ArrayList<>();
// a.add(Byte.valueOf("1"));
// a.add(Byte.valueOf("2"));
// read(a);
// System.out.println(a.get(0));
// System.out.println(a.get(1));
// }
// public static int read(byte[] ss){
// ss[0] = 1;
// ss[1] = 1;
// return 0;
// }
// public static int read(List<Byte> ss){
// ss.set(0,Byte.valueOf("3"));
// ss.set(1,Byte.valueOf("4"));
// return 0;
// }
/**
* Send iso packet.
*
* @param size
* the size
* @return the int
* 1 ==> 成功
* 0 ==> 不成功
*/
protected int sendISOPacket(int size) {
size += 4;
//下面包装的 是TPKT 字节包第一次握手
this.msgOut[0] = (byte) 0x03; //Version 默认版本3
this.msgOut[1] = (byte) 0x0; //保留字节 默认0
//这两个字节代表请求字节数通常说都是固定的22因为 TPKT字节数+COTP字节数 = 22
this.msgOut[2] = (byte) (size / 0x100);//高位字节 除以8 取整 就放在高字节
this.msgOut[3] = (byte) (size % 0x100);//低位字节 取余8 就放在低字节
/*
* if (messageNumber == 0) { messageNumber = 1; msgOut[11] = (byte)
* ((messageNumber + 1) & 0xff); messageNumber++; messageNumber &= 0xff;
* //!! }
*/
//像这种 write 和read 都是 传的是指针里面数据消费掉了外部就没了
int writeRes = this.iface.write(this.msgOut, 0, size);
return writeRes;
}
}

View File

@ -0,0 +1,178 @@
/*
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.impl.serializer;
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.S7Serializer;
import com.qgs.dc.s7.my.s7connector.exception.S7Exception;
import com.qgs.dc.s7.my.s7connector.impl.serializer.parser.BeanEntry;
import com.qgs.dc.s7.my.s7connector.impl.serializer.parser.BeanParseResult;
import com.qgs.dc.s7.my.s7connector.impl.serializer.parser.BeanParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Array;
/**
* The Class S7Serializer is responsible for serializing S7 TCP Connection
*/
public final class S7SerializerImpl implements S7Serializer {
/** Local Logger. */
private static final Logger logger = LoggerFactory.getLogger(S7SerializerImpl.class);
/**
* Extracts bytes from a buffer.
*
* @param <T>
* the generic type
* @param beanClass
* the bean class
* @param buffer
* the buffer
* @param byteOffset
* the byte offset
* @return the t
*/
public static <T> T extractBytes(final Class<T> beanClass, final byte[] buffer, final int byteOffset) {
logger.trace("Extracting type {} from buffer with size: {} at offset {}", beanClass.getName(), buffer.length,
byteOffset);
try {
final T obj = beanClass.newInstance();
final BeanParseResult result = BeanParser.parse(beanClass);
for (final BeanEntry entry : result.entries) {
Object value = null;
if (entry.isArray) {
value = Array.newInstance(entry.type, entry.arraySize);
for (int i = 0; i < entry.arraySize; i++) {
final Object component = entry.serializer.extract(entry.type, buffer,
entry.byteOffset + byteOffset + (i * entry.s7type.getByteSize()),
entry.bitOffset + (i * entry.s7type.getBitSize()));
Array.set(value, i, component);
}
} else {
value = entry.serializer.extract(entry.type, buffer, entry.byteOffset + byteOffset,
entry.bitOffset);
}
entry.field.set(obj, value);
}
return obj;
} catch (final Exception e) {
throw new S7Exception("extractBytes", e);
}
}
/**
* Inserts the bytes to the buffer.
*
* @param bean
* the bean
* @param buffer
* the buffer
* @param byteOffset
* the byte offset
*/
public static void insertBytes(final Object bean, final byte[] buffer, final int byteOffset) {
logger.trace("Inerting buffer with size: {} at offset {} into bean: {}", buffer.length, byteOffset, bean);
try {
final BeanParseResult result = BeanParser.parse(bean);
for (final BeanEntry entry : result.entries) {
final Object fieldValue = entry.field.get(bean);
if (fieldValue != null) {
if (entry.isArray) {
for (int i = 0; i < entry.arraySize; i++) {
final Object arrayItem = Array.get(fieldValue, i);
if (arrayItem != null) {
entry.serializer.insert(arrayItem, buffer,
entry.byteOffset + byteOffset + (i * entry.s7type.getByteSize()),
entry.bitOffset + (i * entry.s7type.getBitSize()), entry.size);
}
}
} else {
entry.serializer.insert(fieldValue, buffer, entry.byteOffset + byteOffset, entry.bitOffset,
entry.size);
}
}
}
} catch (final Exception e) {
throw new S7Exception("insertBytes", e);
}
}
/** The Connector. */
private final S7Connector connector;
/**
* Instantiates a new s7 serializer.
*
* @param connector
* the connector
*/
public S7SerializerImpl(final S7Connector connector) {
this.connector = connector;
}
/** {@inheritDoc} */
@Override
public synchronized <T> T dispense(final Class<T> beanClass, final int dbNum, final int byteOffset)
throws S7Exception {
try {
final BeanParseResult result = BeanParser.parse(beanClass);
final byte[] buffer = this.connector.read(DaveArea.DB, dbNum, result.blockSize, byteOffset);
return extractBytes(beanClass, buffer, 0);
} catch (final Exception e) {
throw new S7Exception("dispense", e);
}
}
/** {@inheritDoc} */
@Override
public synchronized <T> T dispense(final Class<T> beanClass, final int dbNum, final int byteOffset,
final int blockSize) throws S7Exception {
try {
final byte[] buffer = this.connector.read(DaveArea.DB, dbNum, blockSize, byteOffset);
return extractBytes(beanClass, buffer, 0);
} catch (final Exception e) {
throw new S7Exception(
"dispense dbnum(" + dbNum + ") byteoffset(" + byteOffset + ") blocksize(" + blockSize + ")", e);
}
}
/** {@inheritDoc} */
@Override
public synchronized void store(final Object bean, final int dbNum, final int byteOffset) {
try {
final BeanParseResult result = BeanParser.parse(bean);
final byte[] buffer = new byte[result.blockSize];
logger.trace("store-buffer-size: " + buffer.length);
insertBytes(bean, buffer, 0);
this.connector.write(DaveArea.DB, dbNum, byteOffset, buffer);
} catch (final Exception e) {
throw new S7Exception("store", e);
}
}
}

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.impl.serializer.converter;
import com.qgs.dc.s7.my.s7connector.api.S7Serializable;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
/**
* The Class BitConverter is responsible for converting bit values
*/
public final class BitConverter implements S7Serializable {
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final byte bufValue = buffer[byteOffset];
return targetClass.cast(bufValue == (bufValue | (0x01 << bitOffset)));
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.BOOL;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 1;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 0;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Boolean value = (Boolean) javaType;
if (value) {
byte bufValue = buffer[byteOffset];
bufValue |= (0x01 << bitOffset);
buffer[byteOffset] = bufValue;
}
}
}

View File

@ -0,0 +1,55 @@
/*
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.impl.serializer.converter;
import com.qgs.dc.s7.my.s7connector.api.S7Serializable;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
public class ByteConverter implements S7Serializable {
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
return targetClass.cast(buffer[byteOffset]);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.BYTE;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 1;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Byte value = (Byte) javaType;
buffer[byteOffset] = value;
}
}

View File

@ -0,0 +1,186 @@
/*
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.impl.serializer.converter;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
import java.util.Calendar;
import java.util.Date;
public final class DateAndTimeConverter extends ByteConverter {
public static final int OFFSET_DAY = 2;
public static final int OFFSET_HOUR = 3;
public static final int OFFSET_MILLIS_1_AND_DOW = 7;
public static final int OFFSET_MILLIS_100_10 = 6;
public static final int OFFSET_MINUTE = 4;
public static final int OFFSET_MONTH = 1;
public static final int OFFSET_SECOND = 5;
public static final int OFFSET_YEAR = 0;
// 18, 1,16,16, 5,80,0,3, (dec)
// 12, 1,10,10, 5,50,0,3, (hex)
// 12-01-10 10:05:50.000
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final Calendar c = Calendar.getInstance();
c.clear();
int year = this.getFromPLC(buffer, OFFSET_YEAR + byteOffset);
if (year < 90) {
// 1900 - 1989
year += 2000;
} else {
// 2000 - 2090
year += 1900;
}
int month = this.getFromPLC(buffer, OFFSET_MONTH + byteOffset);
if (month > 0) {
month--;
}
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, this.getFromPLC(buffer, OFFSET_DAY + byteOffset));
c.set(Calendar.HOUR_OF_DAY, this.getFromPLC(buffer, OFFSET_HOUR + byteOffset));
c.set(Calendar.MINUTE, this.getFromPLC(buffer, OFFSET_MINUTE + byteOffset));
c.set(Calendar.SECOND, this.getFromPLC(buffer, OFFSET_SECOND + byteOffset));
/*
* TODO byte upperMillis = super.extract(Byte.class, buffer,
* OFFSET_MILLIS_100_10+byteOffset, bitOffset); byte lowerMillis =
* super.extract(Byte.class, buffer, OFFSET_MILLIS_1_AND_DOW+byteOffset,
* bitOffset);
*
* int ms100 = ( upperMillis >> 4 ); int ms10 = ( upperMillis & 0x0F );
* int ms1 = ( lowerMillis >> 4 );
*
* int millis = ms1 + ( 10*ms10 ) + ( 100*ms100 );
* c.set(Calendar.MILLISECOND, millis);
*
* int dow = ( lowerMillis & 0x0F ); c.set(Calendar.DAY_OF_WEEK, dow);
*/
return targetClass.cast(c.getTime());
}
/**
* Dec -> Hex 10 = 0a 16 = 0f 17 = 10
*
* @param buffer
* @param offset
* @return
*/
public byte getFromPLC(final byte[] buffer, final int offset) {
try {
final byte ret = super.extract(Byte.class, buffer, offset, 0);
return (byte) Integer.parseInt(Integer.toHexString(ret & 0xFF));
} catch (final NumberFormatException e) {
return 0;
}
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.DATE_AND_TIME;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 8;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Date date = (Date) javaType;
final Calendar c = Calendar.getInstance();
c.setTime(date);
int year = c.get(Calendar.YEAR);
/*
* if (year < 1990 || year > 2090) throw new
* S7Exception("Invalid year: " + year + " @ offset: " + byteOffset);
*/
if (year < 2000) {
// 1990 -1999
year -= 1900;
} else {
// 2000 - 2089
year -= 2000;
}
this.putToPLC(buffer, byteOffset + OFFSET_YEAR, year);
this.putToPLC(buffer, byteOffset + OFFSET_MONTH, c.get(Calendar.MONTH) + 1);
this.putToPLC(buffer, byteOffset + OFFSET_DAY, c.get(Calendar.DAY_OF_MONTH));
this.putToPLC(buffer, byteOffset + OFFSET_HOUR, c.get(Calendar.HOUR_OF_DAY));
this.putToPLC(buffer, byteOffset + OFFSET_MINUTE, c.get(Calendar.MINUTE));
this.putToPLC(buffer, byteOffset + OFFSET_SECOND, c.get(Calendar.SECOND));
/*
* TODO int msec1 = 0, msec10 = 0, msec100 = 0; Integer millis =
* c.get(Calendar.MILLISECOND); String mStr = millis.toString();
*
* if (mStr.length() > 2) { msec100 = Integer.parseInt(
* mStr.substring(0, 1) ); msec10 = Integer.parseInt( mStr.substring(1,
* 2) ); msec1 = Integer.parseInt( mStr.substring(2, 3) ); } else if
* (mStr.length() > 1) { msec10 = Integer.parseInt( mStr.substring(0, 1)
* ); msec1 = Integer.parseInt( mStr.substring(1, 2) ); } else { msec1 =
* Integer.parseInt( mStr.substring(0, 1) ); }
*
* super.insert( (byte)( (byte)msec10 | (byte)(msec100 << 4) ), buffer,
* OFFSET_MILLIS_100_10+byteOffset, 0, 1);
*
* int dow = c.get(Calendar.DAY_OF_WEEK);
*
* super.insert( (byte)( (byte)dow | (byte)(msec1 << 4) ), buffer,
* OFFSET_MILLIS_1_AND_DOW+byteOffset, 0, 1);
*/
}
/**
* Hex -> dec 0a = 10 0f = 16 10 = 17
*
* @param buffer
* @param offset
* @param i
*/
public void putToPLC(final byte[] buffer, final int offset, final int i) {
try {
final int ret = Integer.parseInt("" + i, 16);
buffer[offset] = (byte) ret;
} catch (final NumberFormatException e) {
return;
}
}
}

View File

@ -0,0 +1,94 @@
/*
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.impl.serializer.converter;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
import java.util.Calendar;
import java.util.Date;
public final class DateConverter extends IntegerConverter {
private static final long MILLI_TO_DAY_FACTOR = 24 * 60 * 60 * 1000;
/**
* 1.1.1990
*/
private static final long OFFSET_1990;
static {
final Calendar c = Calendar.getInstance();
c.clear();
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.YEAR, 1990);
OFFSET_1990 = c.getTime().getTime();
}
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final long days = super.extract(Integer.class, buffer, byteOffset, bitOffset);
long millis = days * MILLI_TO_DAY_FACTOR;
millis += OFFSET_1990;
final Calendar c = Calendar.getInstance();
c.setTimeInMillis(millis);
c.set(Calendar.MILLISECOND, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.HOUR_OF_DAY, 0);
return targetClass.cast(c.getTime());
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.DATE;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Date d = (Date) javaType;
long millis = d.getTime();
millis -= OFFSET_1990;
final double days = (double) millis / (double) MILLI_TO_DAY_FACTOR;
final long ROUND = 1000;
final long expected = (long) ((days * MILLI_TO_DAY_FACTOR) / ROUND);
final long actual = millis / ROUND;
if (expected != actual) {
throw new IllegalArgumentException("Expected: " + expected + " got: " + actual);
}
if (millis < 0) {
super.insert(0, buffer, byteOffset, bitOffset, 2);
} else {
super.insert((int) Math.round(days), buffer, byteOffset, bitOffset, 2);
}
}
}

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.impl.serializer.converter;
import com.qgs.dc.s7.my.s7connector.api.S7Serializable;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
public class IntegerConverter implements S7Serializable {
private static final int OFFSET_HIGH_BYTE = 0;
private static final int OFFSET_LOW_BYTE = 1;
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final byte lower = buffer[byteOffset + OFFSET_LOW_BYTE];
final byte higher = buffer[byteOffset + OFFSET_HIGH_BYTE];
final Integer i = (lower & 0xFF) | ((higher << 8) & 0xFF00);
return targetClass.cast(i);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.WORD;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 2;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Integer value = (Integer) javaType;
final byte lower = (byte) ((value >> 0) & 0xFF);
final byte higher = (byte) ((value >> 8) & 0xFF);
buffer[byteOffset + OFFSET_LOW_BYTE] = lower;
buffer[byteOffset + OFFSET_HIGH_BYTE] = higher;
}
}

View File

@ -0,0 +1,70 @@
/*
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.impl.serializer.converter;
import com.qgs.dc.s7.my.s7connector.api.S7Serializable;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
public final class LongConverter implements S7Serializable {
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final byte b1 = buffer[byteOffset + 0];
final byte b2 = buffer[byteOffset + 1];
final byte b3 = buffer[byteOffset + 2];
final byte b4 = buffer[byteOffset + 3];
final Integer i = ((b1 << 0) & 0x000000FF) | ((b2 << 8) & 0x0000FF00) | ((b3 << 16) & 0x00FF0000)
| ((b4 << 24) & 0xFF000000);
return targetClass.cast(i);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.WORD;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 4;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Long value = (Long) javaType;
final byte b1 = (byte) ((value >> 0) & 0xFF);
final byte b2 = (byte) ((value >> 8) & 0xFF);
final byte b3 = (byte) ((value >> 16) & 0xFF);
final byte b4 = (byte) ((value >> 24) & 0xFF);
buffer[byteOffset + 0] = b1;
buffer[byteOffset + 1] = b2;
buffer[byteOffset + 2] = b3;
buffer[byteOffset + 3] = b4;
}
}

View File

@ -0,0 +1,78 @@
/*
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.impl.serializer.converter;
import com.qgs.dc.s7.my.s7connector.api.S7Serializable;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
public final class RealConverter implements S7Serializable {
private static final int OFFSET_POS1 = 0;
private static final int OFFSET_POS2 = 1;
private static final int OFFSET_POS3 = 2;
private static final int OFFSET_POS4 = 3;
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final int iValue = ((buffer[byteOffset + OFFSET_POS4] & 0xFF) << 0)
| ((buffer[byteOffset + OFFSET_POS3] & 0xFF) << 8) | ((buffer[byteOffset + OFFSET_POS2] & 0xFF) << 16)
| ((buffer[byteOffset + OFFSET_POS1] & 0xFF) << 24);
final Float fValue = Float.intBitsToFloat(iValue);
Object ret = fValue;
if (targetClass == Double.class) {
ret = Double.parseDouble(fValue.toString());
}
return targetClass.cast(ret);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.REAL;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 4;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final float fValue = Float.parseFloat(javaType.toString());
final int iValue = Float.floatToIntBits(fValue);
buffer[byteOffset + OFFSET_POS4] = (byte) ((iValue >> 0) & 0xFF);
buffer[byteOffset + OFFSET_POS3] = (byte) ((iValue >> 8) & 0xFF);
buffer[byteOffset + OFFSET_POS2] = (byte) ((iValue >> 16) & 0xFF);
buffer[byteOffset + OFFSET_POS1] = (byte) ((iValue >> 24) & 0xFF);
}
}

View File

@ -0,0 +1,82 @@
/*
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.impl.serializer.converter;
import com.qgs.dc.s7.my.s7connector.api.S7Serializable;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
public final class StringConverter implements S7Serializable {
private static final int OFFSET_CURRENT_LENGTH = 1;
private static final int OFFSET_OVERALL_LENGTH = 0;
private static final int OFFSET_START = 2;
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final int len = buffer[byteOffset + OFFSET_CURRENT_LENGTH];
final byte[] bytes = new byte[len];
for (int i = 0; i < len; i++) {
bytes[i] = buffer[byteOffset + OFFSET_START + i];
}
return targetClass.cast(new String(bytes));
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.STRING;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
// Not static
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
// Not static
return 2; // 2 bytes overhead
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final String value = (String) javaType;
final int len = value.length();
if (len > size) {
throw new IllegalArgumentException("String to big: " + len);
}
buffer[byteOffset + OFFSET_OVERALL_LENGTH] = (byte) size;
buffer[byteOffset + OFFSET_CURRENT_LENGTH] = (byte) len;
final byte[] strBytes = value.getBytes();
for (int i = 0; i < len; i++) {
buffer[byteOffset + OFFSET_START + i] = (byte) (strBytes[i] & 0xFF);
}
}
}

View File

@ -0,0 +1,55 @@
/*
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.impl.serializer.converter;
import com.qgs.dc.s7.my.s7connector.impl.serializer.S7SerializerImpl;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
import com.qgs.dc.s7.my.s7connector.api.S7Serializable;
public final class StructConverter implements S7Serializable {
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
return S7SerializerImpl.extractBytes(targetClass, buffer, byteOffset);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return null;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 0;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
S7SerializerImpl.insertBytes(javaType, buffer, byteOffset);
}
}

View File

@ -0,0 +1,65 @@
/*
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.impl.serializer.converter;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
public final class TimeConverter extends ByteConverter {
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final byte b1 = super.extract(Byte.class, buffer, byteOffset + 3, bitOffset);
final byte b2 = super.extract(Byte.class, buffer, byteOffset + 2, bitOffset);
final byte b3 = super.extract(Byte.class, buffer, byteOffset + 1, bitOffset);
final byte b4 = super.extract(Byte.class, buffer, byteOffset + 0, bitOffset);
final long l = ((long) b1 & 0xFF) << 0 | ((long) b2 & 0xFF) << 8 | ((long) b3 & 0xFF) << 16
| ((long) b4 & 0xFF) << 24;
return targetClass.cast(l);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.TIME;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 4;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final long l = (Long) javaType;
final byte b1 = (byte) ((byte) (l >> 0) & 0xFF);
final byte b2 = (byte) ((byte) (l >> 8) & 0xFF);
final byte b3 = (byte) ((byte) (l >> 16) & 0xFF);
final byte b4 = (byte) ((byte) (l >> 24) & 0xFF);
super.insert(b1, buffer, byteOffset + 3, bitOffset, 1);
super.insert(b2, buffer, byteOffset + 2, bitOffset, 1);
super.insert(b3, buffer, byteOffset + 1, bitOffset, 1);
super.insert(b4, buffer, byteOffset + 0, bitOffset, 1);
}
}

View File

@ -0,0 +1,63 @@
/*
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.impl.serializer.parser;
import com.qgs.dc.s7.my.s7connector.api.S7Serializable;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
import java.lang.reflect.Field;
/**
* A Bean-Entry
*
* @author Thomas Rudin
*/
public final class BeanEntry {
/**
* The Array size
*/
public int arraySize;
/**
* Offsets and size
*/
public int byteOffset, bitOffset, size;
/**
* The corresponding field
*/
public Field field;
/**
* Array type
*/
public boolean isArray;
/**
* The S7 Type
*/
public S7Type s7type;
/**
* The corresponding serializer
*/
public S7Serializable serializer;
/**
* The Java type
*/
public Class<?> type;
}

View File

@ -0,0 +1,32 @@
/*
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.impl.serializer.parser;
import java.util.Vector;
public final class BeanParseResult {
/**
* The needed blocksize
*/
public int blockSize;
/**
* The Bean entries
*/
public Vector<BeanEntry> entries = new Vector<BeanEntry>();
}

View File

@ -0,0 +1,154 @@
/*
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.impl.serializer.parser;
import com.qgs.dc.s7.my.s7connector.api.S7Serializable;
import com.qgs.dc.s7.my.s7connector.api.annotation.S7Variable;
import com.qgs.dc.s7.my.s7connector.impl.utils.S7Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
public final class BeanParser {
/**
* Local Logger
*/
private static final Logger logger = LoggerFactory.getLogger(BeanParser.class);
/**
* Returns the wrapper for the primitive type
*
* @param primitiveType
* @return
*/
private static Class<?> getWrapperForPrimitiveType(final Class<?> primitiveType) {
if (primitiveType == boolean.class) {
return Boolean.class;
} else if (primitiveType == byte.class) {
return Byte.class;
} else if (primitiveType == int.class) {
return Integer.class;
} else if (primitiveType == float.class) {
return Float.class;
} else if (primitiveType == double.class) {
return Double.class;
} else if (primitiveType == long.class) {
return Long.class;
} else {
// Fallback
return primitiveType;
}
}
/**
* Parses a Class
*
* @param jclass
* @return
* @throws Exception
*/
public static BeanParseResult parse(final Class<?> jclass) throws Exception {
final BeanParseResult res = new BeanParseResult();
logger.trace("Parsing: " + jclass.getName());
for (final Field field : jclass.getFields()) {
final S7Variable dataAnnotation = field.getAnnotation(S7Variable.class);
if (dataAnnotation != null) {
logger.trace("Parsing field: " + field.getName());
logger.trace(" type: " + dataAnnotation.type());
logger.trace(" byteOffset: " + dataAnnotation.byteOffset());
logger.trace(" bitOffset: " + dataAnnotation.bitOffset());
logger.trace(" size: " + dataAnnotation.size());
logger.trace(" arraySize: " + dataAnnotation.arraySize());
final int offset = dataAnnotation.byteOffset();
// update max offset
if (offset > res.blockSize) {
res.blockSize = offset;
}
if (dataAnnotation.type() == S7Type.STRUCT) {
// recurse
logger.trace("Recursing...");
final BeanParseResult subResult = parse(field.getType());
res.blockSize += subResult.blockSize;
logger.trace(" New blocksize: " + res.blockSize);
}
logger.trace(" New blocksize (+offset): " + res.blockSize);
// Add dynamic size
res.blockSize += dataAnnotation.size();
// Plain element
final BeanEntry entry = new BeanEntry();
entry.byteOffset = dataAnnotation.byteOffset();
entry.bitOffset = dataAnnotation.bitOffset();
entry.field = field;
entry.type = getWrapperForPrimitiveType(field.getType());
entry.size = dataAnnotation.size();
entry.s7type = dataAnnotation.type();
entry.isArray = field.getType().isArray();
entry.arraySize = dataAnnotation.arraySize();
if (entry.isArray) {
entry.type = getWrapperForPrimitiveType(entry.type.getComponentType());
}
// Create new serializer
final S7Serializable s = entry.s7type.getSerializer().newInstance();
entry.serializer = s;
res.blockSize += (s.getSizeInBytes() * dataAnnotation.arraySize());
logger.trace(" New blocksize (+array): " + res.blockSize);
if (s.getSizeInBits() > 0) {
boolean offsetOfBitAlreadyKnown = false;
for (final BeanEntry parsedEntry : res.entries) {
if (parsedEntry.byteOffset == entry.byteOffset) {
offsetOfBitAlreadyKnown = true;
}
}
if (!offsetOfBitAlreadyKnown) {
res.blockSize++;
}
}
res.entries.add(entry);
}
}
logger.trace("Parsing done, overall size: " + res.blockSize);
return res;
}
/**
* Parses an Object
*
* @param obj
* @return
* @throws Exception
*/
public static BeanParseResult parse(final Object obj) throws Exception {
return parse(obj.getClass());
}
}

View File

@ -0,0 +1,105 @@
/*
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.impl.utils;
import com.qgs.dc.s7.my.s7connector.api.S7Serializable;
import com.qgs.dc.s7.my.s7connector.impl.serializer.converter.*;
/**
* Type of the Address
*
* @author Thomas Rudin Libnodave: http://libnodave.sourceforge.net/
*/
public enum S7Type {
/**
* Boolean type
*/
BOOL(BitConverter.class, 0, 1),
/**
* Byte type
*/
BYTE(ByteConverter.class, 1, 0),
/**
* Simple Date with 2 bytes in length
*/
DATE(DateConverter.class, 2, 0),
/**
* Full Date and time format with precision in milliseconds
*/
DATE_AND_TIME(DateAndTimeConverter.class, 8, 0),
/**
* Double word
*/
DWORD(LongConverter.class, 4, 0),
/**
* Real-type, corresponds to float or double
*/
REAL(RealConverter.class, 4, 0),
/**
* String type, size must be specified manually
*/
STRING(StringConverter.class, 2, 0),
/**
* Structure type
*/
STRUCT(StructConverter.class, 0, 0),
/**
* Time-type, 4 bytes in length, number of millis
*/
TIME(TimeConverter.class, 4, 0),
/**
* A Word-type (same as int-type)
*/
WORD(IntegerConverter.class, 2, 0);
private int byteSize, bitSize;
private Class<? extends S7Serializable> serializer;
/**
* Enum Constructor
*
* @param serializer
* @param byteSize
* @param bitSize
*/
S7Type(final Class<? extends S7Serializable> serializer, final int byteSize, final int bitSize) {
this.serializer = serializer;
this.bitSize = bitSize;
this.byteSize = byteSize;
}
public int getBitSize() {
return this.bitSize;
}
public int getByteSize() {
return this.byteSize;
}
public Class<? extends S7Serializable> getSerializer() {
return this.serializer;
}
}

View File

@ -0,0 +1,56 @@
/*
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.impl.utils;
/**
* S7-Utility class
*
* @author Thomas Rudin Libnodave: http://libnodave.sourceforge.net/
*
*/
public final class S7Utils {
/**
* Converts a byte to 8 bits
*
* @param buffer
* The Input-Byte
* @return The 8 bits
*/
public static boolean[] getBits(int buffer) {
if (buffer < 0) {
buffer += 256;
}
final String binString = Integer.toBinaryString(buffer);
/*
* String-Pos: 0 1 2 3 4 5 6 7 Bit: 128 64 32 16 8 4 2 1
*/
final boolean[] ret = new boolean[8];
for (int i = binString.length() - 1; i >= 0; i--) {
// Check for the '1'-Char and mirror-set the result
final int mirrorPos = (binString.length() - 1) - i;
if (binString.charAt(i) == '1') {
ret[mirrorPos] = true;
}
}
return ret;
}
/** Constructor */
private S7Utils() {
// Not needed. Utility class.
}
}

View File

@ -0,0 +1,165 @@
package com.qgs.dc.s7.my.s7connector.service;
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.enmuc.PlcVarActual;
import com.qgs.dc.s7.my.s7connector.enmuc.S7Client;
import com.qgs.dc.s7.my.s7connector.type.PlcVar;
import com.qgs.dc.s7.my.s7connector.type.TransportSize;
import io.netty.channel.EventLoopGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2022/1/17 14:20
*/
@Component
public class S7Service {
private final Logger logger = LoggerFactory.getLogger(getClass());
S7Service(){
}
/**
* 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>
*
*
*
* */
public Object read(PlcVarActual plcVarActual,S7Client s7Client) throws UnsupportedEncodingException, ParseException {
S7Connector connector = s7Client.getConnector();
if(plcVarActual.getType().equals(PlcVar.STRING)){
Integer readBytes = 2;
byte[] read = connector.read(
plcVarActual.getArea(),
plcVarActual.getAreaNumber(),
readBytes,
plcVarActual.getByteOffset(),
plcVarActual.getBitOffset(),
plcVarActual.getType().getTransportSize()
);
Integer allLength = Integer.valueOf(read[1])+2;
byte[] readF = connector.read(
plcVarActual.getArea(),
plcVarActual.getAreaNumber(),
allLength,
plcVarActual.getByteOffset(),
plcVarActual.getBitOffset(),
plcVarActual.getType().getTransportSize()
);
return plcVarActual.getType().toObject(readF);
}else {
Integer readBytes = plcVarActual.getType().getTransportSize().getSizeInBytes() * plcVarActual.getLength();
byte[] read = connector.read(
plcVarActual.getArea(),
plcVarActual.getAreaNumber(),
readBytes,
plcVarActual.getByteOffset(),
plcVarActual.getBitOffset(),
plcVarActual.getType().getTransportSize()
);
return plcVarActual.getType().toObject(read);
}
}
/**
*
* 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];
* //如果有其他数据类型 这里没有找我扩展
*
*
* */
public void write(PlcVarActual plcVarActual,Object newValue,S7Client s7Client){
S7Connector connector = s7Client.getConnector();
byte[] bytes = plcVarActual.getType().toBytes(newValue);
connector.write(
plcVarActual.getArea(),
plcVarActual.getAreaNumber(),
plcVarActual.getByteOffset(),
plcVarActual.getBitOffset(),
bytes,
plcVarActual.getType()
);
}
}

View File

@ -0,0 +1,82 @@
package com.qgs.dc.s7.my.s7connector.type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2022/1/7 14:41
*/
public enum DataTransportSize {
NULL((short) 0x00, (boolean) false),
BIT((short) 0x03, (boolean) true),
BYTE_WORD_DWORD((short) 0x04, (boolean) true),
INTEGER((short) 0x05, (boolean) true),
DINTEGER((short) 0x06, (boolean) false),
REAL((short) 0x07, (boolean) false),
OCTET_STRING((short) 0x09, (boolean) false);
private static final Logger logger = LoggerFactory.getLogger(DataTransportSize.class);
private static final Map<Short, DataTransportSize> map;
static {
map = new HashMap<>();
for (DataTransportSize value : DataTransportSize.values()) {
map.put((short) value.getValue(), value);
}
}
private short value;
private boolean sizeInBits;
DataTransportSize(short value, boolean sizeInBits) {
this.value = value;
this.sizeInBits = sizeInBits;
}
public short getValue() {
return value;
}
public boolean getSizeInBits() {
return sizeInBits;
}
public static DataTransportSize firstEnumForFieldSizeInBits(boolean fieldValue) {
for (DataTransportSize _val : DataTransportSize.values()) {
if(_val.getSizeInBits() == fieldValue) {
return _val;
}
}
return null;
}
public static List<DataTransportSize> enumsForFieldSizeInBits(boolean fieldValue) {
List<DataTransportSize> _values = new ArrayList();
for (DataTransportSize _val : DataTransportSize.values()) {
if(_val.getSizeInBits() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public static DataTransportSize enumForValue(short value) {
if (!map.containsKey(value)) {
logger.error("No DataTransportSize for value {}", value);
}
return map.get(value);
}
public static Boolean isDefined(short value) {
return map.containsKey(value);
}
}

View File

@ -0,0 +1,523 @@
package com.qgs.dc.s7.my.s7connector.type;
import com.qgs.dc.s7.my.s7connector.api.DaveArea;
import com.qgs.dc.s7.my.s7connector.api.utils.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
public enum PlcVar {
//单体变量
BOOL("BOOL",TransportSize.BOOL,1,false),
BYTE("BYTE",TransportSize.BYTE,2,false),
WORD("WORD",TransportSize.WORD,3,false),
DWORD("DWORD",TransportSize.DWORD,4,false),
LWORD("LWORD",TransportSize.LWORD,5,false),
INT("INT",TransportSize.INT,6,false),
UINT("UINT",TransportSize.UINT,7,false),
SINT("SINT",TransportSize.SINT,8,false),
USINT("USINT",TransportSize.USINT,9,false),
DINT("DINT",TransportSize.DINT,10,false),
UDINT("UDINT",TransportSize.UDINT,11,false),
LINT("LINT",TransportSize.LINT,12,false),
ULINT("ULINT",TransportSize.ULINT,13,false),
REAL("REAL",TransportSize.REAL,14,false),
LREAL("LREAL",TransportSize.LREAL,15,false),
CHAR("CHAR",TransportSize.CHAR,16,false),
WCHAR("WCHAR",TransportSize.WCHAR,17,false),
STRING("STRING",TransportSize.STRING,18,false),
WSTRING("WSTRING",TransportSize.WSTRING,19,false),
TIME("TIME",TransportSize.TIME,20,false),
LTIME("LTIME",TransportSize.LTIME,21,false),
DATE("DATE",TransportSize.DATE,22,false),
TIME_OF_DAY("TIME_OF_DAY",TransportSize.TIME_OF_DAY,23,false),
TOD("TOD",TransportSize.TOD,24,false),
DATE_AND_TIME("DATE_AND_TIME",TransportSize.DATE_AND_TIME,25,false),
DT("DT",TransportSize.DT,26,false),
DTL("DTL",TransportSize.DTL,27,false),
//todo DTL 捋清 事件关系
//todo Array 再搞一搞
BOOL_Array("BOOL",TransportSize.BOOL,1,true),
BYTE_Array("BYTE",TransportSize.BYTE,2,true),
WORD_Array("WORD",TransportSize.WORD,3,true),
DWORD_Array("DWORD",TransportSize.DWORD,4,true),
CHAR_Array("CHAR",TransportSize.CHAR,5,true),
SINT_Array("SINT",TransportSize.SINT,6,true),
INT_Array("INT",TransportSize.INT,7,true),
DINT_Array("DINT",TransportSize.DINT,8,true),
UINT_Array("UINT",TransportSize.UINT,9,true),
USINT_Array("USINT",TransportSize.USINT,10,true),
UDINT_Array("UDINT",TransportSize.UDINT,11,true),
;
private static final Logger logger = LoggerFactory.getLogger(TransportSize.class);
private static final Map<TransportSize, PlcVar> map;
static {
map = new HashMap<>();
for (PlcVar value : PlcVar.values()) {
if(!value.isArray){
map.put(value.getTransportSize(), value);
}
}
}
public static PlcVar valueOf(TransportSize transportSize){
return map.get(transportSize);
}
private String name;
private TransportSize transportSize;
private Integer dataType;
private boolean isArray;
PlcVar(String name , TransportSize transportSize, Integer dataType,Boolean isArray){
this.name = name;
this.transportSize = transportSize;
this.dataType = dataType;
this.isArray = isArray;
}
public String getName() {
return name;
}
public TransportSize getTransportSize() {
return transportSize;
}
public boolean isArray() {
return isArray;
}
public byte[] toBytes(Object value){
//todo here 扩展数组 toBytes
if(!isArray){
byte[] res = null;
switch (dataType) {
case 1:
res = ByteUtils.boolToBytes((boolean)value);
break;
case 2:
//ByteUtils.setbytes(Byte.valueOf((byte) 0xFF))
res = ByteUtils.setbytes(Byte.valueOf((byte) value));
break;
case 3:
//ByteUtils.wordToBytes(Short.valueOf("-55"))
res = ByteUtils.wordToBytes(Short.valueOf(value.toString()));
break;
case 4:
//ByteUtils.dwordToBytes(99)
res = ByteUtils.dwordToBytes((int)value);
break;
case 5:
//null
break;
case 6:
//ByteUtils.intToBytes(-99)
res = ByteUtils.intToBytes((int)value);
break;
case 7:
//ByteUtils.uintToBytes(9)
res = ByteUtils.uintToBytes((int)value);
break;
case 8:
//ByteUtils.sintToBytes(-9)
res =ByteUtils.sintToBytes((int)value);
break;
case 9:
//ByteUtils.usintToBytes(9);
res =ByteUtils.usintToBytes((int)value);
break;
case 10:
//ByteUtils.dintToBytes(-999)
res =ByteUtils.dintToBytes((int)value);
break;
case 11:
//ByteUtils.udintToBytes(99)
res =ByteUtils.udintToBytes((int)value);
break;
case 12:
//待补充
break;
case 13:
//待补充
break;
case 14:
//ByteUtils.realToBytes(Float.valueOf("-11.2"))
res =ByteUtils.realToBytes(Float.valueOf(value.toString()));
break;
case 15:
//ByteUtils.lrealToBytes(Double.parseDouble("-111.1"));
res =ByteUtils.lrealToBytes(Double.parseDouble(value.toString()));
break;
case 16:
//ByteUtils.charToBytes('b')
res =ByteUtils.charToBytes((char)value);
break;
case 17:
//ByteUtils.wcharToBytes('菜')
res =ByteUtils.wcharToBytes((char)value);
break;
case 18:
//String s = "你好啊";
//ByteUtils.strToBytes(s);
res = ByteUtils.strToBytes(value.toString());
break;
case 19:
//待补充
break;
case 20:
//不常用 待补充
break;
case 21:
//不常用
break;
case 22:
//不常用
break;
case 23:
////不常用
break;
case 24:
////不常用
break;
case 25:
//不常用
break;
case 26:
//不常用
break;
case 27:
//不常用
break;
default:
//什么也不做
break;
}
return res;
}else {
byte[] res = null;
switch (dataType) {
case 1:
// boolean[] booleanArray = new boolean[2];
// booleanArray[0] = true;
// booleanArray[1] = true;
// ByteUtils.booleanArrayToBytes(booleanArray)
res = ByteUtils.booleanArrayToBytes((boolean[]) value);
break;
case 2:
// byte[] write_byteArrays = new byte[2];
// write_byteArrays[0] = 1;
// write_byteArrays[1] = 2;
//
res = (byte[]) value;
break;
case 3:
// short[] shortArrays_content = new short[2];
// shortArrays_content[0] = 1;
// shortArrays_content[1] = -1;
//
res = ByteUtils.wordArrayToBytes((short[]) value);
break;
case 4:
// int[] intArrays_content = new int[2];
// intArrays_content[0] = 1;
// intArrays_content[1] = -1;
//
res = ByteUtils.dwordArrayToBytes((int[]) value);
break;
case 5:
// char[] charArrays_content = new char[2];
// charArrays_content[0] = '1';
// charArrays_content[1] = 'b';
//
res = ByteUtils.charArrayToBytes((char[]) value);
break;
case 6:
// int[] sintArrays_content = new int[2];
// sintArrays_content[0] = 1;
// sintArrays_content[1] = -1;
//
res = ByteUtils.sintArrayToBytes((int[]) value);
break;
case 7:
// int[] iintArrays_content = new int[2];
// iintArrays_content[0] = 12;
// iintArrays_content[1] = -21;
//
res = ByteUtils.intArrayToBytes((int[]) value);
break;
case 8:
// int[] dintArrays_content = new int[2];
// dintArrays_content[0] = 12;
// dintArrays_content[1] = -21;
//
res = ByteUtils.dintArrayToBytes((int[]) value);
break;
case 9:
// int[] uintArrays_content = new int[3];
// uintArrays_content[0] = 12;
// uintArrays_content[1] = 99;
// uintArrays_content[2] = 1;
res = ByteUtils.uintArrayToBytes((int[]) value);
break;
case 10:
// int[] usintArrays_content = new int[3];
// usintArrays_content[0] = 12;
// usintArrays_content[1] = 99;
// usintArrays_content[2] = 1;
res = ByteUtils.usintArrayToBytes((int[]) value);
break;
case 11:
// int[] udintArrays_content = new int[3];
// udintArrays_content[0] = 12;
// udintArrays_content[1] = 99;
// udintArrays_content[2] = 1;
res = ByteUtils.udintArrayToBytes((int[]) value);
break;
default:
//什么也不做
break;
}
return res;
}
}
//todo here read write操作都封装一下 然后 toByte toObject 整合
/**
* 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>
*
*
*
* */
public Object toObject(byte[] value) throws ParseException, UnsupportedEncodingException {
if(!isArray){
Object res = null;
switch (dataType) {
case 1:
//
res = ByteUtils.toBoolean(value);
break;
case 2:
//
res = ByteUtils.toInt(value[0]);
break;
case 3:
//
res = ByteUtils.toInt(value[0],value[1]);
break;
case 4:
//
res = ByteUtils.toInt(value[0],value[1],value[2],value[3]);
break;
case 5:
//null
break;
case 6:
//
res = ByteUtils.toUInt(value[0],value[1]);
break;
case 7:
//
res = ByteUtils.toUInt(value[0],value[1]);
break;
case 8:
//
res = ByteUtils.toUInt(value[0]);
break;
case 9:
//
res = ByteUtils.toUInt(value[0]);
break;
case 10:
//
res = ByteUtils.toInt(value[0],value[1],value[2],value[3]);
break;
case 11:
//
res = ByteUtils.toInt(value[0],value[1],value[2],value[3]);
break;
case 12:
//
break;
case 13:
//
break;
case 14:
//
res = ByteUtils.realbytesToFloat(value);
break;
case 15:
//ByteUtils.lrealbytesToDouble(lreal)
res = ByteUtils.lrealbytesToDouble(value);
break;
case 16:
//
res = ByteUtils.toChar(value);
break;
case 17:
//
res = ByteUtils.toChar(value);
break;
case 18:
//
res = ByteUtils.toStr(value);
break;
case 19:
//
break;
case 20:
//
res = ByteUtils.toInt(value[0], value[1], value[2], value[3]);
break;
case 21:
//
break;
case 22:
//
Long aLong = Long.valueOf(ByteUtils.toInt(value[0], value[1]).toString());
String s = ByteUtils.addDate("1990-01-01", aLong);
res = s;
break;
case 23:
//
break;
case 24:
//
break;
case 25:
//
break;
case 26:
//
break;
case 27:
//
byte[] year = new byte[2];
year[0] = value[0];
year[1] = value[1];
Integer yearInt = ByteUtils.toInt(year[0], year[1]);
Integer monthInt = ByteUtils.toInt(value[2]);
Integer dayInt = ByteUtils.toInt(value[3]);
Integer worddayInt = ByteUtils.toInt(value[4]);
Integer hourInt = ByteUtils.toInt(value[5]);
Integer minuInt = ByteUtils.toInt(value[6]);
Integer secondInt = ByteUtils.toInt(value[7]);
res = yearInt+"-"+monthInt+"-"+dayInt+"-"+worddayInt+"-"+hourInt+"-"+minuInt+"-"+secondInt;
break;
default:
//什么也不做
break;
}
return res;
}else {
Object res = null;
switch (dataType) {
case 1:
//
res = ByteUtils.toBoolArray(value);
break;
case 2:
//
res = ByteUtils.toByteArray(value);
break;
case 3:
//
res = ByteUtils.toWordArray(value);
break;
case 4:
//
res = ByteUtils.toDWordArray(value);
break;
case 5:
//
res = ByteUtils.toCharArray(value);
break;
case 6:
//
res =ByteUtils.toSIntArray(value);
break;
case 7:
res = ByteUtils.toIntArray(value);
break;
case 8:
res = ByteUtils.toDIntArray(value);
break;
case 9:
res = ByteUtils.toUIntArray(value);
break;
case 10:
res = ByteUtils.toUSIntArray(value);
break;
case 11:
res = ByteUtils.toUDIntArray(value);
break;
case 12:
//后续有其他数组类型 再补充好了先列出一些常用的
break;
default:
//什么也不做
break;
}
return res;
}
}
/**
* area :DB块 或者其他块
* areaNumber :数据块编号 DB3
* byteOffset :字节偏移量
* byteOffset :比特偏移量不填就是0 差不多只有读bool类型才会用到
* length :长度1 代表单个变量 2/3/4 代表读一个数组
* */
}

View File

@ -0,0 +1,350 @@
package com.qgs.dc.s7.my.s7connector.type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public enum TransportSize {
BOOL((byte) 0x01, (boolean) true, (boolean) true, (short) 0x01, (short) 1, (boolean) true, (boolean) true, (short) 'X', (boolean) true, DataTransportSize.BIT, null, (String) "IEC61131_BOOL"),
BYTE((byte) 0x02, (boolean) true, (boolean) true, (short) 0x02, (short) 1, (boolean) true, (boolean) true, (short) 'B', (boolean) true, DataTransportSize.BYTE_WORD_DWORD, null, (String) "IEC61131_BYTE"),
WORD((byte) 0x03, (boolean) true, (boolean) true, (short) 0x04, (short) 2, (boolean) true, (boolean) true, (short) 'W', (boolean) true, DataTransportSize.BYTE_WORD_DWORD, null, (String) "IEC61131_WORD"),
DWORD((byte) 0x04, (boolean) true, (boolean) true, (short) 0x06, (short) 4, (boolean) true, (boolean) true, (short) 'D', (boolean) true, DataTransportSize.BYTE_WORD_DWORD, TransportSize.WORD, (String) "IEC61131_DWORD"),
LWORD((byte) 0x05, (boolean) false, (boolean) false, (short) 0x00, (short) 8, (boolean) false, (boolean) false, (short) 'X', (boolean) true, null, null, (String) "IEC61131_LWORD"),
INT((byte) 0x06, (boolean) true, (boolean) true, (short) 0x05, (short) 2, (boolean) true, (boolean) true, (short) 'W', (boolean) true, DataTransportSize.INTEGER, null, (String) "IEC61131_INT"),
UINT((byte) 0x07, (boolean) false, (boolean) true, (short) 0x05, (short) 2, (boolean) false, (boolean) true, (short) 'W', (boolean) true, DataTransportSize.INTEGER, TransportSize.INT, (String) "IEC61131_UINT"),
SINT((byte) 0x08, (boolean) false, (boolean) true, (short) 0x02, (short) 1, (boolean) false, (boolean) true, (short) 'B', (boolean) true, DataTransportSize.BYTE_WORD_DWORD, TransportSize.INT, (String) "IEC61131_SINT"),
USINT((byte) 0x09, (boolean) false, (boolean) true, (short) 0x02, (short) 1, (boolean) false, (boolean) true, (short) 'B', (boolean) true, DataTransportSize.BYTE_WORD_DWORD, TransportSize.INT, (String) "IEC61131_USINT"),
DINT((byte) 0x0A, (boolean) true, (boolean) true, (short) 0x07, (short) 4, (boolean) true, (boolean) true, (short) 'D', (boolean) true, DataTransportSize.INTEGER, TransportSize.INT, (String) "IEC61131_DINT"),
UDINT((byte) 0x0B, (boolean) false, (boolean) true, (short) 0x07, (short) 4, (boolean) false, (boolean) true, (short) 'D', (boolean) true, DataTransportSize.INTEGER, TransportSize.INT, (String) "IEC61131_UDINT"),
LINT((byte) 0x0C, (boolean) false, (boolean) false, (short) 0x00, (short) 8, (boolean) false, (boolean) false, (short) 'X', (boolean) true, null, TransportSize.INT, (String) "IEC61131_LINT"),
ULINT((byte) 0x0D, (boolean) false, (boolean) false, (short) 0x00, (short) 16, (boolean) false, (boolean) false, (short) 'X', (boolean) true, null, TransportSize.INT, (String) "IEC61131_ULINT"),
REAL((byte) 0x0E, (boolean) true, (boolean) true, (short) 0x08, (short) 4, (boolean) true, (boolean) true, (short) 'D', (boolean) true, DataTransportSize.REAL, null, (String) "IEC61131_REAL"),
LREAL((byte) 0x0F, (boolean) false, (boolean) false, (short) 0x30, (short) 8, (boolean) false, (boolean) true, (short) 'X', (boolean) true, null, TransportSize.REAL, (String) "IEC61131_LREAL"),
CHAR((byte) 0x10, (boolean) true, (boolean) true, (short) 0x03, (short) 1, (boolean) true, (boolean) true, (short) 'B', (boolean) true, DataTransportSize.BYTE_WORD_DWORD, null, (String) "IEC61131_CHAR"),
WCHAR((byte) 0x11, (boolean) false, (boolean) true, (short) 0x13, (short) 2, (boolean) false, (boolean) true, (short) 'X', (boolean) true, null, null, (String) "IEC61131_WCHAR"),
STRING((byte) 0x12, (boolean) true, (boolean) true, (short) 0x03, (short) 1, (boolean) true, (boolean) true, (short) 'X', (boolean) true, DataTransportSize.BYTE_WORD_DWORD, null, (String) "IEC61131_STRING"),
WSTRING((byte) 0x13, (boolean) false, (boolean) true, (short) 0x00, (short) 2, (boolean) false, (boolean) true, (short) 'X', (boolean) true, null, null, (String) "IEC61131_WSTRING"),
TIME((byte) 0x14, (boolean) true, (boolean) true, (short) 0x0B, (short) 4, (boolean) true, (boolean) true, (short) 'X', (boolean) true, null, null, (String) "IEC61131_TIME"),
LTIME((byte) 0x16, (boolean) false, (boolean) false, (short) 0x00, (short) 8, (boolean) false, (boolean) false, (short) 'X', (boolean) true, null, TransportSize.TIME, (String) "IEC61131_LTIME"),
DATE((byte) 0x17, (boolean) true, (boolean) true, (short) 0x09, (short) 2, (boolean) true, (boolean) true, (short) 'X', (boolean) true, DataTransportSize.BYTE_WORD_DWORD, null, (String) "IEC61131_DATE"),
TIME_OF_DAY((byte) 0x18, (boolean) true, (boolean) true, (short) 0x06, (short) 4, (boolean) true, (boolean) true, (short) 'X', (boolean) true, DataTransportSize.BYTE_WORD_DWORD, null, (String) "IEC61131_TIME_OF_DAY"),
TOD((byte) 0x19, (boolean) true, (boolean) true, (short) 0x06, (short) 4, (boolean) true, (boolean) true, (short) 'X', (boolean) true, DataTransportSize.BYTE_WORD_DWORD, null, (String) "IEC61131_TIME_OF_DAY"),
DATE_AND_TIME((byte) 0x1A, (boolean) true, (boolean) false, (short) 0x0F, (short) 12, (boolean) true, (boolean) false, (short) 'X', (boolean) true, null, null, (String) "IEC61131_DATE_AND_TIME"),
DT((byte) 0x1B, (boolean) true, (boolean) false, (short) 0x0F, (short) 12, (boolean) true, (boolean) false, (short) 'X', (boolean) true, null, null, (String) "IEC61131_DATE_AND_TIME"),
DTL((byte) 0x1C, (boolean) true, (boolean) false, (short) 0x02, (short) 12, (boolean) true, (boolean) false, (short) 'X', (boolean) true, null, null, (String) "IEC61131_DATE_AND_TIME");
private static final Logger logger = LoggerFactory.getLogger(TransportSize.class);
private static final Map<Byte, TransportSize> map;
static {
map = new HashMap<>();
for (TransportSize value : TransportSize.values()) {
map.put((byte) value.getValue(), value);
}
}
//value 类似于TransportSize 的ID
private byte value;
private boolean supported_S7_300;
private boolean supported_LOGO;
private short code;
private short sizeInBytes;
private boolean supported_S7_400;
private boolean supported_S7_1200;
private short shortName;
private boolean supported_S7_1500;
private DataTransportSize dataTransportSize;
private TransportSize baseType;
private String dataProtocolId;
TransportSize(byte value, boolean supported_S7_300, boolean supported_LOGO, short code, short sizeInBytes, boolean supported_S7_400, boolean supported_S7_1200, short shortName, boolean supported_S7_1500, DataTransportSize dataTransportSize, TransportSize baseType, String dataProtocolId) {
this.value = value;
this.supported_S7_300 = supported_S7_300;
this.supported_LOGO = supported_LOGO;
//Parameter ITEM 的transportSize
this.code = code;
this.sizeInBytes = sizeInBytes;
this.supported_S7_400 = supported_S7_400;
this.supported_S7_1200 = supported_S7_1200;
this.shortName = shortName;
this.supported_S7_1500 = supported_S7_1500;
//Data ITEM 的transportSize
this.dataTransportSize = dataTransportSize;
this.baseType = baseType;
this.dataProtocolId = dataProtocolId;
}
public byte getValue() {
return value;
}
public boolean getSupported_S7_300() {
return supported_S7_300;
}
public static TransportSize firstEnumForFieldSupported_S7_300(boolean fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_300() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSupported_S7_300(boolean fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_300() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public boolean getSupported_LOGO() {
return supported_LOGO;
}
public static TransportSize firstEnumForFieldSupported_LOGO(boolean fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_LOGO() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSupported_LOGO(boolean fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_LOGO() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public short getCode() {
return code;
}
public static TransportSize firstEnumForFieldCode(short fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getCode() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldCode(short fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getCode() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public short getSizeInBytes() {
return sizeInBytes;
}
public static TransportSize firstEnumForFieldSizeInBytes(short fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSizeInBytes() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSizeInBytes(short fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSizeInBytes() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public boolean getSupported_S7_400() {
return supported_S7_400;
}
public static TransportSize firstEnumForFieldSupported_S7_400(boolean fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_400() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSupported_S7_400(boolean fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_400() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public boolean getSupported_S7_1200() {
return supported_S7_1200;
}
public static TransportSize firstEnumForFieldSupported_S7_1200(boolean fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_1200() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSupported_S7_1200(boolean fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_1200() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public short getShortName() {
return shortName;
}
public static TransportSize firstEnumForFieldShortName(short fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getShortName() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldShortName(short fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getShortName() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public boolean getSupported_S7_1500() {
return supported_S7_1500;
}
public static TransportSize firstEnumForFieldSupported_S7_1500(boolean fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_1500() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSupported_S7_1500(boolean fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_1500() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public DataTransportSize getDataTransportSize() {
return dataTransportSize;
}
public static TransportSize firstEnumForFieldDataTransportSize(DataTransportSize fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getDataTransportSize() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldDataTransportSize(DataTransportSize fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getDataTransportSize() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public TransportSize getBaseType() {
return baseType;
}
public static TransportSize firstEnumForFieldBaseType(TransportSize fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getBaseType() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldBaseType(TransportSize fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getBaseType() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public String getDataProtocolId() {
return dataProtocolId;
}
public static TransportSize firstEnumForFieldDataProtocolId(String fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getDataProtocolId() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldDataProtocolId(String fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getDataProtocolId() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public static TransportSize enumForValue(byte value) {
if (!map.containsKey(value)) {
logger.error("No TransportSize for value {}", value);
}
return map.get(value);
}
public static Boolean isDefined(byte value) {
return map.containsKey(value);
}
}

View File

@ -1,104 +0,0 @@
package com.qgs.dc.s7.service;
import com.qgs.dc.s7.enums.S7DriveManage;
import org.apache.plc4x.java.PlcDriverManager;
import org.apache.plc4x.java.api.PlcConnection;
import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
import org.apache.plc4x.java.api.messages.PlcReadRequest;
import org.apache.plc4x.java.api.messages.PlcReadResponse;
import org.apache.plc4x.java.api.types.PlcResponseCode;
import org.apache.plc4x.java.api.value.PlcValue;
import org.apache.plc4x.java.utils.connectionpool.PooledPlcDriverManager;
import org.apache.plc4x.java.utils.connectionpool2.CachedDriverManager;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2021/9/23 15:16
*/
@Component
public class S7Service {
private final Logger logger = LoggerFactory.getLogger(getClass());
private HashMap<String, PlcConnection> plcConnections = new HashMap<>();
private PlcDriverManager driverManager;
public S7Service(){
driverManager = S7DriveManage.INSTANCE.getInstance();
}
public boolean addPlc(String url) throws PlcConnectionException {
PlcConnection connection = driverManager.getConnection(url);
return connection.isConnected();
}
//url : s7://192.168.0.200
//address : %DB10:10.0:STRING(20)
//get 一个或者多个value
public Object getValue(String url,HashMap<String,String> listAddress) throws PlcConnectionException {
try(PlcConnection conn = driverManager.getConnection(url)) {
if(conn.isConnected()){
if(conn.getMetadata().canRead()){
try {
PlcReadRequest.Builder builder = conn.readRequestBuilder();
for(String key:listAddress.keySet()){
builder.addItem(key, listAddress.get(key));
}
PlcReadRequest readRequest = builder.build();
CompletableFuture<? extends PlcReadResponse> execute = readRequest.execute();
PlcReadResponse response = execute.get();
for (String fieldName : response.getFieldNames()) {
if(response.getResponseCode(fieldName) == PlcResponseCode.OK) {
int numValues = response.getNumberOfValues(fieldName);
PlcValue asPlcValue = response.getAsPlcValue();
if(numValues == 1) {
Object obj = response.getObject(fieldName);
logger.info("Value[" + fieldName + "]: " + response.getObject(fieldName));
return obj;
}
else {
logger.info("Value[" + fieldName + "]:");
List<Object> res = new ArrayList<>();
for(int i = 0; i < numValues; i++) {
logger.info(" - " + response.getObject(fieldName, i));
res.add(response.getObject(fieldName,i));
}
}
}
else {
logger.error("Error[" + fieldName + "]: " + response.getResponseCode(fieldName).name());
}
}
}catch (Exception e){
logger.error(e.getMessage());
}
}else {
System.out.println("can not read");
}
}else {
System.out.println("conn not connected");
}
}catch (Exception e){
System.out.println(e.getMessage());
}
return driverManager.getConnection(url);
}
}