diff --git a/src/main/java/com/qgs/dc/s7/controller/S7Controller.java b/src/main/java/com/qgs/dc/s7/controller/S7Controller.java index 18254de..85c3707 100644 --- a/src/main/java/com/qgs/dc/s7/controller/S7Controller.java +++ b/src/main/java/com/qgs/dc/s7/controller/S7Controller.java @@ -1,21 +1,23 @@ package com.qgs.dc.s7.controller; -import com.qgs.dc.opcua.arg.*; import com.qgs.dc.opcua.controller.R; -import com.qgs.dc.s7.service.S7Service; -import org.apache.plc4x.java.PlcDriverManager; -import org.apache.plc4x.java.api.PlcConnection; +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.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.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.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; 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 @RequestMapping("/s7") @@ -25,18 +27,47 @@ public class S7Controller { @Autowired S7Service s7Service; - - @PostMapping("/addThisPlc") - public R addThisPlc() throws PlcConnectionException { - //s7://192.168.1.51?remote-rack=0&remote-slot=3&controller-type=S7_400,如果参数不是默认的 要向这样往url 后面加。 - return R.ok().put("res",s7Service.addPlc("s7://192.168.0.100")); + //demo1 + @PostMapping("/testReadAll") + public R testReadAll() throws UnsupportedEncodingException, ParseException { + for(PlcVarActual actual:PlcVarActual.values()){ + 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 characters = (List)s7Service.read(PlcVarActual.CharArrays,S7Client.S7_1200); + List booleans = (List)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); } } diff --git a/src/main/java/com/qgs/dc/s7/entity/ReadedBack.java b/src/main/java/com/qgs/dc/s7/entity/ReadedBack.java new file mode 100644 index 0000000..28cfc9d --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/entity/ReadedBack.java @@ -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 content; +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/Constant/S7ClientArg.java b/src/main/java/com/qgs/dc/s7/my/s7connector/Constant/S7ClientArg.java new file mode 100644 index 0000000..303d30b --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/Constant/S7ClientArg.java @@ -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; + } + + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/Main2.java b/src/main/java/com/qgs/dc/s7/my/s7connector/Main2.java new file mode 100644 index 0000000..29bbf32 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/Main2.java @@ -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 st = new Stack(); + int c = 4-in.length; + in = invert(in); + for(int i=0;i st = new Stack(); + for(int i=0;i [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 就像这种情况的话,这个工具就无法读取了。。。。 这个后续可以改他的源码 就是addrees(Byte/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 举例:1(byteLength) * 2(arrayLength) =2(bytes) + byte[] boolArrays = connector.read(DaveArea.DB, 3, 2, 830); + List booleans = ByteUtils.toBoolArray(boolArrays); + System.out.println("DB3.830-boolArrays : " +booleans ); + + byte[] byteArrays = connector.read(DaveArea.DB, 3, 2, 832); + List bytes = ByteUtils.toByteArray(byteArrays); + System.out.println("DB3.832-byteArrays : " +bytes ); + + byte[] charArrays = connector.read(DaveArea.DB, 3, 2, 834); + List chars = ByteUtils.toCharArray(charArrays); + System.out.println("DB3.834-charArrays : " +chars ); + + byte[] wordArrays = connector.read(DaveArea.DB, 3, 4, 836); + List words = ByteUtils.toWordArray(wordArrays); + System.out.println("DB3.836-wordArrays : " +words ); + + byte[] dwordArrays = connector.read(DaveArea.DB, 3, 8, 840); + List dwords = ByteUtils.toDWordArray(dwordArrays); + System.out.println("DB3.840-dwordArrays : " +dwords ); + + byte[] sintArrays = connector.read(DaveArea.DB, 3, 2, 852); + List sints = ByteUtils.toSIntArray(sintArrays); + System.out.println("DB3.852-sintArrays : " +sints ); + + byte[] intArrays = connector.read(DaveArea.DB, 3, 4, 848); + List ints = ByteUtils.toIntArray(intArrays); + System.out.println("DB3.848-intArrays : " +ints ); + + byte[] dintArrays = connector.read(DaveArea.DB, 3, 8, 854); + List dints = ByteUtils.toDIntArray(dintArrays); + System.out.println("DB3.852-dintArrays : " +dints); + + + byte[] usintArrays = connector.read(DaveArea.DB, 3, 3, 3240); + List usints = ByteUtils.toUSIntArray(usintArrays); + System.out.println("DB3.3240-usintArrays : " +usints ); + + byte[] uintArrays = connector.read(DaveArea.DB, 3, 6, 3256); + List uints = ByteUtils.toUIntArray(uintArrays); + System.out.println("DB3.3256-uintArrays : " +uints ); + + byte[] udintArrays = connector.read(DaveArea.DB, 3, 12, 3244); + List 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(); + } + + + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/MainForWrite.java b/src/main/java/com/qgs/dc/s7/my/s7connector/MainForWrite.java new file mode 100644 index 0000000..751dae0 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/MainForWrite.java @@ -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 相关(sint、int、dint 。。 usint、uint、udint) + { + //[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 举例:1(byteLength) * 2(arrayLength) =2(bytes) + 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 booleans = ByteUtils.toBoolArray(boolArrays); + List booleans = (List) 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 bytes = ByteUtils.toByteArray(byteArrays); + List bytes = (List) 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 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 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 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 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 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 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 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 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 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(); + } + + + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/DaveArea.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/DaveArea.java new file mode 100644 index 0000000..e335a80 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/DaveArea.java @@ -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; + } +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Connector.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Connector.java new file mode 100644 index 0000000..a1deeb7 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Connector.java @@ -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); + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Serializable.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Serializable.java new file mode 100644 index 0000000..a27582e --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Serializable.java @@ -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 + * 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 extract(Class 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); +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Serializer.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Serializer.java new file mode 100644 index 0000000..bb9720b --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/S7Serializer.java @@ -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 + * 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 dispense(Class beanClass, int dbNum, int byteOffset) throws S7Exception; + + /** + * Dispense. + * + * @param + * 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 dispense(Class 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); + +} \ No newline at end of file diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/annotation/Array.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/annotation/Array.java new file mode 100644 index 0000000..a023f85 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/annotation/Array.java @@ -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(); +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/annotation/Datablock.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/annotation/Datablock.java new file mode 100644 index 0000000..95026f4 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/annotation/Datablock.java @@ -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 { +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/annotation/S7Variable.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/annotation/S7Variable.java new file mode 100644 index 0000000..a622b48 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/annotation/S7Variable.java @@ -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(); + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/factory/S7ConnectorFactory.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/factory/S7ConnectorFactory.java new file mode 100644 index 0000000..ebd75fd --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/factory/S7ConnectorFactory.java @@ -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(); + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/factory/S7SerializerFactory.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/factory/S7SerializerFactory.java new file mode 100644 index 0000000..65fb383 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/factory/S7SerializerFactory.java @@ -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); + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/api/utils/ByteUtils.java b/src/main/java/com/qgs/dc/s7/my/s7connector/api/utils/ByteUtils.java new file mode 100644 index 0000000..6c9f782 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/api/utils/ByteUtils.java @@ -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 st = new Stack(); + for(int i=0;i(b.length-2)){ + return null; + } + byte[] content = new byte[b.length-2]; + for(int i=0;i toBoolArray(byte[] b) throws UnsupportedEncodingException { + List res = new ArrayList<>(); + for(int i=0;i toByteArray(byte[] b) throws UnsupportedEncodingException { + List res = new ArrayList<>(); + for(int i=0;i toCharArray(byte[] b) throws UnsupportedEncodingException { + List res = new ArrayList<>(); + for(int i=0;i 有符号的整形 + * */ + public static List toWordArray(byte[] b) throws UnsupportedEncodingException { + List 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 toDWordArray(byte[] b) throws UnsupportedEncodingException { + List 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 toUSIntArray(byte[] b) throws UnsupportedEncodingException { + List res = new ArrayList<>(); + for(int i=0;i toUIntArray(byte[] b) throws UnsupportedEncodingException { + List 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 toUDIntArray(byte[] b) throws UnsupportedEncodingException { + List 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 toSIntArray(byte[] b) throws UnsupportedEncodingException { + List res = new ArrayList<>(); + for(int i=0;i toIntArray(byte[] b) throws UnsupportedEncodingException { + List 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 toDIntArray(byte[] b) throws UnsupportedEncodingException { + List 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> 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; + } + + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/blocks/CONT_C.java b/src/main/java/com/qgs/dc/s7/my/s7connector/blocks/CONT_C.java new file mode 100644 index 0000000..857c89a --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/blocks/CONT_C.java @@ -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 + "]"; + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/PlcVarActual.java b/src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/PlcVarActual.java new file mode 100644 index 0000000..b62cfe5 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/PlcVarActual.java @@ -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; + } +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/S7Client.java b/src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/S7Client.java new file mode 100644 index 0000000..227c2ea --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/enmuc/S7Client.java @@ -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 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 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); + } + } +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/S7TCPConnection.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/S7TCPConnection.java new file mode 100644 index 0000000..433645f --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/S7TCPConnection.java @@ -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); + } + + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/Nodave.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/Nodave.java new file mode 100644 index 0000000..9533c8b --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/Nodave.java @@ -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 + } + +} \ No newline at end of file diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PDU.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PDU.java new file mode 100644 index 0000000..c20325e --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PDU.java @@ -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 =》 msgOut、msgIn + * 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 Address,3-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 Address,3-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; + + //下面两个方法,是拼接 keyItem、valueItem + 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 Type(1byte): 消息类型 : + //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 == 7(4字节TPKT + 3个字节COTP) + //this.param = 7+12 = 19; + this.param = this.header + this.hlen; + //读取S7-Header 里面的Param-length(2个字节转为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-length(2个字节转为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; + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PLCinterface.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PLCinterface.java new file mode 100644 index 0000000..3ce3c8b --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/PLCinterface.java @@ -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; + } + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/Result.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/Result.java new file mode 100644 index 0000000..5dadac3 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/Result.java @@ -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; +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/ResultSet.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/ResultSet.java new file mode 100644 index 0000000..adf7e78 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/ResultSet.java @@ -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; + }; +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/S7Connection.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/S7Connection.java new file mode 100644 index 0000000..ed58d1a --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/S7Connection.java @@ -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, //Function:step 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; + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/TCPConnection.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/TCPConnection.java new file mode 100644 index 0000000..f47ae82 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/nodave/TCPConnection.java @@ -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-code:dst-tsap 上位机 + (byte) 0x02, //parameter-length + (byte) 0x01, //Source rack 机架 + (byte) 0x00, //Source slot 槽位号 + + (byte) 0xC2, //parameter-code:dst-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-code:tpdu-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 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 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; + } + + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/S7SerializerImpl.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/S7SerializerImpl.java new file mode 100644 index 0000000..a4e1434 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/S7SerializerImpl.java @@ -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 + * the generic type + * @param beanClass + * the bean class + * @param buffer + * the buffer + * @param byteOffset + * the byte offset + * @return the t + */ + public static T extractBytes(final Class 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 dispense(final Class 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 dispense(final Class 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); + } + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/BitConverter.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/BitConverter.java new file mode 100644 index 0000000..8b264a6 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/BitConverter.java @@ -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 extract(final Class 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; + } + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/ByteConverter.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/ByteConverter.java new file mode 100644 index 0000000..d5a58fb --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/ByteConverter.java @@ -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 extract(final Class 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; + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/DateAndTimeConverter.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/DateAndTimeConverter.java new file mode 100644 index 0000000..2db0bf5 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/DateAndTimeConverter.java @@ -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 extract(final Class 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; + } + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/DateConverter.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/DateConverter.java new file mode 100644 index 0000000..e312625 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/DateConverter.java @@ -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 extract(final Class 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); + } + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/IntegerConverter.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/IntegerConverter.java new file mode 100644 index 0000000..6d68d27 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/IntegerConverter.java @@ -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 extract(final Class 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; + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/LongConverter.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/LongConverter.java new file mode 100644 index 0000000..340f9ab --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/LongConverter.java @@ -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 extract(final Class 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; + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/RealConverter.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/RealConverter.java new file mode 100644 index 0000000..48f7d72 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/RealConverter.java @@ -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 extract(final Class 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); + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/StringConverter.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/StringConverter.java new file mode 100644 index 0000000..7339397 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/StringConverter.java @@ -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 extract(final Class 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); + } + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/StructConverter.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/StructConverter.java new file mode 100644 index 0000000..15b659a --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/StructConverter.java @@ -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 extract(final Class 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); + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/TimeConverter.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/TimeConverter.java new file mode 100644 index 0000000..3a0db00 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/converter/TimeConverter.java @@ -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 extract(final Class 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); + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/parser/BeanEntry.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/parser/BeanEntry.java new file mode 100644 index 0000000..c2e8b20 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/parser/BeanEntry.java @@ -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; +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/parser/BeanParseResult.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/parser/BeanParseResult.java new file mode 100644 index 0000000..107155c --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/parser/BeanParseResult.java @@ -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 entries = new Vector(); + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/parser/BeanParser.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/parser/BeanParser.java new file mode 100644 index 0000000..4bba269 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/serializer/parser/BeanParser.java @@ -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()); + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/utils/S7Type.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/utils/S7Type.java new file mode 100644 index 0000000..eb194cb --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/utils/S7Type.java @@ -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 serializer; + + /** + * Enum Constructor + * + * @param serializer + * @param byteSize + * @param bitSize + */ + S7Type(final Class 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 getSerializer() { + return this.serializer; + } +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/impl/utils/S7Utils.java b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/utils/S7Utils.java new file mode 100644 index 0000000..57b1ec0 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/impl/utils/S7Utils.java @@ -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. + } +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/service/S7Service.java b/src/main/java/com/qgs/dc/s7/my/s7connector/service/S7Service.java new file mode 100644 index 0000000..f2ea98b --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/service/S7Service.java @@ -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 + * ByteArray ===> List + * WordArray ===> List + * DWordArray ===> List + * CharArray ===> List + * SIntArray ===> List + * IntArray ===> List + * DIntArray ===> List + * UIntArray ===> List + * USIntArray ===> List + * UDIntArray ===> List + * + * + * + * */ + 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() + ); + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/type/DataTransportSize.java b/src/main/java/com/qgs/dc/s7/my/s7connector/type/DataTransportSize.java new file mode 100644 index 0000000..0decdb5 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/type/DataTransportSize.java @@ -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 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 enumsForFieldSizeInBits(boolean fieldValue) { + List _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); + } + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/type/PlcVar.java b/src/main/java/com/qgs/dc/s7/my/s7connector/type/PlcVar.java new file mode 100644 index 0000000..3ed25ee --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/type/PlcVar.java @@ -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 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 + * ByteArray ===> List + * WordArray ===> List + * DWordArray ===> List + * CharArray ===> List + * SIntArray ===> List + * IntArray ===> List + * DIntArray ===> List + * UIntArray ===> List + * USIntArray ===> List + * UDIntArray ===> List + * + * + * + * */ + 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 代表读一个数组。) + * */ + + + +} diff --git a/src/main/java/com/qgs/dc/s7/my/s7connector/type/TransportSize.java b/src/main/java/com/qgs/dc/s7/my/s7connector/type/TransportSize.java new file mode 100644 index 0000000..b54cd20 --- /dev/null +++ b/src/main/java/com/qgs/dc/s7/my/s7connector/type/TransportSize.java @@ -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 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 enumsForFieldSupported_S7_300(boolean fieldValue) { + List _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 enumsForFieldSupported_LOGO(boolean fieldValue) { + List _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 enumsForFieldCode(short fieldValue) { + List _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 enumsForFieldSizeInBytes(short fieldValue) { + List _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 enumsForFieldSupported_S7_400(boolean fieldValue) { + List _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 enumsForFieldSupported_S7_1200(boolean fieldValue) { + List _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 enumsForFieldShortName(short fieldValue) { + List _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 enumsForFieldSupported_S7_1500(boolean fieldValue) { + List _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 enumsForFieldDataTransportSize(DataTransportSize fieldValue) { + List _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 enumsForFieldBaseType(TransportSize fieldValue) { + List _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 enumsForFieldDataProtocolId(String fieldValue) { + List _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); + } + +} diff --git a/src/main/java/com/qgs/dc/s7/service/S7Service.java b/src/main/java/com/qgs/dc/s7/service/S7Service.java deleted file mode 100644 index 222d2e0..0000000 --- a/src/main/java/com/qgs/dc/s7/service/S7Service.java +++ /dev/null @@ -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 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 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 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 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); - } -}