diff --git a/pom.xml b/pom.xml index 0b6df18..f82caa9 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,8 @@ ym-basic ym-schedule-task ym-websocket + ym-packing + ym-s7 pom diff --git a/ym-gateway/pom.xml b/ym-gateway/pom.xml index 713f8da..455a248 100644 --- a/ym-gateway/pom.xml +++ b/ym-gateway/pom.xml @@ -3,8 +3,8 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - ym-pass com.cnbm + ym-pass 1.0-SNAPSHOT 4.0.0 @@ -22,11 +22,31 @@ ym-common 1.0-SNAPSHOT + + com.cnbm + ym-s7 + 1.0-SNAPSHOT + + + + + com.cnbm + ym-packing + 1.0-SNAPSHOT + + com.cnbm ym-admin 1.0-SNAPSHOT + + + com.cnbm + ym-packing + 1.0-SNAPSHOT + + com.cnbm ym-barcode diff --git a/ym-gateway/src/main/java/com/cnbm/config/SwaggerConfig.java b/ym-gateway/src/main/java/com/cnbm/config/SwaggerConfig.java index 730a480..d84ac01 100644 --- a/ym-gateway/src/main/java/com/cnbm/config/SwaggerConfig.java +++ b/ym-gateway/src/main/java/com/cnbm/config/SwaggerConfig.java @@ -54,6 +54,22 @@ public class SwaggerConfig { .securitySchemes(Arrays.asList(new ApiKey("token", "token", "header"))); } + @Bean + public Docket packingApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("ym-packing") + .apiInfo(apiInfo("包装模块", "V1")) + .useDefaultResponseMessages(true) + .forCodeGeneration(false) + .select() + .apis(RequestHandlerSelectors.basePackage("com.cnbm.packing")) + .paths(PathSelectors.any()) + .build() + .securityContexts(Arrays.asList(securityContext())) + // ApiKey的name需与SecurityReference的reference保持一致 + .securitySchemes(Arrays.asList(new ApiKey("token", "token", "header"))); + } + @Bean public Docket adminApi() { return new Docket(DocumentationType.SWAGGER_2) @@ -69,6 +85,21 @@ public class SwaggerConfig { // ApiKey的name需与SecurityReference的reference保持一致 .securitySchemes(Arrays.asList(new ApiKey("token", "token", "header"))); } + @Bean + public Docket s7Api() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("ym-s7") + .apiInfo(apiInfo("基于西门子S7协议采集模块", "基于西门子S7协议采集模块")) + .useDefaultResponseMessages(true) + .forCodeGeneration(false) + .select() + .apis(RequestHandlerSelectors.basePackage("com.cnbm.s7")) + .paths(PathSelectors.any()) + .build() + .securityContexts(Arrays.asList(securityContext())) + // ApiKey的name需与SecurityReference的reference保持一致 + .securitySchemes(Arrays.asList(new ApiKey("token", "token", "header"))); + } /** * 创建该API的基本信息(这些基本信息会展现在文档页面中) diff --git a/ym-gateway/src/main/resources/application.yml b/ym-gateway/src/main/resources/application.yml index dd83b23..e2a1db0 100644 --- a/ym-gateway/src/main/resources/application.yml +++ b/ym-gateway/src/main/resources/application.yml @@ -7,7 +7,7 @@ server: min-spare: 30 port: 8080 servlet: - context-path: /ym-pass + context-path: /ym-cigs4 session: cookie: http-only: true diff --git a/ym-packing/pom.xml b/ym-packing/pom.xml new file mode 100644 index 0000000..c3b729f --- /dev/null +++ b/ym-packing/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + com.cnbm + ym-pass + 1.0-SNAPSHOT + + + ym-packing + + + 8 + 8 + UTF-8 + + + + + com.cnbm + ym-common + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/ym-packing/src/main/java/com/cnbm/packing/Main.java b/ym-packing/src/main/java/com/cnbm/packing/Main.java new file mode 100644 index 0000000..c8e809d --- /dev/null +++ b/ym-packing/src/main/java/com/cnbm/packing/Main.java @@ -0,0 +1,7 @@ +package com.cnbm.packing; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/ym-packing/src/main/java/com/cnbm/packing/controller/TestController.java b/ym-packing/src/main/java/com/cnbm/packing/controller/TestController.java new file mode 100644 index 0000000..f61c559 --- /dev/null +++ b/ym-packing/src/main/java/com/cnbm/packing/controller/TestController.java @@ -0,0 +1,24 @@ +package com.cnbm.packing.controller; + +import io.swagger.annotations.Api; +import org.springframework.web.bind.annotation.*; +import com.cnbm.common.utils.Result; +/** + * @Author weihongyang + * @Date 2022/6/24 8:57 AM + * @Version 1.0 + */ +@RestController +@RequestMapping("/packing") +public class TestController { + + @GetMapping("get") + public Result get(){ + return new Result().ok(1); + } + + @PostMapping("get2") + public Result get2(){ + return new Result().ok(2); + } +} diff --git a/ym-s7/pom.xml b/ym-s7/pom.xml new file mode 100644 index 0000000..6145cba --- /dev/null +++ b/ym-s7/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.cnbm + ym-pass + 1.0-SNAPSHOT + + + ym-s7 + + + 8 + 8 + UTF-8 + + + + + + org.springframework.retry + spring-retry + 1.2.2.RELEASE + + + org.testng + testng + 6.11 + compile + + + junit + junit + + + + + \ No newline at end of file diff --git a/ym-s7/src/main/java/com/cnbm/Main.java b/ym-s7/src/main/java/com/cnbm/Main.java new file mode 100644 index 0000000..ed8fa4f --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/Main.java @@ -0,0 +1,7 @@ +package com.cnbm; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/ym-s7/src/main/java/com/cnbm/s7/controller/S7DemoController.java b/ym-s7/src/main/java/com/cnbm/s7/controller/S7DemoController.java new file mode 100644 index 0000000..98976f9 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/controller/S7DemoController.java @@ -0,0 +1,270 @@ +package com.cnbm.s7.controller; + +import com.cnbm.s7.entity.R; +import com.cnbm.s7.entity.AGVInfoCallBack; + +import com.cnbm.s7.s7connector.enmuc.PlcVarActual; + + +import com.cnbm.s7.s7connector.enmuc.S7Client; +import com.cnbm.s7.s7connector.type.PlcVar; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.*; +import java.io.UnsupportedEncodingException; +import java.text.ParseException; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +@RestController +@RequestMapping("/s7") +public class S7DemoController { + private static final Logger logger = LoggerFactory.getLogger(S7DemoController.class); + +// @Autowired +// InfluxDBClient influxDBClient; +// +// @PostMapping("/insert") +// public void insert() throws InterruptedException { +// Event event = new Event(); +// event.setTime(Instant.now()); +// event.setTransationId("asasd11"); +// event.setArgName("argName11"); +// event.setArgValue(7d); +// Point asProcessCompleteEvent = insert(event, "ASProcessCompleteEventSSS"); +// influxDBClient.makeWriteApi().writePoint(asProcessCompleteEvent); +// } + + @PostMapping(value = "testFor") + private void testFor(@RequestBody AGVInfoCallBack agvInfoCallBack) { + + System.out.println(agvInfoCallBack.toString()); + } + + //demo1 + @PostMapping("/testReadAll") + public R testReadAll() throws UnsupportedEncodingException, ParseException { + for(PlcVarActual actual:PlcVarActual.values()){ + System.out.println(read(S7Client.S7_1500,actual)); + } + return R.ok(); + } +// public Point insert(Event event, String measurement){ +// Point point = Point.measurement(measurement) +// .addTag("transationId", event.getTransationId()) +// .addTag("argName", event.getArgName()) +// .addField("argValue", event.getArgValue()) +// .time(event.getTime().toEpochMilli(), WritePrecision.MS); +// return point; +// } + //demo2 + @PostMapping("/readTest") + public R getTestForS7() throws UnsupportedEncodingException, ParseException { + Boolean heartBeat = (Boolean)read(S7Client.S7_1500,PlcVarActual.HeartBeat); + String ddtl = (String)read(S7Client.S7_1500,PlcVarActual.DTL); + List characters = (List)read(S7Client.S7_1500,PlcVarActual.CharArrays); + + List booleans = (List)read(S7Client.S7_1500,PlcVarActual.BooleanArrays); + + String stri = (String)read(S7Client.S7_1500,PlcVarActual.STRING1); + + return R.ok().put("res",heartBeat).put("characters",characters).put("ddtl",ddtl).put("bools",booleans).put("str",stri); + } + + + @PostMapping("/readTest2") + public R getTest2ForS7() throws Exception { +// List characters = (List)read(PlcVarActual.CharArrays,S7Client.S7_1500); +// +// List booleans = (List)read(PlcVarActual.BooleanArrays,S7Client.S7_1500); +// String stri = (String)read(PlcVarActual.STRING1,S7Client.S7_1500); +// +// return R.ok().put("res",heartBeat).put("characters",characters).put("ddtl",ddtl).put("bools",booleans).put("str",stri); + String con = "1233ADSDA"; + write(S7Client.S7_1500,PlcVarActual.STRING1, con); + String read = (String)read(S7Client.S7_1500,PlcVarActual.STRING1); + String sub0 = (String)read(S7Client.S7_1500,PlcVarActual.SUBID0); + + + return R.ok().put("str",read).put("sub0",sub0); + } + + @PostMapping("/testWriteBooleanArray") + public R testWriteBooleanArray() throws Exception { + + List string = (List)read(S7Client.S7_1500,PlcVarActual.BooleanArrays1200); + System.out.println(); + boolean[] res = new boolean[15]; + res[0] = false; + res[1] = true; + res[2] = false; + res[3] = true; + res[4] = false; + res[5] = true; + res[6] = false; + res[7] = false; + res[8] = false; + res[9] = true; + res[10] = false; + res[11] = false; + res[12] = true; + res[13] = false; + res[14] = true; + write(S7Client.S7_1500,PlcVarActual.BooleanArrays1200, res); + System.out.println(); + return R.ok(); + } + + @PostMapping("/testForString") + public R testForStrings() throws Exception { + //测试结果 l => 66ms + long l = System.currentTimeMillis(); + String[] subs = (String[])read(S7Client.S7_1500,PlcVarActual.SubIdArrays); + long l1 = System.currentTimeMillis(); + + long dl = System.currentTimeMillis(); + String string = (String)read(S7Client.S7_1500,PlcVarActual.STRING1); + long dl1 = System.currentTimeMillis(); + + String[] toWrite = new String[60]; + for(int i=0;i<60;i++){ + int i1 = new Random().nextInt(100); + toWrite[i] = "2212"+ i1; + } + ////测试结果 c => 57ms + long c1 = System.currentTimeMillis(); + write(S7Client.S7_1500,PlcVarActual.SubIdArrays,toWrite); + long c2 = System.currentTimeMillis(); + String s = "cai xiang"; + write(S7Client.S7_1500,PlcVarActual.STRING1,s); + + String string2 = (String)read(S7Client.S7_1500,PlcVarActual.STRING1); + String[] subs2 = (String[])read(S7Client.S7_1500,PlcVarActual.SubIdArrays); + + return R.ok().put("l",(l1-l)).put("c",(c2-c1)).put("dl",(dl1-dl)); + } + + @PostMapping("/testForString1200") + public R testForString1200() throws Exception { + //测试结果 l => 66ms + long l = System.currentTimeMillis(); + String[] subs = (String[])read(S7Client.S7_1500,PlcVarActual.SubIdArrays1200); //65ms + long l1 = System.currentTimeMillis(); + //System.out.println(Arrays.toString(subs)); + + String[] toWrite = new String[63]; + for(int i=0;i<63;i++){ + //int i1 = new Random().nextInt(100); + toWrite[i] = "abcd-"+ i; + } + ////测试结果 c => 57ms + long c1 = System.currentTimeMillis(); + write(S7Client.S7_1500,PlcVarActual.SubIdArrays1200,toWrite); + long c2 = System.currentTimeMillis(); + + String[] subs2 = (String[])read(S7Client.S7_1500,PlcVarActual.SubIdArrays1200); + logger.info("正常测试: l:"+(l1-l)+"c:"+(c2-c1)+";;read1:"+Arrays.toString(subs)+";;read2:"+Arrays.toString(subs2)); + + return R.ok().put("l",(l1-l)).put("c",(c2-c1)); + } + + @PostMapping("/testForString1500") + public R testForString1500() throws Exception { + //测试结果 l => 66ms + long l = System.currentTimeMillis(); + String[] subs = (String[])read(S7Client.S7_1500,PlcVarActual.SubIdArrays); //25ms + long l1 = System.currentTimeMillis(); + System.out.println(Arrays.toString(subs)); + String[] toWrite = new String[63]; + for(int i=0;i<63;i++){ + //int i1 = new Random().nextInt(100); + toWrite[i] = "abcd-"+ i; + } + ////测试结果 c => 57ms + long c1 = System.currentTimeMillis(); + write(S7Client.S7_1500,PlcVarActual.SubIdArrays,toWrite); + long c2 = System.currentTimeMillis(); + + + String[] subs2 = (String[])read(S7Client.S7_1500,PlcVarActual.SubIdArrays); + + return R.ok().put("l",(l1-l)).put("c",(c2-c1)); + } + + + + + + @PostMapping("/testFor1200") + public R testFor1200() throws Exception { + //Object subs = read(PlcVarActual.INT1200, S7Client.S7_1500); + Object read = read(S7Client.S7_1500, PlcVarActual.SubIdArrays1200); + + String[] toWrite = new String[60]; + for(int i=0;i<60;i++){ + int i1 = new Random().nextInt(100); + toWrite[i] = "2212"+ i1; + } + write(S7Client.S7_1500,PlcVarActual.SubIdArrays1200,toWrite); + + Object read2 = read(S7Client.S7_1500, PlcVarActual.SubIdArrays1200); + return R.ok(); + } + + + + //PlcVarActual 到时候改成你们的 xxxPlcToWcs 或者 xxxWcsToPlc + /** + * return + * 成功: 返回相应的object对象 + * 失败: 返回null + * */ + private Object read(S7Client s7Client,PlcVarActual var) throws UnsupportedEncodingException, ParseException { + try { + return s7Client.read(var.getArea(), var.getAreaNumber(), var.getByteOffset(), var.getBitOffset(), var.getLength(), var.getStrSize(), var.getType()); + }catch (Exception e){ + logger.error("host:"+s7Client.getHost()+" ; read 操作出现问题: "+e.getMessage()); + e.printStackTrace(); + return null; + } + } + private void write(S7Client s7Client,PlcVarActual var,Object newValue) throws Exception { + if(var.getType().equals(PlcVar.STRING_Array)){ + String[] s = (String[])newValue; + String[] ss = (String[])newValue; + if(s.length > var.getLength() ){ + ss = new String[var.getLength()]; + for(int i=0;i< var.getLength();i++){ + ss[i] = s[i]; + } + } + s7Client.write(var.getArea(), var.getAreaNumber(), var.getByteOffset(), var.getBitOffset(), var.getStrSize(), var.getType(),ss); + }else { + s7Client.write(var.getArea(), var.getAreaNumber(), var.getByteOffset(), var.getBitOffset(), var.getStrSize(), var.getType(),newValue); + } + } + +// //demo3 +// @PostMapping("/writeTest") +// public R writeTest() throws PlcConnectionException, UnsupportedEncodingException { +// write(S7Client.S7_1500,PlcVarActual.HeartBeat, false); +// +// char[] charArrays_content = new char[2]; +// charArrays_content[0] = '1'; +// charArrays_content[1] = 'c'; +// write( S7Client.S7_1500,PlcVarActual.CharArrays, charArrays_content); +// +// boolean[] boolArrays_content = new boolean[2]; +// boolArrays_content[0] = true; +// boolArrays_content[1] = false; +// write(S7Client.S7_1500,PlcVarActual.BooleanArrays, boolArrays_content); +// +// String str = "你好啊aa"; +// //todo string 的读写有问题 待会看看 +// write(S7Client.S7_1500,PlcVarActual.STRING1, str); +// return R.ok().put("res",true); +// } + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/demo/RetryDemoTask.java b/ym-s7/src/main/java/com/cnbm/s7/demo/RetryDemoTask.java new file mode 100644 index 0000000..f3c05ca --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/demo/RetryDemoTask.java @@ -0,0 +1,40 @@ +package com.cnbm.s7.demo; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomUtils; +import org.springframework.remoting.RemoteAccessException; + +/** + * @Author: zgd + * @Description: + */ +@Slf4j +public class RetryDemoTask { + + + /** + * 重试方法 + * @return + */ + public static boolean retryTask(String param) { + log.info("收到请求参数:{}",param); + + int i = RandomUtils.nextInt(0,11); + log.info("随机生成的数:{}",i); + if (i == 0) { + log.info("为0,抛出参数异常."); + throw new IllegalArgumentException("参数异常"); + }else if (i == 1){ + log.info("为1,返回true."); + return true; + }else if (i == 2){ + log.info("为2,返回false."); + return false; + }else{ + //为其他 + log.info("大于2,抛出自定义异常."); + throw new RemoteAccessException("大于2,抛出远程访问异常"); + } + } + +} \ No newline at end of file diff --git a/ym-s7/src/main/java/com/cnbm/s7/demo/SpringRetryTemplateTest.java b/ym-s7/src/main/java/com/cnbm/s7/demo/SpringRetryTemplateTest.java new file mode 100644 index 0000000..59b6cab --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/demo/SpringRetryTemplateTest.java @@ -0,0 +1,72 @@ +package com.cnbm.s7.demo; + + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.remoting.RemoteAccessException; +import org.springframework.retry.backoff.FixedBackOffPolicy; +import org.springframework.retry.policy.SimpleRetryPolicy; +import org.springframework.retry.support.RetryTemplate; +import org.testng.annotations.Test; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Desc: "" + * @Author: caixiang + * @DATE: 2022/12/16 15:26 + */ +@Slf4j +public class SpringRetryTemplateTest { + + /** + * 重试间隔时间ms,默认1000ms + * */ + private long fixedPeriodTime = 1000L; + /** + * 最大重试次数,默认为3 + */ + private int maxRetryTimes = 3; + /** + * 表示哪些异常需要重试,key表示异常的字节码,value为true表示需要重试 + */ + private Map, Boolean> exceptionMap = new HashMap<>(); + + + @Test + public void test() { + exceptionMap.put(RemoteAccessException.class,true); + + // 构建重试模板实例 + RetryTemplate retryTemplate = new RetryTemplate(); + + // 设置重试回退操作策略,主要设置重试间隔时间 + FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); + backOffPolicy.setBackOffPeriod(fixedPeriodTime); + + // 设置重试策略,主要设置重试次数 + SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap); + + retryTemplate.setRetryPolicy(retryPolicy); + retryTemplate.setBackOffPolicy(backOffPolicy); + + Boolean execute = retryTemplate.execute( + //RetryCallback + retryContext -> { + boolean b = RetryDemoTask.retryTask("abc"); + log.info("调用的结果:{}", b); + return b; + }, + retryContext -> { + //RecoveryCallback + log.info("已达到最大重试次数或抛出了不重试的异常~~~"); + return false; + } + ); + + log.info("执行结果:{}",execute); + + } + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/entity/AGVInfoCallBack.java b/ym-s7/src/main/java/com/cnbm/s7/entity/AGVInfoCallBack.java new file mode 100644 index 0000000..c0b185b --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/entity/AGVInfoCallBack.java @@ -0,0 +1,24 @@ +package com.cnbm.s7.entity; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *

+ * 表 + *

+ * + * @author caixiang + * @since 2022-05-31 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class AGVInfoCallBack implements Serializable { + + private static final long serialVersionUID = 1L; + private String warehouseName; + private Integer status; + private String content; +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/entity/R.java b/ym-s7/src/main/java/com/cnbm/s7/entity/R.java new file mode 100644 index 0000000..9212c2f --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/entity/R.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2016-2019 人人开源 All rights reserved. + * + * https://www.renren.io + * + * 版权所有,侵权必究! + */ + +package com.cnbm.s7.entity; + + +import org.springframework.http.HttpStatus; + +import java.util.HashMap; +import java.util.Map; + +/** + * 返回数据 + * + * @author Mark sunlightcs@gmail.com + */ +public class R extends HashMap { + private static final long serialVersionUID = 1L; + //默认成功 是1 + public R() { + put("code", 1); + put("msg", "success"); + } + + public static R error() { + return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "未知异常,请联系管理员"); + } + + public static R error(String msg) { + return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg); + } + + public static R error(int code, String msg) { + R r = new R(); + r.put("code", code); + r.put("msg", msg); + return r; + } + + public static R ok(String msg) { + R r = new R(); + r.put("msg", msg); + return r; + } + + public static R ok(Map map) { + R r = new R(); + r.putAll(map); + return r; + } + + public static R ok() { + return new R(); + } + + @Override + public R put(String key, Object value) { + super.put(key, value); + return this; + } +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/entity/ReadedBack.java b/ym-s7/src/main/java/com/cnbm/s7/entity/ReadedBack.java new file mode 100644 index 0000000..d778e56 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/entity/ReadedBack.java @@ -0,0 +1,16 @@ +package com.cnbm.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/ym-s7/src/main/java/com/cnbm/s7/retry/S7RetryTemplate.java b/ym-s7/src/main/java/com/cnbm/s7/retry/S7RetryTemplate.java new file mode 100644 index 0000000..bf723a8 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/retry/S7RetryTemplate.java @@ -0,0 +1,64 @@ +package com.cnbm.s7.retry; + +import com.cnbm.s7.s7connector.exception.S7CheckResultException; +import com.cnbm.s7.s7connector.exception.S7IOException; +import com.cnbm.s7.s7connector.exception.S7ParseDataException; +import org.springframework.remoting.RemoteAccessException; +import org.springframework.retry.backoff.FixedBackOffPolicy; +import org.springframework.retry.policy.SimpleRetryPolicy; +import org.springframework.retry.support.RetryTemplate; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author caixiang + * @description 重试模板和重试策略 + */ +public class S7RetryTemplate extends RetryTemplate { + + private volatile static S7RetryTemplate instance = null; + /** + * 重试间隔时间ms,默认1000ms + * */ + private static long fixedPeriodTime = 500L; + /** + * 最大重试次数,默认为3 + */ + private static int maxRetryTimes = 3; + /** + * 表示哪些异常需要重试,key表示异常的字节码,value为true表示需要重试 + */ + private static Map, Boolean> exceptionMap = new HashMap<>(); + private S7RetryTemplate() { + // 代表S7IOException 这个异常是需要重试的(true), 如果你想设置这个异常不去重试,那么可以把它设置为false。 + exceptionMap.put(S7IOException.class,true); + exceptionMap.put(S7CheckResultException.class,true); + exceptionMap.put(S7ParseDataException.class,true); + //Exception + //exceptionMap.put(Exception.class,true); + + } + public static int getMaxRetryTimes() { + return maxRetryTimes; + } + + public static S7RetryTemplate getInstance() { + if (instance == null) { + synchronized (S7RetryTemplate.class) { + if (instance == null) { + FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); + // 定义重试间隔-间隔 fixedPeriodTime ms再重试,总共重试3次 + backOffPolicy.setBackOffPeriod(fixedPeriodTime); + instance = new S7RetryTemplate(); + // 定义重试次数- maxRetryTimes + SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap); + instance.setRetryPolicy(retryPolicy); + + instance.setBackOffPolicy(backOffPolicy); + } + } + } + return instance; + } +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/retrydemo/RetryDemoTask.java b/ym-s7/src/main/java/com/cnbm/s7/retrydemo/RetryDemoTask.java new file mode 100644 index 0000000..00b8e11 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/retrydemo/RetryDemoTask.java @@ -0,0 +1,94 @@ +package com.cnbm.s7.retrydemo; + + +import com.cnbm.s7.s7connector.exception.S7CheckResultException; +import com.cnbm.s7.s7connector.impl.S7BaseConnection; +import com.cnbm.s7.s7connector.impl.nodave.Nodave; +import org.apache.commons.lang3.RandomUtils; +import org.springframework.remoting.RemoteAccessException; + +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.Arrays; + +/** + * @Author: cx + * @Description: + */ +public class RetryDemoTask { + + public static void main(String[] args) { + PipedInputStream pipedInputStream=new PipedInputStream(); + PipedOutputStream pipedOutputStream=new PipedOutputStream(); + try { + pipedInputStream.connect(pipedOutputStream); + //默认一次最多只能写入1024字节 + byte[] data=new byte[1000]; + byte[] store=new byte[20]; + Arrays.fill(data, (byte)1); + System.out.println("first writing data"); + //每次写1000字节数据 + pipedOutputStream.write(data,0,data.length); + System.out.println("finish first writing"); + int count=1; + while(count<100){ + System.out.println(count+" times read data"); + pipedInputStream.read(store, 0, store.length); //每次读20字节数据 + System.out.println(count+" times finish reading data"); + System.out.println((count+1)+" times write data"); + pipedOutputStream.write(data);//每次写1000字节数据 + System.out.println((count+1)+" times finish writing data"); + count++; + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 重试方法 + * @return + */ + public synchronized static boolean retryTask(String param) { + System.out.println("retry-task : 收到请求参数:"+param); + + int i = RandomUtils.nextInt(0,11); + System.out.println("retry-task : 随机生成的数:"+i); + if (i == 0) { + System.out.println("retry-task : 为0,抛出参数异常."); + //因为Illeague这个异常我们没有在exceptionMap里面配置,所以 抛出这个异常后 + //spring-retry不会进行重试,而是会直接进入recovery函数 + throw new IllegalArgumentException("retry-task : 参数异常"); + }else if (i == 1){ + System.out.println("retry-task : 为1,返回true."); + return true; + }else if (i == 2){ + System.out.println("retry-task : 为2,返回false."); + return false; + }else{ + //因为RemoteAccessExcep这个异常我们在exceptionMap里面配置了,所以 抛出这个异常后 + //spring-retry会进行重试 + System.out.println("retry-task : 大于2,抛出自定义异常."); + throw new RemoteAccessException("retry-task : 大于2,抛出远程访问异常"); + } + } + public static void checkResults(final int libnodaveResult) { + if (libnodaveResult != Nodave.RESULT_OK) { + final String msg = Nodave.strerror(libnodaveResult); +// throw new IllegalArgumentException("Result: " + msg); + throw new S7CheckResultException("errMsg : "+msg); + } + } + + public synchronized static boolean retryTask2(String param) { + System.out.println("retry-task : 收到请求参数:"+param+" hashCode:"+Thread.currentThread().hashCode()); + + int i = 1; + System.out.println("retry-task : 随机生成的数:"+i+" hashCode:"+Thread.currentThread().hashCode()); + checkResults(i); + Integer a =1; + a=a+1; + return true; + } +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/retrydemo/RetryMain.java b/ym-s7/src/main/java/com/cnbm/s7/retrydemo/RetryMain.java new file mode 100644 index 0000000..1966ab5 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/retrydemo/RetryMain.java @@ -0,0 +1,60 @@ +package com.cnbm.s7.retrydemo; + +import com.cnbm.s7.s7connector.exception.S7CheckResultException; +import org.springframework.remoting.RemoteAccessException; +import org.springframework.retry.backoff.FixedBackOffPolicy; +import org.springframework.retry.policy.SimpleRetryPolicy; +import org.springframework.retry.support.RetryTemplate; + +import java.util.HashMap; +import java.util.Map; + +public class RetryMain { + public static void main(String[] args) { + + /** + * 重试间隔时间ms,默认1000ms + * */ + long fixedPeriodTime = 1000L; + /** + * 最大重试次数,默认为3 + */ + int maxRetryTimes = 3; + /** + * 表示哪些异常需要重试,key表示异常的字节码,value为true表示需要重试 + */ + Map, Boolean> exceptionMap = new HashMap<>(); + + + exceptionMap.put(RemoteAccessException.class,true); + exceptionMap.put(S7CheckResultException.class,true); + + // 构建重试模板实例 + RetryTemplate retryTemplate = new RetryTemplate(); + + // 设置重试回退操作策略,主要设置重试间隔时间 + FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); + backOffPolicy.setBackOffPeriod(fixedPeriodTime); + + // 设置重试策略,主要设置重试次数 + SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap); + + retryTemplate.setRetryPolicy(retryPolicy); + retryTemplate.setBackOffPolicy(backOffPolicy); + + Boolean execute = retryTemplate.execute( + //RetryCallback + retryContext -> { + boolean b = RetryDemoTask.retryTask2("abc"); + System.err.println("retry-main : 调用的结果:"+b+",times:"+retryContext.getRetryCount()+" hashCode:"+Thread.currentThread().hashCode()); + return b; + }, + retryContext -> { + //RecoveryCallback + System.err.println("retry-main : 已达到最大重试次数或抛出了不重试的异常~~~"+" hashCode:"+Thread.currentThread().hashCode()); + return false; + } + ); + System.err.println("retry-main : 执行结果:"+execute); + } +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/retrydemo/SpringS7RetryTemplateTest.java b/ym-s7/src/main/java/com/cnbm/s7/retrydemo/SpringS7RetryTemplateTest.java new file mode 100644 index 0000000..155b10f --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/retrydemo/SpringS7RetryTemplateTest.java @@ -0,0 +1,66 @@ +package com.cnbm.s7.retrydemo; + +import org.junit.Test; +import org.springframework.remoting.RemoteAccessException; +import org.springframework.retry.backoff.FixedBackOffPolicy; +import org.springframework.retry.policy.SimpleRetryPolicy; +import org.springframework.retry.support.RetryTemplate; +import java.util.HashMap; +import java.util.Map; + +/** + * @Author: zgd + * @Description: spring-retry 重试框架 + */ +public class SpringS7RetryTemplateTest { + + /** + * 重试间隔时间ms,默认1000ms + * */ + private long fixedPeriodTime = 1000L; + /** + * 最大重试次数,默认为3 + */ + private int maxRetryTimes = 3; + /** + * 表示哪些异常需要重试,key表示异常的字节码,value为true表示需要重试 + */ + private Map, Boolean> exceptionMap = new HashMap<>(); + + + @Test + public void test() { + + //文档:https://blog.csdn.net/minghao0508/article/details/123972703 + exceptionMap.put(RemoteAccessException.class,true); + + // 构建重试模板实例 + RetryTemplate retryTemplate = new RetryTemplate(); + + // 设置重试回退操作策略,主要设置重试间隔时间 + FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); + backOffPolicy.setBackOffPeriod(fixedPeriodTime); + + // 设置重试策略,主要设置重试次数 + SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap); + + retryTemplate.setRetryPolicy(retryPolicy); + retryTemplate.setBackOffPolicy(backOffPolicy); + + Boolean execute = retryTemplate.execute( + //RetryCallback + retryContext -> { + boolean b = RetryDemoTask.retryTask("abc"); + System.out.println("调用的结果:"+b); + return b; + }, + retryContext -> { + //RecoveryCallback + System.out.println("已达到最大重试次数或抛出了不重试的异常~~~"); + return false; + } + ); + System.out.println("执行结果:"+execute); + } + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/retrydemo/TestMain.java b/ym-s7/src/main/java/com/cnbm/s7/retrydemo/TestMain.java new file mode 100644 index 0000000..6ce33a2 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/retrydemo/TestMain.java @@ -0,0 +1,20 @@ +package com.cnbm.s7.retrydemo; + +import java.util.ArrayList; +import java.util.List; + +public class TestMain { + public static void main(String[] args) { + Integer a = 1; + Integer a1 = 2; + Integer a2 = 3; + List list = new ArrayList(); + list.add(a); + list.add(a1); + list.add(a2); + Integer integer = list.get(0); + list.remove(integer); + System.out.println(); + + } +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/retrydemo/lock/LockDemo.java b/ym-s7/src/main/java/com/cnbm/s7/retrydemo/lock/LockDemo.java new file mode 100644 index 0000000..e1e7e3d --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/retrydemo/lock/LockDemo.java @@ -0,0 +1,49 @@ +package com.cnbm.s7.retrydemo.lock; + +/** + * @Desc: "" + * @Author: caixiang + * @DATE: 2022/12/23 12:08 + */ +public class LockDemo { + + //对象锁 + public synchronized void func1() { + System.out.println("func1!"); + try { + Thread.sleep(10000); + System.out.println("sleep time over!"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public synchronized void func2() { + System.out.println("func2!"); + } + + public static void main(String[] args) { + + LockDemo test2 = new LockDemo(); + Thread t1 = new Thread(() -> { +// test.func1(); +// try { +// S7ClientNew.S7_1500.retry1(); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } + }, "t1"); + + Thread t2 = new Thread(() -> { +// test2.func2(); +// try { +// S7ClientNew.S7_1500.retry1(); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } + }, "t2"); + + t1.start(); + t2.start(); + } +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/Constant/S7ClientArg.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/Constant/S7ClientArg.java new file mode 100644 index 0000000..d1f732e --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/Constant/S7ClientArg.java @@ -0,0 +1,17 @@ +package com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/Main2.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/Main2.java new file mode 100644 index 0000000..6c31fb4 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/Main2.java @@ -0,0 +1,233 @@ +package com.cnbm.s7.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= 0; i--) { //对于byte的每bit进行判定 + + array[i] = (b & 1) == 1; //判定byte的最后一位是否为1,若为1,则是true;否则是false + + b = (byte) (b >> 1); //将byte右移一位 + + } + if(returns){ + boolean[] array1 = new boolean[8]; + array1[0] = array[7]; + array1[1] = array[6]; + array1[2] = array[5]; + array1[3] = array[4]; + array1[4] = array[3]; + array1[5] = array[2]; + array1[6] = array[1]; + array1[7] = array[0]; + array = array1; + } + + return array; + + } + + public static void main(String[] args) throws UnsupportedEncodingException { + //1000 0101 + //1010 0001 + + } + + +// public static void main(String[] args) throws Exception { +// //前言: +// //DB3.1.1 中间那个1是byte区,后面那个1 是bit +// //缺陷: 不支持 DB3.1.1 +// +// //Create connection +//// S7Connector connector = +//// S7ConnectorFactory +//// .buildTCPConnector() +//// .withHost("192.168.0.51") +//// .withRack(0) //optional rack 是机架号 +//// .withSlot(0) //optional slot 是插槽号 +//// .build(); +// +// S7Connector connector = S7Client.S7_1500.getConnector(); +// +// // // [0] +//// byte[] bool = connector.read(DaveArea.DB, 3, 1, 3266,0); +//// byte[] bool2 = connector.read(DaveArea.DB, 3, 1, 3266,1); +//// System.out.println("DB3.0-bool : " + ByteUtils.toBoolean(bool)); +//// System.out.println("DB3.0-bool2 : " + ByteUtils.toBoolean(bool2)); +// +// +// +// //bool3 => [1] 0000 0001 ==> 1000 0000 10....... +// //bool3 => [3] 0000 0011 ==> 1100 0000 11....... +// //bool3 => [10] 0000 1010 ==> 0101 0000 0101..... +// //bool3 => [25] 0001 1001 ==> 1001 1000 10011.... +// byte[] bool3 = connector.read(DaveArea.DB, 3, 1, 3267); +// System.out.println("DB3.0-bool3 : " + ByteUtils.toBoolean(bool3)); +// +// byte[] boolArrays = connector.read(DaveArea.DB, 3, 11, 3262); +// List booleans = ByteUtils.toBoolArray(boolArrays); +// System.out.println("DB3.830-boolArrays : " +booleans ); +// +// byte[] boolArrays14 = connector.read(DaveArea.DB, 3, 16, 3264); +// List booleans14 = ByteUtils.toBoolArray(boolArrays14); +// System.out.println("DB3.3268-boolArrays : " +booleans14 ); +// +// // +//// 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/ym-s7/src/main/java/com/cnbm/s7/s7connector/MainForReadDemo.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/MainForReadDemo.java new file mode 100644 index 0000000..159e08b --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/MainForReadDemo.java @@ -0,0 +1,70 @@ +package com.cnbm.s7.s7connector; + +import com.cnbm.s7.s7connector.api.DaveArea; +import com.cnbm.s7.s7connector.api.S7Connector; +import com.cnbm.s7.s7connector.api.utils.ByteUtils; +import com.cnbm.s7.s7connector.enmuc.S7Client; + + +/** + * @Desc: "" + * @Author: caixiang + * @DATE: 2021/12/10 10:17 + */ +public class MainForReadDemo { + public static void main(String[] args) throws Exception { + +// //1200 部分 +// S7Connector connector1200 = S7Client.S7_1200.getConnector(); +// +// byte[] bool3_1200 = connector1200.read(DaveArea.DB, 1, 1, 0); +// System.out.println("DB1-bool3_1200 : " + ByteUtils.toBoolean(bool3_1200)); +//// byte[] bool4_1200 = connector1200.read(DaveArea.DB, 3, 1, 3267,5,PlcVar.BOOL.getTransportSize()); +//// System.out.println("DB1-bool4_1200 : " + ByteUtils.toBoolean(bool4_1200)); + + //1500 部分 + S7Connector connector1500 = S7Client.S7_1500.getConnector(); +// byte[] bool11 = connector1500.read(DaveArea.DB, 3, 1, 3267); +// //30110012105301115 +// //[18, 17, 51, 48, 49, 49, 48, 48, 49, 50, 49, 48, 53, 51, 48, 49, 49, 49, 53, 0] +// // 3 0 +// byte[] str1500 = connector1500.read(DaveArea.DB, 3, 20, 3270); +// byte[] str15001 = connector1500.read(DaveArea.DB, 3, 20, 3290); +// byte[] bool1 = connector1500.read(DaveArea.DB, 3, 1, 3267,4, PlcVar.BOOL.getTransportSize()); +// String str1500s = ByteUtils.toStr(str1500); +// System.out.println("DB3.0-str1500 : " + str1500s); +// System.out.println("DB3.0-bool1 : " + ByteUtils.toBoolean(bool1)); + + + + //String + //write + String one = "12111"; + byte[] bytes1 = ByteUtils.strToBytes(one, 18); + connector1500.write(DaveArea.DB,3,3270,bytes1); + + //read + byte[] str_1500 = connector1500.read(DaveArea.DB, 3, 20, 3270); + String string = ByteUtils.toStr(str_1500); + System.out.println(string); + + + //StringArray + //write + String[] src = new String[3]; + src[0]= "1"; + src[1]= "30110012105301117222"; + src[2]= "3"; + byte[] bytes = ByteUtils.strArrayToBytes(src, 18); + connector1500.write(DaveArea.DB,3,3270,bytes); + + //read + byte[] strList_1500 = connector1500.read(DaveArea.DB, 3, 60, 3270); + String[] strings = ByteUtils.toStrArray(strList_1500, 3, 18); + System.out.println(strings); + + } + + + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/MainForWrite.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/MainForWrite.java new file mode 100644 index 0000000..5fcbb80 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/MainForWrite.java @@ -0,0 +1,391 @@ +package com.cnbm.s7.s7connector; + +import com.cnbm.s7.s7connector.api.DaveArea; +import com.cnbm.s7.s7connector.api.S7Connector; +import com.cnbm.s7.s7connector.api.factory.S7ConnectorFactory; +import com.cnbm.s7.s7connector.api.utils.ByteUtils; +import com.cnbm.s7.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 Exception { + //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/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/DaveArea.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/DaveArea.java new file mode 100644 index 0000000..9a2e584 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/DaveArea.java @@ -0,0 +1,53 @@ +/* +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.cnbm.s7.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 //DB块 + DI(0x85), //DBI块 + FLAGS(0x83), //M块(M块 的 areaNumber 都是0 , 只配置 byteOffset 和 bitOffset) + INPUTS(0x81), //I块 + T(0x1D), //T块ed + C(0x1C), //C块 + + LOCAL(0x86), // data blocks //LD块 + OUTPUTS(0x82), // instance data blocks //Q块 + P(0x80), // not tested //D块 + 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/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/S7Connector.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/S7Connector.java new file mode 100644 index 0000000..3cb2ab6 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.api; + +import com.cnbm.s7.s7connector.type.PlcVar; +import com.cnbm.s7.s7connector.type.TransportSize; + +import java.io.Closeable; + +public interface S7Connector extends Closeable { + /** + * Reads an area + * desc : 只要未抛出异常,都是 操作成功的 ( 锁 ) + * @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变量的时候调用这个方法。) + * desc : 只要未抛出异常,都是 操作成功的 ( 锁 ) + * @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 + * desc : 只要未抛出异常,都是 操作成功的 ( 锁 ) + * @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/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/S7Serializable.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/S7Serializable.java new file mode 100644 index 0000000..0ac5c52 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.api; + +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/S7Serializer.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/S7Serializer.java new file mode 100644 index 0000000..75d7354 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.api; + +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/annotation/Array.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/annotation/Array.java new file mode 100644 index 0000000..5cb6c96 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/annotation/Datablock.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/annotation/Datablock.java new file mode 100644 index 0000000..05bca93 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/annotation/S7Variable.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/annotation/S7Variable.java new file mode 100644 index 0000000..ca25ddc --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.api.annotation; + +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/factory/S7ConnectorFactory.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/factory/S7ConnectorFactory.java new file mode 100644 index 0000000..310591a --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.api.factory; + +import com.cnbm.s7.s7connector.api.S7Connector; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/factory/S7SerializerFactory.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/factory/S7SerializerFactory.java new file mode 100644 index 0000000..14fdbec --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.api.factory; + +import com.cnbm.s7.s7connector.api.S7Connector; +import com.cnbm.s7.s7connector.api.S7Serializer; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/utils/ByteUtil.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/utils/ByteUtil.java new file mode 100644 index 0000000..ce3b07d --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/utils/ByteUtil.java @@ -0,0 +1,328 @@ +package com.cnbm.s7.s7connector.api.utils; + +import java.nio.ByteOrder; + +/** + * @Desc: "" + * @Author: caixiang + * @DATE: 2022/4/1 17:32 + */ +//对数字和字节进行转换。 假设数据存储是以大端模式存储的: +// byte: 字节类型 占8位二进制 00000000 +// char: 字符类型 占2个字节 16位二进制 byte[0] byte[1] +// int : 整数类型 占4个字节 32位二进制 byte[0] byte[1] byte[2] byte[3] +// long: 长整数类型 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] +// long: 长整数类型 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] +// float: 浮点数(小数) 占4个字节 32位二进制 byte[0] byte[1] byte[2] byte[3] +// double: 双精度浮点数(小数) 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4]byte[5] byte[6] byte[7] + +public class ByteUtil { + + /** + * int转byte + * + * @param intValue int值 + * @return byte值 + */ + public static byte intToByte(int intValue) { + return (byte) intValue; + } + + /** + * byte转无符号int + * + * @param byteValue byte值 + * @return 无符号int值 + * @since 3.2.0 + */ + public static int byteToUnsignedInt(byte byteValue) { + // Java 总是把 byte 当做有符处理;我们可以通过将其和 0xFF 进行二进制与得到它的无符值 + return byteValue & 0xFF; + } + + /** + * byte数组转short
+ * 默认以小端序转换 + * + * @param bytes byte数组 + * @return short值 + */ + public static short bytesToShort(byte[] bytes) { + return bytesToShort(bytes, ByteOrder.LITTLE_ENDIAN); + } + + /** + * byte数组转short
+ * 自定义端序 + * + * @param bytes byte数组 + * @param byteOrder 端序 + * @return short值 + */ + public static short bytesToShort(byte[] bytes, ByteOrder byteOrder) { + if (ByteOrder.LITTLE_ENDIAN == byteOrder) { + //小端模式,数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中 + return (short) (bytes[0] & 0xff | (bytes[1] & 0xff) << Byte.SIZE); + } else { + return (short) (bytes[1] & 0xff | (bytes[0] & 0xff) << Byte.SIZE); + } + } + + /** + * short转byte数组
+ * 默认以小端序转换 + * + * @param shortValue short值 + * @return byte数组 + */ + public static byte[] shortToBytes(short shortValue) { + return shortToBytes(shortValue, ByteOrder.LITTLE_ENDIAN); + } + + /** + * short转byte数组
+ * 自定义端序 + * + * @param shortValue short值 + * @param byteOrder 端序 + * @return byte数组 + */ + public static byte[] shortToBytes(short shortValue, ByteOrder byteOrder) { + byte[] b = new byte[Short.BYTES]; + if (ByteOrder.LITTLE_ENDIAN == byteOrder) { + b[0] = (byte) (shortValue & 0xff); + b[1] = (byte) ((shortValue >> Byte.SIZE) & 0xff); + } else { + b[1] = (byte) (shortValue & 0xff); + b[0] = (byte) ((shortValue >> Byte.SIZE) & 0xff); + } + return b; + } + + /** + * byte[]转int值
+ * 默认以小端序转换 + * + * @param bytes byte数组 + * @return int值 + */ + public static int bytesToInt(byte[] bytes) { + return bytesToInt(bytes, ByteOrder.LITTLE_ENDIAN); + } + + /** + * byte[]转int值
+ * 自定义端序 + * + * @param bytes byte数组 + * @param byteOrder 端序 + * @return int值 + */ + public static int bytesToInt(byte[] bytes, ByteOrder byteOrder) { + if (ByteOrder.LITTLE_ENDIAN == byteOrder) { + return bytes[0] & 0xFF | // + (bytes[1] & 0xFF) << 8 | // + (bytes[2] & 0xFF) << 16 | // + (bytes[3] & 0xFF) << 24; // + } else { + return bytes[3] & 0xFF | // + (bytes[2] & 0xFF) << 8 | // + (bytes[1] & 0xFF) << 16 | // + (bytes[0] & 0xFF) << 24; // + } + + } + + /** + * int转byte数组
+ * 默认以小端序转换 + * + * @param intValue int值 + * @return byte数组 + */ + public static byte[] intToBytes(int intValue) { + return intToBytes(intValue, ByteOrder.LITTLE_ENDIAN); + } + + /** + * int转byte数组
+ * 自定义端序 + * + * @param intValue int值 + * @param byteOrder 端序 + * @return byte数组 + */ + public static byte[] intToBytes(int intValue, ByteOrder byteOrder) { + + if (ByteOrder.LITTLE_ENDIAN == byteOrder) { + return new byte[]{ // + (byte) (intValue & 0xFF), // + (byte) ((intValue >> 8) & 0xFF), // + (byte) ((intValue >> 16) & 0xFF), // + (byte) ((intValue >> 24) & 0xFF) // + }; + + } else { + return new byte[]{ // + (byte) ((intValue >> 24) & 0xFF), // + (byte) ((intValue >> 16) & 0xFF), // + (byte) ((intValue >> 8) & 0xFF), // + (byte) (intValue & 0xFF) // + }; + } + + } + + /** + * long转byte数组
+ * 默认以小端序转换
+ * from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java + * + * @param longValue long值 + * @return byte数组 + */ + public static byte[] longToBytes(long longValue) { + return longToBytes(longValue, ByteOrder.LITTLE_ENDIAN); + } + + /** + * long转byte数组
+ * 自定义端序
+ * from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java + * + * @param longValue long值 + * @param byteOrder 端序 + * @return byte数组 + */ + public static byte[] longToBytes(long longValue, ByteOrder byteOrder) { + byte[] result = new byte[Long.BYTES]; + if (ByteOrder.LITTLE_ENDIAN == byteOrder) { + for (int i = 0; i < result.length; i++) { + result[i] = (byte) (longValue & 0xFF); + longValue >>= Byte.SIZE; + } + } else { + for (int i = (result.length - 1); i >= 0; i--) { + result[i] = (byte) (longValue & 0xFF); + longValue >>= Byte.SIZE; + } + } + return result; + } + + /** + * byte数组转long
+ * 默认以小端序转换
+ * from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java + * + * @param bytes byte数组 + * @return long值 + */ + public static long bytesToLong(byte[] bytes) { + return bytesToLong(bytes, ByteOrder.LITTLE_ENDIAN); + } + + /** + * byte数组转long
+ * 自定义端序
+ * from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java + * + * @param bytes byte数组 + * @param byteOrder 端序 + * @return long值 + */ + public static long bytesToLong(byte[] bytes, ByteOrder byteOrder) { + long values = 0; + if (ByteOrder.LITTLE_ENDIAN == byteOrder) { + for (int i = (Long.BYTES - 1); i >= 0; i--) { + values <<= Byte.SIZE; + values |= (bytes[i] & 0xff); + } + } else { + for (int i = 0; i < Long.BYTES; i++) { + values <<= Byte.SIZE; + values |= (bytes[i] & 0xff); + } + } + + return values; + } + + /** + * double转byte数组
+ * 默认以小端序转换
+ * + * @param doubleValue double值 + * @return byte数组 + */ + public static byte[] doubleToBytes(double doubleValue) { + return doubleToBytes(doubleValue, ByteOrder.LITTLE_ENDIAN); + } + + /** + * double转byte数组
+ * 自定义端序
+ * from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java + * + * @param doubleValue double值 + * @param byteOrder 端序 + * @return byte数组 + */ + public static byte[] doubleToBytes(double doubleValue, ByteOrder byteOrder) { + return longToBytes(Double.doubleToLongBits(doubleValue), byteOrder); + } + + /** + * byte数组转Double
+ * 默认以小端序转换
+ * + * @param bytes byte数组 + * @return long值 + */ + public static double bytesToDouble(byte[] bytes) { + return bytesToDouble(bytes, ByteOrder.LITTLE_ENDIAN); + } + + /** + * byte数组转double
+ * 自定义端序
+ * + * @param bytes byte数组 + * @param byteOrder 端序 + * @return long值 + */ + public static double bytesToDouble(byte[] bytes, ByteOrder byteOrder) { + return Double.longBitsToDouble(bytesToLong(bytes, byteOrder)); + } + + /** + * 将{@link Number}转换为 + * + * @param number 数字 + * @return bytes + */ + public static byte[] numberToBytes(Number number) { + return numberToBytes(number, ByteOrder.LITTLE_ENDIAN); + } + + /** + * 将{@link Number}转换为 + * + * @param number 数字 + * @param byteOrder 端序 + * @return bytes + */ + public static byte[] numberToBytes(Number number, ByteOrder byteOrder) { + if (number instanceof Double) { + return doubleToBytes((Double) number, byteOrder); + } else if (number instanceof Long) { + return longToBytes((Long) number, byteOrder); + } else if (number instanceof Integer) { + return intToBytes((Integer) number, byteOrder); + } else if (number instanceof Short) { + return shortToBytes((Short) number, byteOrder); + } else { + return doubleToBytes(number.doubleValue(), byteOrder); + } + } +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/utils/ByteUtils.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/utils/ByteUtils.java new file mode 100644 index 0000000..1cd1d20 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/api/utils/ByteUtils.java @@ -0,0 +1,933 @@ +package com.cnbm.s7.s7connector.api.utils; + +/** + * @Desc: "" + * @Author: caixiang + * @DATE: 2021/12/16 9:08 + */ + +import com.cnbm.s7.s7connector.exception.S7ParseDataException; +import com.cnbm.s7.s7connector.utils.CommonFunctions; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +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) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); // 日期格式 + Date date = null; + try { + date = dateFormat.parse(timeParam); // 指定日期 + }catch (Exception e){ + throw new S7ParseDataException("ByteUtils.addDate error:", e); + } + + long time = date.getTime(); // 得到指定日期的毫秒数 + day = day * 24 * 60 * 60 * 1000; // 要加上的天数转换成毫秒数 + 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= 0; i--) { //对于byte的每bit进行判定 + + array[i] = (b & 1) == 1; //判定byte的最后一位是否为1,若为1,则是true;否则是false + + b = (byte) (b >> 1); //将byte右移一位 + + } + if(returns){ + boolean[] array1 = new boolean[8]; + array1[0] = array[7]; + array1[1] = array[6]; + array1[2] = array[5]; + array1[3] = array[4]; + array1[4] = array[3]; + array1[5] = array[2]; + array1[6] = array[1]; + array1[7] = array[0]; + array = array1; + } + + return array; + + } + + + public static Byte fromBooleanArray(boolean[] a) { + byte b = 0; + if (a[7]) + b += 1; + if (a[6]) + b += 2; + if (a[5]) + b += 4; + if (a[4]) + b += 8; + if (a[3]) + b += 16; + if (a[2]) + b += 32; + if (a[1]) + b += 64; + if (a[0]) + b += 128; + return b; + } + + public static List toBoolArray(byte[] b) { + List res = new ArrayList<>(); + for(int i=0;i queue = new LinkedList(); + for(int i=0;i0){ + boolean[] input = new boolean[8]; + + if(boolLength>=8){ + for(int i = 0;i<8;i++){ + input[i]= queue.poll(); + } + + + Byte aByte = fromBooleanArray(reverse(input)); + res[z] = aByte; + z++; + }else { + for(int i=0;i toBoolArray(byte[] b) throws UnsupportedEncodingException { +// List res = new ArrayList<>(); +// for(int i=0;i toByteArray(byte[] b) { + List res = new ArrayList<>(); + for(int i=0;i toCharArray(byte[] b) { + List res = new ArrayList<>(); + for(int i=0;i 有符号的整形 + * */ + public static List toWordArray(byte[] b) { + 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) { + 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) { + List res = new ArrayList<>(); + for(int i=0;i toUIntArray(byte[] b) { + 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) { + 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) { + List res = new ArrayList<>(); + for(int i=0;i toIntArray(byte[] b) { + 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) { + 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;istrSize){ + res[1] = strSize.byteValue(); + for(int i=0;i bytes[] +// String[] src = new String[3]; +// src[0]= "1"; +// src[1]= "30110012105301117222"; +// src[2]= "3"; +// byte[] bytes = strArrayToBytes(src, 18); +// String[] strings = toStrArray(bytes, 3, 18); +// System.out.println(bytes); +// +// //string <=> bytes[] +// String s = "12323221"; +// byte[] bytes1 = strToBytes(s, 18); +// String s1 = toStr(bytes1); +// System.out.println(s1); + + boolean[] res = new boolean[22]; + res[0] = false; + res[1] = true; + res[2] = false; + res[3] = true; + res[4] = false; + res[5] = true; + res[6] = false; + res[7] = false; + + res[8] = false; + res[9] = true; + res[10] = false; + res[11] = false; + res[12] = true; + res[13] = false; + res[14] = true; + res[15] = false; + + + res[16] = true; + res[17] = false; + res[18] = false; + res[19] = false; + res[20] = true; + res[21] = false; + + byte[] bytes = toByteArray(res); + + Integer integer = CommonFunctions.exactDivision(7, 8); + System.out.println(); + + + } + + /** + * 参数 + * desc : 把字符串数组 ==> byte[] [x,x,..,..,..,..,... x,x,..,..,..,..,... ...] + * array : 就是需要被写入plc的内容,, array.length 就是你要写入数组的长度。 注意array.length必须小于plc中设置的 “数组变量” length + * strSize : 就是数组中某个变量的长度 比如 String str = new String[18] 这里的18就是strSize,但是在博图偏移量地址上可以发现它已经把你+2了,就是你设置最大string长度18 但偏移量=18+2=20;; + * 而上面的array.length是 List list = new ArrayList<>; 的list.length + * 返回 + * byte[] 就是整个数组字符串的 总的字节流 + * + * */ + public static byte[] strArrayToBytes(String[] array,Integer strSize) { + //str0 [18, 17, 51, 48, 49, 49, 48, 48, 49, 50, 49, 48, 53, 51, 48, 49, 49, 49, 53] + //str1 [18, 18, 51, 48, 49, 49, 48, 48, 49, 50, 49, 48, 53, 51, 48, 49, 49, 49, 53, 49] + //str2 [18, 18, 51, 48, 49, 49, 48, 48, 49, 50, 49, 48, 53, 51, 48, 49, 49, 49, 53, 50] + + //list [18, 17, 51, 48, 49, 49, 48, 48, 49, 50, 49, 48, 53, 51, 48, 49, 49, 49, 53, 0, 18, 18, 51, 48, 49, 49, 48, 48, 49, 50, 49, 48, 53, 51, 48, 49, 49, 49, 53, 49, 18, 18, 51, 48, 49, 49, 48, 48, 49, 50, 49, 48, 53, 51, 48, 49, 49, 49, 53, 50] + //strSize+2 以后就是 实际plc 变量的字节数了。 + Integer allStrSize = strSize+2; + byte[] res = new byte[array.length*allStrSize]; + 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/ym-s7/src/main/java/com/cnbm/s7/s7connector/blocks/CONT_C.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/blocks/CONT_C.java new file mode 100644 index 0000000..b188c6b --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.blocks; + +import com.cnbm.s7.s7connector.api.annotation.S7Variable; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/enmuc/PlcVarActual.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/enmuc/PlcVarActual.java new file mode 100644 index 0000000..f2b92c8 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/enmuc/PlcVarActual.java @@ -0,0 +1,111 @@ +package com.cnbm.s7.s7connector.enmuc; + +import com.cnbm.s7.s7connector.api.DaveArea; +import com.cnbm.s7.s7connector.type.PlcVar; + +//实际 Plc中的变量。。。 要实现录入到这个枚举类中。。。 为后续做准备。 +//注意: +// ① 单体变量 length 都是1 +// ② 数组变量 length 就是 你要read 或者write数组的长度 +// ③ bitOffset 是针对Bool 来说的,其他变量bitOffset 都是0 +// ④ strSize 这个字段只是给 String变量 和 String[]变量 用的, 这个字段是plc中设置的最大String长度 +// ⑤ String类型 在write的时候禁止 write中文,因为plc中编码并没有采用UTF-8 而是采用ASCII +// (同理也是,当plc中有设置String类型或者StringArray那么告诉他们必须指定长度 通常他们也会指定长度的。) +public enum PlcVarActual { + //心跳变量(这个可以要求电控同事加一个,不和业务关联,只用于通讯) + HeartBeat("HeartBeat",PlcVar.BOOL,1,DaveArea.DB,3,3267,5), + HeartBeatFor1200("HeartBeatFor1200",PlcVar.BOOL,1,DaveArea.DB,1,0,0), + + //单体变量 + 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,20), + SUBID0("SUBID0",PlcVar.STRING,1,DaveArea.DB,3,3270,0), + INT1200("INT1200",PlcVar.INT,1,DaveArea.DB,1,2,0), + + //数组变量 + CharArrays("CharArrays",PlcVar.CHAR_Array,2,DaveArea.DB,3,834,0), + BooleanArrays("BooleanArrays",PlcVar.BOOL_Array,2,DaveArea.DB,3,830,0), + + BooleanArrays16("BooleanArrays16",PlcVar.BOOL_Array,16,DaveArea.DB,3,3268,0), + BooleanArrays8("BooleanArrays8",PlcVar.BOOL_Array,1,DaveArea.DB,3,3262,0), + + + SubIdArrays("SubIdArrays",PlcVar.STRING_Array,60,DaveArea.DB,3,3268,0,18), + SubIdArrays1200("SubIdArrays1200",PlcVar.STRING_Array,60,DaveArea.DB,1,20,0,18), + + + BooleanArrays1200("BooleanArrays1200",PlcVar.BOOL_Array,15,DaveArea.DB,3,3264,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; + + //这个字段只是给 字符串变量 和 字符串数组 用, 这个字段是plc中设置的最大String长度 + private Integer strSize; + + 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; + this.strSize = 0; + } + PlcVarActual(String name, PlcVar type,Integer length, DaveArea area, Integer areaNumber, Integer byteOffset, Integer bitOffset,Integer strSize){ + this.name = name; + this.type = type; + this.length = length; + this.area = area; + this.areaNumber = areaNumber; + this.byteOffset = byteOffset; + this.bitOffset = bitOffset; + this.strSize = strSize; + } + + public Integer getStrSize() { + return strSize; + } + + public void setStrSize(Integer strSize) { + this.strSize = strSize; + } + + 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/ym-s7/src/main/java/com/cnbm/s7/s7connector/enmuc/S7Client.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/enmuc/S7Client.java new file mode 100644 index 0000000..9234962 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/enmuc/S7Client.java @@ -0,0 +1,400 @@ +package com.cnbm.s7.s7connector.enmuc; + + +import com.cnbm.s7.s7connector.api.DaveArea; +import com.cnbm.s7.s7connector.api.S7Connector; +import com.cnbm.s7.s7connector.api.factory.S7ConnectorFactory; +import com.cnbm.s7.s7connector.api.utils.ByteUtils; +import com.cnbm.s7.s7connector.exception.S7CheckResultException; +import com.cnbm.s7.s7connector.exception.S7Exception; +import com.cnbm.s7.s7connector.type.PlcVar; +import com.cnbm.s7.s7connector.utils.CommonFunctions; +import com.cnbm.s7.retry.S7RetryTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + + +/** + * @Desc: "" + * @Author: caixiang + * @DATE: 2022/1/15 13:01 + */ +public enum S7Client { + //TODO 步骤1 这里是配置多PLC 的,,,有多个plc 就在这里配置一个枚举类 + //1500 西门子200smart、1200、1500默认的 机架号=0 槽位号=1; 300/400 默认的 机架-0 插槽-2 +// S7_1200("192.168.0.52",0,1,1,PlcVarActual.HeartBeatFor1200), + S7_15001("192.168.0.51",0,1,1), + S7_1500("192.168.0.51",0,1,1), + //1500 机架-0 插槽-1 + //后续 在这里扩展 多PLC应用。 + ; + private String host; + //默认 0 机架号 + private Integer rack; + //默认 0 + private Integer slot; + + //心跳变量,如果plc没有让电控的人加一个,这个是必填的 + private PlcVarActual heartBeat; + + private List connections; + //coreSize 是线程池的大小 + private Integer coreSize; + //pickOne 就是一个初始化 的轮询取余值 + private int pickOne; + private static final Logger logger = LoggerFactory.getLogger(S7Client.class); + + + + + //coreSize 是线程池的数量 + S7Client(String host, Integer rack, Integer slot, Integer coreSize){ + this.host = host; + this.rack = rack; + this.slot = slot; + this.pickOne = 0; + + this.coreSize = coreSize; + + connections = new ArrayList<>(); + connectionPool(); + + } + + public String getHost(){ + return this.host; + } + + /** + * PlcVar(byte[]) 转 java对象 对照表 + * 单体变量 + * Bool ===> Boolean + * LREAL ===> Double + * REAL ===> Float + * DATE ===> String(yyyy-MM-dd 这种形式的类型) + * DTL ===> String("年-月-日-工作日-时-分-秒" 这种格式) + * TIME ===> Integer(单位 ms) + * USINT ===> Integer + * SINT ===> Integer + * UINT ===> Integer + * INT ===> Integer + * DINT ===> Integer + * UINT ===> Long + * Byte ===> Integer(有符号)(默认) + * Integer(无符号)(后续扩展) + * Char ===> Character + * WChar ===> Character + * String ===> String (特殊) + + + * 数组变量 + * BoolArray ===> List + * ByteArray ===> List + * WordArray ===> List + * DWordArray ===> List + * CharArray ===> List + * SIntArray ===> List + * IntArray ===> List + * DIntArray ===> List + * UIntArray ===> List + * USIntArray ===> List + * UDIntArray ===> List + * StringArray ===> String[] (特殊) + * + * 如果返回null,就代表出现了异常,并且尝试了 retryMax 次数,并且尝试重置连接 + * */ + public Object read(DaveArea area, Integer areaNumber, Integer byteOffset, Integer bitOffset, Integer length, Integer strSizes, PlcVar type) { + S7Connector connector = getConnector(); + if(connector == null){ + logger.info("read No connector found , 现在connections pool为空,并且创建connection失败 "); + return null; + } + return S7RetryTemplate.getInstance().execute( + context -> { + + try { + //String 类型比较特殊。 String[] 也是同理,Sring数组里面的子项 也是有两个字节的 readBytes。 + if(type.equals(PlcVar.STRING)){ + Integer readBytes = 2; + byte[] read = connector.read( + area, + areaNumber, + readBytes, + byteOffset, + bitOffset, + type.getTransportSize() + ); + + Integer allLength = Integer.valueOf(read[1])+2; + byte[] readF = connector.read( + area, + areaNumber, + allLength, + byteOffset, + bitOffset, + type.getTransportSize() + ); + return type.toObject(readF); + }else if(type.equals(PlcVar.BOOL_Array)){ + byte[] read = connector.read( + area, + areaNumber, + CommonFunctions.exactDivision(length,8), + byteOffset, + bitOffset, + type.getTransportSize() + ); + List booleans = ByteUtils.toBoolArray(read); + List res = new ArrayList(); + for(int i=0;i { + logger.info("S7-Retry : 已达到最大重试次数: "+S7RetryTemplate.getMaxRetryTimes()+", 现在尝试重新连接;"); + if(replaceConnector(connector)==1){ + logger.info("S7-Retry-Read 现在恢复成功,创建新connection成功"); + return null; + }else { + logger.info("S7-Retry-Read 现在恢复失败,创建新connection失败"); + return null; + } + } + ); + } + + + + /** + * + * eg : + * Object newValue = Boolean.FALSE + * s7Service.write(PlcVarActual.HeartBeat, newValue, S7Client.S7_1200); + * + * PlcVar(byte[]) 转 java对象 对照表 + * 单体变量 + * Bool ===> Object newValue = Boolean.FALSE + * LREAL ===> Object newValue = Boolean.FALSE + * REAL ===> Object newValue = Boolean.FALSE + * DATE ===> 暂时没需求(有问题找我) + * DTL ===> 暂时没需求(有问题找我) + * TIME ===> 暂时没需求(有问题找我) + * USINT ===> Object newValue = new Integer(1) + * SINT ===> Object newValue = new Integer(1) + * UINT ===> Object newValue = new Integer(1) + * INT ===> Object newValue = new Integer(1) + * DINT ===> Object newValue = new Integer(1) + * UINT ===> Object newValue = new Integer(1) + * Byte ===> Object newValue = 0x11 + * + * Char ===> Object newValue = 'a' + * WChar ===> Object newValue = '菜' + * String ===> Object newValue = '你好啊' (特殊) + + + * 数组变量 + * 注意:在write的时候,你write的数量 一定要和 plc中存在的数量一一对应 + * BoolArray ===> boolean[] booleanArray = new boolean[2]; .... 赋予值 + * ByteArray ===> byte[] write_byteArrays = new byte[2]; + * WordArray ===> short[] shortArrays_content = new short[2]; + * DWordArray ===> int[] intArrays_content = new int[2]; + * CharArray ===> char[] charArrays_content = new char[2]; + * SIntArray ===> int[] sintArrays_content = new int[2]; + * IntArray ===> int[] iintArrays_content = new int[2]; + * DIntArray ===> int[] dintArrays_content = new int[2]; + * UIntArray ===> int[] uintArrays_content = new int[3]; + * USIntArray ===> int[] usintArrays_content = new int[3]; + * UDIntArray ===> int[] udintArrays_content = new int[3]; + * StringArray ===> String[] stringArrays_content = new String[3]; + * //如果有其他数据类型 这里没有找我扩展 + * + * + * */ + public void write(DaveArea area, Integer areaNumber, Integer byteOffset, Integer bitOffset,Integer strSize, PlcVar type, Object newValue) { + S7Connector connector = getConnector(); + if(connector == null){ + logger.info("write No connector found , 现在connections pool为空,并且创建connection失败 "); + return; + } + S7RetryTemplate.getInstance().execute( + context -> { + try { + //String 类型比较特殊。 String[] 也是同理,Sring数组里面的子项 也是有两个字节的 readBytes。 + if(type.equals(PlcVar.STRING)){ + connector.write( + area, + areaNumber, + byteOffset, + bitOffset, + ByteUtils.strToBytes(newValue.toString(), strSize), + type + ); + }else if(type.equals(PlcVar.BOOL_Array)){ + connector.write( + area, + areaNumber, + byteOffset, + bitOffset, + ByteUtils.toByteArray((boolean[])newValue), + type + ); + }else if(type.equals(PlcVar.STRING_Array)){ + //todo here 检查 read write service + connector.write( + area, + areaNumber, + byteOffset, + bitOffset, + ByteUtils.strArrayToBytes((String[])newValue, strSize), + type + ); + }else { + byte[] bytes = type.toBytes(newValue); + connector.write( + area, + areaNumber, + byteOffset, + bitOffset, + bytes, + type + ); + } + return null; + }catch (Exception e) { + throw new S7CheckResultException("write errMsg : "+e.getMessage()); + } + }, + context -> { + logger.info("S7-Retry-Write : 已达到最大重试次数: "+S7RetryTemplate.getMaxRetryTimes()+", 现在尝试重新连接"); + if( replaceConnector(connector) == 1 ){ + logger.info("S7-Retry-Write 现在恢复成功,创建新connection成功"); + return null; + }else { + logger.info("S7-Retry-Write 现在恢复失败,创建新connection失败"); + return null; + } + } + ); + } + +// public void retry1() throws InterruptedException { +// S7RetryTemplate instance = S7RetryTemplate.getInstance(); +// System.out.println(instance.hashCode()+ ", "+Thread.currentThread().getName()); +// instance.execute( +// retryContext -> { +// System.out.println("retry1 : " + Thread.currentThread().getName() ); +// Thread.sleep(10000); +// System.out.println("retry1 sleepdown : " + Thread.currentThread().getName() ); +// return true; +// }, +// retryContext -> { +// System.out.println("retry1 err: " + Thread.currentThread().getName() ); +// return false; +// } +// ); +// } + + /** + * desc: 传入的connection 是需要被替换的 + * return: + * 1 代表替换成功 + * -1 代表替换失败(创建connection失败,原因:出现异常.原来的connection也被舍弃掉了) + * */ + private synchronized Integer replaceConnector(S7Connector oldOne){ + connections.remove(oldOne); + + S7Connector connect = connect(host, rack, slot); + if(connect == null){ + return -1; + }else { + connections.add(connect); + return 1; + } + + } + + //虽然是枚举类,但这个是对象锁(不是类锁) + public synchronized S7Connector getConnector() { + int size = connections.size(); + if(size == 0){ + S7Connector connect = connect(host, rack, slot); + if(connect == null){ + return null; + }else { + connections.add(connect); + return connect; + } + } + + S7Connector s7Connector = connections.get((pickOne + size) % size); + pickOne+=1; + pickOne = (pickOne)%size; + return s7Connector; + } + + + + private synchronized S7Connector connect(String host,Integer rack,Integer slot ){ + try { + S7Connector connector = S7ConnectorFactory + .buildTCPConnector() + .withHost(host) + .withRack(rack) //optional rack 是机架号 + .withSlot(slot) //optional slot 是插槽号 + .build(); + return connector; + }catch (S7Exception e){ + logger.info("创建S7Connector 连接失败,原因:"+e.getMessage()); +// throw new S7CheckResultException("errMsg : "+e.getMessage()); + return null; + } + + } + + private void resetConnetctions(){ + this.connections = new ArrayList(); + connectionPool(); + } + + private synchronized void connectionPool(){ + for(int i=0;i map; + + static { + map = new HashMap<>(); + for (S7ParamErrorCode subevent : S7ParamErrorCode .values()) { + map.put(subevent.code, subevent); + } + } + + private final String event; + private final short code; + + S7ParamErrorCode(short code, String event){ + this.event = event; + this.code = code; + } + + public String getEvent(){ + return event; + } + + public short getCode() { + return code; + } + + public static S7ParamErrorCode valueOfEvent(String event) { + for (S7ParamErrorCode value : S7ParamErrorCode .values()) { + if(value.getEvent().equals(event)) { + return value; + } + } + return null; + } + public static S7ParamErrorCode valueOf(short code) { + return map.get(code); + } +} \ No newline at end of file diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7CheckResultException.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7CheckResultException.java new file mode 100644 index 0000000..e7ca0de --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7CheckResultException.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.cnbm.s7.s7connector.exception; + +/** + * The Class S7Exception is an exception related to S7 Communication + */ +public final class S7CheckResultException extends RuntimeException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = -4761415733559374116L; + + /** + * Instantiates a new s7 exception. + */ + public S7CheckResultException() { + } + + /** + * Instantiates a new s7 exception. + * + * @param message + * the message + */ + public S7CheckResultException(final String message) { + super(message); + } + + /** + * Instantiates a new s7 exception. + * + * @param message + * the message + * @param cause + * the cause + */ + public S7CheckResultException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new s7 exception. + * + * @param cause + * the cause + */ + public S7CheckResultException(final Throwable cause) { + super(cause); + } + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7Exception.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7Exception.java new file mode 100644 index 0000000..b318dbb --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7Exception.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.cnbm.s7.s7connector.exception; + +/** + * The Class S7Exception is an exception related to S7 Communication + */ +public final class S7Exception extends RuntimeException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = -4761415733559374116L; + + /** + * Instantiates a new s7 exception. + */ + public S7Exception() { + } + + /** + * Instantiates a new s7 exception. + * + * @param message + * the message + */ + public S7Exception(final String message) { + super(message); + } + + /** + * Instantiates a new s7 exception. + * + * @param message + * the message + * @param cause + * the cause + */ + public S7Exception(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new s7 exception. + * + * @param cause + * the cause + */ + public S7Exception(final Throwable cause) { + super(cause); + } + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7IOException.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7IOException.java new file mode 100644 index 0000000..6ddde0c --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7IOException.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.cnbm.s7.s7connector.exception; + +import java.io.IOException; + +/** + * The Class S7IOException is an exception related to S7 Communication + */ +public final class S7IOException extends RuntimeException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = -4761415733559374116L; + + /** + * Instantiates a new s7 exception. + */ + public S7IOException() { + } + + /** + * Instantiates a new s7 exception. + * + * @param message + * the message + */ + public S7IOException(final String message) { + super(message); + } + + /** + * Instantiates a new s7 exception. + * + * @param message + * the message + * @param cause + * the cause + */ + public S7IOException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new s7 exception. + * + * @param cause + * the cause + */ + public S7IOException(final Throwable cause) { + super(cause); + } + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7ParseDataException.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7ParseDataException.java new file mode 100644 index 0000000..fb582a9 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/exception/S7ParseDataException.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.cnbm.s7.s7connector.exception; + +/** + * The Class S7Exception is an exception related to S7 Communication + */ +public final class S7ParseDataException extends RuntimeException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = -4761415733559374116L; + + /** + * Instantiates a new s7 exception. + */ + public S7ParseDataException() { + } + + /** + * Instantiates a new s7 exception. + * + * @param message + * the message + */ + public S7ParseDataException(final String message) { + super(message); + } + + /** + * Instantiates a new s7 exception. + * + * @param message + * the message + * @param cause + * the cause + */ + public S7ParseDataException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Instantiates a new s7 exception. + * + * @param cause + * the cause + */ + public S7ParseDataException(final Throwable cause) { + super(cause); + } + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/S7BaseConnection.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/S7BaseConnection.java new file mode 100644 index 0000000..b5f4168 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/S7BaseConnection.java @@ -0,0 +1,216 @@ +/* +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.cnbm.s7.s7connector.impl; + +import com.cnbm.s7.controller.S7DemoController; +import com.cnbm.s7.s7connector.api.DaveArea; +import com.cnbm.s7.s7connector.api.S7Connector; +import com.cnbm.s7.s7connector.exception.S7CheckResultException; +import com.cnbm.s7.s7connector.exception.S7Exception; +import com.cnbm.s7.s7connector.impl.nodave.Nodave; +import com.cnbm.s7.s7connector.impl.nodave.S7Connection; +import com.cnbm.s7.s7connector.type.PlcVar; +import com.cnbm.s7.s7connector.type.TransportSize; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base-Connection for the S7-PLC Connection Libnodave: + * http://libnodave.sourceforge.net/ + * + * @author Thomas Rudin + */ +public abstract class S7BaseConnection implements S7Connector { + + private static final Logger logger = LoggerFactory.getLogger(S7BaseConnection.class); + + + /** The Constant MAX_SIZE. */ + //private static final int MAX_SIZE = 96; //原版96 ;; ioserver 127 ;; + private static final int MAX_SIZE = 212; //S7-1200-maxReadBytes = 212 ;; S7-1500-maxReadBytes = 452 + + /** The Constant PROPERTY_AREA. */ + public static final String PROPERTY_AREA = "area"; + + /** The Constant PROPERTY_AREANUMBER. */ + public static final String PROPERTY_AREANUMBER = "areanumber"; + + /** The Constant PROPERTY_BYTES. */ + public static final String PROPERTY_BYTES = "bytes"; + + /** The Constant PROPERTY_OFFSET. */ + public static final String PROPERTY_OFFSET = "offset"; + + /** + * Checks the Result. + * + * @param libnodaveResult + * the libnodave result + */ + public static void checkResult(final int libnodaveResult) { + if (libnodaveResult != Nodave.RESULT_OK) { + final String msg = Nodave.strerror(libnodaveResult); +// throw new IllegalArgumentException("Result: " + msg); + logger.info("checkResult 失败,errValue:"+libnodaveResult+", errMsg: "+msg); + throw new S7CheckResultException("errMsg : "+msg); + } + } + + /** + * Dump data + * + * @param b + * the byte stream + */ + protected static void dump(final byte[] b) { + for (final byte element : b) { + System.out.print(Integer.toHexString(element & 0xFF) + ","); + } + } + + /** The dc. */ + private S7Connection dc; + + /** + * Initialize the connection + * + * @param dc + * the connection instance + */ + protected void init(final S7Connection dc) { + this.dc = dc; + } + + /** {@inheritDoc} */ + @Override + public synchronized byte[] read(final DaveArea area, final int areaNumber, final int bytes, final int offset) { + if (bytes > MAX_SIZE) { + final byte[] ret = new byte[bytes]; + //注意这里 嵌套了 递归,让无限递归去解决 MAX_SIZE的问题。 + final byte[] currentBuffer = this.read(area, areaNumber, MAX_SIZE, offset); + System.arraycopy(currentBuffer, 0, ret, 0, currentBuffer.length); + + final byte[] nextBuffer = this.read(area, areaNumber, bytes - MAX_SIZE, offset + MAX_SIZE); + System.arraycopy(nextBuffer, 0, ret, currentBuffer.length, nextBuffer.length); + + return ret; + } else { + //接收结果的 容器,传进去指针 + final byte[] buffer = new byte[bytes]; + final int ret = this.dc.readBytes(area, areaNumber, offset, bytes, buffer); + + //判断结果 + + if(ret == Nodave.RESULT_OK){ + return buffer; + }else { + final String msg = Nodave.strerror(ret); + logger.info("read checkResult 失败,errValue:"+ret+", errMsg: "+msg); + throw new S7CheckResultException("errMsg : "+msg); + } + + } + } + + +// private Integer checkResult(final int libnodaveResult) { +// if (libnodaveResult != Nodave.RESULT_OK) { +// final String msg = Nodave.strerror(libnodaveResult); +//// throw new IllegalArgumentException("Result: " + msg); +// logger.info("checkResult 失败,errValue:"+libnodaveResult+", errMsg: "+msg); +// throw new S7CheckResultException("errMsg : "+msg); +// } +// } + + + + @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); + + //thr resultException + if(ret == Nodave.RESULT_OK){ + return buffer; + }else { + final String msg = Nodave.strerror(ret); + logger.info("read checkResult 失败,errValue:"+ret+", errMsg: "+msg); + throw new S7CheckResultException("errMsg : "+msg); + } + } + } + + /** {@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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/S7TCPConnection.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/S7TCPConnection.java new file mode 100644 index 0000000..b82a047 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/S7TCPConnection.java @@ -0,0 +1,124 @@ +/* +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.cnbm.s7.s7connector.impl; + +import com.cnbm.s7.s7connector.api.DaveArea; +import com.cnbm.s7.s7connector.exception.S7CheckResultException; +import com.cnbm.s7.s7connector.exception.S7Exception; +import com.cnbm.s7.s7connector.impl.nodave.Nodave; +import com.cnbm.s7.s7connector.impl.nodave.PLCinterface; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/Nodave.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/Nodave.java new file mode 100644 index 0000000..020465b --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/PDU.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/PDU.java new file mode 100644 index 0000000..9815b72 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/PDU.java @@ -0,0 +1,622 @@ +/* +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.cnbm.s7.s7connector.impl.nodave; + +import com.cnbm.s7.s7connector.api.DaveArea; +import com.cnbm.s7.s7connector.type.PlcVar; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/PLCinterface.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/PLCinterface.java new file mode 100644 index 0000000..a41a16b --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/PLCinterface.java @@ -0,0 +1,96 @@ +/* +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.cnbm.s7.s7connector.impl.nodave; + +import com.cnbm.s7.s7connector.exception.S7IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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; + private static final Logger logger = LoggerFactory.getLogger(PLCinterface.class); + 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)) { + //如果网络延迟比较大 改下这里(如果把这个数值改大了,那么性能会降低) + while ((this.in.available() <= 0) && (retry < 1000)) { + try { + if (retry > 0) { + Thread.sleep(1); + } + retry++; + } catch (final InterruptedException e) { + logger.info("Socket read wait字节流失败,in.available: "+this.in.available()+" ,retry: "+retry+" , errMsg: "+e); + } + } + res = -1; + while ((this.in.available() > 0) && (len > 0)) { + res = this.in.read(b, start, len); + start += res; + len -= res; + } + return res; + } catch (final IOException e) { + String errMsg = "Socket read字节流失败: "+e.getMessage(); + logger.info(errMsg); + throw new S7IOException(errMsg); + + } + } + + /** + * return : + * 1 成功 + * 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) { + String errMsg = "Socket write字节流失败: "+e.getMessage(); + logger.info(errMsg); + throw new S7IOException(errMsg); + } + } + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/Result.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/Result.java new file mode 100644 index 0000000..90bccc1 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/ResultSet.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/ResultSet.java new file mode 100644 index 0000000..acb8a87 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/S7Connection.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/S7Connection.java new file mode 100644 index 0000000..6542e16 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/S7Connection.java @@ -0,0 +1,503 @@ +/* +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.cnbm.s7.s7connector.impl.nodave; + +import com.cnbm.s7.s7connector.api.DaveArea; +import com.cnbm.s7.s7connector.exception.S7IOException; +import com.cnbm.s7.s7connector.type.PlcVar; +import com.cnbm.s7.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) throws Exception { + 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 a 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 a 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() throws Exception { + 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); + //通过tcp链接 发送read var request,thr S7IOException + 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.err.println("p1.udlen:"+p1.udlen+",,udlen:"+ p2.udlen +",,"+"bufferSize:"+buffer.length); +// if(p2.udlen>buffer.length){ +// p2.udlen = buffer.length; +// } + 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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/TCPConnection.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/TCPConnection.java new file mode 100644 index 0000000..9a382a1 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/nodave/TCPConnection.java @@ -0,0 +1,187 @@ +/* +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.cnbm.s7.s7connector.impl.nodave; + +import com.cnbm.s7.s7connector.api.utils.ByteUtils; +import com.cnbm.s7.s7connector.exception.S7IOException; +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() throws Exception { + //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) 0x10, //Source TSAP: 0x01->PG; 0x02->OP; 0x03->S7单边通讯(服务端模式); 0x10->S7双边通讯;;; 这里是0x01 + (byte) 0x00, //机架(rack)(前4个bit位)+ cpu槽位(slot)(后4个bit位) ,, 因为是上位机端 所以这里都是00(rack=0 slot=2) + + (byte) 0xC2, //parameter-code:dst-tsap PLC + (byte) 0x02, //parameter-length + (byte) 0x03, //Destination TSAP: 0x01->PG; 0x02->OP; 0x03->S7单边通讯(服务端模式); 0x10->S7双边通讯;;; 这里是0x01 + (byte) 0x01, //机架(rack)(前4个bit位)+ cpu槽位(slot)(后4个bit位),, 西门子200smart、1200、1500默认 机架号=0,槽位号=1 ;;;; 西门子300、400默认 机架号=0,槽位号=2 + + (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); //这里写的不太对,这里应该是配置dst-tsap(plc) + //this.msgOut[18] = (byte) this.slot; + this.msgOut[18] = combineToByte(rack,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(); + } + + private static byte combineToByte(Integer i,Integer j){ + Integer cc = ((i << 4)+j); + return cc.byteValue(); + } + + /** + * 构建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 { + //writeRes == 1 && readRes == 0 + String errMsg = "exchange 出现了问题:① writeRes:"+(writeRes!=0?"writeISOPacket成功":"writeISOPacket失败")+"② readRes:"+(readRes!=0?"readISOPacket成功":"readISOPacket失败"); + logger.info(errMsg); + return 1; + } + } + + /** + * Read iso packet. + * + * @return the int + * 0 ==> 不成功(异常) + * -1 ==> 读到一个空的字节流 (这种情况很少) + * 其他 ==> 成功 + */ + protected int readISOPacket(){ + //read return 为0 就是异常 + try { + int res = this.iface.read(this.msgIn, 0, 4); + if (res == 4) { + final int len = (0x100 * ByteUtils.toUInt(this.msgIn[2])) + ByteUtils.toUInt(this.msgIn[3]); + res += this.iface.read(this.msgIn, 4, len); + } else { + return 0; + } + return res; + + }catch (Exception e){ + 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; + * //!! } + */ + +// System.out.println("send bytes报文: "+ Arrays.toString(this.msgOut)+";报文size:"+this.msgOut.length); + //像这种 write 和read 都是 传的是指针,里面数据消费掉了,外部就没了 + int writeRes = this.iface.write(this.msgOut, 0, size); + return writeRes; + } + + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/S7SerializerImpl.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/S7SerializerImpl.java new file mode 100644 index 0000000..b5b3b84 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer; + +import com.cnbm.s7.s7connector.api.DaveArea; +import com.cnbm.s7.s7connector.api.S7Connector; +import com.cnbm.s7.s7connector.api.S7Serializer; +import com.cnbm.s7.s7connector.exception.S7Exception; +import com.cnbm.s7.s7connector.impl.serializer.parser.BeanEntry; +import com.cnbm.s7.s7connector.impl.serializer.parser.BeanParseResult; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/BitConverter.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/BitConverter.java new file mode 100644 index 0000000..3342e2c --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.converter; + +import com.cnbm.s7.s7connector.api.S7Serializable; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/ByteConverter.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/ByteConverter.java new file mode 100644 index 0000000..e2e5eb5 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.converter; + +import com.cnbm.s7.s7connector.api.S7Serializable; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/DateAndTimeConverter.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/DateAndTimeConverter.java new file mode 100644 index 0000000..0075c73 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.converter; + +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/DateConverter.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/DateConverter.java new file mode 100644 index 0000000..7a38a7d --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.converter; + +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/IntegerConverter.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/IntegerConverter.java new file mode 100644 index 0000000..6c5ddd2 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.converter; + +import com.cnbm.s7.s7connector.api.S7Serializable; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/LongConverter.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/LongConverter.java new file mode 100644 index 0000000..f128cc4 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.converter; + +import com.cnbm.s7.s7connector.api.S7Serializable; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/RealConverter.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/RealConverter.java new file mode 100644 index 0000000..5355585 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.converter; + +import com.cnbm.s7.s7connector.api.S7Serializable; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/StringConverter.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/StringConverter.java new file mode 100644 index 0000000..723fcc9 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.converter; + +import com.cnbm.s7.s7connector.api.S7Serializable; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/StructConverter.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/StructConverter.java new file mode 100644 index 0000000..c5270c3 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.converter; + +import com.cnbm.s7.s7connector.impl.serializer.S7SerializerImpl; +import com.cnbm.s7.s7connector.impl.utils.S7Type; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/TimeConverter.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/converter/TimeConverter.java new file mode 100644 index 0000000..5ed5f90 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.converter; + +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/parser/BeanEntry.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/parser/BeanEntry.java new file mode 100644 index 0000000..0708743 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.parser; + +import com.cnbm.s7.s7connector.api.S7Serializable; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/parser/BeanParseResult.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/parser/BeanParseResult.java new file mode 100644 index 0000000..d5177ab --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/parser/BeanParser.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/serializer/parser/BeanParser.java new file mode 100644 index 0000000..31063c7 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.serializer.parser; + +import com.cnbm.s7.s7connector.api.S7Serializable; +import com.cnbm.s7.s7connector.api.annotation.S7Variable; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/utils/S7Type.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/utils/S7Type.java new file mode 100644 index 0000000..b36071b --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.s7connector.impl.utils; + +import com.cnbm.s7.s7connector.api.S7Serializable; +import com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/utils/S7Utils.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/impl/utils/S7Utils.java new file mode 100644 index 0000000..73891de --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/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.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/type/DataTransportSize.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/type/DataTransportSize.java new file mode 100644 index 0000000..8649fca --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/type/DataTransportSize.java @@ -0,0 +1,82 @@ +package com.cnbm.s7.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.info("No DataTransportSize for value {}", value); + } + return map.get(value); + } + + public static Boolean isDefined(short value) { + return map.containsKey(value); + } + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/type/PlcVar.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/type/PlcVar.java new file mode 100644 index 0000000..93cf495 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/type/PlcVar.java @@ -0,0 +1,529 @@ +package com.cnbm.s7.s7connector.type; + +import com.cnbm.s7.s7connector.api.DaveArea; +import com.cnbm.s7.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 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), + STRING_Array("STRING",TransportSize.STRING,12,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; + case 12: + + 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 //注意: String类型 不能用枚举类里的 toObject() / toBytes() 的方法,具体方式看Demo + + + * 数组变量 + * BoolArray ===> List + * ByteArray ===> List + * WordArray ===> List + * DWordArray ===> List + * CharArray ===> List + * SIntArray ===> List + * IntArray ===> List + * DIntArray ===> List + * UIntArray ===> List + * USIntArray ===> List + * UDIntArray ===> List + * StringArray ===> List //注意: String类型 不能用枚举类里的 toObject() / toBytes() 的方法,具体方式看Demo + * + * + * */ + public Object toObject(byte[] value) { + 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: + //后续有其他数组类型 ,再补充好了,先列出一些常用的。 + //本来这里 是吧byte[] => String[] 的,但是这里拿不到 String[] 的length 和 strSize, 所以就不在这里解析了在外面解析 + + break; + default: + //什么也不做 + break; + } + return res; + } + } + + /** + * area :DB块 或者其他块 + * areaNumber :数据块编号 DB3 + * byteOffset :字节偏移量 + * byteOffset :比特偏移量(不填就是0 ,差不多只有读bool类型才会用到) + * length :长度(1 代表单个变量 ,2/3/4 代表读一个数组。) + * */ + + + +} diff --git a/ym-s7/src/main/java/com/cnbm/s7/s7connector/type/TransportSize.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/type/TransportSize.java new file mode 100644 index 0000000..d1c87b6 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/type/TransportSize.java @@ -0,0 +1,350 @@ +package com.cnbm.s7.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/ym-s7/src/main/java/com/cnbm/s7/s7connector/utils/CommonFunctions.java b/ym-s7/src/main/java/com/cnbm/s7/s7connector/utils/CommonFunctions.java new file mode 100644 index 0000000..5ed2392 --- /dev/null +++ b/ym-s7/src/main/java/com/cnbm/s7/s7connector/utils/CommonFunctions.java @@ -0,0 +1,21 @@ +package com.cnbm.s7.s7connector.utils; + +/** + * @Desc: "" + * @Author: caixiang + * @DATE: 2022/12/5 11:11 + */ +public class CommonFunctions { + /** + * a 整除 b ,如果有余数+1 + * */ + public static Integer exactDivision(Integer a,Integer b) { + + int c = a/b; + + if(a%b!=0){ + c = a/b+1; + } + return c; + } +}