This commit is contained in:
caixiang 2023-02-14 09:08:21 +08:00
parent e0a947f050
commit 9d75942afb
74 changed files with 9435 additions and 2 deletions

View File

@ -16,6 +16,8 @@
<module>ym-basic</module>
<module>ym-schedule-task</module>
<module>ym-websocket</module>
<module>ym-packing</module>
<module>ym-s7</module>
</modules>
<packaging>pom</packaging>

View File

@ -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">
<parent>
<artifactId>ym-pass</artifactId>
<groupId>com.cnbm</groupId>
<artifactId>ym-pass</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -22,11 +22,31 @@
<artifactId>ym-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.cnbm</groupId>
<artifactId>ym-s7</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.cnbm</groupId>
<artifactId>ym-packing</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.cnbm</groupId>
<artifactId>ym-admin</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.cnbm</groupId>
<artifactId>ym-packing</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.cnbm</groupId>
<artifactId>ym-barcode</artifactId>

View File

@ -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的基本信息这些基本信息会展现在文档页面中

View File

@ -7,7 +7,7 @@ server:
min-spare: 30
port: 8080
servlet:
context-path: /ym-pass
context-path: /ym-cigs4
session:
cookie:
http-only: true

27
ym-packing/pom.xml Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cnbm</groupId>
<artifactId>ym-pass</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>ym-packing</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.cnbm</groupId>
<artifactId>ym-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,7 @@
package com.cnbm.packing;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

View File

@ -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<Integer> get(){
return new Result<Integer>().ok(1);
}
@PostMapping("get2")
public Result<Integer> get2(){
return new Result<Integer>().ok(2);
}
}

40
ym-s7/pom.xml Normal file
View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cnbm</groupId>
<artifactId>ym-pass</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>ym-s7</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- retry 结束 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.11</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- retry 结束 -->
</dependencies>
</project>

View File

@ -0,0 +1,7 @@
package com.cnbm;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

View File

@ -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<Character> characters = (List<Character>)read(S7Client.S7_1500,PlcVarActual.CharArrays);
List<Boolean> booleans = (List<Boolean>)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<Character> characters = (List<Character>)read(PlcVarActual.CharArrays,S7Client.S7_1500);
//
// List<Boolean> booleans = (List<Boolean>)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<Boolean> string = (List<Boolean>)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);
// }
}

View File

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

View File

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

View File

@ -0,0 +1,24 @@
package com.cnbm.s7.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @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;
}

View File

@ -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<String, Object> {
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<String, Object> 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Class<? extends Throwable>, Boolean> exceptionMap = new HashMap<>();
@Test
public void test() {
//文档https://blog.csdn.net/minghao0508/article/details/123972703
exceptionMap.put(RemoteAccessException.class,true);
// 构建重试模板实例
RetryTemplate retryTemplate = new RetryTemplate();
// 设置重试回退操作策略主要设置重试间隔时间
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(fixedPeriodTime);
// 设置重试策略主要设置重试次数
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap);
retryTemplate.setRetryPolicy(retryPolicy);
retryTemplate.setBackOffPolicy(backOffPolicy);
Boolean execute = retryTemplate.execute(
//RetryCallback
retryContext -> {
boolean b = RetryDemoTask.retryTask("abc");
System.out.println("调用的结果:"+b);
return b;
},
retryContext -> {
//RecoveryCallback
System.out.println("已达到最大重试次数或抛出了不重试的异常~~~");
return false;
}
);
System.out.println("执行结果:"+execute);
}
}

View File

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

View File

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

View File

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

View File

@ -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<Byte> st = new Stack<Byte>();
int c = 4-in.length;
in = invert(in);
for(int i=0;i<in.length;i++){
st.push(in[i]);
}
for(int i=0;i<c;i++){
st.push(Byte.valueOf((byte) 0));
}
for(int i=0;i<4;i++){
bytes[i] = st.pop();
}
}else {
bytes = in;
}
int value=0;
//由高位到低位
for(int i = 0; i < 4; i++) {
int shift= (4-1-i) * 8;
value +=(bytes[i] & 0x000000FF) << shift;//往高位游
}
return value;
}
public static Integer byteToUSInt(byte bytes) {
int value=0;
value +=(bytes & 0xFF);
return value;
}
public static Integer byteToSInt(byte bytes) {
return Byte.valueOf(bytes).intValue();
}
public static byte[] invert(byte[] a){
byte[] b = new byte[a.length];
Stack<Byte> st = new Stack<Byte>();
for(int i=0;i<a.length;i++){
st.push(a[i]);
}
for(int i=0;i<a.length;i++){
b[i] = st.pop();
}
return b;
}
}

View File

@ -0,0 +1,294 @@
package com.cnbm.s7.s7connector;
import java.io.UnsupportedEncodingException;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2021/12/10 10:17
*/
public class MainForRead {
/**
* 将byte转换为一个长度为8的boolean数组(每bit代表一个boolean值)
*
* @param b byte, return(将数组翻转过来)
* @return boolean数组
*/
public static boolean[] getBooleanArray(byte b,boolean returns) {
boolean[] array = new boolean[8];
for (int i = 7; 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<Boolean> booleans = ByteUtils.toBoolArray(boolArrays);
// System.out.println("DB3.830-boolArrays : " +booleans );
//
// byte[] boolArrays14 = connector.read(DaveArea.DB, 3, 16, 3264);
// List<Boolean> 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 就像这种情况的话这个工具就无法读取了 这个后续可以改他的源码 就是addreesByte/Bit那里 后续再说好了
//// byte[] bool2 = connector.read(DaveArea.DB, 3, 1, 3266);
//// System.out.println("DB3.3266-bool : " + ByteUtils.toBoolean(bool2));
////
////
//// //byte 目前读取以 有符号的十进制
//// //[8]
//// //[-63]
//// //1011 1111
//// byte[] byteOne = connector.read(DaveArea.DB, 3, 1, 1);
//// System.out.println("DB3.1-byteOne-有符号 : " + ByteUtils.toInt(byteOne[0])); //有符号 整形
////
//// System.out.println("DB3.1-byteOne-无符号 : " + ByteUtils.toUInt(byteOne[0])); //无符号 整形
////
////
//// // [0, 5] word 2/8/16进制 可无符号 可有符号就看你真没用
//// byte[] word = connector.read(DaveArea.DB, 3, 2, 2);
//// System.out.println("DB3.2-word : " + ByteUtils.toInt(word[0],word[1]));
////
//// // [-1, -1, -1, -4] 把java自动转换成byte类型 的补码形式 ,如果电控变量是这个要问下他是无符号还是有符号的
//// // [FF, FF, FF, FC] 原码
//// byte[] dword = connector.read(DaveArea.DB, 3, 4, 4);
//// System.out.println("DB3.4-dword : " + ByteUtils.toInt(dword[0],dword[1],dword[2],dword[3])); //带符号的 整形
////
//// //[11]
//// byte[] usint = connector.read(DaveArea.DB, 3, 1, 8);
//// System.out.println("DB3.8-usint : " + ByteUtils.toUInt(usint[0]));
////
//// byte[] sint = connector.read(DaveArea.DB, 3, 1, 9);
//// System.out.println("DB3.9-sint : " + ByteUtils.toInt(sint[0]));
////
//// byte[] uint = connector.read(DaveArea.DB, 3, 2, 10);
//// System.out.println("DB3.10-uint : " + ByteUtils.toUInt(uint[0],uint[1]));
////
//// //[-1, -111] -111
//// //[-1, -64] -64
//// //[-4, 25] -999
//// byte[] ints = connector.read(DaveArea.DB, 3, 2, 12);
//// System.out.println("DB3.12-ints : " + ByteUtils.toInt(ints[0],ints[1]));
////
//// byte[] dint = connector.read(DaveArea.DB, 3, 4, 14);
//// System.out.println("DB3.14-dint : " + ByteUtils.toInt(dint[0],dint[1],dint[2],dint[3]));
////
//// byte[] udint = connector.read(DaveArea.DB, 3, 4, 18);
//// System.out.println("DB3.18-udint : " + ByteUtils.toUInt(udint[0],udint[1],udint[2],udint[3]));
////
//// //plc 中char 是1个字节 注意char wchar 都是ascii码格式的
//// byte[] chars = connector.read(DaveArea.DB, 3, 1, 58);
//// Character charss = ByteUtils.toChar(chars);
//// System.out.println("DB3.58-char : " + charss);
//// //plc 中wchar 是2个字节
//// byte[] wchar = connector.read(DaveArea.DB, 3, 2, 60);
//// Character wchars =ByteUtils.toChar(wchar);
//// System.out.println("DB3.60-wchar : " + wchars);
////
//// //n+2 个字节 这里的n 就是实际content的字节数 "123"=> n=3 ;; "@123" ==> n=4
//// //[-2, 4, 64, 65, 83, 68] @ASD 前面-2 4两个字节就是上面的2字节 是无用的
//// //-2 代表 字符串中存储最大的总字节数 4代表字符数 后面跟着的 是内容最多256个字节
//// byte[] str = connector.read(DaveArea.DB, 3, 6, 62);
//// String string = ByteUtils.toStr(str);
//// System.out.println("DB3.62-str : " +string );
////
//// }
////
//// {
//// try {
//// //byte 占用一个字节如果是数组的话就读取2个要事先知道点表规定数组长度实际就是读取 DB3.830
//// //bytes = byteLength * arrayLength 举例1byteLength * 2arrayLength =2bytes
//// byte[] boolArrays = connector.read(DaveArea.DB, 3, 2, 830);
//// List<Boolean> booleans = ByteUtils.toBoolArray(boolArrays);
//// System.out.println("DB3.830-boolArrays : " +booleans );
////
//// byte[] byteArrays = connector.read(DaveArea.DB, 3, 2, 832);
//// List<Byte> bytes = ByteUtils.toByteArray(byteArrays);
//// System.out.println("DB3.832-byteArrays : " +bytes );
////
//// byte[] charArrays = connector.read(DaveArea.DB, 3, 2, 834);
//// List<Character> chars = ByteUtils.toCharArray(charArrays);
//// System.out.println("DB3.834-charArrays : " +chars );
////
//// byte[] wordArrays = connector.read(DaveArea.DB, 3, 4, 836);
//// List<Integer> words = ByteUtils.toWordArray(wordArrays);
//// System.out.println("DB3.836-wordArrays : " +words );
////
//// byte[] dwordArrays = connector.read(DaveArea.DB, 3, 8, 840);
//// List<Integer> dwords = ByteUtils.toDWordArray(dwordArrays);
//// System.out.println("DB3.840-dwordArrays : " +dwords );
////
//// byte[] sintArrays = connector.read(DaveArea.DB, 3, 2, 852);
//// List<Integer> sints = ByteUtils.toSIntArray(sintArrays);
//// System.out.println("DB3.852-sintArrays : " +sints );
////
//// byte[] intArrays = connector.read(DaveArea.DB, 3, 4, 848);
//// List<Integer> ints = ByteUtils.toIntArray(intArrays);
//// System.out.println("DB3.848-intArrays : " +ints );
////
//// byte[] dintArrays = connector.read(DaveArea.DB, 3, 8, 854);
//// List<Integer> dints = ByteUtils.toDIntArray(dintArrays);
//// System.out.println("DB3.852-dintArrays : " +dints);
////
////
//// byte[] usintArrays = connector.read(DaveArea.DB, 3, 3, 3240);
//// List<Integer> usints = ByteUtils.toUSIntArray(usintArrays);
//// System.out.println("DB3.3240-usintArrays : " +usints );
////
//// byte[] uintArrays = connector.read(DaveArea.DB, 3, 6, 3256);
//// List<Integer> uints = ByteUtils.toUIntArray(uintArrays);
//// System.out.println("DB3.3256-uintArrays : " +uints );
////
//// byte[] udintArrays = connector.read(DaveArea.DB, 3, 12, 3244);
//// List<Long> udints = ByteUtils.toUDIntArray(udintArrays);
//// System.out.println("DB3.852-udintArrays : " +udints);
//// }catch (Exception e){
//// e.printStackTrace();
//// }
////
//// };
//
//
//
//
//
// //Write to DB100 10 bytes
//
//
//
// //connector.write(DaveArea.DB, 3, 830, byteArrays);
//
// //Close connection
//
// //connector.close();
// }
//
}

View File

@ -0,0 +1,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);
}
}

View File

@ -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 相关sintintdint usintuintudint
{
//[11] //[9] ==>2304
// byte[] usint_content = ByteUtils.usintToBytes(9);
byte[] usint_content = PlcVar.USINT.toBytes(9);
connector.write(DaveArea.DB, 3, 8,usint_content);
byte[] usint = connector.read(DaveArea.DB, 3, 1, 8);
// Integer integer = ByteUtils.toUInt(usint[0]);
Integer integer = (Integer) PlcVar.USINT.toObject(usint);
System.out.println("DB3.8-usint : " + integer);
// byte[] bytes = ByteUtils.sintToBytes(-9);
byte[] bytes = PlcVar.SINT.toBytes(-9);
connector.write(DaveArea.DB, 3, 9,bytes);
byte[] sint = connector.read(DaveArea.DB, 3, 1, 9);
// Integer integer1 = ByteUtils.toInt(sint[0]);
Integer integer1 = (Integer) PlcVar.SINT.toObject(usint);
System.out.println("DB3.9-sint : " + integer1);
// byte[] unit_content = ByteUtils.uintToBytes(9);
byte[] unit_content = PlcVar.UINT.toBytes(9);
connector.write(DaveArea.DB, 3, 10,unit_content);
byte[] uint = connector.read(DaveArea.DB, 3, 2, 10);
System.out.println("DB3.10-uint : " + ByteUtils.toUInt(uint[0],uint[1]));
//[-1, -111] -111
//[-1, -64] -64
//[-4, 25] -999
//byte[] bytes1 = ByteUtils.intToBytes(-99);
byte[] bytes1 = PlcVar.INT.toBytes(-99);
connector.write(DaveArea.DB, 3, 12,bytes1);
byte[] ints = connector.read(DaveArea.DB, 3, 2, 12);
System.out.println("DB3.12-ints : " + ByteUtils.toInt(ints[0],ints[1]));
// byte[] bytes2 = ByteUtils.dintToBytes(-999);
byte[] bytes2 = PlcVar.DINT.toBytes(-999);
connector.write(DaveArea.DB, 3, 14,bytes2);
byte[] dint = connector.read(DaveArea.DB, 3, 4, 14);
System.out.println("DB3.14-dint : " + ByteUtils.toInt(dint[0],dint[1],dint[2],dint[3]));
// byte[] bytes3 = ByteUtils.udintToBytes(99);
byte[] bytes3 = PlcVar.UDINT.toBytes(99);
connector.write(DaveArea.DB, 3, 18,bytes3);
byte[] udint = connector.read(DaveArea.DB, 3, 4, 18);
System.out.println("DB3.18-udint : " + ByteUtils.toUInt(udint[0],udint[1],udint[2],udint[3]));
}
{
// [0]
// byte[] bytes = ByteUtils.boolToBytes(false);
byte[] bytes = PlcVar.BOOL.toBytes(false);
connector.write(DaveArea.DB, 3, 0,bytes);
byte[] bool = connector.read(DaveArea.DB, 3, 1, 0);
System.out.println("DB3.0-boolean : " + ByteUtils.toBoolean(bool));
//byte 目前读取以 有符号的十进制
//[8]
//[-63]
//1011 1111
// byte[] setbytes = ByteUtils.setbytes(Byte.valueOf((byte) 0xFF));
byte[] setbytes = PlcVar.BYTE.toBytes((byte)0xFF);
connector.write(DaveArea.DB, 3, 1,setbytes);
byte[] byteOne = connector.read(DaveArea.DB, 3, 1, 1);
System.out.println("DB3.1-byteOne-有符号 : " + ByteUtils.toInt(byteOne[0])); //有符号 整形
System.out.println("DB3.1-byteOne-无符号 : " + ByteUtils.toUInt(byteOne[0])); //无符号 整形
// [0, 5] word 2/8/16进制 可无符号 可有符号就看你真没用
// byte[] bytes1 = ByteUtils.wordToBytes(Short.valueOf("-55"));
byte[] bytes1 = PlcVar.WORD.toBytes(-55);
connector.write(DaveArea.DB, 3, 2,bytes1);
byte[] word = connector.read(DaveArea.DB, 3, 2, 2);
System.out.println("DB3.2-word : " + ByteUtils.toInt(word[0],word[1]));
// [-1, -1, -1, -4] 把java自动转换成byte类型 的补码形式 ,如果电控变量是这个要问下他是无符号还是有符号的
// [FF, FF, FF, FC] 原码
byte[] bytes2 = PlcVar.DWORD.toBytes(-99);
connector.write(DaveArea.DB, 3, 4,bytes2);
byte[] dword = connector.read(DaveArea.DB, 3, 4, 4);
System.out.println("DB3.4-dword : " + ByteUtils.toInt(dword[0],dword[1],dword[2],dword[3])); //带符号的 整形
//plc 中char 是1个字节 注意char wchar 都是ascii码格式的
// byte[] bytes3 = ByteUtils.charToBytes('b');
byte[] bytes3 = PlcVar.CHAR.toBytes('b');
connector.write(DaveArea.DB, 3, 58,bytes3);
byte[] chars = connector.read(DaveArea.DB, 3, 1, 58);
System.out.println("DB3.58-char : " + ByteUtils.toChar(chars));
//plc 中wchar 是2个字节 '菜' =[-125, -36]
// byte[] bytes4 = ByteUtils.wcharToBytes('菜');
byte[] bytes4 = PlcVar.WCHAR.toBytes('翔');
connector.write(DaveArea.DB, 3, 60,bytes4);
byte[] wchar = connector.read(DaveArea.DB, 3, 2, 60);
System.out.println("DB3.60-wchar : " + ByteUtils.toChar(wchar));
//n+2 个字节 这里的n 就是实际content的字节数 "123"=> n=3 ;; "@123" ==> n=4
//[-2, 4, 64, 65, 83, 68] @ASD 前面-2 4两个字节就是上面的2字节 是无用的
//-2 代表 字符串中存储最大的总字节数 4代表字符数 后面跟着的 是内容最多256个字节
//[-2, 4, 64, 65, 83, 68] @ASD
//[-2, 4, 64, 64, 64, 64] @ASD
//读取字符串 要事先知道长度否则可能读取的字符串长度不完整
String s = "你好啊呢";
// byte[] str_content = ByteUtils.strToBytes(s);
// byte[] str_content = PlcVar.STRING.toBytes(s);
// connector.write(DaveArea.DB, 3, 62 , str_content);
//byte[] str = connector.read(DaveArea.DB, 3, str_content.length, 62);
//[-2, 6, -60, -29, -70, -61]
//[-2, 8, -53, -58, -75, -60]
byte[] str = connector.read(DaveArea.DB, 3, 10, 62);
String string = ByteUtils.toStr(str);
System.out.println("DB3.62-str : " +string );
//下面这种方式读取字符串是指定长度的
//byte[] str2 = connector.read(DaveArea.DB, 3, 7, 62);
}
{
//byte 占用一个字节如果是数组的话就读取2个要事先知道点表规定数组长度实际就是读取 DB3.830
//bytes = byteLength * arrayLength 举例1byteLength * 2arrayLength =2bytes
boolean[] booleanArray = new boolean[2];
booleanArray[0] = true;
booleanArray[1] = true;
// byte[] bytes1 = ByteUtils.booleanArrayToBytes(booleanArray);
byte[] bytes1 = PlcVar.BOOL_Array.toBytes(booleanArray);
connector.write(DaveArea.DB, 3, 830 , bytes1);
byte[] boolArrays = connector.read(DaveArea.DB, 3, 2, 830);
// List<Boolean> booleans = ByteUtils.toBoolArray(boolArrays);
List<Boolean> booleans = (List<Boolean>) PlcVar.BOOL_Array.toObject(boolArrays);
System.out.println("DB3.830-boolArrays : " +booleans );
//注意 write的长度要和plc中定义的长度要一致
byte[] write_byteArrays = new byte[2];
write_byteArrays[0] = 1;
write_byteArrays[1] = 2;
byte[] bytes2 = PlcVar.BYTE_Array.toBytes(write_byteArrays);
connector.write(DaveArea.DB, 3, 832 , bytes2);
byte[] byteArrays = connector.read(DaveArea.DB, 3, 2, 832);
// List<Byte> bytes = ByteUtils.toByteArray(byteArrays);
List<Byte> bytes = (List<Byte>) PlcVar.BYTE_Array.toObject(boolArrays);
System.out.println("DB3.832-byteArrays : " +bytes );
short[] shortArrays_content = new short[2];
shortArrays_content[0] = 1;
shortArrays_content[1] = -1;
// byte[] bytes4 = ByteUtils.wordArrayToBytes(shortArrays_content);
byte[] bytes4 = PlcVar.WORD_Array.toBytes(shortArrays_content);
connector.write(DaveArea.DB, 3, 836 ,bytes4);
byte[] wordArrays = connector.read(DaveArea.DB, 3, 4, 836);
List<Integer> words = ByteUtils.toWordArray(wordArrays);
System.out.println("DB3.836-wordArrays : " +words );
int[] intArrays_content = new int[2];
intArrays_content[0] = 1;
intArrays_content[1] = -1;
// byte[] bytes5 = ByteUtils.dwordArrayToBytes(intArrays_content);
byte[] bytes5 = PlcVar.DWORD_Array.toBytes(intArrays_content);
connector.write(DaveArea.DB, 3, 840 ,bytes5);
byte[] dwordArrays = connector.read(DaveArea.DB, 3, 8, 840);
List<Integer> dwords = ByteUtils.toDWordArray(dwordArrays);
System.out.println("DB3.840-dwordArrays : " +dwords );
char[] charArrays_content = new char[2];
charArrays_content[0] = '1';
charArrays_content[1] = 'b';
// byte[] bytes3 = ByteUtils.charArrayToBytes(charArrays_content);
byte[] bytes3 = PlcVar.CHAR_Array.toBytes(charArrays_content);
connector.write(DaveArea.DB, 3, 834 ,bytes3);
byte[] charArrays = connector.read(DaveArea.DB, 3, 2, 834);
List<Character> chars = ByteUtils.toCharArray(charArrays);
System.out.println("DB3.834-charArrays : " +chars );
int[] sintArrays_content = new int[2];
sintArrays_content[0] = 1;
sintArrays_content[1] = -1;
// byte[] bytes6 = ByteUtils.sintArrayToBytes(sintArrays_content);
byte[] bytes6 = PlcVar.SINT_Array.toBytes(sintArrays_content);
connector.write(DaveArea.DB, 3, 852 ,bytes6);
byte[] sintArrays = connector.read(DaveArea.DB, 3, 2, 852);
List<Integer> sints = ByteUtils.toSIntArray(sintArrays);
System.out.println("DB3.852-sintArrays : " +sints );
int[] iintArrays_content = new int[2];
iintArrays_content[0] = 12;
iintArrays_content[1] = -21;
// byte[] bytes7 = ByteUtils.intArrayToBytes(iintArrays_content);
byte[] bytes7 = PlcVar.INT_Array.toBytes(iintArrays_content);
connector.write(DaveArea.DB, 3, 848 ,bytes7);
byte[] intArrays = connector.read(DaveArea.DB, 3, 4, 848);
List<Integer> ints = ByteUtils.toIntArray(intArrays);
System.out.println("DB3.848-intArrays : " +ints );
//todo here
int[] dintArrays_content = new int[2];
dintArrays_content[0] = 12;
dintArrays_content[1] = -21;
//ByteUtils.dintArrayToBytes(dintArrays_content)
byte[] bytes11 =PlcVar.DINT_Array.toBytes(dintArrays_content);
connector.write(DaveArea.DB, 3, 854 ,bytes11);
byte[] dintArrays = connector.read(DaveArea.DB, 3, 8, 854);
List<Integer> dints = ByteUtils.toDIntArray(dintArrays);
System.out.println("DB3.852-dintArrays : " +dints);
int[] uintArrays_content = new int[3];
uintArrays_content[0] = 12;
uintArrays_content[1] = 99;
uintArrays_content[2] = 1;
//byte[] bytes9 = ByteUtils.uintArrayToBytes(uintArrays_content);
byte[] bytes9 = PlcVar.UINT_Array.toBytes(uintArrays_content);
connector.write(DaveArea.DB, 3, 3256 ,bytes9);
byte[] uintArrays = connector.read(DaveArea.DB, 3, 6, 3256);
List<Integer> uints = ByteUtils.toUIntArray(uintArrays);
System.out.println("DB3.3256-uintArrays : " +uints );
int[] usintArrays_content = new int[3];
usintArrays_content[0] = 12;
usintArrays_content[1] = 99;
usintArrays_content[2] = 1;
// byte[] bytes8 = ByteUtils.usintArrayToBytes(usintArrays_content);
byte[] bytes8 = PlcVar.USINT_Array.toBytes(usintArrays_content);
connector.write(DaveArea.DB, 3, 3240 ,bytes8);
byte[] usintArrays = connector.read(DaveArea.DB, 3, 3, 3240);
List<Integer> usints = ByteUtils.toUSIntArray(usintArrays);
System.out.println("DB3.3240-usintArrays : " +usints );
int[] udintArrays_content = new int[3];
udintArrays_content[0] = 12;
udintArrays_content[1] = 99;
udintArrays_content[2] = 1;
// byte[] bytes10 = ByteUtils.udintArrayToBytes(udintArrays_content);
byte[] bytes10 = PlcVar.UDINT_Array.toBytes(udintArrays_content);
connector.write(DaveArea.DB, 3, 3244 ,bytes10);
byte[] udintArrays = connector.read(DaveArea.DB, 3, 12, 3244);
List<Long> udints = ByteUtils.toUDIntArray(udintArrays);
System.out.println("DB3.852-udintArrays : " +udints);
}
//Write to DB100 10 bytes
//connector.write(DaveArea.DB, 3, 830, byteArrays);
//Close connection
connector.close();
}
}

View File

@ -0,0 +1,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;
}
}

View File

@ -0,0 +1,58 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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);
}

View File

@ -0,0 +1,78 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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 <T>
* the generic type
* @param targetClass
* the target class
* @param buffer
* the buffer
* @param byteOffset
* the byte offset
* @param bitOffset
* the bit offset
* @return the t
*/
public <T> T extract(Class<T> targetClass, byte[] buffer, int byteOffset, int bitOffset);
/**
* Returns the S7-Type.
*
* @return the s7 type
*/
public S7Type getS7Type();
/**
* Returns the size of the s7 type bytes.
*
* @return the size in bits
*/
public int getSizeInBits();
/**
* Returns the size of the s7 type bytes.
*
* @return the size in bytes
*/
public int getSizeInBytes();
/**
* Inserts a Java Object to the byte buffer.
*
* @param javaType
* the java type
* @param buffer
* the buffer
* @param byteOffset
* the byte offset
* @param bitOffset
* the bit offset
* @param size
* the size
*/
public void insert(Object javaType, byte[] buffer, int byteOffset, int bitOffset, int size);
}

View File

@ -0,0 +1,70 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.cnbm.s7.s7connector.api;
import com.cnbm.s7.s7connector.exception.S7Exception;
public interface S7Serializer {
/**
* Dispenses an Object from the mapping of the Datablock.
*
* @param <T>
* the generic type
* @param beanClass
* the bean class
* @param dbNum
* the db num
* @param byteOffset
* the byte offset
* @return the t
* @throws S7Exception
* the s7 exception
*/
<T> T dispense(Class<T> beanClass, int dbNum, int byteOffset) throws S7Exception;
/**
* Dispense.
*
* @param <T>
* the generic type
* @param beanClass
* the bean class
* @param dbNum
* the db num
* @param byteOffset
* the byte offset
* @param blockSize
* the block size
* @return the t
* @throws S7Exception
* the s7 exception
*/
<T> T dispense(Class<T> beanClass, int dbNum, int byteOffset, int blockSize) throws S7Exception;
/**
* Stores an Object to the Datablock.
*
* @param bean
* the bean
* @param dbNum
* the db num
* @param byteOffset
* the byte offset
*/
void store(Object bean, int dbNum, int byteOffset);
}

View File

@ -0,0 +1,30 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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();
}

View File

@ -0,0 +1,29 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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 {
}

View File

@ -0,0 +1,56 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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();
}

View File

@ -0,0 +1,90 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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();
}
}

View File

@ -0,0 +1,41 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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);
}
}

View File

@ -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<br>
* 默认以小端序转换
*
* @param bytes byte数组
* @return short值
*/
public static short bytesToShort(byte[] bytes) {
return bytesToShort(bytes, ByteOrder.LITTLE_ENDIAN);
}
/**
* byte数组转short<br>
* 自定义端序
*
* @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数组<br>
* 默认以小端序转换
*
* @param shortValue short值
* @return byte数组
*/
public static byte[] shortToBytes(short shortValue) {
return shortToBytes(shortValue, ByteOrder.LITTLE_ENDIAN);
}
/**
* short转byte数组<br>
* 自定义端序
*
* @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值<br>
* 默认以小端序转换
*
* @param bytes byte数组
* @return int值
*/
public static int bytesToInt(byte[] bytes) {
return bytesToInt(bytes, ByteOrder.LITTLE_ENDIAN);
}
/**
* byte[]转int值<br>
* 自定义端序
*
* @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数组<br>
* 默认以小端序转换
*
* @param intValue int值
* @return byte数组
*/
public static byte[] intToBytes(int intValue) {
return intToBytes(intValue, ByteOrder.LITTLE_ENDIAN);
}
/**
* int转byte数组<br>
* 自定义端序
*
* @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数组<br>
* 默认以小端序转换<br>
* 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数组<br>
* 自定义端序<br>
* 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<br>
* 默认以小端序转换<br>
* 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<br>
* 自定义端序<br>
* 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数组<br>
* 默认以小端序转换<br>
*
* @param doubleValue double值
* @return byte数组
*/
public static byte[] doubleToBytes(double doubleValue) {
return doubleToBytes(doubleValue, ByteOrder.LITTLE_ENDIAN);
}
/**
* double转byte数组<br>
* 自定义端序<br>
* 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<br>
* 默认以小端序转换<br>
*
* @param bytes byte数组
* @return long值
*/
public static double bytesToDouble(byte[] bytes) {
return bytesToDouble(bytes, ByteOrder.LITTLE_ENDIAN);
}
/**
* byte数组转double<br>
* 自定义端序<br>
*
* @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);
}
}
}

View File

@ -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<Byte> st = new Stack<Byte>();
for(int i=0;i<a.length;i++){
st.push(a[i]);
}
for(int i=0;i<a.length;i++){
b[i] = st.pop();
}
return b;
}
/**
* 1个字节byte[] 转成有符号的Integer
* */
public static Integer toInt(byte bytes) {
return Integer.valueOf(Byte.toString(bytes));
}
/**
* 1个字节byte[] 转成无符号的Integer
* */
public static Integer toUInt(byte bytes) {
int i = Byte.toUnsignedInt(bytes);
return Integer.valueOf(i);
}
/**
* 2个字节byte[] 转成有符号的Integer
* 默认大端
* */
public static Integer toInt(byte byte1,byte byte2) {
byte[] bytes = new byte[2];
bytes[0] = byte1;
bytes[1] = byte2;
return Integer.valueOf(Short.toString(ByteUtil.bytesToShort(bytes, ByteOrder.BIG_ENDIAN)));
}
/**
* 2个字节byte[] 转成无符号的Integer
* 默认大端
* */
public static Integer toUInt(byte byte1,byte byte2) {
byte[] bytes = new byte[2];
bytes[0] = byte1;
bytes[1] = byte2;
short i = ByteUtil.bytesToShort(bytes, ByteOrder.BIG_ENDIAN);
return Short.toUnsignedInt(i);
}
/**
* 4个字节byte[] 转成有符号的Integer
* 默认大端
* */
public static Integer toInt(byte byte1,byte byte2,byte byte3,byte byte4) {
byte[] bytes = new byte[4];
bytes[0] = byte1;
bytes[1] = byte2;
bytes[2] = byte3;
bytes[3] = byte4;
return ByteUtil.bytesToInt(bytes, ByteOrder.BIG_ENDIAN);
}
/**
* 4个字节byte[] 转成无符号的Long因为如果首位为1 Integer就不满足长度了
* 默认大端
* */
public static Long toUInt(byte byte1,byte byte2,byte byte3,byte byte4) {
byte[] bytes = new byte[8];
bytes[0] = 0;
bytes[1] = 0;
bytes[2] = 0;
bytes[3] = 0;
bytes[4] = byte1;
bytes[5] = byte2;
bytes[6] = byte3;
bytes[7] = byte4;
long l = ByteUtil.bytesToLong(bytes, ByteOrder.BIG_ENDIAN);
return l;
}
/**
*
* 4个字节byte[] 转成有符号的Double
* 默认大端
* */
public static Float forReal(byte[] bytes) {
return Float.intBitsToFloat(toInt(bytes[0],bytes[1],bytes[2],bytes[3]));
}
/**
*
* 8个字节byte[] 转成有符号的Double
* 默认大端
* */
public static Double forLReal(byte[] bytes) {
return ByteUtil.bytesToDouble(bytes, ByteOrder.BIG_ENDIAN);
}
/**
*
* 8个字节byte[] 转成有符号的Double
* 默认大端
* */
public static Double lrealbytesToDouble(byte[] bytes) {
return ByteUtil.bytesToDouble(bytes, ByteOrder.BIG_ENDIAN);
}
public static byte[] lrealToBytes(double doubleValue) {
return ByteUtil.doubleToBytes(doubleValue, ByteOrder.BIG_ENDIAN);
//return double2Bytes(doubleValue);
}
// public static String toChar(byte[] b) throws UnsupportedEncodingException {
// String ascii = new String(b, Charset.forName("UTF-8"));
// return ascii;
// }
public static Character toChar(byte[] b) {
if(b.length==1){
return toChar(b[0]);
}
return byteToChar(b);
}
public static Character toChar(byte b){
return byteToChar(b);
}
// public static String toChar(byte b) throws UnsupportedEncodingException {
// byte[] bs = new byte[1];
// bs[0] = b;
// String ascii = new String(bs, "ascii");
// return ascii;
// }
/**
* return null 代表返回传入参数不正确str 取的length太小
* */
public static String toStr(byte[] b) {
Integer length = Byte.toUnsignedInt(b[1]);
if(length>(b.length-2)){
return null;
}
byte[] content = new byte[b.length-2];
for(int i=0;i<length;i++){
content[i] = b[i+2];
}
String ascii = new String(content, StandardCharsets.UTF_8);
// String s = new String(content);
return ascii;
}
public static String[] toStrArray(byte[] b,Integer length,Integer strSize) {
String[] res = new String[length];
strSize+=2;
for(int i=0;i<length;i++){
byte[] one = new byte[strSize];
System.arraycopy(b,i*strSize,one,0,strSize);
res[i] = toStr(one);
}
return res;
}
/**
* 将byte转换为一个长度为8的boolean数组(每bit代表一个boolean值)
*
* @param b byte, return(将数组翻转过来)
* @return boolean数组
*/
public static boolean[] getBooleanArray(byte b,boolean returns) {
boolean[] array = new boolean[8];
for (int i = 7; 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<Boolean> toBoolArray(byte[] b) {
List<Boolean> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
boolean[] booleanArray = getBooleanArray(b[i], true);
for(int j=0;j<booleanArray.length;j++){
res.add(booleanArray[j]);
}
}
return res;
}
public static boolean[] reverse(boolean[] b) {
boolean[] res = new boolean[8];
res[0] = b[7];
res[1] = b[6];
res[2] = b[5];
res[3] = b[4];
res[4] = b[3];
res[5] = b[2];
res[6] = b[1];
res[7] = b[0];
return res;
}
//bool array to byte array
public static byte[] toByteArray(boolean[] b){
Integer byteLength = CommonFunctions.exactDivision(b.length, 8);
byte[] res = new byte[byteLength];
Queue<Boolean> queue = new LinkedList<Boolean>();
for(int i=0;i<b.length; i++){
queue.add(b[i]);
}
//todo
int z = 0;
Integer boolLength = queue.size();
while (boolLength>0){
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<boolLength;i++){
input[i]= queue.poll();
}
Byte aByte = fromBooleanArray(reverse(input));
res[z] = aByte;
z++;
}
boolLength = boolLength-8;
}
return res;
}
// public st atic List<Boolean> toBoolArray(byte[] b) throws UnsupportedEncodingException {
// List<Boolean> res = new ArrayList<>();
// for(int i=0;i<b.length;i++){
// res.add(toBoolean(b[i]));
// }
// return res;
// }
public static List<Byte> toByteArray(byte[] b) {
List<Byte> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add((b[i]));
}
return res;
}
public static List<Character> toCharArray(byte[] b) {
List<Character> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add(toChar(b[i]));
}
return res;
}
//toWord
/**
* 默认word => 有符号的整形
* */
public static List<Integer> toWordArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
res.add(
toInt(b[i],b[i+1])
);
i+=2;
}
return res;
}
//toDWord
/**
* 默认dword => 有符号的整形
* */
public static List<Integer> toDWordArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
res.add(
toInt(b[i],b[i+1],b[i+2],b[i+3])
);
i+=4;
}
return res;
}
/**
*
* 4个字节byte[] 转成有符号的Double
* 默认大端
* */
public static Float realbytesToFloat(byte[] bytes) {
return Float.intBitsToFloat(toInt(bytes[0],bytes[1],bytes[2],bytes[3]));
}
public static byte[] realToBytes(Float f) {
return invert(float2byte(f));
}
/**
* 浮点转换为字节
*
* @param f
* @return
*/
private static byte[] float2byte(float f) {
// 把float转换为byte[]
int fbit = Float.floatToIntBits(f);
byte[] b = new byte[4];
for (int i = 0; i < 4; i++) {
b[i] = (byte) (fbit >> (24 - i * 8));
}
// 翻转数组
int len = b.length;
// 建立一个与源数组元素类型相同的数组
byte[] dest = new byte[len];
// 为了防止修改源数组将源数组拷贝一份副本
System.arraycopy(b, 0, dest, 0, len);
byte temp;
// 将顺位第i个与倒数第i个交换
for (int i = 0; i < len / 2; ++i) {
temp = dest[i];
dest[i] = dest[len - i - 1];
dest[len - i - 1] = temp;
}
return dest;
}
//toUInt
/**
* USInt 无符号整形 1个字节 = Integer
* */
public static List<Integer> toUSIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add(toUInt(b[i]));
}
return res;
}
/**
* UInt 无符号整形 2个字节 = Integer
* */
public static List<Integer> toUIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
res.add(
toUInt(b[i],b[i+1])
);
i+=2;
}
return res;
}
/**
* UDInt 无符号整形 4个字节 = Long
* */
public static List<Long> toUDIntArray(byte[] b) {
List<Long> res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
res.add(
toUInt(b[i],b[i+1],b[i+2],b[i+3])
);
i+=4;
}
return res;
}
/**
* SInt 无符号整形 1个字节 = Integer
* */
public static List<Integer> toSIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
for(int i=0;i<b.length;i++){
res.add(toInt(b[i]));
}
return res;
}
/**
* Int 无符号整形 2个字节 = Integer
* */
public static List<Integer> toIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+2)<=b.length){
res.add(
toInt(b[i],b[i+1])
);
i+=2;
}
return res;
}
/**
* DInt 无符号整形 4个字节 = Integer
* */
public static List<Integer> toDIntArray(byte[] b) {
List<Integer> res = new ArrayList<>();
int i=0;
while ((i+4)<=b.length){
res.add(
toInt(b[i],b[i+1],b[i+2],b[i+3])
);
i+=4;
}
return res;
}
/**
* sint(1个字节) = byte[]
* 默认大端模式
* */
public static byte[] sintToBytes(Integer i){
byte[] res = new byte[1];
res[0] = Byte.valueOf(i.toString());
return res;
}
/**
* sintArray(1个字节) = byte[]
*
* */
public static byte[] sintArrayToBytes(int[] sintArray) {
byte[] res = new byte[sintArray.length];
for(int i=0;i<sintArray.length;i++){
int i1 = sintArray[i];
res[i] = sintToBytes(i1)[0];
}
return res;
}
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
public static byte[] intToBytes(Integer i) {
Number shortNumber = Short.valueOf(i.toString());
return ByteUtil.numberToBytes(shortNumber,ByteOrder.BIG_ENDIAN);
}
/**
* intArray(2个字节) = byte[]
*
* */
public static byte[] intArrayToBytes(int[] intArray) {
byte[] res = new byte[intArray.length*2];
for(int i=0 ; i< intArray.length ; i++){
byte[] bytes = intToBytes(intArray[i]);
res[i*2] = bytes[0];
res[i*2+1] = bytes[1];
}
return res;
}
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
public static byte[] dintToBytes(Integer i) {
Number intNumber = i;
return ByteUtil.numberToBytes(intNumber,ByteOrder.BIG_ENDIAN);
}
/**
* intArray(2个字节) = byte[]
*
* */
public static byte[] dintArrayToBytes(int[] dintArray) {
byte[] res = new byte[dintArray.length*4];
for(int i=0 ; i< dintArray.length ; i++){
byte[] bytes = dintToBytes(dintArray[i]);
res[i*4] = bytes[0];
res[i*4+1] = bytes[1];
res[i*4+2] = bytes[2];
res[i*4+3] = bytes[3];
}
return res;
}
/**
* sint(1个字节) = byte[]
* 默认大端模式
* */
public static byte[] usintToBytes(Integer i){
if(i<0){
return null;
}
byte[] res = new byte[1];
res[0] = Byte.valueOf(i.toString());
return res;
}
/**
* usintArrayToBytes = byte[]
* 默认大端模式
* */
public static byte[] usintArrayToBytes(int[] usintArray) {
byte[] res = new byte[usintArray.length];
for(int i=0 ; i< usintArray.length ; i++){
byte[] bytes = usintToBytes(usintArray[i]);
if(bytes == null){
return null;
}
res[i] = bytes[0];
}
return res;
}
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
public static byte[] uintToBytes(Integer i) {
if(i<0){
return null;
}
Number shortNumber = Short.valueOf(i.toString());
return ByteUtil.numberToBytes(shortNumber,ByteOrder.BIG_ENDIAN);
}
public static byte[] uintArrayToBytes(int[] uintArray) {
byte[] res = new byte[uintArray.length*2];
for(int i=0 ; i< uintArray.length ; i++){
byte[] bytes = uintToBytes(uintArray[i]);
if(bytes == null){
return null;
}
res[i*2] = bytes[0];
res[i*2+1] = bytes[1];
}
return res;
}
/**
* int(2个字节) = byte[]
* 默认大端模式
* */
public static byte[] udintToBytes(Integer i) {
if(i<0){
return null;
}
Number intNumber = i;
return ByteUtil.numberToBytes(intNumber,ByteOrder.BIG_ENDIAN);
}
public static byte[] udintArrayToBytes(int[] udintArray) {
byte[] res = new byte[udintArray.length*4];
for(int i=0 ; i< udintArray.length ; i++){
byte[] bytes = udintToBytes(udintArray[i]);
if(bytes == null){
return null;
}
res[i*4] = bytes[0];
res[i*4+1] = bytes[1];
res[i*4+2] = bytes[2];
res[i*4+3] = bytes[3];
}
return res;
}
/**
* boolean = byte[]
* 默认大端模式
* */
public static byte[] boolToBytes(Boolean bool) {
byte[] res = new byte[1];
if(bool){
res[0] = 1;
return res;
}else {
res[0] = 0;
return res;
}
}
/**
* booleanArray = byte[]
* */
public static byte[] booleanArrayToBytes(boolean[] boolArray) {
byte[] res = new byte[boolArray.length];
for(int i=0 ; i<boolArray.length ; i++){
boolean b = boolArray[i];
if(b){
res[i] = 1;
}else {
res[i] = 0;
}
}
return res;
}
/**
* charArray = byte[]
* */
public static byte[] charArrayToBytes(char[] charArray) {
byte[] res = new byte[charArray.length];
for(int i=0 ; i< charArray.length ; i++){
byte[] bytes = charToByte(charArray[i]);
if(bytes[0] != 0){
return null;
}else {
res[i] = bytes[1];
}
}
return res;
}
/**
* byte = byte[]
* 默认大端模式
* */
public static byte[] setbytes(Byte bytes) {
byte[] res = new byte[1];
res[0] = bytes;
return res;
}
/**
* word = byte[]
* 默认大端模式
* tip: 目前只支持 带符号的十进制 == word(2个字节)
* */
public static byte[] wordToBytes(Short word) {
return ByteUtil.shortToBytes(word,ByteOrder.BIG_ENDIAN);
}
/**
* wordArray = byte[]
* */
public static byte[] wordArrayToBytes(short[] shortArray) {
byte[] res = new byte[shortArray.length*2];
for(int i=0 ; i< shortArray.length ; i++){
byte[] bytes = wordToBytes(shortArray[i]);
res[i*2] = bytes[0];
res[i*2+1] = bytes[1];
}
return res;
}
/**
* dword = byte[]
* 默认大端模式
* tip: 目前只支持 带符号的十进制 == word(4个字节)
* */
public static byte[] dwordToBytes(Integer dword) {
return ByteUtil.intToBytes(dword,ByteOrder.BIG_ENDIAN);
}
/**
* dwordArray = byte[]
* */
public static byte[] dwordArrayToBytes(int[] intArray) {
byte[] res = new byte[intArray.length*4];
for(int i=0 ; i< intArray.length ; i++){
byte[] bytes = dwordToBytes(intArray[i]);
res[i*4] = bytes[0];
res[i*4+1] = bytes[1];
res[i*4+2] = bytes[2];
res[i*4+3] = bytes[3];
}
return res;
}
/**
* char(1字节) = byte[]
*
* */
public static byte[] charToBytes(Character c) {
return String.valueOf(c).getBytes();
}
public static byte[] wcharToBytes(Character c) {
return charToByte(c);
}
//如果plc中没有设置 strSize,那么 这个字符串变量的 最大长度默认是 254字节就是-2
/**
* 这个函数就用于 非str类型转bytes使用string类型转bytes 用strToBytes(String s,Integer strSize) 这个方法
* */
public static byte[] strToBytes(String s) {
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
byte[] res = new byte[bytes.length+2];
res[0] = -2;
res[1] = Integer.valueOf(bytes.length).byteValue();
for(int i=0;i<bytes.length;i++){
res[i+2] = bytes[i];
}
return res;
}
/**
* desc
* 入参
* 一般来说 plc中 string变量都是会设置 长度的 var1 = String[18],这里的strSize就是18
* 返回
* 返回的字节流是包含2个头字节的也就是 strSize+2 个长度的字节流
* */
public static byte[] strToBytes(String s,Integer strSize) {
if(s==null || (strSize<=0)){
return null;
}
Integer allStrSize = strSize+2;
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
byte[] res = new byte[allStrSize];
res[0] = strSize.byteValue();
if(bytes.length>strSize){
res[1] = strSize.byteValue();
for(int i=0;i<strSize;i++){
res[i+2] = bytes[i];
}
}else {
res[1] = Integer.valueOf(bytes.length).byteValue();
for(int i=0;i<bytes.length;i++){
res[i+2] = bytes[i];
}
}
return res;
}
public static void main(String[] args) throws UnsupportedEncodingException {
// //stringArray <=> 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<String> 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<array.length;i++){
byte[] bytes = strToBytes(array[i],strSize);
System.arraycopy(bytes,0,res,i*allStrSize,bytes.length);
}
return res;
}
//for private
private static byte[] charToByte(char c) {
byte[] b = new byte[2];
b[0] = (byte) ((c & 0xFF00) >> 8);
b[1] = (byte) (c & 0xFF);
return b;
}
private static char byteToChar(byte[] b) {
char c = (char) (((b[0] & 0xFF) << 8) | (b[1] & 0xFF));
return c;
}
private static char byteToChar(byte b) {
char c = (char) (b & 0xFF);
return c;
}
}

View File

@ -0,0 +1,66 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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 + "]";
}
}

View File

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

View File

@ -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 西门子200smart12001500默认的 机架号=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<S7Connector> connections;
//coreSize 是线程池的大小
private Integer coreSize;
//pickOne 就是一个初始化 的轮询取余值
private int pickOne;
private static final Logger logger = LoggerFactory.getLogger(S7Client.class);
//coreSize 是线程池的数量
S7Client(String host, Integer rack, Integer slot, Integer coreSize){
this.host = host;
this.rack = rack;
this.slot = slot;
this.pickOne = 0;
this.coreSize = coreSize;
connections = new ArrayList<>();
connectionPool();
}
public String getHost(){
return this.host;
}
/**
* PlcVar(byte[]) java对象 对照表
* 单体变量
* Bool ===> Boolean
* LREAL ===> Double
* REAL ===> Float
* DATE ===> String(yyyy-MM-dd 这种形式的类型)
* DTL ===> String("年-月-日-工作日-时-分-秒" 这种格式)
* TIME ===> Integer(单位 ms)
* USINT ===> Integer
* SINT ===> Integer
* UINT ===> Integer
* INT ===> Integer
* DINT ===> Integer
* UINT ===> Long
* Byte ===> Integer(有符号)(默认)
* Integer(无符号)(后续扩展)
* Char ===> Character
* WChar ===> Character
* String ===> String 特殊
* 数组变量
* BoolArray ===> List<Boolean>
* ByteArray ===> List<Byte>
* WordArray ===> List<Integer>
* DWordArray ===> List<Integer>
* CharArray ===> List<Character>
* SIntArray ===> List<Integer>
* IntArray ===> List<Integer>
* DIntArray ===> List<Integer>
* UIntArray ===> List<Integer>
* USIntArray ===> List<Integer>
* UDIntArray ===> List<Long>
* StringArray ===> String[] 特殊
*
* 如果返回null就代表出现了异常并且尝试了 retryMax 次数并且尝试重置连接
* */
public Object read(DaveArea area, Integer areaNumber, Integer byteOffset, Integer bitOffset, Integer length, Integer strSizes, PlcVar type) {
S7Connector connector = getConnector();
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<Boolean> booleans = ByteUtils.toBoolArray(read);
List<Boolean> res = new ArrayList<Boolean>();
for(int i=0;i<length;i++){
res.add(i,booleans.get(i));
}
return res;
}else if(type.equals(PlcVar.STRING_Array)){
Integer arrayLength = length;
Integer strSize = strSizes;
byte[] read = connector.read(
area,
areaNumber,
arrayLength*(strSize+2),
byteOffset,
bitOffset,
type.getTransportSize()
);
return ByteUtils.toStrArray(read, arrayLength, strSize);
}else {
Integer readBytes = type.getTransportSize().getSizeInBytes() * length;
byte[] read = connector.read(
area,
areaNumber,
readBytes,
byteOffset,
bitOffset,
type.getTransportSize()
);
return type.toObject(read);
}
}catch (Exception e) {
throw new S7CheckResultException("read errMsg : "+e.getMessage());
}
},
context -> {
logger.info("S7-Retry : 已达到最大重试次数: "+S7RetryTemplate.getMaxRetryTimes()+", 现在尝试重新连接;");
if(replaceConnector(connector)==1){
logger.info("S7-Retry-Read 现在恢复成功创建新connection成功");
return null;
}else {
logger.info("S7-Retry-Read 现在恢复失败创建新connection失败");
return null;
}
}
);
}
/**
*
* eg :
* Object newValue = Boolean.FALSE
* s7Service.write(PlcVarActual.HeartBeat, newValue, S7Client.S7_1200);
*
* PlcVar(byte[]) java对象 对照表
* 单体变量
* Bool ===> Object newValue = Boolean.FALSE
* LREAL ===> Object newValue = Boolean.FALSE
* REAL ===> Object newValue = Boolean.FALSE
* DATE ===> 暂时没需求有问题找我
* DTL ===> 暂时没需求有问题找我
* TIME ===> 暂时没需求有问题找我
* USINT ===> Object newValue = new Integer(1)
* SINT ===> Object newValue = new Integer(1)
* UINT ===> Object newValue = new Integer(1)
* INT ===> Object newValue = new Integer(1)
* DINT ===> Object newValue = new Integer(1)
* UINT ===> Object newValue = new Integer(1)
* Byte ===> Object newValue = 0x11
*
* Char ===> Object newValue = 'a'
* WChar ===> Object newValue = '菜'
* String ===> Object newValue = '你好啊' 特殊
* 数组变量
* 注意在write的时候你write的数量 一定要和 plc中存在的数量一一对应
* BoolArray ===> boolean[] booleanArray = new boolean[2]; .... 赋予值
* ByteArray ===> byte[] write_byteArrays = new byte[2];
* WordArray ===> short[] shortArrays_content = new short[2];
* DWordArray ===> int[] intArrays_content = new int[2];
* CharArray ===> char[] charArrays_content = new char[2];
* SIntArray ===> int[] sintArrays_content = new int[2];
* IntArray ===> int[] iintArrays_content = new int[2];
* DIntArray ===> int[] dintArrays_content = new int[2];
* UIntArray ===> int[] uintArrays_content = new int[3];
* USIntArray ===> int[] usintArrays_content = new int[3];
* UDIntArray ===> int[] udintArrays_content = new int[3];
* StringArray ===> String[] stringArrays_content = new String[3];
* //如果有其他数据类型 这里没有找我扩展
*
*
* */
public void write(DaveArea area, Integer areaNumber, Integer byteOffset, Integer bitOffset,Integer strSize, PlcVar type, Object newValue) {
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<S7Connector>();
connectionPool();
}
private synchronized void connectionPool(){
for(int i=0;i<coreSize;i++){
S7Connector connect = connect(host, rack, slot);
if(connect!=null){
connections.add(connect);
}
}
//todo 在plc上新增一个 变量来解决 心跳问题 okok
}
}

View File

@ -0,0 +1,272 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.enmuc;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author cgarcia
*/
public enum S7ParamErrorCode {
ERROR_0x0000((short) 0x0000, "No error"),
ERROR_0x0110((short) 0x0110, "Invalid block number"),
ERROR_0x0111((short) 0x0111, "Invalid request length"),
ERROR_0x0112((short) 0x0112, "Invalid parameter"),
ERROR_0x0113((short) 0x0113, "Invalid block type"),
ERROR_0x0114((short) 0x0114, "Block not found"),
ERROR_0x0115((short) 0x0115, "Block already exists"),
ERROR_0x0116((short) 0x0116, "Block is write-protected"),
ERROR_0x0117((short) 0x0117, "The block/operating system update is too large"),
ERROR_0x0118((short) 0x0118, "Invalid block number"),
ERROR_0x0119((short) 0x0119, "Incorrect password entered"),
ERROR_0x011A((short) 0x011A, "PG resource error"),
ERROR_0x011B((short) 0x011B, "PLC resource error"),
ERROR_0x011C((short) 0x011C, "Protocol error"),
ERROR_0x011D((short) 0x011D, "Too many blocks (module-related restriction)"),
ERROR_0x011E((short) 0x011E, "There is no longer a connection to the database, or S7DOS handle is invalid"),
ERROR_0x011F((short) 0x011F, "Result buffer too small"),
ERROR_0x0120((short) 0x0120, "End of block list"),
ERROR_0x0140((short) 0x0140, "Insufficient memory available"),
ERROR_0x0141((short) 0x0141, "Job cannot be processed because of a lack of resources"),
ERROR_0x8001((short) 0x8001, "The requested service cannot be performed while the block is in the current status"),
ERROR_0x8003((short) 0x8003, "S7 protocol error: Error occurred while transferring the block"),
ERROR_0x8100((short) 0x8100, "Application, general error: Service unknown to remote module"),
ERROR_0x8104((short) 0x8104, "This service is not implemented on the module or a frame error was reported"),
ERROR_0x8204((short) 0x8204, "The type specification for the object is inconsistent"),
ERROR_0x8205((short) 0x8205, "A copied block already exists and is not linked"),
ERROR_0x8301((short) 0x8301, "Insufficient memory space or work memory on the module, or specified storage medium not accessible"),
ERROR_0x8302((short) 0x8302, "Too few resources available or the processor resources are not available"),
ERROR_0x8304((short) 0x8304, "No further parallel upload possible. There is a resource bottleneck"),
ERROR_0x8305((short) 0x8305, "Function not available"),
ERROR_0x8306((short) 0x8306, "Insufficient work memory (for copying, linking, loading AWP)"),
ERROR_0x8307((short) 0x8307, "Not enough retentive work memory (for copying, linking, loading AWP)"),
ERROR_0x8401((short) 0x8401, "S7 protocol error: Invalid service sequence (for example, loading or uploading a block)"),
ERROR_0x8402((short) 0x8402, "Service cannot execute owing to status of the addressed object"),
ERROR_0x8404((short) 0x8404, "S7 protocol: The function cannot be performed"),
ERROR_0x8405((short) 0x8405, "Remote block is in DISABLE state (CFB). The function cannot be performed"),
ERROR_0x8500((short) 0x8500, "S7 protocol error: Wrong frames"),
ERROR_0x8503((short) 0x8503, "Alarm from the module: Service canceled prematurely"),
ERROR_0x8701((short) 0x8701, "Error addressing the object on the communications partner (for example, area length error)"),
ERROR_0x8702((short) 0x8702, "The requested service is not supported by the module"),
ERROR_0x8703((short) 0x8703, "Access to object refused"),
ERROR_0x8704((short) 0x8704, "Access error: Object damaged"),
ERROR_0xD001((short) 0xD001, "Protocol error: Illegal job number"),
ERROR_0xD002((short) 0xD002, "Parameter error: Illegal job variant"),
ERROR_0xD003((short) 0xD003, "Parameter error: Debugging function not supported by module"),
ERROR_0xD004((short) 0xD004, "Parameter error: Illegal job status"),
ERROR_0xD005((short) 0xD005, "Parameter error: Illegal job termination"),
ERROR_0xD006((short) 0xD006, "Parameter error: Illegal link disconnection ID"),
ERROR_0xD007((short) 0xD007, "Parameter error: Illegal number of buffer elements"),
ERROR_0xD008((short) 0xD008, "Parameter error: Illegal scan rate"),
ERROR_0xD009((short) 0xD009, "Parameter error: Illegal number of executions"),
ERROR_0xD00A((short) 0xD00A, "Parameter error: Illegal trigger event"),
ERROR_0xD00B((short) 0xD00B, "Parameter error: Illegal trigger condition"),
ERROR_0xD011((short) 0xD011, "Parameter error in path of the call environment: Block does not exist"),
ERROR_0xD012((short) 0xD012, "Parameter error: Wrong address in block"),
ERROR_0xD014((short) 0xD014, "Parameter error: Block being deleted/overwritten"),
ERROR_0xD015((short) 0xD015, "Parameter error: Illegal tag address"),
ERROR_0xD017((short) 0xD017, "Parameter error: Illegal trigger number"),
ERROR_0xD025((short) 0xD025, "Parameter error: Invalid path"),
ERROR_0xD026((short) 0xD026, "Parameter error: Illegal access type"),
ERROR_0xD027((short) 0xD027, "Parameter error: This number of data blocks is not permitted"),
ERROR_0xD031((short) 0xD031, "Internal protocol error"),
ERROR_0xD032((short) 0xD032, "Parameter error: Wrong result buffer length"),
ERROR_0xD033((short) 0xD033, "Protocol error: Wrong job length"),
ERROR_0xD03F((short) 0xD03F, "Coding error: Error in parameter section (for example, reserve bytes not equal to 0)"),
ERROR_0xD041((short) 0xD041, "Data error: Illegal status list ID"),
ERROR_0xD042((short) 0xD042, "Data error: Illegal tag address"),
ERROR_0xD043((short) 0xD043, "Data error: Referenced job not found, check job data"),
ERROR_0xD044((short) 0xD044, "Data error: Illegal tag value, check job data"),
ERROR_0xD045((short) 0xD045, "Data error: Exiting the ODIS control is not allowed in HOLD"),
ERROR_0xD046((short) 0xD046, "Data error: Illegal measuring stage during run-time measurement"),
ERROR_0xD047((short) 0xD047, "Data error: Illegal hierarchy in 'Read job list'"),
ERROR_0xD048((short) 0xD048, "Data error: Illegal deletion ID in 'Delete job'"),
ERROR_0xD049((short) 0xD049, "Invalid substitute ID in 'Replace job'"),
ERROR_0xD04A((short) 0xD04A, "Error executing 'program status'"),
ERROR_0xD05F((short) 0xD05F, "Coding error: Error in data section (for example, reserve bytes not equal to 0, ...)"),
ERROR_0xD061((short) 0xD061, "Resource error: No memory space for job"),
ERROR_0xD062((short) 0xD062, "Resource error: Job list full"),
ERROR_0xD063((short) 0xD063, "Resource error: Trigger event occupied"),
ERROR_0xD064((short) 0xD064, "Resource error: Not enough memory space for one result buffer element"),
ERROR_0xD065((short) 0xD065, "Resource error: Not enough memory space for several result buffer elements"),
ERROR_0xD066((short) 0xD066, "Resource error: The timer available for run-time measurement is occupied by another job"),
ERROR_0xD067((short) 0xD067, "Resource error: Too many 'modify tag' jobs active (in particular multi-processor operation)"),
ERROR_0xD081((short) 0xD081, "Function not permitted in current mode"),
ERROR_0xD082((short) 0xD082, "Mode error: Cannot exit HOLD mode"),
ERROR_0xD0A1((short) 0xD0A1, "Function not permitted in current protection level"),
ERROR_0xD0A2((short) 0xD0A2, "Function not possible at present, because a function is running that modifies memory"),
ERROR_0xD0A3((short) 0xD0A3, "Too many 'modify tag' jobs active on the I/O (in particular multi-processor operation)"),
ERROR_0xD0A4((short) 0xD0A4, "Forcing' has already been established"),
ERROR_0xD0A5((short) 0xD0A5, "Referenced job not found"),
ERROR_0xD0A6((short) 0xD0A6, "Job cannot be disabled/enabled"),
ERROR_0xD0A7((short) 0xD0A7, "Job cannot be deleted, for example because it is currently being read"),
ERROR_0xD0A8((short) 0xD0A8, "Job cannot be replaced, for example because it is currently being read or deleted"),
ERROR_0xD0A9((short) 0xD0A9, "Job cannot be read, for example because it is currently being deleted"),
ERROR_0xD0AA((short) 0xD0AA, "Time limit exceeded in processing operation"),
ERROR_0xD0AB((short) 0xD0AB, "Invalid job parameters in process operation"),
ERROR_0xD0AC((short) 0xD0AC, "Invalid job data in process operation"),
ERROR_0xD0AD((short) 0xD0AD, "Operating mode already set"),
ERROR_0xD0AE((short) 0xD0AE, "The job was set up over a different connection and can only be handled over this connection"),
ERROR_0xD0C1((short) 0xD0C1, "At least one error has been detected while accessing the tag(s)"),
ERROR_0xD0C2((short) 0xD0C2, "Change to STOP/HOLD mode"),
ERROR_0xD0C3((short) 0xD0C3, "At least one error was detected while accessing the tag(s). Mode change to STOP/HOLD"),
ERROR_0xD0C4((short) 0xD0C4, "Timeout during run-time measurement"),
ERROR_0xD0C5((short) 0xD0C5, "Display of block stack inconsistent, because blocks were deleted/reloaded"),
ERROR_0xD0C6((short) 0xD0C6, "Job was automatically deleted as the jobs it referenced have been deleted"),
ERROR_0xD0C7((short) 0xD0C7, "The job was automatically deleted because STOP mode was exited"),
ERROR_0xD0C8((short) 0xD0C8, "Block status' aborted because of inconsistencies between test job and running program"),
ERROR_0xD0C9((short) 0xD0C9, "Exit the status area by resetting OB90"),
ERROR_0xD0CA((short) 0xD0CA, "Exiting the status range by resetting OB90 and access error reading tags before exiting"),
ERROR_0xD0CB((short) 0xD0CB, "The output disable for the peripheral outputs has been activated again"),
ERROR_0xD0CC((short) 0xD0CC, "The amount of data for the debugging functions is restricted by the time limit"),
ERROR_0xD201((short) 0xD201, "Syntax error in block name"),
ERROR_0xD202((short) 0xD202, "Syntax error in function parameters"),
ERROR_0xD205((short) 0xD205, "Linked block already exists in RAM: Conditional copying is not possible"),
ERROR_0xD206((short) 0xD206, "Linked block already exists in EPROM: Conditional copying is not possible"),
ERROR_0xD208((short) 0xD208, "Maximum number of copied (not linked) blocks on module exceeded"),
ERROR_0xD209((short) 0xD209, "(At least) one of the given blocks not found on the module"),
ERROR_0xD20A((short) 0xD20A, "The maximum number of blocks that can be linked with one job was exceeded"),
ERROR_0xD20B((short) 0xD20B, "The maximum number of blocks that can be deleted with one job was exceeded"),
ERROR_0xD20C((short) 0xD20C, "OB cannot be copied because the associated priority class does not exist"),
ERROR_0xD20D((short) 0xD20D, "SDB cannot be interpreted (for example, unknown number)"),
ERROR_0xD20E((short) 0xD20E, "No (further) block available"),
ERROR_0xD20F((short) 0xD20F, "Module-specific maximum block size exceeded"),
ERROR_0xD210((short) 0xD210, "Invalid block number"),
ERROR_0xD212((short) 0xD212, "Incorrect header attribute (run-time relevant)"),
ERROR_0xD213((short) 0xD213, "Too many SDBs. Note the restrictions on the module being used"),
ERROR_0xD216((short) 0xD216, "Invalid user program - reset module"),
ERROR_0xD217((short) 0xD217, "Protection level specified in module properties not permitted"),
ERROR_0xD218((short) 0xD218, "Incorrect attribute (active/passive)"),
ERROR_0xD219((short) 0xD219, "Incorrect block lengths (for example, incorrect length of first section or of the whole block)"),
ERROR_0xD21A((short) 0xD21A, "Incorrect local data length or write-protection code faulty"),
ERROR_0xD21B((short) 0xD21B, "Module cannot compress or compression was interrupted early"),
ERROR_0xD21D((short) 0xD21D, "The volume of dynamic project data transferred is illegal"),
ERROR_0xD21E((short) 0xD21E, "Unable to assign parameters to a module (such as FM, CP). The system data could not be linked"),
ERROR_0xD220((short) 0xD220, "Invalid programming language. Note the restrictions on the module being used"),
ERROR_0xD221((short) 0xD221, "The system data for connections or routing are not valid"),
ERROR_0xD222((short) 0xD222, "The system data of the global data definition contain invalid parameters"),
ERROR_0xD223((short) 0xD223, "Error in instance data block for communication function block or maximum number of instance DBs exceeded"),
ERROR_0xD224((short) 0xD224, "The SCAN system data block contains invalid parameters"),
ERROR_0xD225((short) 0xD225, "The DP system data block contains invalid parameters"),
ERROR_0xD226((short) 0xD226, "A structural error occurred in a block"),
ERROR_0xD230((short) 0xD230, "A structural error occurred in a block"),
ERROR_0xD231((short) 0xD231, "At least one loaded OB cannot be copied because the associated priority class does not exist"),
ERROR_0xD232((short) 0xD232, "At least one block number of a loaded block is illegal"),
ERROR_0xD234((short) 0xD234, "Block exists twice in the specified memory medium or in the job"),
ERROR_0xD235((short) 0xD235, "The block contains an incorrect checksum"),
ERROR_0xD236((short) 0xD236, "The block does not contain a checksum"),
ERROR_0xD237((short) 0xD237, "You are about to load the block twice, i.e. a block with the same time stamp already exists on the CPU"),
ERROR_0xD238((short) 0xD238, "At least one of the blocks specified is not a DB"),
ERROR_0xD239((short) 0xD239, "At least one of the DBs specified is not available as a linked variant in the load memory"),
ERROR_0xD23A((short) 0xD23A, "At least one of the specified DBs is considerably different from the copied and linked variant"),
ERROR_0xD240((short) 0xD240, "Coordination rules violated"),
ERROR_0xD241((short) 0xD241, "The function is not permitted in the current protection level"),
ERROR_0xD242((short) 0xD242, "Protection violation while processing F blocks"),
ERROR_0xD250((short) 0xD250, "Update and module ID or version do not match"),
ERROR_0xD251((short) 0xD251, "Incorrect sequence of operating system components"),
ERROR_0xD252((short) 0xD252, "Checksum error"),
ERROR_0xD253((short) 0xD253, "No executable loader available; update only possible using a memory card"),
ERROR_0xD254((short) 0xD254, "Storage error in operating system"),
ERROR_0xD280((short) 0xD280, "Error compiling block in S7-300 CPU"),
ERROR_0xD2A1((short) 0xD2A1, "Another block function or a trigger on a block is active"),
ERROR_0xD2A2((short) 0xD2A2, "A trigger is active on a block. Complete the debugging function first"),
ERROR_0xD2A3((short) 0xD2A3, "The block is not active (linked), the block is occupied or the block is currently marked for deletion"),
ERROR_0xD2A4((short) 0xD2A4, "The block is already being processed by another block function"),
ERROR_0xD2A6((short) 0xD2A6, "It is not possible to save and change the user program simultaneously"),
ERROR_0xD2A7((short) 0xD2A7, "The block has the attribute 'unlinked' or is not processed"),
ERROR_0xD2A8((short) 0xD2A8, "An active debugging function is preventing parameters from being assigned to the CPU"),
ERROR_0xD2A9((short) 0xD2A9, "New parameters are being assigned to the CPU"),
ERROR_0xD2AA((short) 0xD2AA, "New parameters are currently being assigned to the modules"),
ERROR_0xD2AB((short) 0xD2AB, "The dynamic configuration limits are currently being changed"),
ERROR_0xD2AC((short) 0xD2AC, "A running active or deactivate assignment (SFC 12) is temporarily preventing R-KiR process"),
ERROR_0xD2B0((short) 0xD2B0, "An error occurred while configuring in RUN (CiR)"),
ERROR_0xD2C0((short) 0xD2C0, "The maximum number of technological objects has been exceeded"),
ERROR_0xD2C1((short) 0xD2C1, "The same technology data block already exists on the module"),
ERROR_0xD2C2((short) 0xD2C2, "Downloading the user program or downloading the hardware configuration is not possible"),
ERROR_0xD401((short) 0xD401, "Information function unavailable"),
ERROR_0xD402((short) 0xD402, "Information function unavailable"),
ERROR_0xD403((short) 0xD403, "Service has already been logged on/off (Diagnostics/PMC)"),
ERROR_0xD404((short) 0xD404, "Maximum number of nodes reached. No more logons possible for diagnostics/PMC"),
ERROR_0xD405((short) 0xD405, "Service not supported or syntax error in function parameters"),
ERROR_0xD406((short) 0xD406, "Required information currently unavailable"),
ERROR_0xD407((short) 0xD407, "Diagnostics error occurred"),
ERROR_0xD408((short) 0xD408, "Update aborted"),
ERROR_0xD409((short) 0xD409, "Error on DP bus"),
ERROR_0xD601((short) 0xD601, "Syntax error in function parameter"),
ERROR_0xD602((short) 0xD602, "Incorrect password entered"),
ERROR_0xD603((short) 0xD603, "The connection has already been legitimized"),
ERROR_0xD604((short) 0xD604, "The connection has already been enabled"),
ERROR_0xD605((short) 0xD605, "Legitimization not possible because password does not exist"),
ERROR_0xD801((short) 0xD801, "At least one tag address is invalid"),
ERROR_0xD802((short) 0xD802, "Specified job does not exist"),
ERROR_0xD803((short) 0xD803, "Illegal job status"),
ERROR_0xD804((short) 0xD804, "Illegal cycle time (illegal time base or multiple)"),
ERROR_0xD805((short) 0xD805, "No more cyclic read jobs can be set up"),
ERROR_0xD806((short) 0xD806, "The referenced job is in a state in which the requested function cannot be performed"),
ERROR_0xD807((short) 0xD807, "Function aborted due to overload, meaning executing the read cycle takes longer than the set scan cycle time"),
ERROR_0xDC01((short) 0xDC01, "Date and/or time invalid"),
ERROR_0xE201((short) 0xE201, "CPU is already the master"),
ERROR_0xE202((short) 0xE202, "Connect and update not possible due to different user program in flash module"),
ERROR_0xE203((short) 0xE203, "Connect and update not possible due to different firmware"),
ERROR_0xE204((short) 0xE204, "Connect and update not possible due to different memory configuration"),
ERROR_0xE205((short) 0xE205, "Connect/update aborted due to synchronization error"),
ERROR_0xE206((short) 0xE206, "Connect/update denied due to coordination violation"),
ERROR_0xEF01((short) 0xEF01, "S7 protocol error: Error at ID2; only 00H permitted in job"),
ERROR_0xEF02((short) 0xEF02, "S7 protocol error: Error at ID2; set of resources does not exist"),;
private static final Map<Short, S7ParamErrorCode> 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);
}
}

View File

@ -0,0 +1,64 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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);
}
}

View File

@ -0,0 +1,64 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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);
}
}

View File

@ -0,0 +1,66 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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);
}
}

View File

@ -0,0 +1,64 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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);
}
}

View File

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

View File

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

View File

@ -0,0 +1,354 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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
}
}

View File

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

View File

@ -0,0 +1,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);
}
}
}

View File

@ -0,0 +1,28 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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;
}

View File

@ -0,0 +1,41 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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;
};
}

View File

@ -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, //Functionstep Communication
0, //Reserved
0x00, //Max AmQ (parallel jobs with ack) calling也就是说这个connection每次只能调用1次 ;;2个字节
0x01,
0x00, //Max AmQ (parallel jobs with ack) called也就是说这个connection每次只能被调用1次 ;; 2个字节
0x01,
0x03, //PDU Length (2个字节) //这里是960 一般是240/480/960 主要是和CPU型号有关的
(byte) 0xC0,
};
//初始化 S7 Header长度
p.initHeader(1);
//加载 param 参数到 请求里面去
p.addParam(pa);
//构建第二次握手cotp 部分
res = this.exchange(p);
if (res != 0) {
return res;
}
//构建第二次握手 S7COMM 的接收容器
final PDU p2 = new PDU(this.msgIn, this.PDUstartIn);
res = p2.setupReceivedPDU();
if (res != 0) {
return res;
}
this.maxPDUlength = Nodave.USBEWord(this.msgIn, p2.param +6);
return res;
}
//reutrn
// 0 代表成功
// 1 代表失败
// 异常 就会抛出异常
public int readBytes(final DaveArea area, final int DBnum, final int start, final int len, final byte[] buffer) {
int res = 0;
try {
//信号量 加锁防止多处并发访问
this.semaphore.acquire();
} catch (final InterruptedException e) {
e.printStackTrace();
}
//构建 request PDU
final PDU p1 = new PDU(this.msgOut, this.PDUstartOut);
p1.initReadRequest();
p1.addVarToReadRequest(area, DBnum, start, len);
//通过tcp链接 发送read var requestthr 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;
}
}

View File

@ -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-codedst-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-codedst-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位),, 西门子200smart12001500默认 机架号=0,槽位号=1 ;;;; 西门子300400默认 机架号=0,槽位号=2
(byte) 0xC0, //parameter-codetpdu-size
(byte) 0x01, //parameter-length
(byte) 0x09 //TPDU size
};
// b4 这个数组从0开始位置 放到 this.msgOut 这个数组 第四个位置后面 b4.length 个长度
//之所以目的位置4 是因为前面4个字节是TPKT的内容(第一次握手)
// msgOut 是这个socketChannel 往外写的 bytes上位机 => plc msgIn 是指这个socketChanne 外部往里写的bytes上位机 <= plc
System.arraycopy(b4, 0, this.msgOut, 4, b4.length);
//this.msgOut[17] = (byte) (this.rack + 1); //这里写的不太对这里应该是配置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;
}
}

View File

@ -0,0 +1,178 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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 <T>
* the generic type
* @param beanClass
* the bean class
* @param buffer
* the buffer
* @param byteOffset
* the byte offset
* @return the t
*/
public static <T> T extractBytes(final Class<T> beanClass, final byte[] buffer, final int byteOffset) {
logger.trace("Extracting type {} from buffer with size: {} at offset {}", beanClass.getName(), buffer.length,
byteOffset);
try {
final T obj = beanClass.newInstance();
final BeanParseResult result = BeanParser.parse(beanClass);
for (final BeanEntry entry : result.entries) {
Object value = null;
if (entry.isArray) {
value = Array.newInstance(entry.type, entry.arraySize);
for (int i = 0; i < entry.arraySize; i++) {
final Object component = entry.serializer.extract(entry.type, buffer,
entry.byteOffset + byteOffset + (i * entry.s7type.getByteSize()),
entry.bitOffset + (i * entry.s7type.getBitSize()));
Array.set(value, i, component);
}
} else {
value = entry.serializer.extract(entry.type, buffer, entry.byteOffset + byteOffset,
entry.bitOffset);
}
entry.field.set(obj, value);
}
return obj;
} catch (final Exception e) {
throw new S7Exception("extractBytes", e);
}
}
/**
* Inserts the bytes to the buffer.
*
* @param bean
* the bean
* @param buffer
* the buffer
* @param byteOffset
* the byte offset
*/
public static void insertBytes(final Object bean, final byte[] buffer, final int byteOffset) {
logger.trace("Inerting buffer with size: {} at offset {} into bean: {}", buffer.length, byteOffset, bean);
try {
final BeanParseResult result = BeanParser.parse(bean);
for (final BeanEntry entry : result.entries) {
final Object fieldValue = entry.field.get(bean);
if (fieldValue != null) {
if (entry.isArray) {
for (int i = 0; i < entry.arraySize; i++) {
final Object arrayItem = Array.get(fieldValue, i);
if (arrayItem != null) {
entry.serializer.insert(arrayItem, buffer,
entry.byteOffset + byteOffset + (i * entry.s7type.getByteSize()),
entry.bitOffset + (i * entry.s7type.getBitSize()), entry.size);
}
}
} else {
entry.serializer.insert(fieldValue, buffer, entry.byteOffset + byteOffset, entry.bitOffset,
entry.size);
}
}
}
} catch (final Exception e) {
throw new S7Exception("insertBytes", e);
}
}
/** The Connector. */
private final S7Connector connector;
/**
* Instantiates a new s7 serializer.
*
* @param connector
* the connector
*/
public S7SerializerImpl(final S7Connector connector) {
this.connector = connector;
}
/** {@inheritDoc} */
@Override
public synchronized <T> T dispense(final Class<T> beanClass, final int dbNum, final int byteOffset)
throws S7Exception {
try {
final BeanParseResult result = BeanParser.parse(beanClass);
final byte[] buffer = this.connector.read(DaveArea.DB, dbNum, result.blockSize, byteOffset);
return extractBytes(beanClass, buffer, 0);
} catch (final Exception e) {
throw new S7Exception("dispense", e);
}
}
/** {@inheritDoc} */
@Override
public synchronized <T> T dispense(final Class<T> beanClass, final int dbNum, final int byteOffset,
final int blockSize) throws S7Exception {
try {
final byte[] buffer = this.connector.read(DaveArea.DB, dbNum, blockSize, byteOffset);
return extractBytes(beanClass, buffer, 0);
} catch (final Exception e) {
throw new S7Exception(
"dispense dbnum(" + dbNum + ") byteoffset(" + byteOffset + ") blocksize(" + blockSize + ")", e);
}
}
/** {@inheritDoc} */
@Override
public synchronized void store(final Object bean, final int dbNum, final int byteOffset) {
try {
final BeanParseResult result = BeanParser.parse(bean);
final byte[] buffer = new byte[result.blockSize];
logger.trace("store-buffer-size: " + buffer.length);
insertBytes(bean, buffer, 0);
this.connector.write(DaveArea.DB, dbNum, byteOffset, buffer);
} catch (final Exception e) {
throw new S7Exception("store", e);
}
}
}

View File

@ -0,0 +1,64 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final byte bufValue = buffer[byteOffset];
return targetClass.cast(bufValue == (bufValue | (0x01 << bitOffset)));
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.BOOL;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 1;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 0;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Boolean value = (Boolean) javaType;
if (value) {
byte bufValue = buffer[byteOffset];
bufValue |= (0x01 << bitOffset);
buffer[byteOffset] = bufValue;
}
}
}

View File

@ -0,0 +1,55 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
return targetClass.cast(buffer[byteOffset]);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.BYTE;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 1;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Byte value = (Byte) javaType;
buffer[byteOffset] = value;
}
}

View File

@ -0,0 +1,186 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final Calendar c = Calendar.getInstance();
c.clear();
int year = this.getFromPLC(buffer, OFFSET_YEAR + byteOffset);
if (year < 90) {
// 1900 - 1989
year += 2000;
} else {
// 2000 - 2090
year += 1900;
}
int month = this.getFromPLC(buffer, OFFSET_MONTH + byteOffset);
if (month > 0) {
month--;
}
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, this.getFromPLC(buffer, OFFSET_DAY + byteOffset));
c.set(Calendar.HOUR_OF_DAY, this.getFromPLC(buffer, OFFSET_HOUR + byteOffset));
c.set(Calendar.MINUTE, this.getFromPLC(buffer, OFFSET_MINUTE + byteOffset));
c.set(Calendar.SECOND, this.getFromPLC(buffer, OFFSET_SECOND + byteOffset));
/*
* TODO byte upperMillis = super.extract(Byte.class, buffer,
* OFFSET_MILLIS_100_10+byteOffset, bitOffset); byte lowerMillis =
* super.extract(Byte.class, buffer, OFFSET_MILLIS_1_AND_DOW+byteOffset,
* bitOffset);
*
* int ms100 = ( upperMillis >> 4 ); int ms10 = ( upperMillis & 0x0F );
* int ms1 = ( lowerMillis >> 4 );
*
* int millis = ms1 + ( 10*ms10 ) + ( 100*ms100 );
* c.set(Calendar.MILLISECOND, millis);
*
* int dow = ( lowerMillis & 0x0F ); c.set(Calendar.DAY_OF_WEEK, dow);
*/
return targetClass.cast(c.getTime());
}
/**
* Dec -> Hex 10 = 0a 16 = 0f 17 = 10
*
* @param buffer
* @param offset
* @return
*/
public byte getFromPLC(final byte[] buffer, final int offset) {
try {
final byte ret = super.extract(Byte.class, buffer, offset, 0);
return (byte) Integer.parseInt(Integer.toHexString(ret & 0xFF));
} catch (final NumberFormatException e) {
return 0;
}
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.DATE_AND_TIME;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 8;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Date date = (Date) javaType;
final Calendar c = Calendar.getInstance();
c.setTime(date);
int year = c.get(Calendar.YEAR);
/*
* if (year < 1990 || year > 2090) throw new
* S7Exception("Invalid year: " + year + " @ offset: " + byteOffset);
*/
if (year < 2000) {
// 1990 -1999
year -= 1900;
} else {
// 2000 - 2089
year -= 2000;
}
this.putToPLC(buffer, byteOffset + OFFSET_YEAR, year);
this.putToPLC(buffer, byteOffset + OFFSET_MONTH, c.get(Calendar.MONTH) + 1);
this.putToPLC(buffer, byteOffset + OFFSET_DAY, c.get(Calendar.DAY_OF_MONTH));
this.putToPLC(buffer, byteOffset + OFFSET_HOUR, c.get(Calendar.HOUR_OF_DAY));
this.putToPLC(buffer, byteOffset + OFFSET_MINUTE, c.get(Calendar.MINUTE));
this.putToPLC(buffer, byteOffset + OFFSET_SECOND, c.get(Calendar.SECOND));
/*
* TODO int msec1 = 0, msec10 = 0, msec100 = 0; Integer millis =
* c.get(Calendar.MILLISECOND); String mStr = millis.toString();
*
* if (mStr.length() > 2) { msec100 = Integer.parseInt(
* mStr.substring(0, 1) ); msec10 = Integer.parseInt( mStr.substring(1,
* 2) ); msec1 = Integer.parseInt( mStr.substring(2, 3) ); } else if
* (mStr.length() > 1) { msec10 = Integer.parseInt( mStr.substring(0, 1)
* ); msec1 = Integer.parseInt( mStr.substring(1, 2) ); } else { msec1 =
* Integer.parseInt( mStr.substring(0, 1) ); }
*
* super.insert( (byte)( (byte)msec10 | (byte)(msec100 << 4) ), buffer,
* OFFSET_MILLIS_100_10+byteOffset, 0, 1);
*
* int dow = c.get(Calendar.DAY_OF_WEEK);
*
* super.insert( (byte)( (byte)dow | (byte)(msec1 << 4) ), buffer,
* OFFSET_MILLIS_1_AND_DOW+byteOffset, 0, 1);
*/
}
/**
* Hex -> dec 0a = 10 0f = 16 10 = 17
*
* @param buffer
* @param offset
* @param i
*/
public void putToPLC(final byte[] buffer, final int offset, final int i) {
try {
final int ret = Integer.parseInt("" + i, 16);
buffer[offset] = (byte) ret;
} catch (final NumberFormatException e) {
return;
}
}
}

View File

@ -0,0 +1,94 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final long days = super.extract(Integer.class, buffer, byteOffset, bitOffset);
long millis = days * MILLI_TO_DAY_FACTOR;
millis += OFFSET_1990;
final Calendar c = Calendar.getInstance();
c.setTimeInMillis(millis);
c.set(Calendar.MILLISECOND, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.HOUR_OF_DAY, 0);
return targetClass.cast(c.getTime());
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.DATE;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Date d = (Date) javaType;
long millis = d.getTime();
millis -= OFFSET_1990;
final double days = (double) millis / (double) MILLI_TO_DAY_FACTOR;
final long ROUND = 1000;
final long expected = (long) ((days * MILLI_TO_DAY_FACTOR) / ROUND);
final long actual = millis / ROUND;
if (expected != actual) {
throw new IllegalArgumentException("Expected: " + expected + " got: " + actual);
}
if (millis < 0) {
super.insert(0, buffer, byteOffset, bitOffset, 2);
} else {
super.insert((int) Math.round(days), buffer, byteOffset, bitOffset, 2);
}
}
}

View File

@ -0,0 +1,66 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final byte lower = buffer[byteOffset + OFFSET_LOW_BYTE];
final byte higher = buffer[byteOffset + OFFSET_HIGH_BYTE];
final Integer i = (lower & 0xFF) | ((higher << 8) & 0xFF00);
return targetClass.cast(i);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.WORD;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 2;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Integer value = (Integer) javaType;
final byte lower = (byte) ((value >> 0) & 0xFF);
final byte higher = (byte) ((value >> 8) & 0xFF);
buffer[byteOffset + OFFSET_LOW_BYTE] = lower;
buffer[byteOffset + OFFSET_HIGH_BYTE] = higher;
}
}

View File

@ -0,0 +1,70 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final byte b1 = buffer[byteOffset + 0];
final byte b2 = buffer[byteOffset + 1];
final byte b3 = buffer[byteOffset + 2];
final byte b4 = buffer[byteOffset + 3];
final Integer i = ((b1 << 0) & 0x000000FF) | ((b2 << 8) & 0x0000FF00) | ((b3 << 16) & 0x00FF0000)
| ((b4 << 24) & 0xFF000000);
return targetClass.cast(i);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.WORD;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 4;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final Long value = (Long) javaType;
final byte b1 = (byte) ((value >> 0) & 0xFF);
final byte b2 = (byte) ((value >> 8) & 0xFF);
final byte b3 = (byte) ((value >> 16) & 0xFF);
final byte b4 = (byte) ((value >> 24) & 0xFF);
buffer[byteOffset + 0] = b1;
buffer[byteOffset + 1] = b2;
buffer[byteOffset + 2] = b3;
buffer[byteOffset + 3] = b4;
}
}

View File

@ -0,0 +1,78 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final int iValue = ((buffer[byteOffset + OFFSET_POS4] & 0xFF) << 0)
| ((buffer[byteOffset + OFFSET_POS3] & 0xFF) << 8) | ((buffer[byteOffset + OFFSET_POS2] & 0xFF) << 16)
| ((buffer[byteOffset + OFFSET_POS1] & 0xFF) << 24);
final Float fValue = Float.intBitsToFloat(iValue);
Object ret = fValue;
if (targetClass == Double.class) {
ret = Double.parseDouble(fValue.toString());
}
return targetClass.cast(ret);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.REAL;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 4;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final float fValue = Float.parseFloat(javaType.toString());
final int iValue = Float.floatToIntBits(fValue);
buffer[byteOffset + OFFSET_POS4] = (byte) ((iValue >> 0) & 0xFF);
buffer[byteOffset + OFFSET_POS3] = (byte) ((iValue >> 8) & 0xFF);
buffer[byteOffset + OFFSET_POS2] = (byte) ((iValue >> 16) & 0xFF);
buffer[byteOffset + OFFSET_POS1] = (byte) ((iValue >> 24) & 0xFF);
}
}

View File

@ -0,0 +1,82 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final int len = buffer[byteOffset + OFFSET_CURRENT_LENGTH];
final byte[] bytes = new byte[len];
for (int i = 0; i < len; i++) {
bytes[i] = buffer[byteOffset + OFFSET_START + i];
}
return targetClass.cast(new String(bytes));
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.STRING;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
// Not static
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
// Not static
return 2; // 2 bytes overhead
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final String value = (String) javaType;
final int len = value.length();
if (len > size) {
throw new IllegalArgumentException("String to big: " + len);
}
buffer[byteOffset + OFFSET_OVERALL_LENGTH] = (byte) size;
buffer[byteOffset + OFFSET_CURRENT_LENGTH] = (byte) len;
final byte[] strBytes = value.getBytes();
for (int i = 0; i < len; i++) {
buffer[byteOffset + OFFSET_START + i] = (byte) (strBytes[i] & 0xFF);
}
}
}

View File

@ -0,0 +1,55 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
return S7SerializerImpl.extractBytes(targetClass, buffer, byteOffset);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return null;
}
/** {@inheritDoc} */
@Override
public int getSizeInBits() {
return 0;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 0;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
S7SerializerImpl.insertBytes(javaType, buffer, byteOffset);
}
}

View File

@ -0,0 +1,65 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.cnbm.s7.s7connector.impl.serializer.converter;
import com.cnbm.s7.s7connector.impl.utils.S7Type;
public final class TimeConverter extends ByteConverter {
/** {@inheritDoc} */
@Override
public <T> T extract(final Class<T> targetClass, final byte[] buffer, final int byteOffset, final int bitOffset) {
final byte b1 = super.extract(Byte.class, buffer, byteOffset + 3, bitOffset);
final byte b2 = super.extract(Byte.class, buffer, byteOffset + 2, bitOffset);
final byte b3 = super.extract(Byte.class, buffer, byteOffset + 1, bitOffset);
final byte b4 = super.extract(Byte.class, buffer, byteOffset + 0, bitOffset);
final long l = ((long) b1 & 0xFF) << 0 | ((long) b2 & 0xFF) << 8 | ((long) b3 & 0xFF) << 16
| ((long) b4 & 0xFF) << 24;
return targetClass.cast(l);
}
/** {@inheritDoc} */
@Override
public S7Type getS7Type() {
return S7Type.TIME;
}
/** {@inheritDoc} */
@Override
public int getSizeInBytes() {
return 4;
}
/** {@inheritDoc} */
@Override
public void insert(final Object javaType, final byte[] buffer, final int byteOffset, final int bitOffset,
final int size) {
final long l = (Long) javaType;
final byte b1 = (byte) ((byte) (l >> 0) & 0xFF);
final byte b2 = (byte) ((byte) (l >> 8) & 0xFF);
final byte b3 = (byte) ((byte) (l >> 16) & 0xFF);
final byte b4 = (byte) ((byte) (l >> 24) & 0xFF);
super.insert(b1, buffer, byteOffset + 3, bitOffset, 1);
super.insert(b2, buffer, byteOffset + 2, bitOffset, 1);
super.insert(b3, buffer, byteOffset + 1, bitOffset, 1);
super.insert(b4, buffer, byteOffset + 0, bitOffset, 1);
}
}

View File

@ -0,0 +1,63 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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;
}

View File

@ -0,0 +1,32 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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<BeanEntry> entries = new Vector<BeanEntry>();
}

View File

@ -0,0 +1,154 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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());
}
}

View File

@ -0,0 +1,105 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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<? extends S7Serializable> serializer;
/**
* Enum Constructor
*
* @param serializer
* @param byteSize
* @param bitSize
*/
S7Type(final Class<? extends S7Serializable> serializer, final int byteSize, final int bitSize) {
this.serializer = serializer;
this.bitSize = bitSize;
this.byteSize = byteSize;
}
public int getBitSize() {
return this.bitSize;
}
public int getByteSize() {
return this.byteSize;
}
public Class<? extends S7Serializable> getSerializer() {
return this.serializer;
}
}

View File

@ -0,0 +1,56 @@
/*
Copyright 2016 S7connector members (github.com/s7connector)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.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.
}
}

View File

@ -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<Short, DataTransportSize> map;
static {
map = new HashMap<>();
for (DataTransportSize value : DataTransportSize.values()) {
map.put((short) value.getValue(), value);
}
}
private short value;
private boolean sizeInBits;
DataTransportSize(short value, boolean sizeInBits) {
this.value = value;
this.sizeInBits = sizeInBits;
}
public short getValue() {
return value;
}
public boolean getSizeInBits() {
return sizeInBits;
}
public static DataTransportSize firstEnumForFieldSizeInBits(boolean fieldValue) {
for (DataTransportSize _val : DataTransportSize.values()) {
if(_val.getSizeInBits() == fieldValue) {
return _val;
}
}
return null;
}
public static List<DataTransportSize> enumsForFieldSizeInBits(boolean fieldValue) {
List<DataTransportSize> _values = new ArrayList();
for (DataTransportSize _val : DataTransportSize.values()) {
if(_val.getSizeInBits() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public static DataTransportSize enumForValue(short value) {
if (!map.containsKey(value)) {
logger.info("No DataTransportSize for value {}", value);
}
return map.get(value);
}
public static Boolean isDefined(short value) {
return map.containsKey(value);
}
}

View File

@ -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<TransportSize, PlcVar> map;
static {
map = new HashMap<>();
for (PlcVar value : PlcVar.values()) {
if(!value.isArray){
map.put(value.getTransportSize(), value);
}
}
}
public static PlcVar valueOf(TransportSize transportSize){
return map.get(transportSize);
}
private String name;
private TransportSize transportSize;
private Integer dataType;
private boolean isArray;
PlcVar(String name , TransportSize transportSize, Integer dataType,Boolean isArray){
this.name = name;
this.transportSize = transportSize;
this.dataType = dataType;
this.isArray = isArray;
}
public String getName() {
return name;
}
public TransportSize getTransportSize() {
return transportSize;
}
public boolean isArray() {
return isArray;
}
public byte[] toBytes(Object value){
//todo here 扩展数组 toBytes
if(!isArray){
byte[] res = null;
switch (dataType) {
case 1:
res = ByteUtils.boolToBytes((boolean)value);
break;
case 2:
//ByteUtils.setbytes(Byte.valueOf((byte) 0xFF))
res = ByteUtils.setbytes(Byte.valueOf((byte) value));
break;
case 3:
//ByteUtils.wordToBytes(Short.valueOf("-55"))
res = ByteUtils.wordToBytes(Short.valueOf(value.toString()));
break;
case 4:
//ByteUtils.dwordToBytes(99)
res = ByteUtils.dwordToBytes((int)value);
break;
case 5:
//null
break;
case 6:
//ByteUtils.intToBytes(-99)
res = ByteUtils.intToBytes((int)value);
break;
case 7:
//ByteUtils.uintToBytes(9)
res = ByteUtils.uintToBytes((int)value);
break;
case 8:
//ByteUtils.sintToBytes(-9)
res =ByteUtils.sintToBytes((int)value);
break;
case 9:
//ByteUtils.usintToBytes(9);
res =ByteUtils.usintToBytes((int)value);
break;
case 10:
//ByteUtils.dintToBytes(-999)
res =ByteUtils.dintToBytes((int)value);
break;
case 11:
//ByteUtils.udintToBytes(99)
res =ByteUtils.udintToBytes((int)value);
break;
case 12:
//待补充
break;
case 13:
//待补充
break;
case 14:
//ByteUtils.realToBytes(Float.valueOf("-11.2"))
res =ByteUtils.realToBytes(Float.valueOf(value.toString()));
break;
case 15:
//ByteUtils.lrealToBytes(Double.parseDouble("-111.1"));
res =ByteUtils.lrealToBytes(Double.parseDouble(value.toString()));
break;
case 16:
//ByteUtils.charToBytes('b')
res =ByteUtils.charToBytes((char)value);
break;
case 17:
//ByteUtils.wcharToBytes('菜')
res =ByteUtils.wcharToBytes((char)value);
break;
case 18:
//String s = "你好啊";
//ByteUtils.strToBytes(s);
res = ByteUtils.strToBytes(value.toString());
break;
case 19:
//待补充
break;
case 20:
//不常用 待补充
break;
case 21:
//不常用
break;
case 22:
//不常用
break;
case 23:
////不常用
break;
case 24:
////不常用
break;
case 25:
//不常用
break;
case 26:
//不常用
break;
case 27:
//不常用
break;
default:
//什么也不做
break;
}
return res;
}else {
byte[] res = null;
switch (dataType) {
case 1:
// boolean[] booleanArray = new boolean[2];
// booleanArray[0] = true;
// booleanArray[1] = true;
// ByteUtils.booleanArrayToBytes(booleanArray)
res = ByteUtils.booleanArrayToBytes((boolean[]) value);
break;
case 2:
// byte[] write_byteArrays = new byte[2];
// write_byteArrays[0] = 1;
// write_byteArrays[1] = 2;
//
res = (byte[]) value;
break;
case 3:
// short[] shortArrays_content = new short[2];
// shortArrays_content[0] = 1;
// shortArrays_content[1] = -1;
//
res = ByteUtils.wordArrayToBytes((short[]) value);
break;
case 4:
// int[] intArrays_content = new int[2];
// intArrays_content[0] = 1;
// intArrays_content[1] = -1;
//
res = ByteUtils.dwordArrayToBytes((int[]) value);
break;
case 5:
// char[] charArrays_content = new char[2];
// charArrays_content[0] = '1';
// charArrays_content[1] = 'b';
//
res = ByteUtils.charArrayToBytes((char[]) value);
break;
case 6:
// int[] sintArrays_content = new int[2];
// sintArrays_content[0] = 1;
// sintArrays_content[1] = -1;
//
res = ByteUtils.sintArrayToBytes((int[]) value);
break;
case 7:
// int[] iintArrays_content = new int[2];
// iintArrays_content[0] = 12;
// iintArrays_content[1] = -21;
//
res = ByteUtils.intArrayToBytes((int[]) value);
break;
case 8:
// int[] dintArrays_content = new int[2];
// dintArrays_content[0] = 12;
// dintArrays_content[1] = -21;
//
res = ByteUtils.dintArrayToBytes((int[]) value);
break;
case 9:
// int[] uintArrays_content = new int[3];
// uintArrays_content[0] = 12;
// uintArrays_content[1] = 99;
// uintArrays_content[2] = 1;
res = ByteUtils.uintArrayToBytes((int[]) value);
break;
case 10:
// int[] usintArrays_content = new int[3];
// usintArrays_content[0] = 12;
// usintArrays_content[1] = 99;
// usintArrays_content[2] = 1;
res = ByteUtils.usintArrayToBytes((int[]) value);
break;
case 11:
// int[] udintArrays_content = new int[3];
// udintArrays_content[0] = 12;
// udintArrays_content[1] = 99;
// udintArrays_content[2] = 1;
res = ByteUtils.udintArrayToBytes((int[]) value);
break;
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<Boolean>
* ByteArray ===> List<Byte>
* WordArray ===> List<Integer>
* DWordArray ===> List<Integer>
* CharArray ===> List<Character>
* SIntArray ===> List<Integer>
* IntArray ===> List<Integer>
* DIntArray ===> List<Integer>
* UIntArray ===> List<Integer>
* USIntArray ===> List<Integer>
* UDIntArray ===> List<Long>
* StringArray ===> List<String> //注意 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 代表读一个数组
* */
}

View File

@ -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<Byte, TransportSize> map;
static {
map = new HashMap<>();
for (TransportSize value : TransportSize.values()) {
map.put((byte) value.getValue(), value);
}
}
//value 类似于TransportSize 的ID
private byte value;
private boolean supported_S7_300;
private boolean supported_LOGO;
private short code;
private short sizeInBytes;
private boolean supported_S7_400;
private boolean supported_S7_1200;
private short shortName;
private boolean supported_S7_1500;
private DataTransportSize dataTransportSize;
private TransportSize baseType;
private String dataProtocolId;
TransportSize(byte value, boolean supported_S7_300, boolean supported_LOGO, short code, short sizeInBytes, boolean supported_S7_400, boolean supported_S7_1200, short shortName, boolean supported_S7_1500, DataTransportSize dataTransportSize, TransportSize baseType, String dataProtocolId) {
this.value = value;
this.supported_S7_300 = supported_S7_300;
this.supported_LOGO = supported_LOGO;
//Parameter ITEM 的transportSize
this.code = code;
this.sizeInBytes = sizeInBytes;
this.supported_S7_400 = supported_S7_400;
this.supported_S7_1200 = supported_S7_1200;
this.shortName = shortName;
this.supported_S7_1500 = supported_S7_1500;
//Data ITEM 的transportSize
this.dataTransportSize = dataTransportSize;
this.baseType = baseType;
this.dataProtocolId = dataProtocolId;
}
public byte getValue() {
return value;
}
public boolean getSupported_S7_300() {
return supported_S7_300;
}
public static TransportSize firstEnumForFieldSupported_S7_300(boolean fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_300() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSupported_S7_300(boolean fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_300() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public boolean getSupported_LOGO() {
return supported_LOGO;
}
public static TransportSize firstEnumForFieldSupported_LOGO(boolean fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_LOGO() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSupported_LOGO(boolean fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_LOGO() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public short getCode() {
return code;
}
public static TransportSize firstEnumForFieldCode(short fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getCode() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldCode(short fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getCode() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public short getSizeInBytes() {
return sizeInBytes;
}
public static TransportSize firstEnumForFieldSizeInBytes(short fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSizeInBytes() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSizeInBytes(short fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSizeInBytes() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public boolean getSupported_S7_400() {
return supported_S7_400;
}
public static TransportSize firstEnumForFieldSupported_S7_400(boolean fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_400() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSupported_S7_400(boolean fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_400() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public boolean getSupported_S7_1200() {
return supported_S7_1200;
}
public static TransportSize firstEnumForFieldSupported_S7_1200(boolean fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_1200() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSupported_S7_1200(boolean fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_1200() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public short getShortName() {
return shortName;
}
public static TransportSize firstEnumForFieldShortName(short fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getShortName() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldShortName(short fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getShortName() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public boolean getSupported_S7_1500() {
return supported_S7_1500;
}
public static TransportSize firstEnumForFieldSupported_S7_1500(boolean fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_1500() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldSupported_S7_1500(boolean fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getSupported_S7_1500() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public DataTransportSize getDataTransportSize() {
return dataTransportSize;
}
public static TransportSize firstEnumForFieldDataTransportSize(DataTransportSize fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getDataTransportSize() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldDataTransportSize(DataTransportSize fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getDataTransportSize() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public TransportSize getBaseType() {
return baseType;
}
public static TransportSize firstEnumForFieldBaseType(TransportSize fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getBaseType() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldBaseType(TransportSize fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getBaseType() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public String getDataProtocolId() {
return dataProtocolId;
}
public static TransportSize firstEnumForFieldDataProtocolId(String fieldValue) {
for (TransportSize _val : TransportSize.values()) {
if(_val.getDataProtocolId() == fieldValue) {
return _val;
}
}
return null;
}
public static List<TransportSize> enumsForFieldDataProtocolId(String fieldValue) {
List<TransportSize> _values = new ArrayList();
for (TransportSize _val : TransportSize.values()) {
if(_val.getDataProtocolId() == fieldValue) {
_values.add(_val);
}
}
return _values;
}
public static TransportSize enumForValue(byte value) {
if (!map.containsKey(value)) {
logger.error("No TransportSize for value {}", value);
}
return map.get(value);
}
public static Boolean isDefined(byte value) {
return map.containsKey(value);
}
}

View File

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