lin 5 years ago
commit
32bc45b7f9

+ 32 - 0
.gitignore

@@ -0,0 +1,32 @@
+HELP.md
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+out/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/

+ 7 - 0
Dockerfile

@@ -0,0 +1,7 @@
+FROM hub.c.163.com/library/java:8-alpine
+
+ADD build/libs/*.jar app.jar
+
+EXPOSE 8080
+
+ENTRYPOINT ["java", "-jar", "/app.jar"]

+ 53 - 0
build.gradle

@@ -0,0 +1,53 @@
+plugins {
+    id 'org.springframework.boot' version '2.2.5.RELEASE'
+    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
+    id 'java'
+    id ''
+    classpath("se.transmode.gradle:gradle-docker:1.2")
+    //id "com.palantir.docker" version "0.25.0"
+}
+group = 'com.zcxk'
+version = '0.0.1-SNAPSHOT'
+sourceCompatibility = '1.8'
+
+configurations {
+    compileOnly {
+        extendsFrom annotationProcessor
+    }
+}
+
+repositories {
+    mavenLocal()
+    maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
+}
+
+dependencies {
+    //classpath 'se.transmode.gradle:gradle-docker:1.2'
+    implementation('org.springframework.boot:spring-boot-starter-web')
+    compile ('io.netty:netty-all:4.1.30.Final')
+    //json工具
+    compile('com.alibaba:fastjson:1.2.41')
+    compile('org.apache.commons:commons-lang3:3.4')
+
+    //swagger在线接口文档生成工具
+    compile('io.springfox:springfox-swagger2:2.7.0')
+    compile('io.springfox:springfox-swagger-ui:2.7.0')
+
+
+    compileOnly 'org.projectlombok:lombok'
+    annotationProcessor 'org.projectlombok:lombok'
+    testImplementation('org.springframework.boot:spring-boot-starter-test') {
+        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
+    }
+}
+
+test {
+    useJUnitPlatform()
+}
+
+/*docker {
+    name "${project.group}/${bootJar.baseName}"
+    //copySpec.from(tasks.bootJar.outputs.files.singleFile).into("dependency")
+    dockerfile file('Dockerfile')
+}*/
+

+ 1 - 0
settings.gradle

@@ -0,0 +1 @@
+rootProject.name = 'tcp-server'

+ 13 - 0
src/main/java/com/zcxk/tcpserver/TcpServerApplication.java

@@ -0,0 +1,13 @@
+package com.zcxk.tcpserver;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TcpServerApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(TcpServerApplication.class, args);
+    }
+
+}

+ 89 - 0
src/main/java/com/zcxk/tcpserver/config/NettyConfig.java

@@ -0,0 +1,89 @@
+package com.zcxk.tcpserver.config;
+
+import com.zcxk.tcpserver.tcp.TcpSocketChannelInitializer;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+@Configuration
+public class NettyConfig {
+    @Value("${tcp-socket.boss.thread.count}")
+    private int bossCount;
+
+    @Value("${tcp-socket.worker.thread.count}")
+    private int workerCount;
+
+    @Value("${tcp-socket.port}")
+    private int tcpSocketPort;
+
+    @Value("${tcp-socket.so.keepalive}")
+    private boolean keepAlive;
+
+    @Value("${tcp-socket.so.backlog}")
+    private int backlog;
+
+    @Autowired
+    @Qualifier("tcpSocketChannelInitializer")
+    private TcpSocketChannelInitializer protocolInitalizer;
+
+    @SuppressWarnings("unchecked")
+    @Bean(name = "serverBootstrap")
+    public ServerBootstrap bootstrap() {
+        ServerBootstrap b = new ServerBootstrap();
+        b.group(bossGroup(), workerGroup())
+                .channel(NioServerSocketChannel.class)
+                .childHandler(protocolInitalizer);
+        Map<ChannelOption<?>, Object> tcpChannelOptions = tcpChannelOptions();
+        Set<ChannelOption<?>> keySet = tcpChannelOptions.keySet();
+        for (@SuppressWarnings("rawtypes")
+                ChannelOption option : keySet) {
+            b.option(option, tcpChannelOptions.get(option));
+        }
+        return b;
+    }
+
+    @Bean(name = "bossGroup", destroyMethod = "shutdownGracefully")
+    public NioEventLoopGroup bossGroup() {
+        return new NioEventLoopGroup(bossCount);
+    }
+
+    @Bean(name = "workerGroup", destroyMethod = "shutdownGracefully")
+    public NioEventLoopGroup workerGroup() {
+        return new NioEventLoopGroup(workerCount);
+    }
+
+    @Bean(name = "tcpSocketAddress")
+    public InetSocketAddress TcpSocketPort() {
+        return new InetSocketAddress(tcpSocketPort);
+    }
+
+    @Bean(name = "tcpChannelOptions")
+    public Map<ChannelOption<?>, Object> tcpChannelOptions() {
+        Map<ChannelOption<?>, Object> options = new HashMap<ChannelOption<?>, Object>();
+        options.put(ChannelOption.SO_KEEPALIVE, keepAlive);
+        options.put(ChannelOption.SO_BACKLOG, backlog);
+        return options;
+    }
+
+    /**
+     * Necessary to make the Value annotations work.
+     *
+     * @return
+     */
+    @Bean
+    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
+        return new PropertySourcesPlaceholderConfigurer();
+    }
+}

+ 72 - 0
src/main/java/com/zcxk/tcpserver/config/SwaggerConfig.java

@@ -0,0 +1,72 @@
+package com.zcxk.tcpserver.config;
+
+import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+    @Bean
+    public Docket api() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .ignoredParameterTypes(BasicErrorController.class)
+                .groupName("web")
+                .select()
+                .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build()
+                .apiInfo(apiInfo())
+                .securitySchemes(securitySchemes())
+                .securityContexts(securityContexts());
+    }
+
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("智慧城市Api")
+                .description("智慧城市")
+                .version("1.0")
+                .termsOfServiceUrl("Terms of service")
+                .license("测试")
+                .build();
+    }
+
+    private List<ApiKey> securitySchemes() {
+        return newArrayList(
+                new ApiKey("Authorization", "Authorization", "header"));
+    }
+
+    private List<SecurityContext> securityContexts() {
+        return newArrayList(
+                SecurityContext.builder()
+                        .securityReferences(defaultAuth())
+                        .forPaths(PathSelectors.regex("^(?!auth).*$"))
+                        .build()
+        );
+    }
+
+    List<SecurityReference> defaultAuth() {
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        return newArrayList(
+                new SecurityReference("Authorization", authorizationScopes));
+    }
+
+}

+ 87 - 0
src/main/java/com/zcxk/tcpserver/controller/TestController.java

@@ -0,0 +1,87 @@
+package com.zcxk.tcpserver.controller;
+
+import com.zcxk.tcpserver.entity.Document;
+import com.zcxk.tcpserver.tcp.TcpSocketHandler;
+import com.zcxk.tcpserver.tcp.generator.ConcentratorDemo;
+import com.zcxk.tcpserver.util.ConvertCode;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.List;
+
+@Slf4j
+@Controller
+@ResponseBody
+@RequestMapping("test")
+@Api(tags = "测试")
+public class TestController {
+
+    @RequestMapping("test")
+    public String test(
+            @ApiParam(value = "channelId", required = true) @RequestParam String channelId
+    ){
+        System.out.println("连接个数:"+TcpSocketHandler.channels.size());;
+        TcpSocketHandler.channels.forEach(channel -> {
+            ByteBuf bufff = Unpooled.buffer();//netty需要用ByteBuf传输
+            bufff.writeBytes(ConvertCode.hexString2Bytes("1234"));//对接需要16进制
+            channel.writeAndFlush(bufff);
+        });
+        return "test";
+    }
+
+    @RequestMapping("readTime")
+    @ApiOperation(value = "查询时间")
+    public String readTime(
+            @ApiParam(value = "设备号", required = true) @RequestParam String deviceNo
+    ){
+        System.out.println("连接个数:"+TcpSocketHandler.channels.size());;
+        String result = ConcentratorDemo.readTime();
+        TcpSocketHandler.channels.forEach(channel -> {
+            ByteBuf bufff = Unpooled.buffer();//netty需要用ByteBuf传输
+            bufff.writeBytes(ConvertCode.hexString2Bytes(result));//对接需要16进制
+            channel.writeAndFlush(bufff);
+        });
+        return "test";
+    }
+
+    @RequestMapping("issueWaterMeterFiles")
+    @ApiOperation(value = "下发水表档案")
+    public String issueWaterMeterFiles(
+            @ApiParam(value = "设备号", required = true) @RequestParam String deviceNo,
+            @ApiParam(value = "设备号", required = true) @RequestBody List<Document> documents
+    ){
+        System.out.println("连接个数:"+TcpSocketHandler.channels.size());;
+        String result = ConcentratorDemo.issueWaterMeterFiles(deviceNo,documents);
+        TcpSocketHandler.channels.forEach(channel -> {
+            ByteBuf bufff = Unpooled.buffer();//netty需要用ByteBuf传输
+            bufff.writeBytes(ConvertCode.hexString2Bytes(result));//对接需要16进制
+            channel.writeAndFlush(bufff);
+        });
+        return "test";
+    }
+
+    @RequestMapping("delWaterMeterFiles")
+    @ApiOperation(value = "删除水表档案")
+    public String delWaterMeterFiles(
+            @ApiParam(value = "设备号", required = true) @RequestParam String deviceNo,
+            @ApiParam(value = "表序号", required = true) @RequestBody List<Integer> meterNos
+    ){
+        System.out.println("连接个数:"+TcpSocketHandler.channels.size());;
+        String result = ConcentratorDemo.delWaterMeterFiles(deviceNo,meterNos);
+        TcpSocketHandler.channels.forEach(channel -> {
+            ByteBuf bufff = Unpooled.buffer();//netty需要用ByteBuf传输
+            bufff.writeBytes(ConvertCode.hexString2Bytes(result));//对接需要16进制
+            channel.writeAndFlush(bufff);
+        });
+        return "test";
+    }
+}

+ 12 - 0
src/main/java/com/zcxk/tcpserver/entity/DataUnit.java

@@ -0,0 +1,12 @@
+package com.zcxk.tcpserver.entity;
+
+import lombok.Data;
+
+@Data
+public class DataUnit {
+    //数据单元标识
+    private String identify;
+
+    //数据单元
+    private String data;
+}

+ 32 - 0
src/main/java/com/zcxk/tcpserver/entity/Document.java

@@ -0,0 +1,32 @@
+package com.zcxk.tcpserver.entity;
+
+import lombok.Data;
+
+@Data
+public class Document {
+
+    private String meterMac;
+    // 0 普通电表 1 简易多功能电表 2 多功能总电表 3 冷水表 4 热水表 5 总水表
+    private String meterType;
+    private String collector;
+    //private Integer port;
+    //private Integer channel;
+
+
+    //测量点性质
+    public String getMeasuringPointProperties(){
+            //0 预付表 1 预付费表(金额) 2 非预付费表 3 485表 4 MBUS表
+            String h = "0";
+            return meterType+h;
+    }
+
+    //接线方式
+    public String getConnection(){
+        return "00";
+    }
+
+    //电表费率号
+    public String getMeterRateNo(){
+        return "00";
+    }
+}

+ 86 - 0
src/main/java/com/zcxk/tcpserver/entity/FrameFormat.java

@@ -0,0 +1,86 @@
+package com.zcxk.tcpserver.entity;
+
+import com.zcxk.tcpserver.util.HexUtil;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FrameFormat {
+
+    //起始字符68H (字节数1)
+    private String beginCharacter1 = "68";
+
+    //长度L (字节数2)
+    private String length1;
+
+    //长度L (字节数2)
+    private String length2;
+
+    //起始字符68H  (字节数1)
+    private String beginCharacter2 = "68";
+
+    //控制域C  (字节数1)
+    private String controlDomain;
+
+    //地址域A  (字节数5)
+    private String addressDomain;
+
+    //应用层功能码AFN  (字节数1)
+    private String afn;
+
+    //帧序列域SEQ  (字节数1)
+    private String seq;
+
+    private List<DataUnit> dataUnitList;
+
+    //消息认证码PW(下行)/事件计数器EC(上行ACD=1时)
+    private String pwec;
+
+    //时间标签Tp
+    private String tp;
+
+    //校验和CS (字节数1)
+    private String cs;
+
+    //结束字符16H (字节数1)
+    private String endCharacter = "16";
+
+    private String getUserData(){
+        String temp = controlDomain + addressDomain + afn + seq;
+        if (dataUnitList != null) {
+            for (DataUnit dataUnit : dataUnitList) {
+                temp += dataUnit.getIdentify()  + dataUnit.getData();
+            }
+        }
+        return temp;
+    }
+
+
+    public String calculatedLength(){
+        String temp = this.getUserData();
+        String lengthTemp = HexUtil.Demical2Byte(temp.length()/2)+"01";
+        String b = HexUtil.demical2Hex(HexUtil.ByteToDecimal(lengthTemp));
+
+        return HexUtil.hexTransfer(HexUtil.fill(b,4,'0'));
+    }
+
+    public String checkout(){
+        String temp = this.getUserData();
+
+        int z = 0;
+        String tempNum = "00";
+        for(int i=0;i<temp.length()/2;i++){
+            String a = temp.substring(z,z+2);
+            tempNum = HexUtil.hexAddHex(tempNum,a);
+            z = z+2;
+        }
+        String strh = tempNum.substring(tempNum.length() -2);
+        return strh;
+    }
+
+    public String builFrameFormatStr(){
+        String temp = beginCharacter1 + length1 + length2 + beginCharacter2 + this.getUserData() + cs + endCharacter;
+        return temp.toUpperCase();
+    }
+}

+ 21 - 0
src/main/java/com/zcxk/tcpserver/tcp/TcpSocketChannelInitializer.java

@@ -0,0 +1,21 @@
+package com.zcxk.tcpserver.tcp;
+
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+@Component
+@Qualifier("webSocketChannelInitializer")
+public class TcpSocketChannelInitializer extends ChannelInitializer<SocketChannel> {
+
+    @Autowired
+    private TcpSocketHandler tcpSocketHandler;
+    @Override
+    protected void initChannel(SocketChannel ch) throws Exception {
+        ChannelPipeline pipeline = ch.pipeline();
+        pipeline.addLast(tcpSocketHandler);
+    }
+}

+ 101 - 0
src/main/java/com/zcxk/tcpserver/tcp/TcpSocketHandler.java

@@ -0,0 +1,101 @@
+package com.zcxk.tcpserver.tcp;
+
+
+import com.zcxk.tcpserver.tcp.generator.ConcentratorDemo;
+import com.zcxk.tcpserver.tcp.generator.ProtocolGenerator;
+import com.zcxk.tcpserver.util.ConvertCode;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.*;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+@Component
+@Qualifier("webSocketHandler")
+@ChannelHandler.Sharable
+@Slf4j
+public class TcpSocketHandler extends ChannelInboundHandlerAdapter {
+    //获取现有通道,一个通道channel就是一个socket链接在这里
+    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+
+    }
+
+    @Override
+    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+        log.info("客户端" + ctx.channel().id().asLongText() + "连接服务器");
+        channels.add(ctx.channel());
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        ByteBuf buf = (ByteBuf)msg;
+        byte [] bytes = new byte[buf.readableBytes()];
+        buf.readBytes(bytes);//复制内容到字节数组bytes
+        String receiveStr = ConvertCode.receiveHexToString(bytes);
+        log.info("服务端收到消息:" + receiveStr);
+        Integer result = ProtocolGenerator.analysis(receiveStr);
+
+        if(result == 1){
+            String re1 = ConcentratorDemo.login();
+            writeToClient(re1,ctx,"登录");
+        }
+        if(result == 2){
+            String re = ConcentratorDemo.heartbeat();
+            writeToClient(re,ctx,"心跳");
+        }
+    }
+
+    @Override
+    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+        String channelIdLongText = ctx.channel().id().asLongText();
+        log.info("客户端" + channelIdLongText + "断开");
+        channels.remove(ctx.channel());
+
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        String channelIdLongText = ctx.channel().id().asLongText();
+        log.info("客户端" + channelIdLongText + "异常");
+        cause.printStackTrace();
+        channels.remove(ctx.channel());
+
+        ctx.close();
+    }
+
+
+    private void writeToClient(final String receiveStr, ChannelHandlerContext channel, final String mark) {
+        try {
+            ByteBuf bufff = Unpooled.buffer();//netty需要用ByteBuf传输
+            bufff.writeBytes(ConvertCode.hexString2Bytes(receiveStr));//对接需要16进制
+            channel.writeAndFlush(bufff).addListener(new ChannelFutureListener() {
+                @Override
+                public void operationComplete(ChannelFuture future) throws Exception {
+                    StringBuilder sb = new StringBuilder("");
+                    if(!StringUtils.isEmpty(mark)){
+                        sb.append("【").append(mark).append("】");
+                    }
+                    if (future.isSuccess()) {
+                        log.info(sb.toString()+"回写成功"+receiveStr);
+                    } else {
+                        log.error(sb.toString()+"回写失败"+receiveStr);
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.out.println("调用通用writeToClient()异常"+e.getMessage());
+            log.error("调用通用writeToClient()异常:",e);
+        }
+    }
+
+}

+ 54 - 0
src/main/java/com/zcxk/tcpserver/tcp/TcpSocketServer.java

@@ -0,0 +1,54 @@
+package com.zcxk.tcpserver.tcp;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.net.InetSocketAddress;
+
+@Slf4j
+@Component
+public class TcpSocketServer {
+    @Autowired
+    @Qualifier("serverBootstrap")
+    private ServerBootstrap b;
+
+    @Autowired
+    @Qualifier("tcpSocketAddress")
+    private InetSocketAddress tcpSocketPort;
+
+    private ChannelFuture serverChannelFuture;
+
+    @PostConstruct
+    public void start() throws Exception {
+        log.info("Starting server at " + tcpSocketPort);
+        serverChannelFuture = b.bind(tcpSocketPort).sync();
+    }
+
+    @PreDestroy
+    public void stop() throws Exception {
+        log.info("stop server at " + tcpSocketPort);
+        serverChannelFuture.channel().closeFuture().sync();
+    }
+
+    public ServerBootstrap getB() {
+        return b;
+    }
+
+    public void setB(ServerBootstrap b) {
+        this.b = b;
+    }
+
+    public InetSocketAddress getTcpSocketPort() {
+        return tcpSocketPort;
+    }
+
+    public void setTcpSocketPort(InetSocketAddress tcpSocketPort) {
+        this.tcpSocketPort = tcpSocketPort;
+    }
+}

+ 493 - 0
src/main/java/com/zcxk/tcpserver/tcp/generator/ConcentratorDemo.java

@@ -0,0 +1,493 @@
+package com.zcxk.tcpserver.tcp.generator;
+
+import com.zcxk.tcpserver.entity.DataUnit;
+import com.zcxk.tcpserver.entity.Document;
+import com.zcxk.tcpserver.entity.FrameFormat;
+import com.zcxk.tcpserver.util.Accumulator;
+import com.zcxk.tcpserver.util.HexUtil;
+
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class ConcentratorDemo {
+    public static void main(String[] args) throws Exception {
+
+        //String request = "683500350068705507DB27008C600000010003BE16";
+        //String request = "683500350068705507DB27008C600000010003BE16";
+        //ProtocolGenerator.analysis(request);
+        //a();
+        //b();
+        //c();
+        //login();
+        //readTime();
+
+
+
+        Document document1 = new Document();
+        document1.setMeterMac("000123456789");
+        document1.setMeterType("3");
+        document1.setCollector("000000000000");
+
+        Document document2 = new Document();
+        document2.setMeterMac("000123456790");
+        document2.setMeterType("3");
+        document2.setCollector("000000000000");
+
+        issueWaterMeterFiles("",newArrayList(document1,document2));
+
+
+
+
+    }
+
+    public static String readTime(){
+        //--功能:读时间;SEQ:6;帧状态:3;AFN:8C;FN:1;集中器编号:075527DB00;时间:2020-03-10 13:26:15;
+        //68 35 00 35 00 68 70 55 07 DB 27 00 8C 66 00 00 01 00 03 C4 16
+        //68 49 00 49 00 68 88 55 07 DB 27 00 8C 66 00 00 01 00 15 26 13 10 43 20 9A 16 0D
+        String controlDomain = "70";
+        String addressDomain = "5507DB2700";
+        String afn = "8C";
+        String seq = Accumulator.getSeq();
+        DataUnit dataUnit = new DataUnit();
+        dataUnit.setIdentify("00000100");
+        dataUnit.setData("03");
+
+        FrameFormat frameFormat = new FrameFormat();
+        frameFormat.setControlDomain(controlDomain);
+        frameFormat.setAddressDomain(addressDomain);
+        frameFormat.setAfn(afn);
+        frameFormat.setSeq(seq);
+        frameFormat.setDataUnitList(newArrayList(dataUnit));
+        //计算长度
+        String length = frameFormat.calculatedLength();
+        //System.out.println("length="+length);
+        //计算校验码
+        String cs = frameFormat.checkout();
+        //System.out.println("cs="+cs);
+
+        frameFormat.setLength1(length);
+        frameFormat.setLength2(length);
+        frameFormat.setCs(cs);
+
+
+
+        //获取帧格式字符
+        String frameFormatStr = frameFormat.builFrameFormatStr();
+        System.out.println(frameFormatStr);
+        return frameFormatStr;
+    }
+
+    public static String readDoc(){
+        //--功能:读档案;SEQ:8;帧状态:3;AFN:8A;FN:1;集中器编号:075527DB00;数量:2;测量点:1;水表电子号:021506003011;采集器编号:000000000004;测量点:2;水表电子号:021708000339;采集器编号:000000000004;
+        //
+        //-> 68 61 00 61 00 68 70 55 07 DB 27 00 8A 68 00 00 01 00 05 00 01 00 02 00 03 00 04 00 05 00 D5 16
+        //68 E9 00 E9 00 68 88 55 07 DB 27 00 8A 68 00 00 01 00 02 00 01 00 11 30 00 06 15 02 00 00 00 10 00 00 00 00 04 00 00 00 00 00 02 00 39 03 00 08 17 02 00 00 00 10 00 00 00 00 04 00 00 00 00 00 C1 16 0D
+        String controlDomain = "70";
+        String addressDomain = "5507DB2700";
+        String afn = "8A";
+        String seq = Accumulator.getSeq();
+        DataUnit dataUnit = new DataUnit();
+        dataUnit.setIdentify("00000100");
+        dataUnit.setData("050001000200030004000500");
+
+        FrameFormat frameFormat = new FrameFormat();
+        frameFormat.setControlDomain(controlDomain);
+        frameFormat.setAddressDomain(addressDomain);
+        frameFormat.setAfn(afn);
+        frameFormat.setSeq(seq);
+        frameFormat.setDataUnitList(newArrayList(dataUnit));
+        //计算长度
+        String length = frameFormat.calculatedLength();
+        //System.out.println("length="+length);
+        //计算校验码
+        String cs = frameFormat.checkout();
+        //System.out.println("cs="+cs);
+
+        frameFormat.setLength1(length);
+        frameFormat.setLength2(length);
+        frameFormat.setCs(cs);
+
+
+
+        //获取帧格式字符
+        String frameFormatStr = frameFormat.builFrameFormatStr();
+        //System.out.println(frameFormatStr);
+        return frameFormatStr;
+    }
+
+
+    public static String readFreezeData(){
+        //--功能:读冻结数据;SEQ:9;帧状态:3;AFN:8C;FN:57;集中器编号:075527DB00;数量:2;测量点:1;当前累积用量: ;测量点:2;当前累积用量:000003.00;
+        //
+        //-> 68 4D 00 4D 00 68
+        // 70 55 07 DB 27 00 8C 69
+        // 00 00 01 07
+        // 02
+        // 02 00
+        // 01 00
+        // 02 00 D2 16
+        //68 69 00 69 00 68 88 55 07 DB 27 00 8C 69 00 00 01 07 02 00 01 00 EE EE EE EE 02 00 00 03 00 00 A3 16 0D
+        String controlDomain = "70";
+        String addressDomain = "5507DB2700";
+        String afn = "8C";
+        String seq = Accumulator.getSeq();
+        DataUnit dataUnit = new DataUnit();
+        dataUnit.setIdentify("00000107");
+        dataUnit.setData("02020001000200");
+
+        FrameFormat frameFormat = new FrameFormat();
+        frameFormat.setControlDomain(controlDomain);
+        frameFormat.setAddressDomain(addressDomain);
+        frameFormat.setAfn(afn);
+        frameFormat.setSeq(seq);
+        frameFormat.setDataUnitList(newArrayList(dataUnit));
+        //计算长度
+        String length = frameFormat.calculatedLength();
+        //System.out.println("length="+length);
+        //计算校验码
+        String cs = frameFormat.checkout();
+        //System.out.println("cs="+cs);
+
+        frameFormat.setLength1(length);
+        frameFormat.setLength2(length);
+        frameFormat.setCs(cs);
+
+
+
+        //获取帧格式字符
+        String frameFormatStr = frameFormat.builFrameFormatStr();
+        //System.out.println(frameFormatStr);
+        return frameFormatStr;
+    }
+
+    public static String login(){
+        DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+        String controlDomain = "10";
+        String addressDomain = "5507DB2700";
+        String afn = "00";
+        String seq = Accumulator.getSeq();
+        DataUnit dataUnit1 = new DataUnit();
+        dataUnit1.setIdentify("00000400");
+        dataUnit1.setData("02");
+        DataUnit dataUnit2 = new DataUnit();
+        dataUnit2.setIdentify("00000100");
+        dataUnit2.setData("00");
+
+
+        FrameFormat frameFormat = new FrameFormat();
+        frameFormat.setControlDomain(controlDomain);
+        frameFormat.setAddressDomain(addressDomain);
+        frameFormat.setAfn(afn);
+        frameFormat.setSeq(seq);
+        frameFormat.setDataUnitList(newArrayList(dataUnit1,dataUnit2));
+        //计算长度
+        String length = frameFormat.calculatedLength();
+        //System.out.println("length="+length);
+        //计算校验码
+        String cs = frameFormat.checkout();
+        //System.out.println("cs="+cs);
+
+        frameFormat.setLength1(length);
+        frameFormat.setLength2(length);
+        frameFormat.setCs(cs);
+
+
+
+        //获取帧格式字符
+        String frameFormatStr = frameFormat.builFrameFormatStr();
+        System.out.println(frameFormatStr);
+        return frameFormatStr;
+    }
+
+    public static String heartbeat(){
+        DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+        String controlDomain = "10";
+        String addressDomain = "5507DB2700";
+        String afn = "00";
+        String seq = Accumulator.getSeq();
+        DataUnit dataUnit1 = new DataUnit();
+        dataUnit1.setIdentify("00000400");
+        dataUnit1.setData("02");
+        DataUnit dataUnit2 = new DataUnit();
+        dataUnit2.setIdentify("00000400");
+        //dataUnit2.setData("00"+LocalDateTime.now().format(f));
+        dataUnit2.setData("00");
+
+
+        FrameFormat frameFormat = new FrameFormat();
+        frameFormat.setControlDomain(controlDomain);
+        frameFormat.setAddressDomain(addressDomain);
+        frameFormat.setAfn(afn);
+        frameFormat.setSeq(seq);
+        frameFormat.setDataUnitList(newArrayList(dataUnit1,dataUnit2));
+        //计算长度
+        String length = frameFormat.calculatedLength();
+        //System.out.println("length="+length);
+        //计算校验码
+        String cs = frameFormat.checkout();
+        //System.out.println("cs="+cs);
+
+        frameFormat.setLength1(length);
+        frameFormat.setLength2(length);
+        frameFormat.setCs(cs);
+
+
+
+        //获取帧格式字符
+        String frameFormatStr = frameFormat.builFrameFormatStr();
+        System.out.println(frameFormatStr);
+        return frameFormatStr;
+    }
+
+    //下发水表档案
+    public static String issueWaterMeterFiles(String deviceNo, List<Document> documents){
+        //	-2020-03-13 15:19:41,374 [1752] INFO  IDCServer - 收到客户端指令<下发水表档案>,目标设备<075510203>:{"command":"8400000100","concentrator":"10203","region":"0755","documents":[{"port":"1","meter_mac":"000123456789","meter_type":"冷水表","collector":"000000000000","channel":"0"},{"port":"2","meter_mac":"000123456790","meter_type":"冷水表","collector":"000000000000","channel":"0"}]}
+        //	-2020-03-13 15:19:41,413 [10624] INFO  IDCServer - 已向设备<075510203>发送数据包:682901290168705507DB270084710000010002000100896745230100300000100000000000000000000002009067452301003000001000000000000000000000363534333231393837363534333231304416
+        //	-2020-03-13 15:19:45,540 [1752] INFO  IDCServer - 接收到设备<075510203>的指令<0000000100>:683100310068805507DB27000061000001004016
+        String controlDomain = "70";
+        String addressDomain = "5507DB2700";
+        String afn = "84";
+        String seq = Accumulator.getSeq();
+        DataUnit dataUnit1 = new DataUnit();
+        dataUnit1.setIdentify("00000100");
+
+        int size = documents.size();
+        StringBuilder data = new StringBuilder("");
+        String sizeStr = HexUtil.hexTransfer(HexUtil.fill(HexUtil.demical2Hex(size),4,'0'));
+        data.append(sizeStr);
+
+        for (int i = 0; i < size; i++) {
+            //0100
+            //896745230100
+            //30 测量点性质
+            //00 接线方式
+            //00 电表费率号
+            //10 表类型代码
+            //0000 线路编号
+            //0000 表箱编号
+            //000000000000 采集器编号
+            Document document = documents.get(i);
+            String waterNo = HexUtil.hexTransfer(HexUtil.fill(HexUtil.demical2Hex(i+1),4,'0'));//表序号
+            String meterMac = HexUtil.hexTransfer(document.getMeterMac());//表号
+            String measuringPointProperties = document.getMeasuringPointProperties();//测量点性质  数据格式27
+            String connection = document.getConnection();//接线方式 数据格式28 00 表号12位    01表号14位(最高2位在电表费率号)
+            String meterRateNo  = document.getMeterRateNo();;//电表费率号 数据格式29
+            String typeCode = "10";//表类型代码 数据格式30
+            String lineNo = "0000";//线路编号 数据格式31
+            String boxNo = "0000";//表箱编号
+            String collector = HexUtil.hexTransfer(document.getCollector());//采集器编号
+
+            data.append(waterNo);
+            data.append(meterMac);
+            data.append(measuringPointProperties);
+            data.append(connection);
+            data.append(meterRateNo);
+            data.append(typeCode);
+            data.append(lineNo);
+            data.append(boxNo);
+            data.append(collector);
+        }
+        String subjoin = "36353433323139383736353433323130";//默认值
+        data.append(subjoin);
+        //dataUnit1.setData("0200010089674523010030000010000000000000000000000200906745230100300000100000000000000000000036353433323139383736353433323130");
+        System.out.println(data.toString());
+        dataUnit1.setData(data.toString());
+
+
+        FrameFormat frameFormat = new FrameFormat();
+        frameFormat.setControlDomain(controlDomain);
+        frameFormat.setAddressDomain(addressDomain);
+        frameFormat.setAfn(afn);
+        frameFormat.setSeq(seq);
+        frameFormat.setDataUnitList(newArrayList(dataUnit1));
+        //计算长度
+        String length = frameFormat.calculatedLength();
+        System.out.println("length="+length);
+        //计算校验码
+        String cs = frameFormat.checkout();
+        //System.out.println("cs="+cs);
+
+        frameFormat.setLength1(length);
+        frameFormat.setLength2(length);
+        frameFormat.setCs(cs);
+
+
+
+        //获取帧格式字符
+        String frameFormatStr = frameFormat.builFrameFormatStr();
+        System.out.println(frameFormatStr);
+        return frameFormatStr;
+    }
+
+    //删除水表档案
+    public static String delWaterMeterFiles(String deviceNo, List<Integer> meterNos){
+        //	-2020-03-13 15:45:11,189 [3288] INFO  IDCServer - 收到客户端指令<删除水表档案>,目标设备<075510203>:{"command":"8400008002","concentrator":"10203","region":"0755","ports":["1","2","3"]}
+        //	-2020-03-13 15:45:11,274 [1616] INFO  IDCServer - 已向设备<075510203>发送数据包:685500550068705507DB27008472000080020003000100020003004F16
+        //	-2020-03-13 15:45:11,874 [16320] INFO  IDCServer - 接收到设备<075510203>的指令<0000000100>:683100310068805507DB27000062000001004116
+        String controlDomain = "70";
+        String addressDomain = "5507DB2700";
+        String afn = "84";
+        String seq = Accumulator.getSeq();
+        DataUnit dataUnit1 = new DataUnit();
+        dataUnit1.setIdentify("00800200");
+
+        int size = meterNos.size();
+        StringBuilder data = new StringBuilder("");
+        String sizeStr = HexUtil.hexTransfer(HexUtil.fill(HexUtil.demical2Hex(size),4,'0'));
+        data.append(sizeStr);
+
+        for (int i = 0; i < size; i++) {
+            String waterNo = HexUtil.hexTransfer(HexUtil.fill(HexUtil.demical2Hex(i+1),4,'0'));//表序号
+            data.append(waterNo);
+        }
+        System.out.println(data.toString());
+        dataUnit1.setData(data.toString());
+
+
+        FrameFormat frameFormat = new FrameFormat();
+        frameFormat.setControlDomain(controlDomain);
+        frameFormat.setAddressDomain(addressDomain);
+        frameFormat.setAfn(afn);
+        frameFormat.setSeq(seq);
+        frameFormat.setDataUnitList(newArrayList(dataUnit1));
+        //计算长度
+        String length = frameFormat.calculatedLength();
+        System.out.println("length="+length);
+        //计算校验码
+        String cs = frameFormat.checkout();
+        //System.out.println("cs="+cs);
+
+        frameFormat.setLength1(length);
+        frameFormat.setLength2(length);
+        frameFormat.setCs(cs);
+
+
+
+        //获取帧格式字符
+        String frameFormatStr = frameFormat.builFrameFormatStr();
+        System.out.println(frameFormatStr);
+        return frameFormatStr;
+    }
+
+    //水表开阀
+    public static String waterMeterOpen(){
+        //2020-03-13 17:01:06,968 [8808] INFO  IDCServer - 收到客户端指令<开阀>,目标设备<075510203>:{"command":"8500000100","concentrator":"10203","region":"0755","port":"1","keys":"01111111","valve_state":"1"}
+        //2020-03-13 17:01:07,024 [9720] INFO  IDCServer - 已向设备<075510203>发送数据包:688500850068705507DB27008579000001005634120100011111110055393837363534333231300016
+        //2020-03-13 17:04:25,303 [14392] INFO  IDCServer - 接收到设备<075510203>的指令<0000000200>:683100310068805507DB2700006B000002004B16
+
+        return "";
+    }
+
+    //水表关阀
+    public static String waterMeterClose(){
+        //2020-03-13 17:02:39,886 [5332] INFO  IDCServer - 收到客户端指令<关阀>,目标设备<075510203>:{"command":"8500000100","concentrator":"10203","region":"0755","port":"1","keys":"01111111","valve_state":"0"}
+        //2020-03-13 17:02:39,949 [8808] INFO  IDCServer - 已向设备<075510203>发送数据包:688500850068705507DB2700857A0000010056341201000111111100AA393837363534333231305616
+        //2020-03-13 17:02:47,774 [8808] INFO  IDCServer - 接收到设备<075510203>的指令<0000000200>:683100310068805507DB2700006A000002004A16
+        return "";
+    }
+
+    //查询版本
+    public static String queryVersion(){
+        //2020-03-13 17:00:09,765 [11884] INFO  IDCServer - 收到客户端指令<查询集中器版本信息>,目标设备<075510203>:{"command":"8C00000101","concentrator":"10203","region":"0755"}
+        //2020-03-13 17:00:09,867 [8808] INFO  IDCServer - 已向设备<075510203>发送数据包:683500350068705507DB27008C780000010103D716
+        //2020-03-13 17:00:10,464 [14392] INFO  IDCServer - 接收到设备<075510203>的指令<8C00000101>:687100710068885507DB27008C680000010148583635533138384741313548344742CE16
+        //^^{"status":1,"msg":"success","results":[{"soft_version":"HX-65-S-188","hardware_version":"GA-15-H-4GB"}]}$$
+        String controlDomain = "70";
+        String addressDomain = "5507DB2700";
+        String afn = "8C";
+        String seq = Accumulator.getSeq();
+        DataUnit dataUnit1 = new DataUnit();
+        dataUnit1.setIdentify("00000101");
+        dataUnit1.setData("03");
+
+
+        FrameFormat frameFormat = new FrameFormat();
+        frameFormat.setControlDomain(controlDomain);
+        frameFormat.setAddressDomain(addressDomain);
+        frameFormat.setAfn(afn);
+        frameFormat.setSeq(seq);
+        frameFormat.setDataUnitList(newArrayList(dataUnit1));
+        //计算长度
+        String length = frameFormat.calculatedLength();
+        System.out.println("length="+length);
+        //计算校验码
+        String cs = frameFormat.checkout();
+        //System.out.println("cs="+cs);
+
+        frameFormat.setLength1(length);
+        frameFormat.setLength2(length);
+        frameFormat.setCs(cs);
+
+
+
+        //获取帧格式字符
+        String frameFormatStr = frameFormat.builFrameFormatStr();
+        System.out.println(frameFormatStr);
+        return frameFormatStr;
+    }
+
+    //实时抄表 0107
+    public static String meterReading(){
+        //2020-03-13 16:52:29,926 [5332] INFO  IDCServer - 收到客户端指令<实时抄表>,目标设备<075510203>:{"command":"8C00000107","concentrator":"10203","region":"0755","type":"00","ports":["1","2","3"]}
+        //2020-03-13 16:52:30,007 [8808] INFO  IDCServer - 已向设备<075510203>发送数据包:685500550068705507DB27008C7100000107000300010002000300DC16
+        //2020-03-13 16:52:48,152 [8808] INFO  IDCServer - 接收到设备<075510203>的指令<8C00000107>:686900690068885507DB27008C610000010702000100EEEEEEEE0200EEEEEEEE5016
+        return "";
+    }
+
+    //冻结数据 0107
+    public static String freezeData(){
+        //2020-03-13 16:54:08,564 [5332] INFO  IDCServer - 收到客户端指令<实时抄表>,目标设备<075510203>:{"command":"8C00000107","concentrator":"10203","region":"0755","type":"02","ports":["1","2","3"]}
+        //2020-03-13 16:54:08,571 [11884] INFO  IDCServer - 已向设备<075510203>发送数据包:685500550068705507DB27008C7200000107020300010002000300DF16
+        //2020-03-13 16:54:09,175 [9720] INFO  IDCServer - 接收到设备<075510203>的指令<8C00000107>:686900690068885507DB27008C620000010702000100EEEEEEEE0200EEEEEEEE5116
+        String controlDomain = "70";
+        String addressDomain = "5507DB2700";
+        String afn = "8C";
+        String seq = Accumulator.getSeq();
+        DataUnit dataUnit = new DataUnit();
+        dataUnit.setIdentify("00000107");
+        dataUnit.setData("02020001000200");
+
+        FrameFormat frameFormat = new FrameFormat();
+        frameFormat.setControlDomain(controlDomain);
+        frameFormat.setAddressDomain(addressDomain);
+        frameFormat.setAfn(afn);
+        frameFormat.setSeq(seq);
+        frameFormat.setDataUnitList(newArrayList(dataUnit));
+        //计算长度
+        String length = frameFormat.calculatedLength();
+        //System.out.println("length="+length);
+        //计算校验码
+        String cs = frameFormat.checkout();
+        //System.out.println("cs="+cs);
+
+        frameFormat.setLength1(length);
+        frameFormat.setLength2(length);
+        frameFormat.setCs(cs);
+
+
+
+        //获取帧格式字符
+        String frameFormatStr = frameFormat.builFrameFormatStr();
+        //System.out.println(frameFormatStr);
+        return frameFormatStr;
+    }
+
+    //查询水表状态字 0209
+    public static String waterMeterStatus(){
+        //2020-03-13 16:58:48,327 [8808] INFO  IDCServer - 收到客户端指令<实时抄表>,目标设备<075510203>:{"command":"8C00000209","concentrator":"10203","region":"0755","type":"00","ports":["1","2","3"]}
+        //2020-03-13 16:58:48,412 [9720] INFO  IDCServer - 已向设备<075510203>发送数据包:685500550068705507DB27008C7700000209000300010002000300E516
+        //2020-03-13 16:58:51,689 [8808] INFO  IDCServer - 接收到设备<075510203>的指令<0000000200>:683100310068805507DB27000067000002004716
+        return "";
+    }
+
+    //实时抄表 0108
+    public static String meterReading2(){
+        //2020-03-13 16:56:38,144 [11884] INFO  IDCServer - 接收到设备<075510203>的指令<0000000200>:683100310068805507DB27000064000002004416
+        //2020-03-13 16:57:06,972 [9720] INFO  IDCServer - 收到客户端指令<实时抄表>,目标设备<075510203>:{"command":"8C00000108","concentrator":"10203","region":"0755","type":"00","ports":["1","2","3"]}
+        //2020-03-13 16:57:06,977 [5332] INFO  IDCServer - 已向设备<075510203>发送数据包:685500550068705507DB27008C7500000108000300010002000300E116
+        //2020-03-13 16:57:07,642 [9720] INFO  IDCServer - 接收到设备<075510203>的指令<0000000200>:683100310068805507DB27000065000002004516
+        return "";
+    }
+}

+ 17 - 0
src/main/java/com/zcxk/tcpserver/tcp/generator/ConcentratorUpDemo.java

@@ -0,0 +1,17 @@
+package com.zcxk.tcpserver.tcp.generator;
+
+public class ConcentratorUpDemo {
+    public static void main(String[] args) {
+        //--功能:读时间;SEQ:6;帧状态:3;AFN:8C;FN:1;集中器编号:075527DB00;时间:2020-03-10 13:26:15;
+        //68 35 00 35 00 68 70 55 07 DB 27 00 8C 66 00 00 01 00 03 C4 16
+        //68 49 00 49 00 68 88 55 07 DB 27 00 8C 66 00 00 01 00 15 26 13 10 43 20 9A 16 0D
+        //上行数据解析
+        //String up = "684900490068885507DB27008C66000001001526131043209A160D";
+        String up = "68E900E90068885507DB27008A680000010002000100113000061502000000100000000004000000000002003903000817020000001000000000040000000000C1160D";
+        //String up = "686900690068885507DB27008C690000010702000100EEEEEEEE020000030000A3160D";
+        //String up = "683500350068C90313010000027000000100220016";//登录
+        //String up = "683500350068C90313010000027000000400220016";//心跳
+        //"68 31 00 31 00 68 C9 55 07 DB 27 00 02 70 00 00 01 00 9A 16";
+        ProtocolGenerator.analysis(up);
+    }
+}

+ 154 - 0
src/main/java/com/zcxk/tcpserver/tcp/generator/ProtocolGenerator.java

@@ -0,0 +1,154 @@
+package com.zcxk.tcpserver.tcp.generator;
+
+import com.zcxk.tcpserver.entity.FrameFormat;
+import com.zcxk.tcpserver.util.ConvertCode;
+import com.zcxk.tcpserver.util.HexUtil;
+import com.zcxk.tcpserver.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+@Slf4j
+public class ProtocolGenerator {
+
+    public static int analysis(String str){
+        str = str.toUpperCase();
+        int result = 0;
+        String strh = str.substring(str.length() -2);
+        if(StringUtils.equals("0D",strh)){
+            str = str.substring(0,str.length()-2);
+        }
+        //log.info(str);
+        List<String> list = StringUtil.split(str,2);
+        log.info(list.toString());
+        FrameFormat frameFormat = new FrameFormat();
+        frameFormat.setBeginCharacter1(list.get(0));
+        frameFormat.setLength1(list.get(1)+list.get(2));
+        frameFormat.setLength2(list.get(3)+list.get(4));
+        frameFormat.setBeginCharacter2(list.get(5));
+        frameFormat.setControlDomain(list.get(6));
+        frameFormat.setAddressDomain(list.get(7)+list.get(8)+list.get(9)+list.get(10)+list.get(11));
+        frameFormat.setAfn(list.get(12));
+        frameFormat.setSeq(list.get(13));
+        frameFormat.setCs(list.get(list.size()-2));
+        frameFormat.setEndCharacter(list.get(list.size()-1));
+        log.info(frameFormat.toString());
+
+        String length = list.get(2)+list.get(1);
+        //System.out.println(length);
+        String lengthBin = HexUtil.hexString2binaryString(length);
+        //System.out.println(lengthBin);
+        String strBin2 = lengthBin.substring(lengthBin.length() -2);
+        if(StringUtils.equals("01",strBin2)){
+            lengthBin = lengthBin.substring(0,lengthBin.length()-2);
+        }
+        //System.out.println(lengthBin);
+
+        int lengthInt = HexUtil.ByteToDecimal(lengthBin);
+        //log.info("用户数据长度:"+lengthInt);
+
+        List<String> userDatalist = newArrayList();
+
+        for (int i = 0; i < lengthInt; i++) {
+            userDatalist.add(list.get(6+i));
+        }
+
+        log.info(userDatalist.toString());
+        if(StringUtils.equals(frameFormat.getAfn(),"8C")){
+            if(StringUtils.equals(userDatalist.get(10),"01") && StringUtils.equals(userDatalist.get(11),"00")){
+                log.info("读时间");
+                List<String> weekMonth = StringUtil.split(userDatalist.get(16),1);
+                //System.out.println(weekMonth);
+                String weekMonthStr =  HexUtil.hexStringToByte(weekMonth.get(0));
+                //System.out.println(weekMonthStr);
+                String monthPrefix = weekMonthStr.substring(weekMonthStr.length() -1);
+                //System.out.println(monthPrefix+weekMonth.get(1));
+                log.info("时间:"+"20"+userDatalist.get(17)+"年"+monthPrefix+weekMonth.get(1)+"月"+userDatalist.get(15)+"号 "+userDatalist.get(14)+"时"+userDatalist.get(13)+"分"+userDatalist.get(12)+"秒");
+            }
+        }
+
+        //--功能:读档案;SEQ:8;帧状态:3;AFN:8A;FN:1;集中器编号:075527DB00;数量:2;测量点:1;水表电子号:021506003011;采集器编号:000000000004;测量点:2;水表电子号:021708000339;采集器编号:000000000004;
+        //
+        //-> 68 61 00 61 00 68 70 55 07 DB 27 00 8A 68 00 00 01 00 05 00 01 00 02 00 03 00 04 00 05 00 D5 16
+        //68 E9 00 E9 00 68 88 55 07 DB 27 00 8A 68 00 00 01 00 02 00 01 00 11 30 00 06 15 02 00 00 00 10 00 00 00 00 04 00 00 00 00 00 02 00 39 03 00 08 17 02 00 00 00 10 00 00 00 00 04 00 00 00 00 00 C1 16 0D
+
+        if(StringUtils.equals(frameFormat.getAfn(),"8A")){
+            if(StringUtils.equals(userDatalist.get(10),"01") && StringUtils.equals(userDatalist.get(11),"00")){
+                log.info("读文档");
+                int num =  HexUtil.ByteToDecimal(HexUtil.hexString2binaryString(userDatalist.get(13)+userDatalist.get(12)));
+                log.info("数量:"+num);
+                for (int i = 0; i < num; i++) {
+                    log.info("测量点:"+HexUtil.hex2decimal(userDatalist.get(15+ i *22)+userDatalist.get(14+ i *22)));
+                    log.info("水表电子号:"+userDatalist.get(21+ i *22)+userDatalist.get(20+ i *22)+userDatalist.get(19+ i *22)+userDatalist.get(18+ i *22)+userDatalist.get(17+ i *22)+userDatalist.get(16+ i *22));
+                    log.info("采集器编号:"+userDatalist.get(35+ i *22)+userDatalist.get(34+ i *22)+userDatalist.get(33+ i *22)+userDatalist.get(32+ i *22)+userDatalist.get(31+ i *22)+userDatalist.get(30+ i *22));
+                }
+
+            }
+        }
+
+        //--功能:读冻结数据;SEQ:9;帧状态:3;AFN:8C;FN:57;集中器编号:075527DB00;数量:2;测量点:1;当前累积用量: ;测量点:2;当前累积用量:000003.00;
+        //
+        //-> 68 4D 00 4D 00 68 70 55 07 DB 27 00 8C 69 00 00 01 07 02 02 00 01 00 02 00 D2 16
+        //68 69 00 69 00 68 88 55 07 DB 27 00 8C 69 00 00 01 07 02 00 01 00 EE EE EE EE 02 00 00 03 00 00 A3 16 0D
+
+        if(StringUtils.equals(frameFormat.getAfn(),"8C")) {
+            if (StringUtils.equals(userDatalist.get(10), "01") && StringUtils.equals(userDatalist.get(11), "07")) {
+                log.info("读冻结数据");
+                int num =  HexUtil.ByteToDecimal(HexUtil.hexString2binaryString(userDatalist.get(13)+userDatalist.get(12)));
+                log.info("数量:"+num);
+                for (int i = 0; i < num; i++) {
+                    log.info("测量点:"+HexUtil.hex2decimal(userDatalist.get(15+ i *6)+userDatalist.get(14+ i *6)));
+                    if(StringUtils.equals(userDatalist.get(19+ i *6)+userDatalist.get(18+ i *6)+userDatalist.get(17+ i *6)+userDatalist.get(16+ i *6),"EEEEEEEE")){
+                        log.info("当前累积用量:-");
+                    }else {
+
+                        log.info("当前累积用量:"+userDatalist.get(19+ i *6)+userDatalist.get(18+ i *6)+userDatalist.get(17+ i *6)+"."+userDatalist.get(16+ i *6));
+                    }
+                }
+            }
+        }
+
+        if(StringUtils.equals(frameFormat.getAfn(),"02")) {
+            if (StringUtils.equals(userDatalist.get(8), "00")&&StringUtils.equals(userDatalist.get(9), "00")&&StringUtils.equals(userDatalist.get(10), "01") && StringUtils.equals(userDatalist.get(11), "00")) {
+                log.info("登录");
+                result = 1;
+            }
+        }
+        if(StringUtils.equals(frameFormat.getAfn(),"02")) {
+            if (StringUtils.equals(userDatalist.get(8), "00")&&StringUtils.equals(userDatalist.get(9), "00")&&StringUtils.equals(userDatalist.get(10), "04") && StringUtils.equals(userDatalist.get(11), "00")) {
+                log.info("心跳");
+                result = 2;
+            }
+        }
+
+        if(StringUtils.equals(frameFormat.getAfn(),"00")) {
+            if (StringUtils.equals(userDatalist.get(8), "00")&&StringUtils.equals(userDatalist.get(9), "00")) {
+                log.info("确认报文");
+                if(StringUtils.equals(userDatalist.get(10), "01") && StringUtils.equals(userDatalist.get(11), "00")){
+                    log.info("全部确认");
+                }
+                if(StringUtils.equals(userDatalist.get(10), "02") && StringUtils.equals(userDatalist.get(11), "00")){
+                    log.info("全部否认");
+                }
+                if(StringUtils.equals(userDatalist.get(10), "04") && StringUtils.equals(userDatalist.get(11), "00")){
+                    log.info("逐个确认或否认");
+                }
+            }
+        }
+
+        if(StringUtils.equals(frameFormat.getAfn(),"8C")) {
+            if (StringUtils.equals(userDatalist.get(8), "00")&&StringUtils.equals(userDatalist.get(9), "00")&&StringUtils.equals(userDatalist.get(10), "01") && StringUtils.equals(userDatalist.get(11), "01")) {
+                log.info("版本");
+
+                log.info("软件版本:"+ConvertCode.hexString2String(userDatalist.get(12)+userDatalist.get(13))+"-"+ConvertCode.hexString2String(userDatalist.get(14)+userDatalist.get(15))+
+                        "-"+ConvertCode.hexString2String(userDatalist.get(16))+"-"+ConvertCode.hexString2String(userDatalist.get(17)+userDatalist.get(18)+userDatalist.get(19)));
+                log.info("硬件版本:"+ConvertCode.hexString2String(userDatalist.get(20)+userDatalist.get(21))+"-"+ConvertCode.hexString2String(userDatalist.get(22)+userDatalist.get(23))+
+                        "-"+ConvertCode.hexString2String(userDatalist.get(24))+"-"+ConvertCode.hexString2String(userDatalist.get(25)+userDatalist.get(26)+userDatalist.get(27)));
+            }
+        }
+        return result;
+    }
+}

+ 19 - 0
src/main/java/com/zcxk/tcpserver/util/Accumulator.java

@@ -0,0 +1,19 @@
+package com.zcxk.tcpserver.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class Accumulator {
+    static AtomicInteger ai = new AtomicInteger(0);
+    public static int getAndIncrement(){
+        int increment = ai.getAndIncrement();
+        if(increment==255){
+            ai.set(0);
+        }
+            return increment;
+    }
+
+    public static String getSeq(){
+        int increment = Accumulator.getAndIncrement();
+        return "6"+StringUtil.substringLast(HexUtil.demical2Hex(increment),1);
+    }
+}

+ 209 - 0
src/main/java/com/zcxk/tcpserver/util/ConvertCode.java

@@ -0,0 +1,209 @@
+package com.zcxk.tcpserver.util;
+
+public class ConvertCode {
+    /**
+     * @Title:bytes2HexString
+     * @Description:字节数组转16进制字符串
+     * @param b
+     *            字节数组
+     * @return 16进制字符串
+     * @throws
+     */
+    public static String bytes2HexString(byte[] b) {
+        StringBuffer result = new StringBuffer();
+        String hex;
+        for (int i = 0; i < b.length; i++) {
+            hex = Integer.toHexString(b[i] & 0xFF);
+            if (hex.length() == 1) {
+                hex = '0' + hex;
+            }
+            result.append(hex.toUpperCase());
+        }
+        return result.toString();
+    }
+    /**
+     * @Title:hexString2Bytes
+     * @Description:16进制字符串转字节数组
+     * @param src  16进制字符串
+     * @return 字节数组
+     */
+    public static byte[] hexString2Bytes(String src) {
+        int l = src.length() / 2;
+        byte[] ret = new byte[l];
+        for (int i = 0; i < l; i++) {
+            ret[i] = (byte) Integer
+                    .valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue();
+        }
+        return ret;
+    }
+    /**
+     * @Title:string2HexString
+     * @Description:字符串转16进制字符串
+     * @param strPart  字符串
+     * @return 16进制字符串
+     */
+    public static String string2HexString(String strPart) {
+        StringBuffer hexString = new StringBuffer();
+        for (int i = 0; i < strPart.length(); i++) {
+            int ch = (int) strPart.charAt(i);
+            String strHex = Integer.toHexString(ch);
+            hexString.append(strHex);
+        }
+        return hexString.toString();
+    }
+    /**
+     * @Title:hexString2String
+     * @Description:16进制字符串转字符串
+     * @param src
+     *            16进制字符串
+     * @return 字节数组
+     * @throws
+     */
+    public static String hexString2String(String src) {
+        String temp = "";
+        for (int i = 0; i < src.length() / 2; i++) {
+            //System.out.println(Integer.valueOf(src.substring(i * 2, i * 2 + 2),16).byteValue());
+            temp = temp+ (char)Integer.valueOf(src.substring(i * 2, i * 2 + 2),16).byteValue();
+        }
+        return temp;
+    }
+
+    /**
+     * @Title:char2Byte
+     * @Description:字符转成字节数据char-->integer-->byte
+     * @param src
+     * @return
+     * @throws
+     */
+    public static Byte char2Byte(Character src) {
+        return Integer.valueOf((int)src).byteValue();
+    }
+
+    /**
+     * @Title:intToHexString
+     * @Description:10进制数字转成16进制
+     * @param a 转化数据
+     * @param len 占用字节数
+     * @return
+     * @throws
+     */
+    public static String intToHexString(int a,int len){
+        len<<=1;
+        String hexString = Integer.toHexString(a);
+        int b = len -hexString.length();
+        if(b>0){
+            for(int i=0;i<b;i++)  {
+                hexString = "0" + hexString;
+            }
+        }
+        return hexString;
+    }
+
+
+    /**
+     * 将16进制的2个字符串进行异或运算
+     * http://blog.csdn.net/acrambler/article/details/45743157
+     * @param strHex_X
+     * @param strHex_Y
+     * 注意:此方法是针对一个十六进制字符串一字节之间的异或运算,如对十五字节的十六进制字符串异或运算:1312f70f900168d900007df57b4884
+    先进行拆分:13 12 f7 0f 90 01 68 d9 00 00 7d f5 7b 48 84
+    13 xor 12-->1
+    1 xor f7-->f6
+    f6 xor 0f-->f9
+    ....
+    62 xor 84-->e6
+    即,得到的一字节校验码为:e6
+     * @return
+     */
+    public static String xor(String strHex_X,String strHex_Y){
+        //将x、y转成二进制形式
+        String anotherBinary=Integer.toBinaryString(Integer.valueOf(strHex_X,16));
+        String thisBinary=Integer.toBinaryString(Integer.valueOf(strHex_Y,16));
+        String result = "";
+        //判断是否为8位二进制,否则左补零
+        if(anotherBinary.length() != 8){
+            for (int i = anotherBinary.length(); i <8; i++) {
+                anotherBinary = "0"+anotherBinary;
+            }
+        }
+        if(thisBinary.length() != 8){
+            for (int i = thisBinary.length(); i <8; i++) {
+                thisBinary = "0"+thisBinary;
+            }
+        }
+        //异或运算
+        for(int i=0;i<anotherBinary.length();i++){
+            //如果相同位置数相同,则补0,否则补1
+            if(thisBinary.charAt(i)==anotherBinary.charAt(i))
+                result+="0";
+            else{
+                result+="1";
+            }
+        }
+        return Integer.toHexString(Integer.parseInt(result, 2));
+    }
+
+
+    /**
+     *  Convert byte[] to hex string.这里我们可以将byte转换成int
+     * @param src byte[] data
+     * @return hex string
+     */
+    public static String bytes2Str(byte[] src){
+        StringBuilder stringBuilder = new StringBuilder("");
+        if (src == null || src.length <= 0) {
+            return null;
+        }
+        for (int i = 0; i < src.length; i++) {
+            int v = src[i] & 0xFF;
+            String hv = Integer.toHexString(v);
+            if (hv.length() < 2) {
+                stringBuilder.append(0);
+            }
+            stringBuilder.append(hv);
+        }
+        return stringBuilder.toString();
+    }
+    /**
+     * @param msg
+     * @return 接收字节数据并转为16进制字符串
+     */
+    public static String receiveHexToString(byte[] by) {
+        try {
+ 			/*io.netty.buffer.WrappedByteBuf buf = (WrappedByteBuf)msg;
+ 			ByteBufInputStream is = new ByteBufInputStream(buf);
+ 			byte[] by = input2byte(is);*/
+            String str = bytes2Str(by);
+            str = str.toLowerCase();
+            return str;
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            System.out.println("接收字节数据并转为16进制字符串异常");
+        }
+        return null;
+    }
+
+    /**
+     * "7dd",4,'0'==>"07dd"
+     * @param input 需要补位的字符串
+     * @param size 补位后的最终长度
+     * @param symbol 按symol补充 如'0'
+     * @return
+     * N_TimeCheck中用到了
+     */
+    public static String fill(String input, int size, char symbol) {
+        while (input.length() < size) {
+            input = symbol + input;
+        }
+        return input;
+    }
+    public static void main(String args[]) {
+        //String productNo = "3030303032383838";
+        //System.out.println(hexString2String(productNo));
+        //productNo = "04050103000001070302050304";
+        //System.out.println(hexString2String(productNo));
+        String productNo = "129";
+        System.out.println(HexUtil.hexTransfer(fill(productNo,4,'0')));
+    }
+    //用Java语言实现对十六进制字符串异或运算http://blog.csdn.net/acrambler/article/details/45743157
+}

+ 389 - 0
src/main/java/com/zcxk/tcpserver/util/HexUtil.java

@@ -0,0 +1,389 @@
+package com.zcxk.tcpserver.util;
+
+import java.math.BigInteger;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 16进制工具类
+ *
+ * @author wilian.peng
+ * @Description
+ * @date 2018年12月20日 下午3:41:27
+ */
+public class HexUtil {
+    /**
+     * 16进制转10进制
+     *
+     * @param hex
+     * @return
+     */
+    public static BigInteger hex2decimal(String hex) {
+        BigInteger i = new BigInteger(hex, 16);
+        return i;
+    }
+
+
+    /**
+     * 10进制转16进制
+     *
+     * @param i
+     * @return
+     */
+    public static String demical2Hex(int i) {
+        String s = Integer.toHexString(i);
+        return s;
+    }
+
+    /**
+     * 16进制转2进制
+     *
+     * @param hex
+     * @return
+     */
+    public static String hexStringToByte(String hex) {
+        int i = Integer.parseInt(hex, 16);
+        String str2 = Integer.toBinaryString(i);
+        return str2;
+    }
+
+    /**
+     * 2进制转10进制
+     *
+     * @param bytes
+     * @return
+     */
+    public static int ByteToDecimal(String bytes) {
+        BigInteger bi = new BigInteger(bytes, 2); // 转换为BigInteger类型
+        return bi.intValue();
+    }
+
+    /**
+     * 10进制转2进制
+     *
+     * @param n
+     * @return
+     */
+    public static String Demical2Byte(int n) {
+        String result = Integer.toBinaryString(n);
+        return result;
+    }
+
+    /**
+     * 16进制转2进制字符串
+     *
+     * @param hexString
+     * @return
+     */
+    public static String hexString2binaryString(String hexString) {
+        if (hexString == null || hexString.length() % 2 != 0)
+            return null;
+        String bString = "", tmp;
+        for (int i = 0; i < hexString.length(); i++) {
+            tmp = "0000" + Integer.toBinaryString(Integer.parseInt(hexString.substring(i, i + 1), 16));
+            bString += tmp.substring(tmp.length() - 4);
+        }
+        return bString;
+    }
+
+    /**
+     * 2进制转16进制
+     *
+     * @param bString
+     * @return
+     */
+    public static String binaryString2hexString(String bString) {
+        if (bString == null || bString.equals("") || bString.length() % 8 != 0)
+            return null;
+        StringBuffer tmp = new StringBuffer();
+        int iTmp = 0;
+        for (int i = 0; i < bString.length(); i += 4) {
+            iTmp = 0;
+            for (int j = 0; j < 4; j++) {
+                iTmp += Integer.parseInt(bString.substring(i + j, i + j + 1)) << (4 - j - 1);
+            }
+            tmp.append(Integer.toHexString(iTmp));
+        }
+        return tmp.toString();
+    }
+
+    /**
+     * 16进制转换成为string类型字符串
+     *
+     * @param hexStr
+     * @return
+     */
+    public static String hexStr2Str(String hexStr) {
+        String str = "0123456789ABCDEF";
+        char[] hexs = hexStr.toCharArray();
+        byte[] bytes = new byte[hexStr.length() / 2];
+        int n;
+        for (int i = 0; i < bytes.length; i++) {
+            n = str.indexOf(hexs[2 * i]) * 16;
+            n += str.indexOf(hexs[2 * i + 1]);
+            bytes[i] = (byte) (n & 0xff);
+        }
+        return new String(bytes);
+    }
+
+    public static int binary2decimal(String str) {
+        int len = str.length();
+        char ch;
+        int num = 0;
+        for (int i = len - 1; i >= 0; i--) {
+            ch = str.charAt(i);
+            if (ch != '0' && ch != '1') {
+                System.out.println("input wrong!!");
+                break;
+            }
+            if (ch == '1') {
+                num = (int) (num + Math.pow(2, (len - 1) - i));
+            }
+        }
+        return num;
+    }
+
+    /**
+     * 十进制转二进制
+     *
+     * @param decNum
+     * @param digit
+     * @return
+     */
+    public static String decimal2binary(int decNum, int digit) {
+        String binStr = "";
+        for (int i = digit - 1; i >= 0; i--) {
+            binStr += (decNum >> i) & 1;
+        }
+        return binStr;
+    }
+
+    /**
+     * 16进制高低位转换
+     *
+     * @param src
+     * @return
+     */
+    public static String hexTransfer(String src) {
+        if (src.length() % 2 != 0) {
+            System.out.println("16进制数字长度错误!");
+            return "";
+        }
+        // 按照2个字节进行截取
+        List<String> tmp = new ArrayList<String>();
+        for (int i = 0; i < src.length() / 2; i++) {
+            tmp.add(src.substring(i * 2, (i + 1) * 2));
+        }
+        Collections.reverse(tmp);
+        StringBuffer sb = new StringBuffer();
+        for (String str : tmp) {
+            sb.append(str);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 16进制加法
+     *
+     * @param hex1
+     * @param hex2
+     * @return
+     */
+    public static String hexAddHex(String hex1, String hex2) {
+        BigInteger hi1 = hex2decimal(hex1);
+        BigInteger hi2 = hex2decimal(hex2);
+        return demical2Hex(hi1.add(hi2).intValue());
+    }
+
+    /**
+     * 16进制减法
+     *
+     * @param hex1
+     * @param hex2
+     * @return
+     */
+    public static String hexSubHex(String hex1, String hex2) {
+        BigInteger hi1 = hex2decimal(hex1);
+        BigInteger hi2 = hex2decimal(hex2);
+        return demical2Hex(hi1.subtract(hi2).intValue());
+    }
+
+    /**
+     * 16进制减法(去掉补码)
+     *
+     * @param hex1
+     * @param hex2
+     * @return
+     */
+    public static String hexSubtractHex(String hex1, String hex2) {
+        BigInteger hi1 = hex2decimal(hex1);
+        BigInteger hi2 = hex2decimal(hex2);
+        return Integer.toHexString(hi1.subtract(hi2).intValue() & 0xFF).toUpperCase();
+    }
+
+    /**
+     * "7dd",4,'0'==>"07dd"
+     * @param input 需要补位的字符串
+     * @param size 补位后的最终长度
+     * @param symbol 按symol补充 如'0'
+     * @return
+     * N_TimeCheck中用到了
+     */
+    public static String fill(String input, int size, char symbol) {
+        while (input.length() < size) {
+            input = symbol + input;
+        }
+        return input;
+    }
+
+    public static String hexToSingle(String t) {
+        Float value = Float.intBitsToFloat(Integer.valueOf(t, 16));
+        return value.toString();
+    }
+
+    public static byte[] hexStringToBytes(String hexString) {
+        if (hexString == null || hexString.equals("")) {
+            return null;
+        }
+        hexString = hexString.toUpperCase();
+        int length = hexString.length() / 2;
+        char[] hexChars = hexString.toCharArray();
+        byte[] d = new byte[length];
+        for (int i = 0; i < length; i++) {
+            int pos = i * 2;
+            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
+        }
+        return d;
+    }
+
+    /**
+     * 保留小数
+     * @param num
+     * @param length
+     * @return
+     */
+    public static String reserveDecimal(Double num, int length) {
+        String format = "%." + length + "f";
+        return String.format(format, num);
+    }
+
+    /**
+     * 十六进制补位(不足补0)
+     * @param s
+     * @param length
+     * @return
+     */
+    public static String padLeft(String s, int length)
+    {
+        byte[] bs = new byte[length];
+        byte[] ss = s.getBytes();
+        Arrays.fill(bs, (byte) (48 & 0xff));
+        System.arraycopy(ss, 0, bs,length - ss.length, ss.length);
+        return new String(bs);
+    }
+
+    public static String binaryPadLeft(String s)
+    {
+        int number = HexUtil.ByteToDecimal(s);
+        NumberFormat formatter = NumberFormat.getNumberInstance();
+        formatter.setMinimumIntegerDigits(4);
+        formatter.setGroupingUsed(false);
+        return formatter.format(number);
+    }
+
+
+    private static byte charToByte(char c) {
+        return (byte) "0123456789ABCDEF".indexOf(c);
+    }
+
+    public static void printComplementCode(int a)
+    {
+        for (int i = 0; i < 32; i++)
+        {
+            // 0x80000000 是一个首位为1,其余位数为0的整数
+            int t = (a & 0x80000000 >>> i) >>> (31 - i);
+            System.out.print(t);
+        }
+        System.out.println();
+    }
+    // 16进制转浮点小数
+    public static float hexStringToFloat(String hexString) {
+	    Long l = Long.parseLong(hexString, 16);
+	    return Float.intBitsToFloat(l.intValue());
+	}
+    // 电磁水表换算 华旭提供
+    public static int dchs(String hexString) {
+		int temp = 0;
+		for (int i = 0; i < 8; i++) {
+			temp += (Integer.parseInt(hexString.substring(i, i + 1), 16) * Math.pow(16, 7 - i));
+		}
+		return temp;
+	}
+    //	000101100100‬
+    public static void main(String args[]) {
+        // 0x9a 0x99 0x69 0x40
+//		Float value = Float.intBitsToFloat(Integer.valueOf("4069999a", 16));
+        //System.out.println(HexUtil.hexToSingle(hexTransfer("9a996940")));
+        // 41 d0 00 00
+
+        //System.out.println(HexUtil.hex2decimal("ce"));
+//		System.out.println(HexUtil.hexString2binaryString("18"));
+        //System.out.println("000101100100".length());
+        //System.out.println("00000001".substring(7));
+        //System.out.println(HexUtil.binary2decimal("000101100100"));
+		//System.out.println(HexUtil.binaryString2hexString("00001000"));
+        System.out.println(ConvertCode.hexString2String("313838"));
+        System.out.println(HexUtil.hexTransfer(HexUtil.fill(HexUtil.demical2Hex(1),4,'0')));
+//		System.out.println(HexUtil.hexStr2Str("020010"));
+//		String reverse = StringUtils.reverse("12345678");
+//		System.out.println(reverse);
+//		System.out.println(reverse.substring(0, 2+1));
+//		System.out.println(reverse.substring(3, 3+1));
+//		System.out.println(reverse.substring(4, 5+1));
+//		System.out.println(reverse.substring(6, 7+1));
+		
+//		System.out.println(Integer.parseInt(HexUtil.hexSubHex("333333a7", "33333333")));
+//		System.out.println(HexUtil.hex2decimal("cb933333"));
+//		System.out.println(HexUtil.hexTransfer("a7333333")); // 33433333
+//
+//        System.out.println(HexUtil.hexTransfer("937834"));
+//        System.out.println(Integer.parseInt(HexUtil.hexSubHex("347893", "333333")));
+//        System.out.println(HexUtil.hex2decimal("cb933333"));
+//        System.out.println(reserveDecimal(13.12901D,6));
+        //System.out.println(HexUtil.hexTransfer("f1"));
+
+		//System.out.println(Integer.parseInt(HexUtil.hexSubHex("333333a7", "33333333")));
+		//System.out.println(HexUtil.hex2decimal("cb933333"));
+		//System.out.println(HexUtil.hexTransfer("a7333333")); // 33433333
+
+        //System.out.println(HexUtil.hexTransfer("937834"));
+        //System.out.println(Integer.parseInt(HexUtil.hexSubHex("347893", "333333")));
+        //System.out.println(HexUtil.hex2decimal("FD").intValue());
+        //System.out.println(demical2Hex(-1000));
+        //System.out.println(HexUtil.binary2decimal("11101"));
+        //int i = 0xFD;
+        System.out.println(Integer.toBinaryString(~new BigInteger("FD", 16).intValue()).substring(24,32));
+        System.out.println(hexString2binaryString("FD"));
+        System.out.println(-ByteToDecimal(Integer.toBinaryString(~new BigInteger("f", 16).intValue()).substring(24,32))-1);
+
+        BigInteger hi1 = hex2decimal("3333");
+        BigInteger hi2 = hex2decimal("3334");
+        String s = "FF";
+        System.out.println();
+        // T(com.zcxk.smartcity.data.access.util.HexUtil).
+        //   hexString2binaryString(T(com.zcxk.smartcity.data.access.util.HexUtil).
+        //     padLeft(T(com.zcxk.smartcity.data.access.util.HexUtil).
+        //       hexSubHex(T(com.zcxk.smartcity.data.access.util.HexUtil).
+        //         hexTransfer(#value),'3333'),4)).substring(9,10)
+        System.out.println(HexUtil.hexString2binaryString(HexUtil.padLeft(HexUtil.hexSubHex(HexUtil.hexTransfer("9333"),"3333"),4)).substring(9,10));
+        System.out.println(Integer.toHexString(hi1.subtract(hi2).intValue() & 0xFF).toUpperCase());
+        System.out.println(Integer.toHexString(hi1.subtract(hi2).intValue() & 0xFF).toUpperCase());
+    }
+
+
+
+
+}

+ 24 - 0
src/main/java/com/zcxk/tcpserver/util/StringUtil.java

@@ -0,0 +1,24 @@
+package com.zcxk.tcpserver.util;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class StringUtil {
+    public static List<String> split(String str, int max){
+        List<String> list = newArrayList();
+        int z = 0;
+        for(int i=0;i<str.length()/max;i++){
+            String a = str.substring(z,z+max);
+
+            list.add(a);
+            z = z+max;
+        }
+        return list;
+    }
+
+    public static String substringLast(String str,int max){
+        return str.substring(str.length() -max);
+    }
+
+}

+ 11 - 0
src/main/resources/application.properties

@@ -0,0 +1,11 @@
+#netty
+#不能用localhost,否则启动报异常:Unresolved address
+#tcp监听的端口
+tcp-socket.port=5335
+# bossGroup的线程数
+tcp-socket.boss.thread.count=200
+# worker的线程数
+tcp-socket.worker.thread.count=200
+#是否使用长连接
+tcp-socket.so.keepalive=true
+tcp-socket.so.backlog=100

+ 13 - 0
src/test/java/com/zcxk/tcpserver/TcpServerApplicationTests.java

@@ -0,0 +1,13 @@
+package com.zcxk.tcpserver;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class TcpServerApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}