TitanWong před 2 roky
revize
a966a5bc0b
28 změnil soubory, kde provedl 1523 přidání a 0 odebrání
  1. 40 0
      .gitignore
  2. 1 0
      README.md
  3. 56 0
      pom.xml
  4. 27 0
      src/main/java/com/zhili/stationcontrol/MainApp.java
  5. 62 0
      src/main/java/com/zhili/stationcontrol/chargeserver/component/ChargeServer.java
  6. 68 0
      src/main/java/com/zhili/stationcontrol/chargeserver/dto/ChargeMessage.java
  7. 17 0
      src/main/java/com/zhili/stationcontrol/chargeserver/dto/ChargeRate.java
  8. 14 0
      src/main/java/com/zhili/stationcontrol/chargeserver/dto/QueryChargeDto.java
  9. 16 0
      src/main/java/com/zhili/stationcontrol/chargeserver/dto/SetRatesDto.java
  10. 17 0
      src/main/java/com/zhili/stationcontrol/chargeserver/dto/StartChargeDto.java
  11. 14 0
      src/main/java/com/zhili/stationcontrol/chargeserver/dto/StopChargeDto.java
  12. 75 0
      src/main/java/com/zhili/stationcontrol/chargeserver/handler/ChargeMessageDecoder.java
  13. 48 0
      src/main/java/com/zhili/stationcontrol/chargeserver/handler/ChargeMessageEncoder.java
  14. 248 0
      src/main/java/com/zhili/stationcontrol/chargeserver/handler/ChargeMessageHandler.java
  15. 41 0
      src/main/java/com/zhili/stationcontrol/chargeserver/util/ChargeCenter.java
  16. 56 0
      src/main/java/com/zhili/stationcontrol/controller/ChargeController.java
  17. 58 0
      src/main/java/com/zhili/stationcontrol/gatewayserver/component/GatewayServer.java
  18. 14 0
      src/main/java/com/zhili/stationcontrol/gatewayserver/dto/GatewayMessage.java
  19. 72 0
      src/main/java/com/zhili/stationcontrol/gatewayserver/handler/GatewayMessageDecoder.java
  20. 42 0
      src/main/java/com/zhili/stationcontrol/gatewayserver/handler/GatewayMessageEncoder.java
  21. 43 0
      src/main/java/com/zhili/stationcontrol/gatewayserver/handler/GatewayMessageHandler.java
  22. 19 0
      src/main/java/com/zhili/stationcontrol/gatewayserver/util/GatewayCenter.java
  23. 45 0
      src/main/java/com/zhili/stationcontrol/platerecognition/controller/PlateRecognitionController.java
  24. 21 0
      src/main/java/com/zhili/stationcontrol/service/ChargeService.java
  25. 113 0
      src/main/java/com/zhili/stationcontrol/service/impl/ChargeServiceSHImpl.java
  26. 112 0
      src/main/java/com/zhili/stationcontrol/util/BytesUtil.java
  27. 2 0
      src/main/resources/application.yml
  28. 182 0
      src/test/java/SomeTest.java

+ 40 - 0
.gitignore

@@ -0,0 +1,40 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### log ###
+logs/
+-----------------------------------
+©著作权归作者所有:来自51CTO博客作者laolu0837的原创作品,请联系作者获取转载授权,否则将追究法律责任
+IDEA配置.gitignore
+https://blog.51cto.com/u_3664660/5210740

+ 1 - 0
README.md

@@ -0,0 +1 @@
+和充电相关的内容

+ 56 - 0
pom.xml

@@ -0,0 +1,56 @@
+<?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>
+
+    <groupId>org.example</groupId>
+    <artifactId>station-control-elec</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <skipTest>true</skipTest>
+    </properties>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.2.6.RELEASE</version>
+    </parent>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+            <version>4.1.48.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.16.10</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.3</version>
+        </dependency>
+        <dependency>
+            <groupId>com.intelligt.modbus</groupId>
+            <artifactId>jlibmodbus</artifactId>
+            <version>1.2.9.7</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.79</version>
+        </dependency>
+    </dependencies>
+</project>

+ 27 - 0
src/main/java/com/zhili/stationcontrol/MainApp.java

@@ -0,0 +1,27 @@
+package com.zhili.stationcontrol;
+
+import com.zhili.stationcontrol.chargeserver.component.ChargeServer;
+import com.zhili.stationcontrol.gatewayserver.component.GatewayServer;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/18 11:14
+ */
+
+@SpringBootApplication
+@Slf4j
+public class MainApp {
+    public static void main(String[] args) {
+        ApplicationContext ctx = SpringApplication.run(MainApp.class);
+        ChargeServer chargeServer = ctx.getBean(ChargeServer.class);
+        chargeServer.run();
+        GatewayServer gatewayServer = ctx.getBean(GatewayServer.class);
+        gatewayServer.run();
+        log.info("running ... ... ");
+    }
+}

+ 62 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/component/ChargeServer.java

@@ -0,0 +1,62 @@
+package com.zhili.stationcontrol.chargeserver.component;
+
+import com.zhili.stationcontrol.chargeserver.handler.ChargeMessageDecoder;
+import com.zhili.stationcontrol.chargeserver.handler.ChargeMessageEncoder;
+import com.zhili.stationcontrol.chargeserver.handler.ChargeMessageHandler;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.LineBasedFrameDecoder;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/24 10:36
+ */
+@Slf4j
+@Component
+@EnableAsync
+//192.168.1.200:9988
+//255.255.0.0
+//192.168.1.1
+public class ChargeServer {
+    @Async
+    public void run() {
+        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+        EventLoopGroup workerGroup = new NioEventLoopGroup();
+        try {
+            ServerBootstrap server = new ServerBootstrap();
+            server.group(bossGroup, workerGroup)
+                    .channel(NioServerSocketChannel.class)
+                    .childOption(ChannelOption.SO_KEEPALIVE, true)
+                    .childOption(ChannelOption.TCP_NODELAY, true)
+                    .option(ChannelOption.SO_BACKLOG, 1024)
+                    .childHandler(new ChannelInitializer<SocketChannel>() {
+                        @Override
+                        protected void initChannel(SocketChannel socketChannel) throws Exception {
+                            socketChannel.pipeline()
+                                    .addLast(new LineBasedFrameDecoder(1024))
+                                    .addLast(new ChargeMessageDecoder())
+                                    .addLast(new ChargeMessageEncoder())
+                                    .addLast(new ChargeMessageHandler());
+                        }
+                    });
+            ChannelFuture channelFuture = server.bind(9988).sync();
+            channelFuture.channel().closeFuture().sync();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        } finally {
+            bossGroup.shutdownGracefully();
+            workerGroup.shutdownGracefully();
+        }
+    }
+}

+ 68 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/dto/ChargeMessage.java

@@ -0,0 +1,68 @@
+package com.zhili.stationcontrol.chargeserver.dto;
+
+import lombok.Data;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/24 10:45
+ */
+@Data
+public class ChargeMessage {
+    byte version;
+    byte serialNo;
+    int command;
+    byte[] data;
+
+    public static class CommandType {
+        //签到上报服务器
+        public static final int SIGNIN_REPORT = 106;
+        //签到回复
+        public static final int SIGNIN_REPLY = 105;
+        //心跳上报
+        public static final int HEARTBEAT_REPORT = 102;
+        //心跳回复
+        public static final int HEARTBEAT_REPLY = 101;
+        //状态上报
+        public static final int STATE_REPORT = 104;
+        //状态回复
+        public static final int STATE_REPLY = 103;
+        //充电记录上报
+        public static final int RECORD_REPORT = 202;
+        //充电记录回复
+        public static final int RECORD_REPLY =  201;
+        //告警记录上报
+        public static final int ALARM_REPORT = 108;
+        //告警记录回复
+        public static final int ALARM_REPLY = 107;
+        //充电命令
+        public static final int CHARGE_COMMAND = 7;
+        //充电回复
+        public static final int CHARGE_REPLY = 8;
+        //控制命令
+        public static final int CONTROL_COMMAND = 5;
+        //控制回复
+        public static final int CONTROL_REPLY = 6;
+        //查询充电记录
+        public static final int QUERY_CHARGE_COMMAND = 401;
+        //充电记录查询回复
+        public static final int QUERY_CHARGE_REPLY = 402;
+        //下发费率设置命令
+        public static final int SET_RATE_COMMAND = 1103;
+        //费率设置回复
+        public static final int SET_RATE_REPLY = 1104;
+        //上报充电启动完成
+        public static final int CHARGE_START_REPORT = 110;
+        //回复启动完成
+        public static final int CHARGE_START_REPLY = 109;
+        //BMS信息上报
+        public static final int BMS_REPORT = 302;
+        //BMS信息回复
+        public static final int BMS_REPLY = 301;
+    };
+    public static class AlarmType {
+
+    }
+
+
+}

+ 17 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/dto/ChargeRate.java

@@ -0,0 +1,17 @@
+package com.zhili.stationcontrol.chargeserver.dto;
+
+import lombok.Data;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/27 23:33
+ */
+@Data
+public class ChargeRate {
+    int startHour;
+    int startMinute;
+    int endHour;
+    int endMinute;
+    float rate;
+}

+ 14 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/dto/QueryChargeDto.java

@@ -0,0 +1,14 @@
+package com.zhili.stationcontrol.chargeserver.dto;
+
+import lombok.Data;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/28 11:18
+ */
+@Data
+public class QueryChargeDto {
+    String deviceNo;
+    int backIndex;
+}

+ 16 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/dto/SetRatesDto.java

@@ -0,0 +1,16 @@
+package com.zhili.stationcontrol.chargeserver.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/28 11:37
+ */
+@Data
+public class SetRatesDto {
+    String deviceNo;
+    List<ChargeRate> rates;
+}

+ 17 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/dto/StartChargeDto.java

@@ -0,0 +1,17 @@
+package com.zhili.stationcontrol.chargeserver.dto;
+
+import lombok.Data;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/27 16:53
+ */
+@Data
+public class StartChargeDto {
+    String deviceNo;
+    Integer gunNo;
+    Integer stopPwd;
+    String userId;
+    String chrgId;
+}

+ 14 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/dto/StopChargeDto.java

@@ -0,0 +1,14 @@
+package com.zhili.stationcontrol.chargeserver.dto;
+
+import lombok.Data;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/27 21:17
+ */
+@Data
+public class StopChargeDto {
+    String deviceNo;
+    Integer gunNo;
+}

+ 75 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/handler/ChargeMessageDecoder.java

@@ -0,0 +1,75 @@
+package com.zhili.stationcontrol.chargeserver.handler;
+
+import com.zhili.stationcontrol.chargeserver.dto.ChargeMessage;
+import com.zhili.stationcontrol.util.BytesUtil;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.ArrayUtils;
+
+import java.util.List;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/24 10:41
+ */
+@Slf4j
+public class ChargeMessageDecoder extends ByteToMessageDecoder {
+    public static final byte[] START = {(byte) 0xaa, (byte) 0xf5};
+    public static final int baseLen = 9;
+
+    @Override
+    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
+        while (byteBuf.readableBytes() >= START.length) {
+//            寻找头部
+            byteBuf.markReaderIndex();
+            byte[] start = new byte[START.length];
+            byteBuf.readBytes(start);
+            if (start[0] == START[0] && start[1] == START[1]) {
+                //如果读到头部,继续解析
+                if (byteBuf.readableBytes() >= 2) {
+                    //解析长度
+                    byte[] lenBytes = new byte[2];
+                    byteBuf.readBytes(lenBytes);
+                    int len = BytesUtil.toIntWithLowerFirst(lenBytes);
+                    if (byteBuf.readableBytes() >= len - 4) {
+                        //解析剩下的部分
+                        byte[] last = new byte[len - 4];
+                        byteBuf.readBytes(last);
+                        byte[] checkSumObj = ArrayUtils.subarray(last, 2, last.length - 1);
+                        byte b = BytesUtil.sumCheck(checkSumObj);
+                        if (b == last[last.length - 1]) {
+                            //校验和正确,解析到一个正确的包
+                            ChargeMessage chargeMessage = new ChargeMessage();
+                            chargeMessage.setVersion(last[0]);
+                            chargeMessage.setSerialNo(last[1]);
+                            byte[] commandBytes = ArrayUtils.subarray(last, 2, 4);
+                            int i = BytesUtil.toIntWithLowerFirst(commandBytes);
+                            chargeMessage.setCommand(i);
+                            byte[] dataBytes = ArrayUtils.subarray(last, 4, last.length - 1);
+                            chargeMessage.setData(dataBytes);
+                            list.add(chargeMessage);
+                        } else {
+                            //包不正确,整体丢弃
+                        }
+                    } else {
+                        //读不出一个整体的包,等待数据再读
+                        byteBuf.resetReaderIndex();
+                        return;
+                    }
+                } else {
+                    //长度字段不够读,下次再读;
+                    byteBuf.resetReaderIndex();
+                    return;
+                }
+            } else {
+                //没有读到头部,继续往后读
+                byteBuf.resetReaderIndex();
+                byteBuf.skipBytes(1);
+//                byteBuf.readByte();
+            }
+        }
+    }
+}

+ 48 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/handler/ChargeMessageEncoder.java

@@ -0,0 +1,48 @@
+package com.zhili.stationcontrol.chargeserver.handler;
+
+import com.zhili.stationcontrol.chargeserver.dto.ChargeMessage;
+import com.zhili.stationcontrol.util.BytesUtil;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/24 10:42
+ */
+public class ChargeMessageEncoder extends MessageToByteEncoder<ChargeMessage> {
+    public static final byte[] START = {(byte) 0xaa, (byte) 0xf5};
+    public static final int baseLen = 9;
+
+    @Override
+    protected void encode(ChannelHandlerContext channelHandlerContext, ChargeMessage message, ByteBuf byteBuf) throws Exception {
+        //写入起始字节(2)
+        byteBuf.writeBytes(START);
+        //写入长度(2)
+        int len = baseLen + message.getData().length;
+        byte[] lenBytes = BytesUtil.fromIntWithLowerFirst(len, 2);
+        byteBuf.writeBytes(lenBytes);
+        //写入版本(1)
+        byteBuf.writeByte(message.getVersion());
+        //写入序列号域(1)
+        byteBuf.writeByte(message.getSerialNo());
+        //写入命令(2)
+        byte[] commandBytes = BytesUtil.fromIntWithLowerFirst(message.getCommand(), 2);
+        byteBuf.writeBytes(commandBytes);
+        //写入数据(n)
+        byteBuf.writeBytes(message.getData());
+        //写入校验和(1)
+        byte[] checkSumObj = new byte[2 + message.getData().length];
+        byte[] command = BytesUtil.fromIntWithLowerFirst(message.getCommand(), 2);
+        byte[] data = message.getData();
+        for (int i = 0; i < command.length; i++) {
+            checkSumObj[i] = command[i];
+        }
+        for (int i = 0; i < data.length; i++) {
+            checkSumObj[command.length + i] = data[i];
+        }
+        byte b = BytesUtil.sumCheck(checkSumObj);
+        byteBuf.writeByte(b);
+    }
+}

+ 248 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/handler/ChargeMessageHandler.java

@@ -0,0 +1,248 @@
+package com.zhili.stationcontrol.chargeserver.handler;
+
+import com.zhili.stationcontrol.chargeserver.dto.ChargeMessage;
+import com.zhili.stationcontrol.chargeserver.util.ChargeCenter;
+import com.zhili.stationcontrol.util.BytesUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.AttributeKey;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.ArrayUtils;
+
+import java.util.Random;
+
+import static com.zhili.stationcontrol.chargeserver.dto.ChargeMessage.CommandType;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/24 11:02
+ */
+@Slf4j
+public class ChargeMessageHandler extends ChannelInboundHandlerAdapter {
+    static Random r = new Random();
+
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        log.info("channel connected: ", ctx.channel().remoteAddress());
+        ChargeCenter.add(ctx.channel());
+        super.channelActive(ctx);
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        if (msg instanceof ChargeMessage) {
+            ChargeMessage chrgMsg = (ChargeMessage) msg;
+            log.info("receive: " + chrgMsg);
+            ctx.writeAndFlush(chrgMsg);
+            byte[] data;
+            byte[] deviceNoBytes;
+            String deviceNo;
+            byte[] replyData;
+            byte[] gunNoBytes;
+            int gunNo;
+            byte[] startTimeBytes;
+            byte[] endTimeBytes;
+            byte[] secBytes;
+            byte[] startSocBytes;
+            byte[] endSocBytes;
+            byte[] chargeIdBytes;
+            String startTime;
+            String endTime;
+            int sec;
+            //流水号
+            String chgId;
+            int startSoc;
+            int endSoc;
+            switch (chrgMsg.getCommand()) {
+                case CommandType.SIGNIN_REPORT:
+                    //签到信息,写入signin
+                    data = chrgMsg.getData();
+                    byte version = chrgMsg.getVersion();
+                    log.info("version:" + version);
+                    deviceNoBytes = ArrayUtils.subarray(data, 4, 36);
+                    deviceNo = BytesUtil.parseString(deviceNoBytes);
+                    log.info("parse deviceNo: " + deviceNo);
+                    ChargeCenter.mark(ctx.channel(), "signIn", deviceNo);
+                    ChargeMessage signInReplyMessage = new ChargeMessage();
+                    signInReplyMessage.setVersion(chrgMsg.getVersion());
+                    signInReplyMessage.setSerialNo(chrgMsg.getSerialNo());
+                    signInReplyMessage.setCommand(CommandType.SIGNIN_REPLY);
+                    replyData = new byte[151];
+                    int ri = randInt();
+                    byte[] ribs = BytesUtil.fromIntWithLowerFirst(ri, 4);
+                    BytesUtil.setSubBytes(replyData, 4, ribs);
+//                    不启用cmd1202
+                    BytesUtil.setSubBytes(replyData, 8, BytesUtil.fromIntWithLowerFirst(0, 1));
+//                    启用不加密验证
+                    BytesUtil.setSubBytes(replyData, 9, BytesUtil.fromIntWithLowerFirst(0, 1));
+                    BytesUtil.setSubBytes(replyData, 143, BytesUtil.composeNowTimeBytes());
+                    signInReplyMessage.setData(replyData);
+                    ctx.writeAndFlush(signInReplyMessage);
+                    log.info("signIn replyed: " + signInReplyMessage);
+                    break;
+                case CommandType.HEARTBEAT_REPORT:
+//                    //心跳信息,如果和签到信息一致,写入heartBeatNo
+                    data = chrgMsg.getData();
+                    deviceNoBytes = ArrayUtils.subarray(data, 4, 36);
+                    deviceNo = BytesUtil.parseString(deviceNoBytes);
+                    byte[] heatBeatNo = ArrayUtils.subarray(data, 36, 38);
+                    Object signIn = ctx.channel().attr(AttributeKey.valueOf("signIn")).get();
+                    if (signIn == null) {
+                        log.info("not sigin:" + deviceNo);
+                        ctx.channel().close();
+                        break;
+                    }
+                    if (!signIn.equals(deviceNo)) {
+                        log.info("heartbeat not match signin");
+                        ctx.channel().close();
+                        break;
+                    }
+                    byte[] stateBytes = ArrayUtils.subarray(data, 38, 54);
+                    int state = BytesUtil.toIntWithLowerFirst(stateBytes);
+                    log.info("各个枪状态:" + state);
+                    ChargeMessage heartBeatReplyMessage = new ChargeMessage();
+                    heartBeatReplyMessage.setVersion(chrgMsg.getVersion());
+                    heartBeatReplyMessage.setSerialNo(chrgMsg.getSerialNo());
+                    heartBeatReplyMessage.setCommand(CommandType.HEARTBEAT_REPLY);
+                    replyData = new byte[6];
+                    BytesUtil.setSubBytes(replyData, 4, heatBeatNo);
+                    heartBeatReplyMessage.setData(replyData);
+                    ctx.writeAndFlush(heartBeatReplyMessage);
+                    log.info("heartbeat replyed: " + heartBeatReplyMessage);
+                    break;
+                case CommandType.STATE_REPORT:
+                    data = chrgMsg.getData();
+                    byte[] cntBytes = ArrayUtils.subarray(data, 36, 37);
+                    int cnt = BytesUtil.toIntWithLowerFirst(cntBytes);
+                    gunNoBytes = ArrayUtils.subarray(data, 37, 38);
+                    gunNo = BytesUtil.toIntWithLowerFirst(gunNoBytes);
+                    log.info("状态上报: 充电枪数量(" + cnt + "), 充电口号:(" + gunNo + ")");
+                    ChargeMessage stateReplyMessage = new ChargeMessage();
+                    stateReplyMessage.setVersion(chrgMsg.getVersion());
+                    stateReplyMessage.setSerialNo(chrgMsg.getSerialNo());
+                    stateReplyMessage.setCommand(CommandType.STATE_REPLY);
+                    replyData = new byte[6];
+                    BytesUtil.setSubBytes(replyData, 4, gunNoBytes);
+                    replyData[5] = 0;
+                    stateReplyMessage.setData(replyData);
+                    ctx.writeAndFlush(stateReplyMessage);
+                    log.info("report replyed:" + stateReplyMessage);
+                    break;
+                case CommandType.RECORD_REPORT:
+                    data = chrgMsg.getData();
+                    gunNoBytes = ArrayUtils.subarray(data, 37, 38);
+                    gunNo = BytesUtil.toIntWithLowerFirst(gunNoBytes);
+                    startTimeBytes = ArrayUtils.subarray(data, 70, 78);
+                    endTimeBytes = ArrayUtils.subarray(data, 78, 86);
+                    secBytes = ArrayUtils.subarray(data, 86, 90);
+                    startSocBytes = ArrayUtils.subarray(data, 90, 91);
+                    endSocBytes = ArrayUtils.subarray(data, 91, 92);
+                    chargeIdBytes = ArrayUtils.subarray(data, 256, 288);
+                    startTime = BytesUtil.composeTimeString(startTimeBytes);
+                    endTime = BytesUtil.composeTimeString(endTimeBytes);
+                    sec = BytesUtil.toIntWithLowerFirst(secBytes);
+                    chgId = BytesUtil.parseString(chargeIdBytes);
+                    startSoc = BytesUtil.toIntWithLowerFirst(startSocBytes);
+                    endSoc = BytesUtil.toIntWithLowerFirst(endSocBytes);
+                    int power = BytesUtil.toIntWithLowerFirst(ArrayUtils.subarray(data, 96, 100));
+                    int fee = BytesUtil.toIntWithLowerFirst(ArrayUtils.subarray(data, 108, 112));
+                    log.info("充电记录上报(" + chgId + "): 充电口号:(" + gunNo + ")" + " " + startTime + "(" + startSoc + ") -> " + endTime + "(" + endSoc + ") (" + sec + " secs)");
+                    //电量和单位都是0.01
+                    log.info("power:" + power + " fee:" + fee);
+                    ChargeMessage recordReplyMessage = new ChargeMessage();
+                    recordReplyMessage.setVersion(chrgMsg.getVersion());
+                    recordReplyMessage.setSerialNo(chrgMsg.getSerialNo());
+                    recordReplyMessage.setCommand(CommandType.RECORD_REPLY);
+                    replyData = new byte[66];
+                    BytesUtil.setSubBytes(replyData, 4, gunNoBytes);
+                    byte[] innerIndex = ArrayUtils.subarray(data, 112, 116);
+                    BytesUtil.setSubBytes(replyData, 37, innerIndex);
+                    replyData[41] = 0x01;
+                    recordReplyMessage.setData(replyData);
+//                    ctx.writeAndFlush(recordReplyMessage);
+                    break;
+                case CommandType.ALARM_REPORT:
+                    data = chrgMsg.getData();
+                    byte[] alarmBytes = ArrayUtils.subarray(data, 36, 68);
+                    int alarms = BytesUtil.toIntWithLowerFirst(alarmBytes);
+                    log.info("收到告警:" + Integer.toBinaryString(alarms));
+                    break;
+                case CommandType.CHARGE_REPLY:
+                    data = chrgMsg.getData();
+                    gunNo = Byte.toUnsignedInt(data[36]);
+                    byte[] chgResBytes = ArrayUtils.subarray(data, 37, 41);
+                    int chgRes = BytesUtil.toIntWithLowerFirst(chgResBytes);
+                    log.info("---收到" + gunNo + "号枪充电启动应答(" + (chgRes == 0 ? "成功" : "失败") + "):" + chgRes);
+                    break;
+                case CommandType.CONTROL_REPLY:
+                    data = chrgMsg.getData();
+                    gunNo = Byte.toUnsignedInt(data[36]);
+                    int addressSign = BytesUtil.toIntWithLowerFirst(ArrayUtils.subarray(data, 37, 41));
+                    if (addressSign == 2) {
+                        //停止充电命令
+                        int stopRes = Byte.toUnsignedInt(data[42]);
+                        log.info("---收到" + gunNo + "号枪停止充电应答(" + (stopRes == 0 ? "成功" : "失败") + "):" + stopRes);
+                    }
+                    break;
+                case CommandType.QUERY_CHARGE_REPLY:
+                    data = chrgMsg.getData();
+                    gunNo = Byte.toUnsignedInt(data[37]);
+                    startTimeBytes = ArrayUtils.subarray(data, 70, 78);
+                    startTime = BytesUtil.composeTimeString(startTimeBytes);
+                    endTimeBytes = ArrayUtils.subarray(data, 78, 86);
+                    endTime = BytesUtil.composeTimeString(endTimeBytes);
+                    secBytes = ArrayUtils.subarray(data, 86, 90);
+                    sec = BytesUtil.toIntWithLowerFirst(secBytes);
+                    startSocBytes = ArrayUtils.subarray(data, 90, 91);
+                    startSoc = BytesUtil.toIntWithLowerFirst(startSocBytes);
+                    endSocBytes = ArrayUtils.subarray(data, 91, 92);
+                    endSoc = BytesUtil.toIntWithLowerFirst(endSocBytes);
+                    chargeIdBytes = ArrayUtils.subarray(data, 256, 288);
+                    chgId = BytesUtil.parseString(chargeIdBytes);
+                    log.info("记录查询回复上报(" + chgId + "): 充电口号:(" + gunNo + ")" + " " + startTime + "(" + startSoc + ") -> " + endTime + "(" + endSoc + ") (" + sec + " secs)");
+                    break;
+                case CommandType.SET_RATE_REPLY:
+                    data = chrgMsg.getData();
+                    int res = Byte.toUnsignedInt(data[0]);
+                    log.info("--费率设置" + (res == 0 ? "成功" : "失败"));
+                    break;
+                case CommandType.CHARGE_START_REPORT:
+                    data = chrgMsg.getData();
+                    int chrgRes = BytesUtil.toIntWithLowerFirst(ArrayUtils.subarray(data, 37, 41));
+                    chargeIdBytes = ArrayUtils.subarray(data, data.length - 32, data.length);
+                    chgId = BytesUtil.parseString(chargeIdBytes);
+                    log.info("充电命令(chargeId = " + chgId + ")启动" + (chrgRes == 0 ? "成功" : "失败"));
+                    ChargeMessage startReplyMsg = new ChargeMessage();
+                    startReplyMsg.setVersion(chrgMsg.getVersion());
+                    startReplyMsg.setSerialNo(chrgMsg.getSerialNo());
+                    startReplyMsg.setCommand(CommandType.CHARGE_START_REPLY);
+                    startReplyMsg.setData(new byte[4]);
+                    ctx.writeAndFlush(startReplyMsg);
+                    break;
+                case CommandType.BMS_REPORT:
+                    data = chrgMsg.getData();
+                    gunNoBytes = ArrayUtils.subarray(data, 6, 8);
+                    gunNo = BytesUtil.toIntWithLowerFirst(gunNoBytes);
+                    byte[] workStateBytes = ArrayUtils.subarray(data, 40, 41);
+                    int workState = BytesUtil.toIntWithLowerFirst(workStateBytes);
+                    byte[] vConnStateBytes = ArrayUtils.subarray(data, 41, 42);
+                    int vConnState = BytesUtil.toIntWithLowerFirst(vConnStateBytes);
+                    log.info("BMS上报信息(枪号:" + gunNo + "):工作状态(" + workState + "), 车连接状态:(" + vConnState + ")");
+                    ChargeMessage bmsReplyMsg = new ChargeMessage();
+                    bmsReplyMsg.setVersion(chrgMsg.getVersion());
+                    bmsReplyMsg.setSerialNo(chrgMsg.getSerialNo());
+                    bmsReplyMsg.setCommand(CommandType.BMS_REPLY);
+                    bmsReplyMsg.setData(new byte[4]);
+                    ctx.writeAndFlush(bmsReplyMsg);
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    static int randInt() {
+        return r.nextInt();
+    }
+}

+ 41 - 0
src/main/java/com/zhili/stationcontrol/chargeserver/util/ChargeCenter.java

@@ -0,0 +1,41 @@
+package com.zhili.stationcontrol.chargeserver.util;
+
+import com.zhili.stationcontrol.chargeserver.dto.ChargeMessage;
+import io.netty.channel.Channel;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.ChannelMatcher;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.util.AttributeKey;
+import io.netty.util.concurrent.GlobalEventExecutor;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/24 16:41
+ */
+public class ChargeCenter {
+    static final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+    public static void add(Channel ch) {
+        channelGroup.add(ch);
+    }
+
+    public static void mark(Channel ch, String key, Object value) {
+        Channel channel = channelGroup.find(ch.id());
+        channel.attr(AttributeKey.valueOf(key)).set(value);
+    }
+
+    public static void send(String key, Object value, ChargeMessage msg) {
+        channelGroup.writeAndFlush(msg, new ChannelMatcher() {
+            @Override
+            public boolean matches(Channel channel) {
+                try {
+                    Object deviceNo = channel.attr(AttributeKey.valueOf(key)).get();
+                    return deviceNo.equals(value);
+                } catch (Exception e) {
+                    return false;
+                }
+            }
+        });
+    }
+}

+ 56 - 0
src/main/java/com/zhili/stationcontrol/controller/ChargeController.java

@@ -0,0 +1,56 @@
+package com.zhili.stationcontrol.controller;
+
+import com.zhili.stationcontrol.chargeserver.dto.QueryChargeDto;
+import com.zhili.stationcontrol.chargeserver.dto.SetRatesDto;
+import com.zhili.stationcontrol.chargeserver.dto.StartChargeDto;
+import com.zhili.stationcontrol.chargeserver.dto.StopChargeDto;
+import com.zhili.stationcontrol.service.ChargeService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/27 10:51
+ */
+@RestController
+@Slf4j
+public class ChargeController {
+    @Autowired
+    ChargeService chargeService;
+
+    @GetMapping(value = "/test")
+    public Object test() {
+        return "123";
+    }
+
+    @PostMapping(value = "/startChargeImmediatelyUtilFull")
+//    "66792204020001"
+    public Object startChargeImmediatelyUtilFull(@RequestBody StartChargeDto startDto) {
+        log.info("StartChargeDto:" + startDto);
+        return chargeService.startImmediatelyUtilFull(startDto.getDeviceNo(), startDto.getGunNo(), startDto.getStopPwd(), startDto.getUserId(), startDto.getChrgId());
+    }
+
+    @PostMapping(value = "/stopChargeImmediately")
+    public Object stopChargeImmediately(@RequestBody StopChargeDto stopDto) {
+        log.info("StopChargeDto:" + stopDto);
+        return chargeService.stopImmediately(stopDto.getDeviceNo(), stopDto.getGunNo());
+    }
+
+    @PostMapping(value = "queryChargeHistory")
+    public Object queryChargeHistory(@RequestBody QueryChargeDto queryDto) {
+        log.info("QueryChargeDto:"+queryDto);
+        return chargeService.queryChargeHistory(queryDto.getDeviceNo(), queryDto.getBackIndex());
+    }
+
+    @PostMapping(value = "setChargeRate")
+    public Object setChargeRate(@RequestBody SetRatesDto ratesDto){
+        log.info("setChargeRate:"+ratesDto);
+//        return 123;
+        return chargeService.setChargeRate(ratesDto.getDeviceNo(), ratesDto.getRates());
+    }
+}

+ 58 - 0
src/main/java/com/zhili/stationcontrol/gatewayserver/component/GatewayServer.java

@@ -0,0 +1,58 @@
+package com.zhili.stationcontrol.gatewayserver.component;
+
+import com.zhili.stationcontrol.gatewayserver.handler.GatewayMessageDecoder;
+import com.zhili.stationcontrol.gatewayserver.handler.GatewayMessageEncoder;
+import com.zhili.stationcontrol.gatewayserver.handler.GatewayMessageHandler;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/28 15:22
+ */
+@EnableAsync
+@Component
+@Slf4j
+//192.168.3.177:9989
+public class GatewayServer {
+    @Async
+    public void run() {
+        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+        EventLoopGroup workerGroup = new NioEventLoopGroup();
+        try {
+            ServerBootstrap server = new ServerBootstrap();
+            server.group(bossGroup, workerGroup)
+                    .channel(NioServerSocketChannel.class)
+                    .childOption(ChannelOption.SO_KEEPALIVE, true)
+                    .childOption(ChannelOption.TCP_NODELAY, true)
+                    .option(ChannelOption.SO_BACKLOG, 1024)
+                    .childHandler(new ChannelInitializer<SocketChannel>() {
+                        @Override
+                        protected void initChannel(SocketChannel socketChannel) throws Exception {
+                            socketChannel.pipeline()
+                                    .addLast(new GatewayMessageDecoder())
+                                    .addLast(new GatewayMessageEncoder())
+                                    .addLast(new GatewayMessageHandler());
+                        }
+                    });
+            ChannelFuture channelFuture = server.bind(9989).sync();
+            channelFuture.channel().closeFuture().sync();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        } finally {
+            bossGroup.shutdownGracefully();
+            workerGroup.shutdownGracefully();
+        }
+    }
+}

+ 14 - 0
src/main/java/com/zhili/stationcontrol/gatewayserver/dto/GatewayMessage.java

@@ -0,0 +1,14 @@
+package com.zhili.stationcontrol.gatewayserver.dto;
+
+import lombok.Data;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/28 15:30
+ */
+@Data
+public class GatewayMessage {
+    int id;
+    byte[] data;
+}

+ 72 - 0
src/main/java/com/zhili/stationcontrol/gatewayserver/handler/GatewayMessageDecoder.java

@@ -0,0 +1,72 @@
+package com.zhili.stationcontrol.gatewayserver.handler;
+
+import com.zhili.stationcontrol.gatewayserver.dto.GatewayMessage;
+import com.zhili.stationcontrol.util.BytesUtil;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/28 15:28
+ */
+@Slf4j
+public class GatewayMessageDecoder extends ByteToMessageDecoder {
+//    public static final byte[] START = {(byte) 0xaa, (byte) 0x7e};
+    public static final byte[] START = {(byte) 0xaa, (byte) 0xf5};
+
+    @Override
+    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
+        while (byteBuf.readableBytes() >= START.length) {
+            byteBuf.markReaderIndex();
+            byte[] start = new byte[2];
+            byteBuf.readBytes(start);
+            if (start[0] == START[0] && start[1] == START[1]) {
+                //读到头部,读长度
+                log.info("读到头部");
+                if (byteBuf.readableBytes() >= 2) {
+                    byte[] lenBytes = new byte[2];
+                    byteBuf.readBytes(lenBytes);
+                    int len = BytesUtil.toIntWithLowerFirst(lenBytes);
+                    log.info("读到长度:" + len);
+                    //读剩余的数据
+                    if (byteBuf.readableBytes() >= len - 4) {
+                        byte[] last = new byte[len - 4];
+                        byteBuf.readBytes(last);
+                        int size = last.length / 13;
+                        log.info("共有底座消息: "+size+" 个");
+                        List<GatewayMessage> messages = new ArrayList();
+                        for (int i = 0; i < size; i++) {
+                            GatewayMessage msg = new GatewayMessage();
+                            byte[] msgId = ArrayUtils.subarray(last, i * 13 + 1, i * 13 + 5);
+                            msg.setId(BytesUtil.toIntWithLowerFirst(msgId));
+                            byte[] data = ArrayUtils.subarray(last, i * 13 + 5, i * 13 + 13);
+                            msg.setData(data);
+                            messages.add(msg);
+                        }
+                        list.add(messages);
+                    } else {
+                        //读不出一个整体的包,等待数据再读
+                        byteBuf.resetReaderIndex();
+                        return;
+                    }
+                } else {
+                    //长度字段不够读,下次再读;
+                    byteBuf.resetReaderIndex();
+                    return;
+                }
+            } else {
+                //没有读到头部,继续往后读
+                byteBuf.resetReaderIndex();
+                byteBuf.skipBytes(1);
+//                byteBuf.readByte();
+            }
+        }
+    }
+}

+ 42 - 0
src/main/java/com/zhili/stationcontrol/gatewayserver/handler/GatewayMessageEncoder.java

@@ -0,0 +1,42 @@
+package com.zhili.stationcontrol.gatewayserver.handler;
+
+import com.zhili.stationcontrol.gatewayserver.dto.GatewayMessage;
+import com.zhili.stationcontrol.util.BytesUtil;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+import org.apache.commons.lang.ArrayUtils;
+
+import java.util.List;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/28 15:28
+ */
+public class GatewayMessageEncoder extends MessageToByteEncoder<List<GatewayMessage>> {
+
+    public static final byte[] START = {(byte) 0xaa, (byte) 0x7e};
+
+    @Override
+    protected void encode(ChannelHandlerContext channelHandlerContext, List<GatewayMessage> messages, ByteBuf byteBuf) throws Exception {
+        //写入头部
+        byteBuf.writeBytes(START);
+        //写入长度
+        int dataLen = messages.size() * 13;
+        int len = dataLen + 4;
+        byteBuf.writeBytes(BytesUtil.fromIntWithLowerFirst(len, 2));
+        //写入数据
+        for (int i = 0; i < messages.size(); i++) {
+            GatewayMessage msg = messages.get(i);
+            int id = msg.getId();
+            byte[] data = msg.getData();
+            //写 填充位
+            byteBuf.writeByte((byte) 0x88);
+            //写 id
+            byteBuf.writeBytes(BytesUtil.fromIntWithLowerFirst(id, 4));
+            //写 data
+            byteBuf.writeBytes(ArrayUtils.subarray(data, 0, 8));
+        }
+    }
+}

+ 43 - 0
src/main/java/com/zhili/stationcontrol/gatewayserver/handler/GatewayMessageHandler.java

@@ -0,0 +1,43 @@
+package com.zhili.stationcontrol.gatewayserver.handler;
+
+import com.zhili.stationcontrol.gatewayserver.dto.GatewayMessage;
+import com.zhili.stationcontrol.gatewayserver.util.GatewayCenter;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/28 15:28
+ */
+
+@Slf4j
+public class GatewayMessageHandler extends ChannelInboundHandlerAdapter {
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        log.info("channel connected: "+ctx.channel().remoteAddress().toString());
+        GatewayCenter.add(ctx.channel());
+        super.channelActive(ctx);
+    }
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        if (msg instanceof List) {
+            List<GatewayMessage> messages = (List<GatewayMessage>) msg;
+            for (int i = 0; i < messages.size(); i++) {
+                GatewayMessage m = messages.get(i);
+                log.info("接收到网关消息:" + m);
+            }
+            ctx.writeAndFlush(messages);
+        }
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+        log.info("连接断开:"+ctx.channel().remoteAddress().toString());
+        super.channelInactive(ctx);
+    }
+}

+ 19 - 0
src/main/java/com/zhili/stationcontrol/gatewayserver/util/GatewayCenter.java

@@ -0,0 +1,19 @@
+package com.zhili.stationcontrol.gatewayserver.util;
+
+import io.netty.channel.Channel;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.util.concurrent.GlobalEventExecutor;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/28 16:52
+ */
+public class GatewayCenter {
+    static final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+    public static void add(Channel ch) {
+        channelGroup.add(ch);
+    }
+}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 45 - 0
src/main/java/com/zhili/stationcontrol/platerecognition/controller/PlateRecognitionController.java


+ 21 - 0
src/main/java/com/zhili/stationcontrol/service/ChargeService.java

@@ -0,0 +1,21 @@
+package com.zhili.stationcontrol.service;
+
+import com.zhili.stationcontrol.chargeserver.dto.ChargeRate;
+
+import java.util.List;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/27 10:58
+ */
+public interface ChargeService {
+    //立即充电,充满为止, 抢号,停止密码,用户id,充电流水号
+    int startImmediatelyUtilFull(String deviceNo, int gunNo, int stopPwd, String userId, String chrgId);
+    //立即停止充电
+    int stopImmediately(String deviceNo, int gunNo);
+    //查询充电桩历史记录, backIndex.往回查第几次
+    int queryChargeHistory(String deviceNo, int backIndex);
+
+    int setChargeRate(String deviceNo, List<ChargeRate> rates);
+}

+ 113 - 0
src/main/java/com/zhili/stationcontrol/service/impl/ChargeServiceSHImpl.java

@@ -0,0 +1,113 @@
+package com.zhili.stationcontrol.service.impl;
+
+import com.zhili.stationcontrol.chargeserver.dto.ChargeMessage;
+import com.zhili.stationcontrol.chargeserver.dto.ChargeMessage.CommandType;
+import com.zhili.stationcontrol.chargeserver.dto.ChargeRate;
+import com.zhili.stationcontrol.chargeserver.util.ChargeCenter;
+import com.zhili.stationcontrol.service.ChargeService;
+import com.zhili.stationcontrol.util.BytesUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.ArrayUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Random;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/27 13:40
+ */
+@Service
+@Slf4j
+public class ChargeServiceSHImpl implements ChargeService {
+    static Random r = new Random();
+    static byte version = (byte) 0x02;
+
+    @Override
+    public int startImmediatelyUtilFull(String deviceNo, int gunNo, int stopPwd, String userId, String chrgId) {
+        ChargeMessage m = new ChargeMessage();
+        m.setVersion(version);
+        m.setSerialNo(randomSerialNo());
+        m.setCommand(CommandType.CHARGE_COMMAND);
+        byte[] data = new byte[100];
+        //设置枪号
+        data[4] = (byte) gunNo;
+        //设置即时充电
+        BytesUtil.setSubBytes(data, 5, BytesUtil.fromIntWithLowerFirst(0, 4));
+        //停止密码
+        BytesUtil.setSubBytes(data, 9, BytesUtil.fromIntWithLowerFirst(stopPwd, 4));
+        //充电策略(0,充满为止)
+        BytesUtil.setSubBytes(data, 13, BytesUtil.fromIntWithLowerFirst(0, 4));
+        //用户卡号
+        byte[] userIdBytes = BytesUtil.fromString(userId, 32, (byte) 0);
+        BytesUtil.setSubBytes(data, 30, userIdBytes);
+        //充电流水号
+        byte[] chrgIdBytes = BytesUtil.fromString(chrgId, 32, (byte) 0);
+        BytesUtil.setSubBytes(data, 67, chrgIdBytes);
+        m.setData(data);
+        ChargeCenter.send("signIn", deviceNo, m);
+        return 0;
+    }
+
+    @Override
+    public int stopImmediately(String deviceNo, int gunNo) {
+        ChargeMessage m = new ChargeMessage();
+        m.setVersion(version);
+        m.setSerialNo(randomSerialNo());
+        m.setCommand(CommandType.CONTROL_COMMAND);
+        byte[] data = new byte[16];
+        //设置枪号
+        data[4] = (byte) gunNo;
+        //设置起始命令地址,停止充电为2
+        BytesUtil.setSubBytes(data, 5, BytesUtil.fromIntWithLowerFirst(2, 4));
+        //设置命令个数
+        data[9] = (byte) 1;
+        //设置命令参数长度
+        BytesUtil.setSubBytes(data, 10, BytesUtil.fromIntWithLowerFirst(4, 2));
+        //设置命令参数
+        BytesUtil.setSubBytes(data, 12, BytesUtil.fromIntWithLowerFirst(0x55, 4));
+        m.setData(data);
+        ChargeCenter.send("signIn", deviceNo, m);
+        return 0;
+    }
+
+    @Override
+    public int queryChargeHistory(String deviceNo, int backIndex) {
+        ChargeMessage m = new ChargeMessage();
+        m.setVersion(version);
+        m.setSerialNo(randomSerialNo());
+        m.setCommand(CommandType.QUERY_CHARGE_COMMAND);
+        byte[] data = new byte[8];
+        BytesUtil.setSubBytes(data, 4, BytesUtil.fromIntWithLowerFirst(backIndex, 4));
+        m.setData(data);
+        ChargeCenter.send("signIn", deviceNo, m);
+        return 0;
+    }
+
+    @Override
+    public int setChargeRate(String deviceNo, List<ChargeRate> rates) {
+        ChargeMessage m = new ChargeMessage();
+        m.setVersion(version);
+        m.setSerialNo(randomSerialNo());
+        m.setCommand(CommandType.SET_RATE_COMMAND);
+        byte[] data = new byte[8 * 12];
+        for (int i = 0; i < rates.size(); i++) {
+            ChargeRate chargeRate = rates.get(i);
+            data[8 * i] = (byte) chargeRate.getStartHour();
+            data[8 * i + 1] = (byte) chargeRate.getStartMinute();
+            data[8 * i + 2] = (byte) chargeRate.getEndHour();
+            data[8 * i + 3] = (byte) chargeRate.getEndMinute();
+            log.info("----" + Math.round(chargeRate.getRate() * 100));
+            BytesUtil.setSubBytes(data, 8 * i + 4, BytesUtil.fromIntWithLowerFirst(Math.round(chargeRate.getRate() * 100), 4));
+        }
+
+        m.setData(data);
+        ChargeCenter.send("signIn", deviceNo, m);
+        return 0;
+    }
+
+    static byte randomSerialNo() {
+        return BytesUtil.fromIntWithLowerFirst(r.nextInt(255), 1)[0];
+    }
+}

+ 112 - 0
src/main/java/com/zhili/stationcontrol/util/BytesUtil.java

@@ -0,0 +1,112 @@
+package com.zhili.stationcontrol.util;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/18 14:00
+ */
+public class BytesUtil {
+    public static byte[] fromIntWithHigherFirst(int intVal, int bytesLen) {
+        byte[] bytes5 = new byte[bytesLen];
+        while (bytesLen > 0) {
+            bytesLen--;
+            bytes5[bytesLen] = (byte) (intVal >> 8 * (bytes5.length - bytesLen - 1) & 0xFF);
+        }
+        return bytes5;
+    }
+
+    public static byte[] fromIntWithLowerFirst(int intVal, int bytesLen) {
+        byte[] bytes5 = new byte[bytesLen];
+        while (bytesLen > 0) {
+            bytesLen--;
+            bytes5[bytes5.length - bytesLen - 1] = (byte) (intVal >> 8 * (bytes5.length - bytesLen - 1) & 0xFF);
+        }
+        return bytes5;
+    }
+
+    public static int toIntWithLowerFirst(byte[] bytes) {
+        int length = bytes.length;
+        int res = 0;
+        for (int i = 0; i < length; i++) {
+            res = res * 256;
+            res += Byte.toUnsignedInt(bytes[length - i - 1]);
+        }
+        return res;
+    }
+
+    public static byte sumCheck(byte[] b) {
+        int sum = 0;
+        for (int i = 0; i < b.length; i++) {
+            sum = sum + b[i];
+            sum = 0xff & sum;
+        }
+        return (byte) sum;
+    }
+
+    public static String parseString(byte[] b) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < b.length; i++) {
+            sb.append((char) b[i]);
+        }
+        String rawString = sb.toString();
+        int k = rawString.indexOf(0);
+        return rawString.substring(0, k);
+    }
+
+    public static byte[] fromString(String s, int len, byte fill) {
+        byte[] res = new byte[len];
+        for (int i = 0; i < s.length(); i++) {
+            byte b = (byte) s.charAt(i);
+            res[i] = b;
+        }
+        for(int j=s.length();j<len;j++){
+            res[j]=fill;
+        }
+        return res;
+    }
+
+    public static void setSubBytes(byte[] source, int index, byte[] target) {
+        for (int i = 0; i < target.length; i++) {
+            source[index + i] = target[i];
+        }
+    }
+
+    public static byte[] composeNowTimeBytes() {
+        LocalDateTime now = LocalDateTime.now();
+        String timeStr = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now);
+        byte[] timeBytes = new byte[8];
+        for (int i = 0; i < 7; i++) {
+            String partStr = timeStr.substring(2 * i, 2 * (i + 1));
+            timeBytes[i] = BytesUtil.fromIntWithLowerFirst(Integer.valueOf(partStr, 16), 1)[0];
+        }
+        timeBytes[7] = (byte) 0xff;
+        return timeBytes;
+    }
+
+    public static String composeTimeString(byte[] timeBytes) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < timeBytes.length - 1; i++) {
+            byte b = timeBytes[i];
+            String partStr = Integer.toHexString(Byte.toUnsignedInt(b));
+            if (partStr.length() == 1) {
+                partStr = "0" + partStr;
+            }
+            sb.append(partStr);
+//            2015-02-13 12:00:12
+            if (i < 1 || i >= 6) {
+
+            } else if (i < 3) {
+                sb.append("-");
+            } else if (i < 4) {
+                sb.append(" ");
+            } else if (i < 6) {
+                sb.append(":");
+            }
+        }
+        return sb.toString();
+    }
+
+}

+ 2 - 0
src/main/resources/application.yml

@@ -0,0 +1,2 @@
+server:
+  port: 8099

+ 182 - 0
src/test/java/SomeTest.java

@@ -0,0 +1,182 @@
+import com.intelligt.modbus.jlibmodbus.Modbus;
+import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
+import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException;
+import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;
+import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
+import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
+import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
+import com.zhili.stationcontrol.util.BytesUtil;
+import lombok.extern.log4j.Log4j2;
+import org.junit.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+/**
+ * @author :HuangBin
+ * @description:TODO
+ * @date :2022/10/18 14:01
+ */
+@SpringBootTest
+@Log4j2
+public class SomeTest {
+
+    @Test
+    public void test() {
+        byte[] bytes = BytesUtil.fromIntWithHigherFirst(256, 2);
+        log.info(Arrays.toString(bytes));
+    }
+
+    @Test
+    public void test1() {
+        byte[] bytes = BytesUtil.fromIntWithLowerFirst(256, 2);
+        log.info(Arrays.toString(bytes));
+        int i = BytesUtil.toIntWithLowerFirst(bytes);
+        log.info(i);
+    }
+
+    @Test
+    public void test2() {
+        int a = 'a';
+        log.info(a);
+    }
+
+    @Test//291.762999999992
+    public void testModbusRead() throws UnknownHostException, ModbusProtocolException, ModbusNumberException, ModbusIOException {
+        TcpParameters tcpParameters = new TcpParameters();
+//        tcpParameters.setHost(InetAddress.getByName("127.0.0.1"));
+        tcpParameters.setHost(InetAddress.getByName("192.168.3.88"));
+        tcpParameters.setKeepAlive(true);
+        tcpParameters.setPort(502);
+        ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
+        Modbus.setAutoIncrementTransactionId(true);
+//        master.writeSingleRegister(1,65536,1);
+
+        master.writeSingleCoil(1, 1000, true);
+//        boolean[] booleans = master.readCoils(1, 13702, 1);
+//        System.out.println(booleans[0]);
+//        int[] data = new int[4];
+//        data[0]=60816;
+//        data[1]=16252;
+//        data[2]=15413;
+//        data[3]=16498;
+//        master.writeMultipleRegisters(1, 8105, data);
+//        int[] ints = master.readHoldingRegisters(1, 8000, 4);
+//        Double aDouble = PLCUtil.parseFloat64(ints);
+//        log.info("-----"+aDouble);
+//        int[] ints1 = PLCUtil.composeFloat64(aDouble);
+//        master.writeMultipleRegisters(1, 8100, ints1);
+    }
+
+    @Test
+    public void testx(){
+        String res1 = Integer.toHexString(60816);
+        System.out.println(res1);
+        String res2 = Integer.toHexString(16252);
+        System.out.println(res2);
+        String res3 = Integer.toHexString(15413);
+        System.out.println(res3);
+        String res4 = Integer.toHexString(16498);
+        System.out.println(res4);
+//        ed90
+//        3f7c
+//        3c35
+//        4072
+//        291.762999999992
+        String hexString ="40723c353f7ced90";//倒序ping
+        long longBits = Long.valueOf(hexString,16).longValue();
+        double doubleValue = Double.longBitsToDouble(longBits);
+        System.out.println("double float hexString is =" + doubleValue);
+//        Double.toHexString()
+    }
+
+//    @Test
+//    public void testx1(){
+//        int[] ints = PLCUtil.composeFloat64(291.762999999999);
+//        for(int i=0;i<ints.length;i++){
+//            log.info(ints[i]);
+//        }
+//    }
+//    \xaa\xf7\x0a\x00\x13\x00\x12\x00\x25
+    @Test
+    public void testChecksum(){
+        byte[] bs = new byte[9];
+        bs[0]=(byte)0xaa;
+        bs[1]=(byte)0xf7;
+        bs[2]=(byte)0x0a;
+        bs[3]=(byte)0x00;
+        bs[4]=(byte)0x13;
+        bs[5]=(byte)0x00;
+        bs[6]=(byte)0x12;
+        bs[7]=(byte)0x00;
+        bs[8]=(byte)0x25;
+        byte b = BytesUtil.sumCheck(bs);
+        log.info("!!!!"+b);
+    }
+
+    @Test
+    public void testObj(){
+        Object a="123";
+        String b="123";
+        log.info(a.equals(b));
+    }
+
+    @Test
+    public void testB(){
+        byte[] source = new byte[5];
+        log.info(source);
+        byte[] target = new byte[2];
+        target[0]=12;
+        target[1]=13;
+        BytesUtil.setSubBytes(source,2,target);
+        for(int i=0;i<source.length;i++){
+            log.info(source[i]);
+        }
+    }
+
+    @Test
+    public void testTimeBytes(){
+        byte[] bytes = BytesUtil.composeNowTimeBytes();
+        for(int i=0;i<bytes.length;i++){
+            System.out.println(bytes[i]);
+        }
+    }
+
+    @Test
+    public void testStr(){
+        byte[] bytes = new byte[5];
+        bytes[0] = (byte)97;
+        bytes[1] = (byte)98;
+        String s = BytesUtil.parseString(bytes);
+        log.info(s);
+    }
+
+    @Test
+    public void testTime(){
+        byte[] tBytes = new byte[8];
+        tBytes[0]=0x20;
+        tBytes[1]=0x15;
+        tBytes[2]=0x03;
+        tBytes[3]=0x23;
+        tBytes[4]=0x17;
+        tBytes[5]=0x31;
+        tBytes[6]=0x15;
+        tBytes[7]=(byte)0xff;
+        String s = BytesUtil.composeTimeString(tBytes);
+        log.info(s);
+    }
+    @Test
+    public void testStrBytes(){
+        byte[] bytes = BytesUtil.fromString("ab", 5, (byte) 0xff);
+        for(int i=0;i<bytes.length;i++){
+            log.info(Byte.toUnsignedInt(bytes[i]));
+        }
+    }
+
+}
+//        16:45:40.720 [main] INFO SomeTest - 60816
+//        16:45:40.725 [main] INFO SomeTest - 16252
+//        16:45:40.725 [main] INFO SomeTest - 15413
+//        16:45:40.725 [main] INFO SomeTest - 16498

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů