liufei 1 рік тому
коміт
42a3f8ba7d
100 змінених файлів з 8530 додано та 0 видалено
  1. 33 0
      .gitignore
  2. 18 0
      Dockerfile
  3. 151 0
      pom.xml
  4. 43 0
      src/main/java/com/tmzn/devicelinkykc/DeviceLinkYkcApplication.java
  5. 30 0
      src/main/java/com/tmzn/devicelinkykc/config/MybatisPlusConfig.java
  6. 13 0
      src/main/java/com/tmzn/devicelinkykc/constant/Constant.java
  7. 14 0
      src/main/java/com/tmzn/devicelinkykc/constant/DeviceOnlineStatus.java
  8. 20 0
      src/main/java/com/tmzn/devicelinkykc/constant/PortStatusConstant.java
  9. 44 0
      src/main/java/com/tmzn/devicelinkykc/constant/RedisConstant.java
  10. 22 0
      src/main/java/com/tmzn/devicelinkykc/constant/ykc/BillingModelConst.java
  11. 63 0
      src/main/java/com/tmzn/devicelinkykc/constant/ykc/StatusConstant.java
  12. 42 0
      src/main/java/com/tmzn/devicelinkykc/constant/ykc/TransConstant.java
  13. 208 0
      src/main/java/com/tmzn/devicelinkykc/controller/DeviceController.java
  14. 85 0
      src/main/java/com/tmzn/devicelinkykc/controller/TestController.java
  15. 60 0
      src/main/java/com/tmzn/devicelinkykc/entity/BillingModel.java
  16. 65 0
      src/main/java/com/tmzn/devicelinkykc/entity/Device.java
  17. 45 0
      src/main/java/com/tmzn/devicelinkykc/entity/DeviceStatus.java
  18. 40 0
      src/main/java/com/tmzn/devicelinkykc/entity/GdDispostion.java
  19. 57 0
      src/main/java/com/tmzn/devicelinkykc/entity/OrderStatus.java
  20. 118 0
      src/main/java/com/tmzn/devicelinkykc/entity/TransOrder.java
  21. 186 0
      src/main/java/com/tmzn/devicelinkykc/entity/param/AjaxResult.java
  22. 17 0
      src/main/java/com/tmzn/devicelinkykc/entity/param/DeviceInfo.java
  23. 24 0
      src/main/java/com/tmzn/devicelinkykc/entity/param/TransCheck.java
  24. 27 0
      src/main/java/com/tmzn/devicelinkykc/entity/param/dto/BillingModelDTO.java
  25. 24 0
      src/main/java/com/tmzn/devicelinkykc/entity/param/dto/DeviceDTO.java
  26. 24 0
      src/main/java/com/tmzn/devicelinkykc/entity/param/vo/TransOrderVO.java
  27. 138 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/DataConversion.java
  28. 104 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/FrameDataSplicing.java
  29. 306 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/RealTimeMoney.java
  30. 617 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/TransMoney.java
  31. 121 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/BillingModelFrame.java
  32. 126 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/CharngingPushFrame.java
  33. 60 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/CheckTime.java
  34. 53 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/HeartFrameSend.java
  35. 141 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/LoginFrame.java
  36. 166 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/RealTimeStatusPushFrame.java
  37. 72 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/RemoteBalanceUpdatePushFrame.java
  38. 355 0
      src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/TransactionFlowPushFrame.java
  39. 14 0
      src/main/java/com/tmzn/devicelinkykc/mapper/BillingModelMapper.java
  40. 14 0
      src/main/java/com/tmzn/devicelinkykc/mapper/DeviceMapper.java
  41. 14 0
      src/main/java/com/tmzn/devicelinkykc/mapper/DeviceStatusMapper.java
  42. 15 0
      src/main/java/com/tmzn/devicelinkykc/mapper/GdDispostionMapper.java
  43. 14 0
      src/main/java/com/tmzn/devicelinkykc/mapper/OrderStatusMapper.java
  44. 14 0
      src/main/java/com/tmzn/devicelinkykc/mapper/TransOrderMapper.java
  45. 29 0
      src/main/java/com/tmzn/devicelinkykc/mapstruct/BillingModelMapping.java
  46. 24 0
      src/main/java/com/tmzn/devicelinkykc/mapstruct/DeviceMapping.java
  47. 28 0
      src/main/java/com/tmzn/devicelinkykc/mapstruct/TransMapping.java
  48. 833 0
      src/main/java/com/tmzn/devicelinkykc/message/DeviceMsgHandle.java
  49. 582 0
      src/main/java/com/tmzn/devicelinkykc/message/YkcMsgHandle.java
  50. 66 0
      src/main/java/com/tmzn/devicelinkykc/msgEnum/DeviceSendYkc.java
  51. 57 0
      src/main/java/com/tmzn/devicelinkykc/msgEnum/YkcSendDevice.java
  52. 36 0
      src/main/java/com/tmzn/devicelinkykc/openfeign/FeginClientFactory.java
  53. 13 0
      src/main/java/com/tmzn/devicelinkykc/openfeign/FeginClientProxy.java
  54. 23 0
      src/main/java/com/tmzn/devicelinkykc/openfeign/MsgService.java
  55. 13 0
      src/main/java/com/tmzn/devicelinkykc/openfeign/OpenFeignConfig.java
  56. 16 0
      src/main/java/com/tmzn/devicelinkykc/openfeign/transdata/DataParam.java
  57. 42 0
      src/main/java/com/tmzn/devicelinkykc/openfeign/transdata/RpcResult.java
  58. 296 0
      src/main/java/com/tmzn/devicelinkykc/redis/RedisCache.java
  59. 52 0
      src/main/java/com/tmzn/devicelinkykc/redis/RedisConfig.java
  60. 25 0
      src/main/java/com/tmzn/devicelinkykc/redis/RedisMessageListener.java
  61. 12 0
      src/main/java/com/tmzn/devicelinkykc/service/BillingModelService.java
  62. 181 0
      src/main/java/com/tmzn/devicelinkykc/service/DeviceControlerService.java
  63. 17 0
      src/main/java/com/tmzn/devicelinkykc/service/DeviceService.java
  64. 12 0
      src/main/java/com/tmzn/devicelinkykc/service/DeviceStatusService.java
  65. 14 0
      src/main/java/com/tmzn/devicelinkykc/service/GdDispostionService.java
  66. 16 0
      src/main/java/com/tmzn/devicelinkykc/service/OrderStatusService.java
  67. 17 0
      src/main/java/com/tmzn/devicelinkykc/service/TransOrderService.java
  68. 16 0
      src/main/java/com/tmzn/devicelinkykc/service/impl/BillingModelServiceImpl.java
  69. 110 0
      src/main/java/com/tmzn/devicelinkykc/service/impl/DeviceServiceImpl.java
  70. 16 0
      src/main/java/com/tmzn/devicelinkykc/service/impl/DeviceStatusServiceImpl.java
  71. 17 0
      src/main/java/com/tmzn/devicelinkykc/service/impl/GdDispostionServiceImpl.java
  72. 18 0
      src/main/java/com/tmzn/devicelinkykc/service/impl/OrderStatusServiceImpl.java
  73. 38 0
      src/main/java/com/tmzn/devicelinkykc/service/impl/TransOrderServiceImpl.java
  74. 117 0
      src/main/java/com/tmzn/devicelinkykc/socket/DeviceConnectionMsg.java
  75. 78 0
      src/main/java/com/tmzn/devicelinkykc/socket/SocketHandle.java
  76. 168 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/DeviceOnlineTask.java
  77. 62 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/DeviceStatusPushTask.java
  78. 54 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/HeartTask.java
  79. 144 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/StartTask.java
  80. 119 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/TransCheckTask.java
  81. 38 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/TranscationTask.java
  82. 54 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/MsgCharngingQueue.java
  83. 54 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/MsgFreeQueue.java
  84. 54 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/MsgHeartQueue.java
  85. 57 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/MsgTranscationQueue.java
  86. 67 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/TaskExecutePool.java
  87. 590 0
      src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/TaskRunner.java
  88. 16 0
      src/main/java/com/tmzn/devicelinkykc/transdata/DataParam.java
  89. 42 0
      src/main/java/com/tmzn/devicelinkykc/transdata/RpcResult.java
  90. 4 0
      src/main/java/com/tmzn/devicelinkykc/transdata/constant/Constant.java
  91. 121 0
      src/main/java/com/tmzn/devicelinkykc/transdata/constant/NormalChargeConstant.java
  92. 26 0
      src/main/java/com/tmzn/devicelinkykc/transdata/entity/BasicData.java
  93. 17 0
      src/main/java/com/tmzn/devicelinkykc/transdata/entity/DeviceParam.java
  94. 25 0
      src/main/java/com/tmzn/devicelinkykc/transdata/entity/EndCharge.java
  95. 90 0
      src/main/java/com/tmzn/devicelinkykc/transdata/entity/MainBoard.java
  96. 23 0
      src/main/java/com/tmzn/devicelinkykc/transdata/entity/PlanCharge.java
  97. 20 0
      src/main/java/com/tmzn/devicelinkykc/transdata/entity/PortOper.java
  98. 23 0
      src/main/java/com/tmzn/devicelinkykc/transdata/entity/PortStatus.java
  99. 31 0
      src/main/java/com/tmzn/devicelinkykc/transdata/entity/StartCharge.java
  100. 0 0
      src/main/java/com/tmzn/devicelinkykc/transdata/entity/opertype/OperEnum.java

+ 33 - 0
.gitignore

@@ -0,0 +1,33 @@
+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/

+ 18 - 0
Dockerfile

@@ -0,0 +1,18 @@
+FROM java:8
+
+MAINTAINER device-link-ykc
+
+ADD device-link-ykc*.jar device-link-ykc.jar
+
+EXPOSE 8053
+
+ENV JAVA_OPTS=""
+
+ENV APP_FILE=""
+
+# 将外部配置文件复制到容器
+COPY  /config  /wgd/docker-compose/ykc-jar/config
+
+# ENTRYPOINT 执行项目 app.jar及外部配置文件,多个配置文件逗号隔开
+
+ENTRYPOINT java ${JAVA_OPTS} ${APP_FILE} -jar /device-link-ykc.jar --spring.config.location=/wgd/docker-compose/ykc-jar/config/application.yml,/wgd/docker-compose/ykc-jar/config/application-test.yml

+ 151 - 0
pom.xml

@@ -0,0 +1,151 @@
+<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.tmzn</groupId>
+    <artifactId>device-link-ykc</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>device-link-ykc</name>
+    <description>Demo project for Spring Boot</description>
+    <properties>
+        <java.version>1.8</java.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <spring-boot.version>2.7.6</spring-boot.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.weitiandi</groupId>
+            <artifactId>device-common</artifactId>
+            <version>1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+            <version>2.0.25</version>
+        </dependency>
+        <!-- Mysql驱动包 -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.29</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+            <version>2.5.14</version>
+        </dependency>
+        <!--       mybatis-plus-->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.5.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+            <version>1.2.16</version>
+        </dependency>
+        <!-- pagehelper 分页插件 -->
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <version>1.4.6</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>3.11.0</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.github.openfeign</groupId>
+            <artifactId>feign-core</artifactId>
+            <version>11.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.github.openfeign</groupId>
+            <artifactId>feign-jackson</artifactId>
+            <version>11.0</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct -->
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+            <version>1.5.2.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-processor</artifactId>
+            <version>1.5.2.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok-mapstruct-binding</artifactId>
+            <version>0.2.0</version>
+        </dependency>
+
+    </dependencies>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <configuration>
+                    <mainClass>com.tmzn.devicelinkykc.DeviceLinkYkcApplication</mainClass>
+                    <skip>false</skip>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>repackage</id>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 43 - 0
src/main/java/com/tmzn/devicelinkykc/DeviceLinkYkcApplication.java

@@ -0,0 +1,43 @@
+package com.tmzn.devicelinkykc;
+
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import java.util.Set;
+
+@SpringBootApplication
+@EnableTransactionManagement
+public class DeviceLinkYkcApplication {
+
+
+    public static void main(String[] args) {
+        ConfigurableApplicationContext context= SpringApplication.run(DeviceLinkYkcApplication.class, args);
+        // 注册函数,在应用程序关闭前执行
+        context.registerShutdownHook();
+        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+            // 在这里编写你需要在应用程序关闭前执行的代码
+            System.out.println("执行关闭前的代码...");
+            RedisCache redisCache = context.getBean(RedisCache.class);
+            //Set<String> cacheObject = redisCache.getCacheObject(RedisConstant.DEVICE_INFO);
+
+            Set<String> set = (Set<String>) redisCache.keys(RedisConstant.KEYS + "*");
+           // set.stream().forEach(a -> System.out.println(a));
+            if (set.size()>0){
+                set.stream().forEach(key->{
+                   // System.out.println("keys,delete>>>"+key);
+                    redisCache.deleteObject(key);
+                });
+            }
+            if (redisCache.hasKey(RedisConstant.DEVICE_INFO)){
+                redisCache.deleteObject(RedisConstant.DEVICE_INFO);
+            }
+            // 例如,可以在这里关闭数据库连接,清理资源等
+        }));
+    }
+
+}

+ 30 - 0
src/main/java/com/tmzn/devicelinkykc/config/MybatisPlusConfig.java

@@ -0,0 +1,30 @@
+package com.tmzn.devicelinkykc.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author xp
+ * @date 2024/4/13
+ * @explain "  "
+ */
+@Configuration
+@MapperScan("com.tmzn.devicelinkykc.mapper")
+public class MybatisPlusConfig {
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        //添加:分页插件
+        //参数:new PaginationInnerInterceptor(DbType.MYSQL),是专门为mysql定制实现的内部的分页插件
+        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
+        //添加:乐观锁插件
+        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+        return interceptor;
+    }
+
+}

+ 13 - 0
src/main/java/com/tmzn/devicelinkykc/constant/Constant.java

@@ -0,0 +1,13 @@
+package com.tmzn.devicelinkykc.constant;
+
+/**
+ * @author xp
+ * @date 2024/5/11
+ * @explain "  "
+ */
+public class Constant {
+
+    public static final int DEVICE_NOT_LOGIN_STATUS= 0;
+    public static final int DEVICE_LOGIN_STATUS= 1;
+
+}

+ 14 - 0
src/main/java/com/tmzn/devicelinkykc/constant/DeviceOnlineStatus.java

@@ -0,0 +1,14 @@
+package com.tmzn.devicelinkykc.constant;
+
+/**
+ * @author xp
+ * @date 2024/3/15
+ * @explain "  "
+ */
+public class DeviceOnlineStatus {
+    public static final byte OFFLINE= 0;
+    public static final byte ONLINE= 1;
+
+    public static final byte NORMAL=0;
+    public static final byte DISABLED=1;
+}

+ 20 - 0
src/main/java/com/tmzn/devicelinkykc/constant/PortStatusConstant.java

@@ -0,0 +1,20 @@
+package com.tmzn.devicelinkykc.constant;
+
+/**
+ * @author xp
+ * @date 2024/3/16
+ * @explain "  "
+ */
+public class PortStatusConstant {
+    public static final int FREE = 0x01;    //端口空闲即未插枪
+    public static final int CHARGING = 0x02;    //使用中
+    public static final int DISABLED = 0x03;    //禁用
+    public static final int FAULT = 0x04;   //故障
+    public static final int INSERT_GUN = 0x05;   //已连接
+    public static final int BOOKED = 0x06;   //已预约
+    public static final int CHARGING_END = 0x07;   //充电完成
+
+    public static final int EMERGENCY_STOP = 0x0D;   //急停中
+
+
+}

+ 44 - 0
src/main/java/com/tmzn/devicelinkykc/constant/RedisConstant.java

@@ -0,0 +1,44 @@
+package com.tmzn.devicelinkykc.constant;
+
+/**
+ * @author xp
+ * @date 2024/3/16
+ * @explain "  "
+ */
+public class RedisConstant {
+
+    //根据枪号缓存的设备状态表信息,状态变位时更新
+    public static final String ONLINE_DEVICE_ONE= "onlineDevicePortOneYKC";
+    public static final String ONLINE_DEVICE_TWO= "onlineDevicePortTwoYKC";
+
+    //最新设备103消息
+    public static final String DEVICE_PORT_STATUS= "devicePortStatusYKC";
+
+    //通讯帧的密钥
+    public static final String KEYS= "keysYKC";
+
+    //全部设备的imei
+    public static final String DEVICE_INFO="deviceInfoYKC";
+
+    //充电中设备的实时计费信息
+    public static final String DEVICE_CHARNGING_INFO="deviceCharngingInfo";
+
+    //登录验证时,防止设备循环发送登录
+    public static final String DEVICE_LOGIN_YKC="deviceLoginYKC";
+
+    //开始充电时设备是离线状态时,需要缓存当前设备是不是等待插枪充电
+    public static final String START_CHARNGING_YKC="startCharngingYKC";
+
+    //云快充启充但是没插枪
+    public static final String NO_INSERT_GUN_YKC="noInsertGunYKC";
+
+    /**
+     *订单未校验识别
+     */
+    public static final String NO_RESPONSE_WAS_RECEIVED="noResponseWasReceived";
+
+    //重新登录
+    public static final String SIGN_BACK_IN ="signBackIn";
+
+
+}

+ 22 - 0
src/main/java/com/tmzn/devicelinkykc/constant/ykc/BillingModelConst.java

@@ -0,0 +1,22 @@
+package com.tmzn.devicelinkykc.constant.ykc;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 计费模型 "
+ */
+public class BillingModelConst {
+    /**
+     * 计费模型费率
+     */
+    public static final byte SHARP=0x00;
+    public static final byte PEAK=0x01;
+    public static final byte FLAT=0x02;
+    public static final byte VALLEY=0x03;
+
+    /**
+     * 计费模型一致性
+     */
+    public static final byte EQUALLY=0x00;  //桩计费模型与平台一致
+    public static final byte DIFFERENT=0x01;//桩计费模型与平台不一致
+}

+ 63 - 0
src/main/java/com/tmzn/devicelinkykc/constant/ykc/StatusConstant.java

@@ -0,0 +1,63 @@
+package com.tmzn.devicelinkykc.constant.ykc;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 上传实时监测数据 "
+ */
+public class StatusConstant {
+    /**
+     * 自定义相关上报云快充状态
+     */
+    //当前订单状态:0代表正在充电,1代表充电结束
+    public static final byte NOW_ORDER_STATUS_CHARGING=0;
+    public static final byte NOW_ORDER_STATUS_CHARGING_ENDING=1;
+
+    //空闲状态切换至充电状态的首次初始化状态上送:0未上送,1已上送
+    public static final byte CHARGING_INIT_STATUS_OK=1;
+    public static final byte CHARGING_INIT_STATUS_NO=0;
+
+    //停止指令回复云快充:0未回复,1已回复(预防设备停止充电多次上送消息)
+    public static final byte STOP_CHARGING_REPLY_OK=1;
+    public static final byte STOP_CHARGING_REPLY_NO=0;
+
+    //交易流水中间协议层上送云快充状态:0未上送,1已上送
+    public static final byte TRANSACTION_ORDER_REPORTING_ACTION_STATUS_OK=1;
+    public static final byte TRANSACTION_ORDER_REPORTING_ACTION_STATUS_NO=0;
+
+    //云快充对中间协议层上送是交易流水解析结果:0默认是未收到回复,1解析成功,2解析失败非法账单
+    public static final byte TRANSACTION_ORDER_REPLY_STATUS_FAIL=0;
+    public static final byte TRANSACTION_ORDER_REPLY_STATUS_SUCC=1;
+    public static final byte TRANSACTION_ORDER_REPLY_STATUS_ILLEGAL=2;
+
+
+
+
+
+    /**
+     *心跳包枪状态
+     */
+    public static final byte HEART_GUNS_STATUS_OK = 0x00;
+    public static final byte HEART_GUNS_STATUS_FAULT = 0x01;
+
+    /**
+     * 变位上送枪状态
+     */
+    public static final byte OFFLINE = 0x00;//离线
+    public static final byte FAULT = 0x01;//故障
+    public static final byte FREE = 0x02;//空闲
+    public static final byte CHARGING = 0x03;//充电
+    /**
+     * 枪是否归位
+     */
+    public static final byte NO = 0x00;
+    public static final byte YES = 0x01;
+    public static final byte UNKNOWN = 0x02;
+
+    public static final byte INSERT_GUNS_YES= 0x01;
+    public static final byte INSERT_GUNS_NO= 0x00;
+    /**
+     * 硬件故障????后续补充
+     */
+    public static final byte[] NO_FAULTS = {0x00, 0x00};
+}

+ 42 - 0
src/main/java/com/tmzn/devicelinkykc/constant/ykc/TransConstant.java

@@ -0,0 +1,42 @@
+package com.tmzn.devicelinkykc.constant.ykc;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 交易流水 "
+ */
+public class TransConstant {
+    /**
+     * 交易标识
+     */
+    public static final byte APP_START=0x01;
+    public static final byte CARD_START=0x02;
+    public static final byte OFFLINE_CARD_START=0x04;
+    public static final byte VIN_CODE_START=0x05;
+
+    /**
+     * 停止原因??使用到类型再添加
+     */
+    //充电完成
+    //远程APP停止
+    public static final int APP_REMOTE_STOP=0x40;
+    //充满停充
+    public static final int SOC_FULL_OF_STOP=0x41;
+    public static final int THE_CHARGING_CAPACITY_IS_SUFFICIENT_STOP=0x42;
+    public static final int THE_CHARGING_MONEY_IS_SUFFICIENT_STOP=0x43;
+    public static final int THE_CHARGING_TIME_IS_SUFFICIENT_STOP=0x44;
+    //手动停止
+    public static final int MANUAL_STOP=0x45;
+    public static final int OTHER_STOP=0x46;
+    //充电启动失败
+    public static final int INSUFFICIENT_BALANCE_START_FAIL=0x4E;//充电启动失败,余额不足
+    public static final int START_FAIL=0x58;//云快充预留位,当作启充失败原因上报
+
+    //充电异常中止
+
+    //余额不足停充
+    public static final int INSUFFICIENT_BALANCE_EXCEPTION_STOP=0x6e;
+    public static final int EMERGENCY_STOP_EXCEPTION_STOP=0x72;//急停开入
+    public static final int CHARGING_STATION_POWER_OUTAGE=0x83; //充电桩断电
+
+}

+ 208 - 0
src/main/java/com/tmzn/devicelinkykc/controller/DeviceController.java

@@ -0,0 +1,208 @@
+package com.tmzn.devicelinkykc.controller;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.server.HttpServerRequest;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.tmzn.devicelinkykc.constant.Constant;
+import com.tmzn.devicelinkykc.constant.DeviceOnlineStatus;
+import com.tmzn.devicelinkykc.constant.PortStatusConstant;
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.constant.ykc.StatusConstant;
+import com.tmzn.devicelinkykc.entity.*;
+import com.tmzn.devicelinkykc.entity.param.AjaxResult;
+import com.tmzn.devicelinkykc.entity.param.vo.TransOrderVO;
+import com.tmzn.devicelinkykc.mapstruct.DeviceMapping;
+import com.tmzn.devicelinkykc.mapstruct.TransMapping;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.service.*;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import com.tmzn.devicelinkykc.transdata.entity.DeviceParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * @author xp
+ * @date 2024/4/10
+ * @explain " 开放接口 "
+ */
+@RestController
+@CrossOrigin(origins = "*")
+@RequestMapping("api/device")
+public class DeviceController {
+
+    private static final Logger logger = LoggerFactory.getLogger(DeviceController.class);
+
+    @Autowired
+    private DeviceService deviceService;
+    @Autowired
+    private DeviceStatusService deviceStatusService;
+    @Autowired
+    private DeviceMapping deviceMapping;
+    @Autowired
+    private TransMapping transMapping;
+    @Autowired
+    private BillingModelService billingModelService;
+
+    @Autowired
+    private SocketHandle socketHandle;
+    @Autowired
+    private TransOrderService transOrderService;
+
+    @Autowired
+    private RedisCache redisCachel;
+    @Autowired
+    private DeviceControlerService deviceControlerService;
+
+  /*  *//**
+     * 接收到充电桩后台提交的云快充连接设备进行保存
+     *
+     * @param
+     * @return
+     *//*
+    @PostMapping
+    public AjaxResult addAndUpdate(@RequestBody DeviceDTO deviceDTO) {
+        String deviceSn = deviceDTO.getDeviceSn();
+        String deviceImei = deviceDTO.getDeviceImei();
+        String pileCode = deviceDTO.getPileCode();
+        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+        deviceQueryWrapper.eq("device_sn", deviceSn).eq("device_imei", deviceImei).eq("pile_code", pileCode);
+        Device device = deviceService.getOne(deviceQueryWrapper);
+        if (device == null) {
+            Device add = deviceMapping.deviceDTOTOdevice(deviceDTO);
+            boolean save = deviceService.save(add);
+            if (save) {
+                return AjaxResult.success("设备连接云快充信息保存成功!");
+            } else {
+                return AjaxResult.error("设备连接云快充信息保存异常!");
+            }
+        } else {
+            //更新操作分情况;TODO:设备双枪,枪禁用情况?
+            QueryWrapper<DeviceStatus> deviceStatusQueryWrapper = new QueryWrapper<>();
+            deviceStatusQueryWrapper.eq("device_sn", deviceSn).eq("device_imei", deviceImei).eq("pile_code", pileCode);
+            List<DeviceStatus> deviceStatusList = deviceStatusService.list(deviceStatusQueryWrapper);
+            if (device.getDisabled() == deviceDTO.getDisabled()) {
+                //禁用状态没变化,更新设备信息
+                deviceService.updateById(device);
+                return AjaxResult.success();
+            } else {
+                if (device.getDisabled() == DeviceOnlineStatus.DISABLED) {
+                    //1.禁用变正常状态
+                    device.setDisabled(DeviceOnlineStatus.NORMAL);
+                    deviceService.updateById(device);
+                    return AjaxResult.success();
+                } else {
+                    //2.设备是正常状态,更新成禁用状态,设备禁用后设备离线:一.校验设备是充电中不能操作,二.deviceStatus需要删除,不保留设备的状态信息
+                    List<DeviceStatus> collect = deviceStatusList.stream().filter(deviceStatus ->
+                            deviceStatus.getGunStatus() == StatusConstant.CHARGING
+                    ).collect(Collectors.toList());
+                    if (collect.size() > 0) {
+                        //设备有正在充电的端口
+                        return AjaxResult.error("设备有正在充电端口,不能设置禁用状态!");
+                    } else {
+                        //断开socket
+                        if (socketHandle.existDeviceConnection(pileCode)) {
+                            socketHandle.removeDeviceConnection(pileCode);
+                        }
+                        deviceStatusService.removeBatchByIds(deviceStatusList);
+                        device.setDisabled(DeviceOnlineStatus.DISABLED);
+                        deviceService.updateById(device);
+                        QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+                        billingModelQueryWrapper.eq("device_imei", deviceImei).eq("device_sn", deviceSn).eq("pile_code", pileCode);
+                        billingModelService.remove(billingModelQueryWrapper);
+                        return AjaxResult.success();
+                    }
+                }
+            }
+        }
+    }
+*/
+    @DeleteMapping("/{pileCode}")
+    public AjaxResult delete( @PathVariable("pileCode") String pileCode) {
+        AjaxResult ajaxResult = deviceService.deleteDevice(pileCode);
+        return ajaxResult;
+    }
+
+    @GetMapping("order")
+    public AjaxResult getOrder(TransOrderVO transOrderVO) {
+        Page<TransOrder> transOrderPage = transOrderService.selectAndPage(transOrderVO);
+        return AjaxResult.success(transOrderPage);
+    }
+
+    @PostMapping("/openLink/{imei}")
+    public AjaxResult openLink(@PathVariable("imei")String imei) {
+
+        Set<String> imeis = new HashSet<>();
+        if (redisCachel.hasKey(RedisConstant.DEVICE_INFO)) {
+            imeis = redisCachel.getCacheObject(RedisConstant.DEVICE_INFO);
+        }
+        //启用通知
+        imeis.add(imei);
+        //redisCachel.setCacheObject(RedisConstant.DEVICE_INFO, imeis, 6 * 1000 * 60, TimeUnit.MILLISECONDS);
+        redisCachel.setCacheObject(RedisConstant.DEVICE_INFO,imeis);
+        //imei保存后发个获取103,让新增设备立马上线
+        DeviceParam deviceParam = new DeviceParam();
+        deviceParam.setDeviceId(imei);
+        deviceParam.setCcid(imei);
+        deviceControlerService.sendPortDetailCmd(deviceParam);
+
+        return AjaxResult.success();
+    }
+
+    @PostMapping("/handoff")
+    public AjaxResult  handoff(@RequestBody List<Device> deviceList) {
+        //ip和端口的修改,必须从新登录
+        if (deviceList.size()>0){
+            for (Device device : deviceList) {
+                QueryWrapper<DeviceStatus> deviceStatusQueryWrapper = new QueryWrapper<>();
+                deviceStatusQueryWrapper.eq("gun_status",StatusConstant.CHARGING).eq("device_imei",device.getDeviceImei());
+                List<DeviceStatus> list = deviceStatusService.list(deviceStatusQueryWrapper);
+                if (list.size()>0){
+                    return AjaxResult.error("有充电中设备IP和端口无法修改!");
+                }
+            }
+            deviceList.stream().forEach(device -> {
+                //修改
+                if (redisCachel.hasKey(RedisConstant.KEYS+device.getPileCode())){
+                    if (socketHandle.existDeviceConnection(device.getPileCode())){
+                        socketHandle.removeDeviceConnection(device.getPileCode());
+                    }
+                    redisCachel.deleteObject(RedisConstant.KEYS+device.getPileCode());
+                }
+                Device byId = deviceService.getById(device.getId());
+                byId.setIp(device.getIp());
+                byId.setPort(device.getPort());
+                deviceService.updateById(byId);
+
+                logger.info(device.toString());
+                DeviceParam deviceParam = new DeviceParam();
+                deviceParam.setDeviceId(byId.getDeviceImei());
+                deviceParam.setCcid(byId.getDeviceImei());
+                deviceControlerService.sendPortDetailCmd(deviceParam);
+            });
+        }
+        return AjaxResult.success();
+    }
+
+    /**
+     * 主动断开与运营商平台的TCPsocket连接,触发重新登录
+     * @param pileCode
+     * @return
+     */
+    @PostMapping("/restLogin/{pileCode}")
+    public AjaxResult restLogin(@PathVariable("pileCode")String pileCode,HttpServerRequest request) {
+
+        socketHandle.removeDeviceConnection(pileCode);
+
+        return AjaxResult.success("断开连接!!!!!!");
+    }
+}

+ 85 - 0
src/main/java/com/tmzn/devicelinkykc/controller/TestController.java

@@ -0,0 +1,85 @@
+package com.tmzn.devicelinkykc.controller;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.tmzn.devicelinkykc.entity.BillingModel;
+import com.tmzn.devicelinkykc.entity.param.dto.BillingModelDTO;
+import com.tmzn.devicelinkykc.mapstruct.BillingModelMapping;
+import com.tmzn.devicelinkykc.service.BillingModelService;
+import com.tmzn.devicelinkykc.service.DeviceControlerService;
+import com.tmzn.devicelinkykc.transdata.entity.DeviceParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.HashMap;
+
+/**
+ * @author xp
+ * @date 2024/4/13
+ * @explain "  "
+ */
+@Controller
+@RequestMapping
+public class TestController {
+    @Autowired
+    private DeviceControlerService deviceControlerService;
+    @Autowired
+    private BillingModelService billingModelService;
+    @Autowired
+    private BillingModelMapping billingModelMapping;
+    @Value("${billingInterface}")
+    private String url;
+
+    @GetMapping("/test")
+    public String test(@RequestParam("method") String method, @RequestParam("imei") String imei) {
+
+        String deviceId = imei;
+        String ccid = imei;
+        if ("start".equals(method)) {
+            deviceControlerService.startCharge(imei, imei, 1,50000);
+        } else if ("stop".equals(method)) {
+            deviceControlerService.stopCharge(imei, imei, 1);
+        } else if ("port".equals(method)) {
+
+            DeviceParam deviceParam = new DeviceParam();
+            deviceParam.setDeviceId(imei);
+            deviceParam.setCcid(imei);
+            deviceControlerService.sendPortDetailCmd(deviceParam);
+        } else if ("restartDevice".equals(method)) {
+
+            deviceControlerService.restart(imei, imei);
+        } else if ("rest".equals(method)) {
+            deviceControlerService.reset(imei, imei);
+        }
+        return "ok";
+    }
+
+    @RequestMapping("/testElec")
+    public JSONObject testCompute(@RequestParam("end")Long end,@RequestParam("start")Long star,@RequestParam("imei")String imei,@RequestParam("port")int port) {
+        QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+        BillingModel one = billingModelService.getOne(billingModelQueryWrapper);
+        BillingModelDTO billingModelDTO = billingModelMapping.billingModelToBillingModelDto(one);
+        HashMap<String, Object> map = new HashMap<>();
+        map.put("port",port);
+        map.put("billingModel",billingModelDTO);
+        map.put("startTime",star/1000);
+        map.put("endTime",end/1000);
+        JSONObject jsonObject = new JSONObject(map);
+        String s = jsonObject.toString();
+        HttpResponse execute = HttpRequest.post(url)
+                .form("data",s)
+                .timeout(3000)
+                .execute();
+        int status = execute.getStatus();
+        String body = execute.body();
+        System.out.println("body:>>>"+body);
+        return null;
+
+    }
+}

+ 60 - 0
src/main/java/com/tmzn/devicelinkykc/entity/BillingModel.java

@@ -0,0 +1,60 @@
+package com.tmzn.devicelinkykc.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 计费模型 "
+ */
+@Data
+@TableName(value = "ims_ykc_billing_model")
+public class BillingModel implements Serializable {
+    /**
+     `id` int(11) NOT NULL AUTO_INCREMENT,
+     `device_imei` varchar(64) DEFAULT NULL COMMENT '硬件设备编码',
+     `device_sn` varchar(64) DEFAULT NULL COMMENT '设备sn码',
+     `model_no` int(11) NOT NULL DEFAULT '0' COMMENT '计费模型编号',
+     `pile_code` varchar(64) DEFAULT NULL COMMENT '云快充桩编码',
+     `sharp_price` decimal(10,5) DEFAULT '0.00000' COMMENT '尖费电费费率',
+     `sharp_service_fee` decimal(10,5) DEFAULT '0.00000' COMMENT '尖服务费费率',
+     `peak_price` decimal(10,5) DEFAULT '0.00000' COMMENT '峰电费费率',
+     `peak_service_fee` decimal(10,5) DEFAULT '0.00000' COMMENT '峰服务费费率',
+     `flat_price` decimal(10,5) DEFAULT '0.00000' COMMENT '平电费费率',
+     `flat_service_fee` decimal(10,5) DEFAULT '0.00000' COMMENT '平服务费费率',
+     `valley_price` decimal(10,5) DEFAULT '0.00000' COMMENT '谷电费费率',
+     `valley_service_fee` decimal(10,5) DEFAULT '0.00000' COMMENT '谷服务费费率',
+     `loss_ratio` int(11) DEFAULT NULL COMMENT '计损比例',
+     `time_slot` varchar(48) DEFAULT NULL COMMENT '0:00开始24小时分48个段,0:尖费率 1:峰费率 2:平费率 3:谷费率',
+     `update_time` bigint(16) DEFAULT NULL COMMENT '更新时间',
+     */
+
+    @TableId(type = IdType.AUTO,value = "id")
+    private Integer id;
+    private String deviceImei;
+    private String deviceSn;
+    private Integer modelNo;
+    private String pileCode;
+    private BigDecimal sharpPrice=BigDecimal.ZERO;
+    private BigDecimal sharpServiceFee=BigDecimal.ZERO;
+    private BigDecimal peakPrice=BigDecimal.ZERO;
+    private BigDecimal peakServiceFee=BigDecimal.ZERO;
+    private BigDecimal flatPrice=BigDecimal.ZERO;
+    private BigDecimal flatServiceFee=BigDecimal.ZERO;
+    private BigDecimal valleyPrice=BigDecimal.ZERO;
+    private BigDecimal valleyServiceFee=BigDecimal.ZERO;
+    private int lossRatio;
+    private String timeSlot;
+    private long updateTime;
+
+
+
+
+
+}

+ 65 - 0
src/main/java/com/tmzn/devicelinkykc/entity/Device.java

@@ -0,0 +1,65 @@
+package com.tmzn.devicelinkykc.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 云快充设备信息 "
+ */
+@Data
+@TableName(value = "ims_ykc_device")
+public class Device implements Serializable {
+    /**
+     `id` int(11) NOT NULL AUTO_INCREMENT,
+     `device_imei` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '硬件设备编码',
+     `device_sn` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '设备sn码',
+     `pile_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '云快充桩编码',
+     `pile_type` tinyint(1) DEFAULT '1' COMMENT '充电桩类型,0:直流,1:交流',
+     `ip` varchar(64) DEFAULT NULL COMMENT '第三方对接ip',
+     `port` int(11) DEFAULT NULL COMMENT '第三方对接端口',
+     `charger_gun_num` tinyint(1) DEFAULT '0' COMMENT '充电枪连接数',
+     `comm_protocol_ver` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '通讯协议版本',
+     `program_version` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '程序版本',
+     `net_link_type` tinyint(1) DEFAULT '3' COMMENT '网络链接类型,0x00SIM卡 0x01LAN 0x02WAN 0x03其他',
+     `sim_card` varchar(10) DEFAULT '0000000000' COMMENT 'sim 卡',
+     `operators` tinyint(1) DEFAULT '4' COMMENT '运营商,0x00移动 0x02电信 0x03联通 0x04其他',
+     `token` varchar(14) DEFAULT NULL COMMENT '自助调试列表中的档案编号为对接token',
+     `operators_id` int(11) DEFAULT NULL COMMENT '第三方平台id',
+     `disabled` tinyint(1) DEFAULT '0' COMMENT '设备禁用:1禁用,0正常',
+     `create_time` bigint(16) DEFAULT NULL COMMENT '创建时间',
+     `update_time` bigint(16) DEFAULT NULL COMMENT '更新时间',
+
+     */
+    @TableId(type = IdType.AUTO,value = "id")
+    private Integer id;
+    private String deviceImei;
+    private String deviceSn;
+    private String pileCode;
+    private byte pileType;
+    private String ip;
+    private int port;
+    private byte chargerGunNum;
+    private String commProtocolVer;
+    private String programVersion;
+    private byte netLinkType;
+    private String simCard;
+    private byte operators;
+    private String token;
+    private Integer operatorsId;
+    private byte disabled;
+    private long createTime;
+    private long updateTime;
+    public void setCreateTime(long createTime) {
+        this.createTime = createTime;
+    }
+
+    public void setUpdateTime(long updateTime) {
+        this.updateTime = updateTime;
+    }
+}

+ 45 - 0
src/main/java/com/tmzn/devicelinkykc/entity/DeviceStatus.java

@@ -0,0 +1,45 @@
+package com.tmzn.devicelinkykc.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 设备状态 "
+ */
+@Data
+@TableName(value = "ims_ykc_device_status")
+public class DeviceStatus implements Serializable {
+    /**
+     `id` int(11) NOT NULL AUTO_INCREMENT,
+     `device_imei` varchar(64) DEFAULT NULL COMMENT '硬件设备编码',
+     `device_sn` varchar(64) DEFAULT NULL COMMENT '设备sn码',
+     `pile_code` varchar(64) DEFAULT NULL COMMENT '云快充桩编码',
+     `gun_port` tinyint(1) DEFAULT NULL COMMENT '设备端口',
+     `gun_status` tinyint(1) DEFAULT '0' COMMENT '枪状态,0离线,1故障,2空闲,3充电',
+     `insert_gun_status` tinyint(1) DEFAULT '0' COMMENT '插枪状态,0未插枪,1已插枪',
+     `online_status` tinyint(1) DEFAULT '0' COMMENT '充电桩在线状态,0:离线,1:在线',
+     `charging_status` tinyint(1) DEFAULT '0' COMMENT '设备充电状态,0未充电,1充电中',
+     `create_time` bigint(16) DEFAULT NULL,
+     `update_time` bigint(16) DEFAULT NULL,
+     `flage` int(11) DEFAULT '0' COMMENT '标识位0是不区分充电中断网断电再恢复,1是断网2是断电',
+     */
+    @TableId(type = IdType.AUTO,value = "id")
+    private Integer id;
+    private String deviceImei;
+    private String deviceSn;
+    private String pileCode;
+    private byte gunPort;
+    private byte gunStatus;
+    private byte insertGunStatus;
+    private byte onlineStatus;
+    private byte chargingStatus;
+    private long createTime;
+    private long updateTime;
+    private int flage;
+}

+ 40 - 0
src/main/java/com/tmzn/devicelinkykc/entity/GdDispostion.java

@@ -0,0 +1,40 @@
+package com.tmzn.devicelinkykc.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @author xp
+ * @date 2024/7/2
+ * @explain "  "
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName(value = "ims_gd_disposition")
+public class GdDispostion implements Serializable {
+    /**
+     *  `id` int(11) NOT NULL AUTO_INCREMENT,
+     *   `code` varchar(255) NOT NULL COMMENT '编码',
+     *   `parameter_one` varchar(255) DEFAULT NULL COMMENT '参数1',
+     *   `parameter_two` varchar(255) DEFAULT NULL COMMENT '参数2',
+     *   `parameter_three` varchar(255) DEFAULT NULL COMMENT '参数3',
+     *   `parameter_four` varchar(255) DEFAULT NULL COMMENT '参数4',
+     *   `type` int(11) DEFAULT NULL COMMENT '0: 云快充平台参数;1. 高德组织机构代码',
+     */
+
+    @TableId(type = IdType.AUTO,value = "id")
+    private Integer id;
+    private String code;
+    private String parameter_one;
+    private String parameter_two;
+    private String parameter_three;
+    private String parameter_four;
+    private int type;
+}

+ 57 - 0
src/main/java/com/tmzn/devicelinkykc/entity/OrderStatus.java

@@ -0,0 +1,57 @@
+package com.tmzn.devicelinkykc.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 订单状态 "
+ */
+@Data
+@TableName(value = "ims_ykc_order_status")
+public class OrderStatus implements Serializable {
+    /**
+     `id` int(11) NOT NULL AUTO_INCREMENT,
+     `device_sn` varchar(64) DEFAULT NULL COMMENT '设备sn码',
+     `device_imei` varchar(64) DEFAULT NULL COMMENT '设备imei',
+     `trans_order` blob COMMENT '交易流水号',
+     `card` blob,
+     `pile_code` varchar(64) DEFAULT NULL COMMENT '云快充桩编码',
+     `guns_code` tinyint(1) DEFAULT NULL COMMENT '枪号',
+     `start_money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '启充金额',
+     `now_order_status` tinyint(1) DEFAULT '0' COMMENT '当前订单状态:0代表正在充电,1代表充电结束',
+     `charging_init_status` tinyint(1) DEFAULT '0' COMMENT '空闲状态切换至充电状态的首次初始化状态上送:0未上送,1已上送',
+     `stop_charging_reply` tinyint(1) DEFAULT '0' COMMENT '停止指令回复云快充:0未回复,1已回复(预防设备停止充电多次上送消息)',
+     `transaction_order_reporting_action_status` tinyint(1) DEFAULT '0' COMMENT '交易流水中间协议层上送云快充状态:0未上送,1已上送',
+     `transaction_order_reply_status` tinyint(1) DEFAULT '0' COMMENT '云快充对中间协议层上送是交易流水解析结果:0默认是未收到回复,1解析成功,2解析失败非法账单',
+     `reason_stop_charging` tinyint(1) DEFAULT '0' COMMENT '0否停止原因',特殊情况未插枪云快充启充,为了不当断网停充处理原因给1
+     `original_text` blob COMMENT '上报云快充交易记录原文',
+     `create_time` bigint(16) DEFAULT NULL COMMENT '创建订单状态时间即充电开始时间',
+     `end_time` bigint(16) DEFAULT '0' COMMENT '停止充电时间',
+     */
+    @TableId(type = IdType.AUTO,value = "id")
+    private Integer id;
+    private String deviceSn;
+    private String deviceImei;
+    private byte[] transOrder;
+    private byte[] card;
+    private String pileCode;
+    private byte gunsCode;
+    private BigDecimal startMoney;
+    private byte nowOrderStatus;
+    private byte chargingInitStatus;
+    private byte stopChargingReply;
+    private byte transactionOrderReportingActionStatus;
+    private byte transactionOrderReplyStatus;
+    private int reasonStopCharging;
+    private byte[] originalText;
+    private long createTime;
+    private long endTime;
+
+
+}

+ 118 - 0
src/main/java/com/tmzn/devicelinkykc/entity/TransOrder.java

@@ -0,0 +1,118 @@
+package com.tmzn.devicelinkykc.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+* 
+*
+*/
+@Data
+@TableName(value = "ims_ykc_trans_order")
+public class TransOrder implements Serializable {
+
+    /**
+    * 
+    */
+    @TableId(type = IdType.AUTO,value = "id")
+    private Integer id;
+
+    private String trans;
+    /**
+    * 设备sn码
+    */
+    private String deviceSn;
+    /**
+    * 设备imei
+    */
+
+    private String deviceImei;
+    /**
+    * 云快充桩编码
+    */
+
+    private String pileCode;
+    /**
+    * 枪号
+    */
+    private Integer gunsCode;
+    /**
+    * 启充金额
+    */
+    private BigDecimal startMoney;
+    /**
+    * 0否停止原因
+    */
+    private Integer reasonStopCharging;
+    /**
+    * 创建订单状态时间即充电开始时间
+    */
+    private Long createTime;
+    /**
+    * 充电开始时间
+    */
+    @JsonFormat( pattern= "yyyy-MM-dd HH:ss:mm",timezone="GMT+8")
+    private Date startTime;
+    /**
+    * 充电结束时间
+    */
+    @JsonFormat( pattern= "yyyy-MM-dd HH:ss:mm",timezone="GMT+8")
+    private Date endTime;
+    /**
+    * 尖电量
+    */
+    private BigDecimal sharpElec;
+    /**
+    * 尖金额
+    */
+    private BigDecimal sharpMoney;
+    /**
+    * 峰电量
+    */
+    private BigDecimal peakElec;
+    /**
+    * 峰金额
+    */
+    private BigDecimal peakMoney;
+    /**
+    * 平电量
+    */
+    private BigDecimal flatElec;
+    /**
+    * 平金额
+    */
+    private BigDecimal flatMoney;
+    /**
+    *  谷电量
+    */
+    private BigDecimal valleyElec;
+    /**
+    * 谷金额
+    */
+    private BigDecimal valleyMoney;
+    /**
+    * 总电量
+    */
+    private BigDecimal elec;
+    /**
+    * 总金额
+    */
+    private BigDecimal money;
+    /**
+    * 云快充校验通过,1是校验通过0是校验失败
+    */
+    private byte flage;
+    /**
+    * 交易流水号
+    */
+    private byte[] transOrder;
+
+}

+ 186 - 0
src/main/java/com/tmzn/devicelinkykc/entity/param/AjaxResult.java

@@ -0,0 +1,186 @@
+package com.tmzn.devicelinkykc.entity.param;
+
+
+import cn.hutool.http.HttpStatus;
+
+import java.util.HashMap;
+
+/**
+ * 操作消息提醒
+ * 
+ * @author ruoyi
+ */
+public class AjaxResult extends HashMap<String, Object>
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 状态码 */
+    public static final String CODE_TAG = "code";
+
+    /** 返回内容 */
+    public static final String MSG_TAG = "msg";
+
+    /** 数据对象 */
+    public static final String DATA_TAG = "data";
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
+     */
+    public AjaxResult()
+    {
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     * 
+     * @param code 状态码
+     * @param msg 返回内容
+     */
+    public AjaxResult(int code, String msg)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     * 
+     * @param code 状态码
+     * @param msg 返回内容
+     * @param data 数据对象
+     */
+    public AjaxResult(int code, String msg, Object data)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+        if (data != null)
+        {
+            super.put(DATA_TAG, data);
+        }
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @return 成功消息
+     */
+    public static AjaxResult success()
+    {
+        return AjaxResult.success("操作成功");
+    }
+
+    /**
+     * 返回成功数据
+     * 
+     * @return 成功消息
+     */
+    public static AjaxResult success(Object data)
+    {
+        return AjaxResult.success("操作成功", data);
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @param msg 返回内容
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg)
+    {
+        return AjaxResult.success(msg, null);
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg, Object data)
+    {
+        return new AjaxResult(HttpStatus.HTTP_OK, msg, data);
+    }
+
+    /**
+     * 返回警告消息
+     *
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult warn(String msg)
+    {
+        return AjaxResult.warn(msg, null);
+    }
+
+    /**
+     * 返回警告消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static AjaxResult warn(String msg, Object data)
+    {
+        return new AjaxResult(HttpStatus.HTTP_OK, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @return 错误消息
+     */
+    public static AjaxResult error()
+    {
+        return AjaxResult.error("操作失败");
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @param msg 返回内容
+     * @return 错误消息
+     */
+    public static AjaxResult error(String msg)
+    {
+        return AjaxResult.error(msg, null);
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 错误消息
+     */
+    public static AjaxResult error(String msg, Object data)
+    {
+        return new AjaxResult(HttpStatus.HTTP_INTERNAL_ERROR, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @param code 状态码
+     * @param msg 返回内容
+     * @return 错误消息
+     */
+    public static AjaxResult error(int code, String msg)
+    {
+        return new AjaxResult(code, msg, null);
+    }
+
+    /**
+     * 方便链式调用
+     *
+     * @param key 键
+     * @param value 值
+     * @return 数据对象
+     */
+    @Override
+    public AjaxResult put(String key, Object value)
+    {
+        super.put(key, value);
+        return this;
+    }
+}

+ 17 - 0
src/main/java/com/tmzn/devicelinkykc/entity/param/DeviceInfo.java

@@ -0,0 +1,17 @@
+package com.tmzn.devicelinkykc.entity.param;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author xp
+ * @date 2024/4/10
+ * @explain "  "
+ */
+@Data
+public class DeviceInfo implements Serializable {
+    private String deviceImei;
+    private String deviceSn;
+    private String pileCode;
+}

+ 24 - 0
src/main/java/com/tmzn/devicelinkykc/entity/param/TransCheck.java

@@ -0,0 +1,24 @@
+package com.tmzn.devicelinkykc.entity.param;
+
+import lombok.Data;
+
+/**
+ * @author xp
+ * @date 2024/7/23
+ * @explain "  "
+ */
+@Data
+public class TransCheck {
+    private Long time;
+    private byte[] trans;
+    private int check_time;
+
+    public TransCheck() {
+    }
+
+    public TransCheck(Long time, byte[] trans, int check_time) {
+        this.time = time;
+        this.trans = trans;
+        this.check_time = check_time;
+    }
+}

+ 27 - 0
src/main/java/com/tmzn/devicelinkykc/entity/param/dto/BillingModelDTO.java

@@ -0,0 +1,27 @@
+package com.tmzn.devicelinkykc.entity.param.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * @author xp
+ * @date 2024/4/17
+ * @explain "  "
+ */
+@Data
+public class BillingModelDTO implements Serializable {
+
+    private String deviceImei;
+    private float sharpPrice;
+    private float sharpServiceFee;
+    private float peakPrice;
+    private float peakServiceFee;
+    private float flatPrice;
+    private float flatServiceFee;
+    private float valleyPrice;
+    private float valleyServiceFee;
+    private String timeSlot;
+}

+ 24 - 0
src/main/java/com/tmzn/devicelinkykc/entity/param/dto/DeviceDTO.java

@@ -0,0 +1,24 @@
+package com.tmzn.devicelinkykc.entity.param.dto;
+
+import com.tmzn.devicelinkykc.entity.param.DeviceInfo;
+import lombok.Data;
+
+/**
+ * @author xp
+ * @date 2024/4/10
+ * @explain " 设备新增DTO "
+ */
+@Data
+public class DeviceDTO extends DeviceInfo {
+    private byte pileType;              //类型
+    private byte chargerGunNum;         //枪数量
+    private String commProtocolVer;     //云快充通讯协议版本
+    private String programVersion;      //桩程序版本
+    private byte netLinkType;           //网络连接类型
+    private String simCard;             //sim卡
+    private byte operators;             //运营商
+    private Integer operatorsId;             //第三方平台ID
+    private String token;               //token
+    private byte disabled;           //是否禁用云快充连接
+
+}

+ 24 - 0
src/main/java/com/tmzn/devicelinkykc/entity/param/vo/TransOrderVO.java

@@ -0,0 +1,24 @@
+package com.tmzn.devicelinkykc.entity.param.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.tmzn.devicelinkykc.entity.param.DeviceInfo;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+/**
+ * @author xp
+ * @date 2024/4/13
+ * @explain "  "
+ */
+@Data
+public class TransOrderVO extends DeviceInfo {
+    private String trans;
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:SS")
+    private Date startTime;
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:SS")
+    private Date endTime;
+    private int pageSize;       //页长
+    private int pageCurrent;    //当前页
+}

+ 138 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/DataConversion.java

@@ -0,0 +1,138 @@
+package com.tmzn.devicelinkykc.frameMsg;
+
+import java.math.BigDecimal;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 数据转换 "
+ */
+
+public class DataConversion {
+    private static final BigDecimal four= new BigDecimal("10000");
+    private static final BigDecimal five= new BigDecimal("100000");
+
+    /**
+     *
+     * @param arr
+     * @param decimal  当前需要除几位得到小数
+     * @param scale  保留位数
+     * @return
+     */
+    public static BigDecimal arrToBigDec(byte[] arr,int decimal,int scale){
+        byte[] binArray = new byte[arr.length];
+        for (int i = 0; i < arr.length; i++) {
+            binArray[i]=arr[arr.length-1-i];
+        }
+        //转十六进制字符串
+        String s = bytesToHexString(binArray);
+        long l = Long.parseLong(s, 16);
+        BigDecimal bigDecimal = new BigDecimal(l);
+        BigDecimal di = new BigDecimal(Math.pow(10,decimal));
+        BigDecimal divide = bigDecimal.divide(di, scale, BigDecimal.ROUND_DOWN);
+        return divide;
+    }
+
+    public static byte[] bin2ToTwo(double param) {
+        int intValue = (int) (param * 100); // 将小数部分乘以100000并转换为整数
+        byte[] byteArray = new byte[2]; // 创建一个长度为4的byte数组来表示尖单价
+        byteArray[1] = (byte) ((intValue >> 8) & 0xFF);
+        byteArray[0] = (byte) (intValue & 0xFF);
+        return byteArray;
+    }
+
+    public static byte[] bin2ToOne(BigDecimal param) {
+        int intValue = param .multiply(new BigDecimal("10")).intValue(); // 将小数部分乘以100000并转换为整数
+        byte[] byteArray = new byte[2]; // 创建一个长度为4的byte数组来表示尖单价
+        byteArray[1] = (byte) ((intValue >> 8) & 0xFF);
+        byteArray[0] = (byte) (intValue & 0xFF);
+        return byteArray;
+    }
+    public static byte[] bigDecimalToByteArr(double decimalValue){
+        // 使用 BigDecimal 进行精确计算并设置精度为小数点后四位
+        BigDecimal decimal = new BigDecimal(decimalValue);
+        BigDecimal binaryValue = decimal.setScale(4, BigDecimal.ROUND_DOWN);
+
+        // 将二进制表示转换为 byte 类型数组
+        String binaryString = binaryValue.toPlainString();
+        byte[] byteArray = new byte[binaryString.length()];
+        for (int i = 0; i < binaryString.length(); i++) {
+            if (binaryString.charAt(i) == '1') {
+                byteArray[i] = 1;
+            } else {
+                byteArray[i] = 0;
+            }
+        }
+        return byteArray;
+    }
+    public static byte[] bin4Tofiv(BigDecimal param) {
+        //double price = 2.12345; // 尖单价
+        BigDecimal value =five.multiply(param) ; // 将小数部分乘以100000并转换为整数
+        int intValue = value.intValue();
+        byte[] byteArray = new byte[4]; // 创建一个长度为4的byte数组来表示尖单价
+
+// 将整数按照大端字节序存储到byte数组中
+        byteArray[3] = (byte) ((intValue >> 24) & 0xFF);
+        byteArray[2] = (byte) ((intValue >> 16) & 0xFF);
+        byteArray[1] = (byte) ((intValue >> 8) & 0xFF);
+        byteArray[0] = (byte) (intValue & 0xFF);
+        return byteArray;
+    }
+
+    public static byte[] bin4Tofour(BigDecimal param) {
+        BigDecimal value =four.multiply(param) ; // 将小数部分乘以100000并转换为整数
+        int intValue = value.intValue(); // 将小数部分乘以10000并转换为整数
+        byte[] byteArray = new byte[4]; // 创建一个长度为4的byte数组来表示尖单价
+// 将整数按照大端字节序存储到byte数组中
+        byteArray[3] = (byte) ((intValue >> 24) & 0xFF);
+        byteArray[2] = (byte) ((intValue >> 16) & 0xFF);
+        byteArray[1] = (byte) ((intValue >> 8) & 0xFF);
+        byteArray[0] = (byte) (intValue & 0xFF);
+        return byteArray;
+    }
+
+
+    public static byte[] bin5Tofour(BigDecimal totalValue) {
+        BigDecimal value =four.multiply(totalValue) ; // 将小数部分乘以100000并转换为整数
+        int intValue = value.intValue(); // 将小数部分乘以10000并转换为整数/ 将小数部分乘以10000并转换为整数
+        //901200
+        byte[] binArray = new byte[5];
+        binArray[4] = (byte) ((intValue >> 24) & 0xFF); // 最高位
+        binArray[3] = (byte) ((intValue >> 24) & 0xFF); // 取高8位
+        binArray[2] = (byte) ((intValue >> 16) & 0xFF); // 取次高8位
+        binArray[1] = (byte) ((intValue >> 8) & 0xFF); // 取次低8位
+        binArray[0] = (byte) (intValue & 0xFF); // 取低8位
+        return binArray;
+    }
+    // 方法:将BCD码表示的字符串转换成byte数组
+    public static byte[] bcdToBytes(String data, int length) {
+        String formattedData = String.format("%-" + length * 2 + "s", data).replace(' ', '0');
+        int len = formattedData.length();
+        byte[] bcd = new byte[len / 2];
+        for (int i = 0; i < bcd.length; i++) {
+            int index = i * 2;
+            int high = Integer.parseInt(formattedData.substring(index, index + 1), 16);
+            int low = Integer.parseInt(formattedData.substring(index + 1, index + 2), 16);
+            bcd[i] = (byte) ((high << 4) | low);
+        }
+        return bcd;
+    }
+    public static String bytesToHexString(byte[] bytes) {
+        StringBuilder sb = new StringBuilder();
+        for (byte b : bytes) {
+            String hex = Integer.toHexString(b & 0xFF);
+            if (hex.length() == 1) {
+                sb.append('0');
+            }
+            sb.append(hex);
+            //sb.append("");
+        }
+        return sb.toString();
+    }
+    public static byte[] temp(byte[] b){
+        for (int i = 0; i < b.length; i++) {
+            b[i]=0x00;
+        }
+        return b;
+    }
+}

+ 104 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/FrameDataSplicing.java

@@ -0,0 +1,104 @@
+package com.tmzn.devicelinkykc.frameMsg;
+
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 帧数据拼接 "
+ */
+public class FrameDataSplicing {
+    /**
+     *
+     * @param seq  序列号
+     * @param frameType     帧类型
+     * @param encryptFlag   加密标志
+     * @param data          消息体
+     * @param length           长度
+     * @return
+     */
+    public static byte[] spliceing(int seq, int frameType, int encryptFlag, byte[] data, int length) {
+        byte[] outBuf = new byte[8 + data.length];
+        int index = 0;
+        outBuf[index++] = (byte) 0x68; //其实字节
+        outBuf[index++] = (byte) (length + 4); //字节长度
+        int seqTemp = seq;
+        outBuf[index++] = (byte) ((seqTemp >> 8) & 0x00FF);
+        outBuf[index++] = (byte) (seqTemp & 0x00FF);
+        outBuf[index++] = (byte) encryptFlag;
+        outBuf[index++] = (byte) frameType;
+        System.arraycopy(data, 0, outBuf, index, length);
+        index += length;
+        int crc = modbusCRC(Arrays.copyOfRange(outBuf, 2, index), index - 2);
+        int crc16 = modbus16CRC(Arrays.copyOfRange(outBuf, 2, index), index - 2);
+        outBuf[index++] = (byte) (crc & 0x00FF);
+        outBuf[index++] = (byte) ((crc >> 8) & 0x00FF);
+        System.out.println("发送消息>>>"+ DeviceSendYkc.getNameByframeType(frameType)+">>>>>>>>>>"+DataConversion.bytesToHexString(outBuf));
+        return outBuf;
+    }
+
+    /**
+     * CRC 计算函数
+     */
+    private static int modbusCRC(byte[] pData, int len) {
+        int crc = 0xFFFF;
+        for (int i = 0; i < len; i++) {
+            crc = (crc ^ pData[i]) & 0xFF;
+            for (int j = 0; j < 8; j++) {
+                if ((crc & 0x0001) != 0) {
+                    crc = ((crc >> 1) ^ 0xA001) & 0xFFFF;
+                } else {
+                    crc >>= 1;
+                }
+            }
+        }
+        return crc;
+    }
+
+    /**
+     * CRC 计算函数
+     */
+    private static int modbus16CRC(byte[] pData, int len) {
+        int crc = 0xFFFF;
+        for (int i = 0; i < len; i++) {
+            crc ^= pData[i] & 0xFF;
+            for (int j = 0; j < 8; j++) {
+                if ((crc & 0x0001) != 0) {
+                    crc >>= 1;
+                    crc ^= 0xA001;
+                } else {
+                    crc >>= 1;
+                }
+            }
+        }
+        return crc;
+    }
+    /**
+     * 流水号生成
+     */
+    public static byte[] transactionNum(String pilecode,int msgCount){
+        int index = 0;
+        byte[] transaction = new byte[16];  //1.交易流水,自己生成上报状态
+//        byte[] pileCodebytes = DataConversion.bcdToBytes(pilecode, 7);
+//        Date date = new Date();
+//        SimpleDateFormat yy = new SimpleDateFormat("yy-MM-dd-HH-mm-ss");
+//        String format = yy.format(date);
+//        System.out.println(format);
+//        String[] split = format.split("-");
+//        byte[] time1 = new byte[6];
+//        time1[0] = 20;
+//        for (int i = 1; i < split.length; i++) {
+//            time1[i] = Byte.parseByte(split[i - 1]);
+//        }
+//
+//        System.arraycopy(pileCodebytes, 0, transaction, 0, pileCodebytes.length);
+//        System.arraycopy(time1, 0, transaction, 7, time1.length);
+//        transaction[14] = (byte) ((msgCount >> 8) & 0xFF);//自增
+//        transaction[15] = (byte) (msgCount & 0xFF);
+        return transaction;
+    }
+}

+ 306 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/RealTimeMoney.java

@@ -0,0 +1,306 @@
+package com.tmzn.devicelinkykc.frameMsg;
+
+import com.tmzn.devicelinkykc.constant.ykc.BillingModelConst;
+import com.tmzn.devicelinkykc.entity.BillingModel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.time.*;
+import java.time.temporal.ChronoUnit;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author xp
+ * @date 2024/3/22
+ * @explain " TODO:这里存在问题,功率是变化的,测试时也可能会出现问题,考虑完善该逻辑,当前测试时将功率固定测试 "
+ */
+@Component
+@Slf4j
+public class RealTimeMoney {
+
+    public static final BigDecimal hourToMin = new BigDecimal(60);
+
+
+    public Map<String, BigDecimal> start(Integer power, BillingModel billingModel, Long startTimeMillis, Long nowTimeMillis) {
+        BigDecimal powerInt = new BigDecimal(power);
+        String str = billingModel.getTimeSlot();
+        String[] model = str.split("");
+
+        //开始
+        LocalDateTime startDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(startTimeMillis), java.time.ZoneId.systemDefault());
+        LocalDate starlocalDate = Instant.ofEpochMilli(startTimeMillis).atZone(ZoneId.systemDefault()).toLocalDate();
+        int startHour = startDateTime.getHour();
+        int startMinute = startDateTime.getMinute();
+        LocalTime startTime = LocalTime.of(startHour, startMinute);
+
+        //现在
+        LocalDateTime nowDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(nowTimeMillis), java.time.ZoneId.systemDefault());
+        LocalDate nowlocalDate = Instant.ofEpochMilli(nowTimeMillis).atZone(ZoneId.systemDefault()).toLocalDate();
+        int nowHour = nowDateTime.getHour();
+        int nowMinute = nowDateTime.getMinute();
+        LocalTime nowTime = LocalTime.of(nowHour, nowMinute);
+
+        //开始时间点在的半小时时间段
+        int startTimeSlot = startTime.toSecondOfDay() / 1800;
+        String ss = model[startTimeSlot];
+        byte startTimePirce = Byte.parseByte(ss);
+
+        //结束时间点在的半小时时间段
+        int nowTimeSlot = nowTime.toSecondOfDay() / 1800;
+        String ns = model[nowTimeSlot];
+        byte nowTimePirce = Byte.parseByte(ns);
+
+
+        //当前时间和开始充电时间相差天数,防止跨天充电
+        long until = starlocalDate.until(nowlocalDate, ChronoUnit.DAYS);
+        //BigDecimal money = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        Map<String, BigDecimal> map = new HashMap<>();
+        if (until < 1) {
+            //相差0,同一天的充电
+            log.info("实时数据:同一天的充电,数据计算>>>>");
+            map = onDayMoney(powerInt, billingModel, startTimeMillis, nowTimeMillis);
+            //money = money.add(onDayMoney(powerInt, billingModel, startTimeMillis, nowTimeMillis));
+
+        } else if (until == 1) {
+            //跨一天 开始时间到晚上24:00  24:00到结束时间
+            log.info("实时数据:跨一天的充电,数据计算>>>>");
+            map = differentDayMoney(powerInt, billingModel, startTimeMillis, nowTimeMillis);
+        } else {
+            //跨多天
+            log.info("实时数据:跨多天的充电,数据计算>>>>");
+            Map<String, BigDecimal> mapall = allDayMoney(powerInt, billingModel);
+            map = differentDayMoney(powerInt, billingModel, startTimeMillis, nowTimeMillis);
+
+            BigDecimal elecAll = mapall.get("elec");
+            BigDecimal moneyAll = mapall.get("money");
+
+            BigDecimal elecDiff = map.get("elec");
+            BigDecimal moneyDiff = map.get("money");
+
+            BigDecimal elec = elecDiff.add(elecAll.multiply(new BigDecimal(until - 1L)).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal money = moneyDiff.add(moneyAll.multiply(new BigDecimal(until - 1L)).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+            map.put("elec", elec);
+            map.put("money", money);
+        }
+        Map<String, BigDecimal> finalMap = map;
+        map.keySet().stream().forEach(s ->{
+            log.info(s+":"+finalMap.get(s));
+        });
+        return map;
+    }
+
+    /**
+     * 跨整天的费用数据
+     *
+     * @param power
+     * @param billingModel
+     * @return
+     */
+    private Map<String, BigDecimal> allDayMoney(BigDecimal power, BillingModel billingModel) {
+        BigDecimal money = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        BigDecimal elec = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        String str = billingModel.getTimeSlot();
+        String[] model = str.split("");
+        for (String s : model) {
+            byte b = Byte.parseByte(s);
+            money = money.add(money(b, new BigDecimal(30), power, billingModel));
+            BigDecimal chargTime = new BigDecimal(30).divide(hourToMin,4,BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(chargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+        Map<String, BigDecimal> decimalHashMap = new HashMap<>();
+        decimalHashMap.put("money", money);
+        decimalHashMap.put("elec", elec);
+        return decimalHashMap;
+    }
+
+    /**
+     * 跨天数的费率计算
+     *
+     * @param power
+     * @param billingModel
+     * @param startTimeMillis
+     * @param nowTimeMillis
+     * @return
+     */
+    private Map<String, BigDecimal> differentDayMoney(BigDecimal power, BillingModel billingModel, Long startTimeMillis, Long nowTimeMillis) {
+        BigDecimal money = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        BigDecimal elec = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        String str = billingModel.getTimeSlot();
+        String[] model = str.split("");
+
+        //开始
+        LocalDateTime startDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(startTimeMillis), java.time.ZoneId.systemDefault());
+        LocalDate starlocalDate = Instant.ofEpochMilli(startTimeMillis).atZone(ZoneId.systemDefault()).toLocalDate();
+        int startHour = startDateTime.getHour();
+        int startMinute = startDateTime.getMinute();
+        int startSecond=startDateTime.getSecond();
+        LocalTime startTime = LocalTime.of(startHour, startMinute);
+
+        //现在
+        LocalDateTime nowDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(nowTimeMillis), java.time.ZoneId.systemDefault());
+        LocalDate nowlocalDate = Instant.ofEpochMilli(nowTimeMillis).atZone(ZoneId.systemDefault()).toLocalDate();
+        int nowHour = nowDateTime.getHour();
+        int nowMinute = nowDateTime.getMinute();
+        int nowSecond=nowDateTime.getSecond();
+        LocalTime nowTime = LocalTime.of(nowHour, nowMinute);
+
+        //开始时间点在的半小时时间段
+        int startTimeSlot = startTime.toSecondOfDay() / 1800;
+        String ss = model[startTimeSlot];
+        byte startTimePirce = Byte.parseByte(ss);
+
+        //结束时间点在的半小时时间段
+        int nowTimeSlot = nowTime.toSecondOfDay() / 1800;
+        String ns = model[nowTimeSlot];
+        byte nowTimePirce = Byte.parseByte(ns);
+
+        if (startTimeSlot == 47) {
+            //特殊情况:开始时间段就在最后半小时实际那段,没有24:00 的时间,只能单独计算费用
+            BigDecimal bigDecimal = new BigDecimal(24 * 60 * 60 - (startHour * 60 * 60 + startMinute * 60 + startSecond));
+            BigDecimal time = bigDecimal.divide(hourToMin, 2, BigDecimal.ROUND_DOWN);
+            money = money.add(money(startTimePirce, time, power, billingModel));
+            BigDecimal startchargTime = time.divide(hourToMin,4,BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(startchargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+        //47段不会进这个循环
+        for (int i = startTimeSlot + 1; i <= 47; i++) {
+            byte b = Byte.parseByte(model[i]);
+            money = money.add(money(b, new BigDecimal(30), power, billingModel));
+            BigDecimal chargTime = new BigDecimal(30).divide(hourToMin,4,BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(chargTime.divide(hourToMin).multiply(power)).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+        //计算00:00到当前时间的费用
+        //计算当前半小时段内充的电费
+        BigDecimal bigDecimal = new BigDecimal((nowHour * 60 * 60 + nowMinute * 60 + nowSecond) - (nowTimeSlot) * 30 * 60);
+        BigDecimal time = bigDecimal.divide(hourToMin, 2, BigDecimal.ROUND_DOWN);
+        money = money.add(money(nowTimePirce, time, power, billingModel));
+        BigDecimal nowchargTime =time.divide(hourToMin,4,BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+        elec = elec.add(nowchargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+
+        //当前第一段时不会进循环
+        for (int i = 0; i < nowTimeSlot; i++) {
+            byte b = Byte.parseByte(model[i]);
+            money = money.add(money(b, new BigDecimal(30), power, billingModel));
+            BigDecimal chargTime = new BigDecimal(30).divide(hourToMin,4,BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(chargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+        Map<String, BigDecimal> decimalHashMap = new HashMap<>();
+        decimalHashMap.put("money", money);
+        decimalHashMap.put("elec", elec);
+        return decimalHashMap;
+    }
+
+    /**
+     * 同一天的费率计算
+     *
+     * @param power
+     * @param billingModel
+     * @param startTimeMillis
+     * @param nowTimeMillis
+     * @return
+     */
+    private Map<String, BigDecimal> onDayMoney(BigDecimal power, BillingModel billingModel, Long startTimeMillis, Long nowTimeMillis) {
+        String str = billingModel.getTimeSlot();
+        String[] model = str.split("");
+        //时间段的模型
+        //String[] model = billingModel.getTimeSlot().split("");
+        LocalDateTime startDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(startTimeMillis), java.time.ZoneId.systemDefault());
+        // 获取日,小时和分钟
+        //int
+        int startHour = startDateTime.getHour();
+        int startMinute = startDateTime.getMinute();
+        int startSecond=startDateTime.getSecond();
+
+        // 当前时间戳创建LocalDateTime对象
+        LocalDateTime nowDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(nowTimeMillis), java.time.ZoneId.systemDefault());
+        // 获取小时和分钟 秒
+        int nowHour = nowDateTime.getHour();
+        int nowMinute = nowDateTime.getMinute();
+        int nowSecond = nowDateTime.getSecond();
+
+        //开始时间点
+        LocalTime startTime = LocalTime.of(startHour, startMinute);
+        //当前时间点
+        LocalTime nowTime = LocalTime.of(nowHour, nowMinute);
+        //开始时间点在的半小时时间段
+        int startTimeSlot = startTime.toSecondOfDay() / 1800;
+        String ss = model[startTimeSlot];
+        byte startTimePirce = Byte.parseByte(ss);
+
+        //结束时间点在的半小时时间段
+        int nowTimeSlot = nowTime.toSecondOfDay() / 1800;
+        String ns = model[nowTimeSlot];
+        byte nowTimePirce = Byte.parseByte(ns);
+
+        BigDecimal money = new BigDecimal(0);
+        BigDecimal elec = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        if (startTimeSlot != nowTimeSlot) {
+            //1111.算出,同一天跨时段充电 电费
+            //启充时间到现在夸多个时段 19  31
+            for (int i = startTimeSlot + 1; i <= nowTimeSlot - 1; i++) {
+                byte b = Byte.parseByte(model[i]);
+                BigDecimal startchargTime = new BigDecimal(30).divide(hourToMin,4,BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+                money = money.add(money(b, startchargTime, power, billingModel));
+                elec = elec.add(startchargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+            }
+            //开始段费率计算
+            BigDecimal start = new BigDecimal((startTimeSlot + 1) * 30 * 60 - (startHour * 60 * 60 + startMinute * 60 + startSecond));
+            BigDecimal startBigDecimal = start.divide(hourToMin, 2, BigDecimal.ROUND_DOWN);
+            money = money.add(money(startTimePirce, startBigDecimal, power, billingModel));
+            BigDecimal startchargTime = startBigDecimal.divide(hourToMin,4,BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(startchargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+            //结束段费率计算
+            //int now = (nowHour * 60 * 60 + nowMinute * 60 +nowSecond)  - nowTimeSlot * 30 * 60;
+            BigDecimal now = new BigDecimal( (nowHour * 60 * 60 + nowMinute * 60 +nowSecond)  - nowTimeSlot * 30 * 60);
+            BigDecimal nowBigDecimal = now.divide(hourToMin, 2, BigDecimal.ROUND_DOWN);
+            money = money.add(money(nowTimePirce, nowBigDecimal, power, billingModel));
+            BigDecimal nowchargTime = nowBigDecimal.divide(hourToMin,4,BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(nowchargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        } else {
+            //2222.算出同一天的不跨时段充电的电费
+            BigDecimal charging = new BigDecimal((nowMinute * 60 +nowSecond) - (startMinute * 60 + startSecond)).setScale(0);
+            BigDecimal chargingTime = charging.divide(hourToMin, 2, BigDecimal.ROUND_DOWN);
+            //money=money(nowTimePirce,chargingTime,power);一样的计费还在同一个费率段内
+            money = money.add(money(startTimePirce, chargingTime, power, billingModel)).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal chargTime = chargingTime.divide(hourToMin,4,BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(chargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+
+        Map<String, BigDecimal> decimalHashMap = new HashMap<>();
+        decimalHashMap.put("money", money);
+        decimalHashMap.put("elec", elec);
+        return decimalHashMap;
+    }
+
+    /**
+     * @param b            模型
+     * @param chargingTime 充电时间
+     * @param power        功率
+     * @return
+     */
+    private BigDecimal money(byte b, BigDecimal chargingTime, BigDecimal power, BillingModel billingModel) {
+        //电价  段电价+服务费
+        BigDecimal sharpPrice = billingModel.getSharpPrice().add(billingModel.getSharpServiceFee()).setScale(5, BigDecimal.ROUND_DOWN);
+        BigDecimal peakPrice = billingModel.getPeakPrice().add(billingModel.getPeakServiceFee()).setScale(5, BigDecimal.ROUND_DOWN);
+        BigDecimal flatPrice = billingModel.getFlatPrice().add(billingModel.getFlatServiceFee()).setScale(5, BigDecimal.ROUND_DOWN);
+        BigDecimal valleyPrice = billingModel.getValleyPrice().add(billingModel.getValleyServiceFee()).setScale(5, BigDecimal.ROUND_DOWN);
+
+        BigDecimal money = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+
+        BigDecimal bigDecimal = new BigDecimal(60);
+        BigDecimal divideTime = chargingTime.divide(bigDecimal, 4, BigDecimal.ROUND_DOWN);
+        if (b == BillingModelConst.SHARP) {
+            //power*pirce*time/60
+            money = divideTime.multiply(sharpPrice).setScale(4, BigDecimal.ROUND_DOWN).multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+        } else if (b == BillingModelConst.PEAK) {
+            money = divideTime.multiply(peakPrice).setScale(4, BigDecimal.ROUND_DOWN).multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+        } else if (b == BillingModelConst.FLAT) {
+            money = divideTime.multiply(flatPrice).setScale(4, BigDecimal.ROUND_DOWN).multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+        } else if (b == BillingModelConst.VALLEY) {
+            money = divideTime.multiply(valleyPrice).setScale(4, BigDecimal.ROUND_DOWN).multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+        return money;
+    }
+}

+ 617 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/TransMoney.java

@@ -0,0 +1,617 @@
+package com.tmzn.devicelinkykc.frameMsg;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import com.alibaba.fastjson2.JSONObject;
+import com.tmzn.devicelinkykc.constant.ykc.BillingModelConst;
+import com.tmzn.devicelinkykc.entity.BillingModel;
+import com.tmzn.devicelinkykc.entity.param.dto.BillingModelDTO;
+import com.tmzn.devicelinkykc.mapstruct.BillingModelMapping;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.time.*;
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author xp
+ * @date 2024/3/23
+ * @explain "  "
+ */
+@Component
+@Slf4j
+public class TransMoney {
+
+    public static final BigDecimal hourToMin = new BigDecimal(60);
+
+    public static Map<String, BigDecimal> transData = new HashMap<>();
+
+    @Value("${billingInterface}")
+    private String url;
+    @Autowired
+    private BillingModelMapping billingModelMapping;
+
+    /**
+     * 调用后台计费
+     * @param port
+     * @param billingModel
+     * @param startTime
+     * @param endTime
+     * @param retMap
+     * @return
+     */
+    public Map<String,BigDecimal> compute(int port,BillingModel billingModel,Long startTime,Long endTime,boolean retMap) throws Exception{
+        BigDecimal bigDecimal = new BigDecimal("0.0000");
+        transData.put("elec",bigDecimal);
+        transData.put("elec1",bigDecimal);
+        transData.put("elec2",bigDecimal);
+        transData.put("elec3",bigDecimal);
+        transData.put("elec4",bigDecimal);
+        transData.put("money",bigDecimal);
+        transData.put("money1",bigDecimal);
+        transData.put("money2",bigDecimal);
+        transData.put("money3",bigDecimal);
+        transData.put("money4",bigDecimal);
+        if (!retMap){
+            return transData;
+        }
+        try {
+            TimeUnit.MILLISECONDS.sleep(3000);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        HashMap<String, Object> map = new HashMap<>();
+        BillingModelDTO billingModelDTO = billingModelMapping.billingModelToBillingModelDto(billingModel);
+        map.put("port",port);
+        map.put("billingModel",billingModelDTO);
+        //为了防止落库时间差
+        map.put("startTime",(startTime-5*1000)/1000);
+        map.put("endTime",(endTime+5*1000)/1000);
+        JSONObject jsonObject = new JSONObject(map);
+        String s = jsonObject.toString();
+        log.info("后台计费请求参数>>>>"+s);
+        //String url = "http://your-api-url.com/post";
+//        og.info("后台计费请求参数>>>>"+json);
+        //final String url = "http://your-api-url.com/post"; // 替换成您的API URL
+        //final String data = "s"; // 替换成您要发送的数据
+
+        OkHttpClient client = new OkHttpClient.Builder()
+                .connectTimeout(10000, TimeUnit.MILLISECONDS)
+                .readTimeout(10000, TimeUnit.MILLISECONDS)
+                // 其他配置...
+                .build();
+
+        // 创建表单数据
+        FormBody.Builder formBuilder = new FormBody.Builder();
+        formBuilder.add("data", s);
+
+        // 创建请求
+        Request request = new Request.Builder()
+                .url(url)
+                .post(formBuilder.build())
+                .build();
+
+        // 执行请求
+        try (Response response = client.newCall(request).execute()) {
+            // 检查响应状态码
+            if (!response.isSuccessful()) {
+                log.info("Unexpected code " + response);
+                return transData;
+            }
+
+            // 读取响应内容
+            String responseBody = response.body().string();
+            System.out.println("Response Body: " + responseBody);
+            try {
+
+                if (null==responseBody||"".equals(responseBody)){
+                    log.info("后台响应responseBody-null>>>"+responseBody);
+                    return transData;
+                }
+                log.info("responseBody>>>"+responseBody);
+                JSONObject parseObject = JSONObject.parseObject(responseBody);
+                //log.info("后台计费响应参数>>>>"+parseObject.toString());
+                boolean rv1 = parseObject.containsKey("rv");
+                if (!rv1){
+                    log.info("rv>>>not found>transData:0");
+                    return transData;
+                }
+                Integer rv = parseObject.getInteger("rv");
+                if (rv==1){
+                    //返回正确,PHP端防止精度丢失全部采用整数发送 需要保留四位小数 除以10000
+                    BigDecimal precise = new BigDecimal(10000);  //1000模拟大电流充电
+                    JSONObject parseObjectJSONObject = parseObject.getJSONObject("elec_map");
+                    transData.put("elec",parseObjectJSONObject.getBigDecimal("elec").divide(precise,4,BigDecimal.ROUND_DOWN));
+                    transData.put("elec1",parseObjectJSONObject.getBigDecimal("elec1").divide(precise,4,BigDecimal.ROUND_DOWN));
+                    transData.put("elec2",parseObjectJSONObject.getBigDecimal("elec2").divide(precise,4,BigDecimal.ROUND_DOWN));
+                    transData.put("elec3",parseObjectJSONObject.getBigDecimal("elec3").divide(precise,4,BigDecimal.ROUND_DOWN));
+                    transData.put("elec4",parseObjectJSONObject.getBigDecimal("elec4").divide(precise,4,BigDecimal.ROUND_DOWN));
+                    transData.put("money",parseObjectJSONObject.getBigDecimal("money").divide(precise,4,BigDecimal.ROUND_DOWN));
+                    transData.put("money1",parseObjectJSONObject.getBigDecimal("money1").divide(precise,4,BigDecimal.ROUND_DOWN));
+                    transData.put("money2",parseObjectJSONObject.getBigDecimal("money2").divide(precise,4,BigDecimal.ROUND_DOWN));
+                    transData.put("money3",parseObjectJSONObject.getBigDecimal("money3").divide(precise,4,BigDecimal.ROUND_DOWN));
+                    transData.put("money4",parseObjectJSONObject.getBigDecimal("money4").divide(precise,4,BigDecimal.ROUND_DOWN));
+                    return transData;
+                }
+            }catch (Exception e){
+                log.info("计费处理异常>>"+e);
+                e.printStackTrace();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+//        HttpResponse execute = HttpRequest.post(url).form("data",s).timeout(10000).execute();
+//        int status = execute.getStatus();
+//        log.info("HttpResponse>>>>"+execute.toString());
+////        System.out.println(execute.toString());
+////        System.out.println("status>>>"+status);
+//        if (status==200){
+//            try {
+//
+//                String body = execute.body();
+//                if (null==body||"".equals(body)){
+//                    log.info("后台响应body-null>>>"+body);
+//                    return transData;
+//                }
+//                log.info("body>>>"+body);
+//                JSONObject parseObject = JSONObject.parseObject(body);
+//                //log.info("后台计费响应参数>>>>"+parseObject.toString());
+//                boolean rv1 = parseObject.containsKey("rv");
+//                if (!rv1){
+//                    log.info("rv>>>not found>transData:0");
+//                    return transData;
+//                }
+//                Integer rv = parseObject.getInteger("rv");
+//                if (rv==1){
+//                    //返回正确,PHP端防止精度丢失全部采用整数发送 需要保留四位小数 除以10000
+//                    BigDecimal precise = new BigDecimal(10000);  //1000模拟大电流充电
+//                    JSONObject parseObjectJSONObject = parseObject.getJSONObject("elec_map");
+//                    transData.put("elec",parseObjectJSONObject.getBigDecimal("elec").divide(precise,4,BigDecimal.ROUND_DOWN));
+//                    transData.put("elec1",parseObjectJSONObject.getBigDecimal("elec1").divide(precise,4,BigDecimal.ROUND_DOWN));
+//                    transData.put("elec2",parseObjectJSONObject.getBigDecimal("elec2").divide(precise,4,BigDecimal.ROUND_DOWN));
+//                    transData.put("elec3",parseObjectJSONObject.getBigDecimal("elec3").divide(precise,4,BigDecimal.ROUND_DOWN));
+//                    transData.put("elec4",parseObjectJSONObject.getBigDecimal("elec4").divide(precise,4,BigDecimal.ROUND_DOWN));
+//                    transData.put("money",parseObjectJSONObject.getBigDecimal("money").divide(precise,4,BigDecimal.ROUND_DOWN));
+//                    transData.put("money1",parseObjectJSONObject.getBigDecimal("money1").divide(precise,4,BigDecimal.ROUND_DOWN));
+//                    transData.put("money2",parseObjectJSONObject.getBigDecimal("money2").divide(precise,4,BigDecimal.ROUND_DOWN));
+//                    transData.put("money3",parseObjectJSONObject.getBigDecimal("money3").divide(precise,4,BigDecimal.ROUND_DOWN));
+//                    transData.put("money4",parseObjectJSONObject.getBigDecimal("money4").divide(precise,4,BigDecimal.ROUND_DOWN));
+//                    return transData;
+//                }
+//            }catch (Exception e){
+//                log.info("计费处理异常>>"+e);
+//                e.printStackTrace();
+//            }
+//
+//        }
+
+        return transData;
+    }
+
+
+
+
+    /**
+     * 测试一个时间段的计费
+     *
+     * @param model
+     * @param startTime
+     * @param endTime
+     * @param elecAll
+     * @return
+     */
+    public static Map<String, BigDecimal> startElec(BillingModel model, Long startTime, Long endTime, BigDecimal elecAll)  throws Exception{
+        //elecAll = new BigDecimal("3.5");
+        BigDecimal star = new BigDecimal(startTime);
+        BigDecimal end = new BigDecimal(endTime);
+        BigDecimal time = (end.subtract(star)).divide(new BigDecimal(1000 * 60 * 60), 4, BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+        BigDecimal elec = time.multiply(elecAll).setScale(4, BigDecimal.ROUND_DOWN);
+        BigDecimal money = elec.multiply(model.getSharpPrice().add(model.getSharpServiceFee())).setScale(4, BigDecimal.ROUND_DOWN);
+
+
+        BigDecimal init = new BigDecimal(0);
+        transData.put("money", money);
+        transData.put("elec", elec);
+        transData.put("money1", money);
+        transData.put("money2", init);
+        transData.put("money3", init);
+        transData.put("money4", init);
+        transData.put("elec1", elec);
+        transData.put("elec2", init);
+        transData.put("elec3", init);
+        transData.put("elec4", init);
+        log.info("测试计费map>>>");
+        transData.keySet().stream().forEach(s -> log.info(s + ":" + transData.get(s)));
+        return transData;
+    }
+
+
+    /**
+     * 功率计算费用
+     *
+     * @param power
+     * @param billingModel
+     * @param startTimeMillis
+     * @param endTime
+     * @return
+     */
+    public Map<String, BigDecimal> start(Integer power, BillingModel billingModel, Long startTimeMillis, Long endTime)  throws Exception{
+        Date startDate = new Date(startTimeMillis);
+        Date endDate = new Date(endTime);
+        log.info("startTime:"+startDate+";endTime:"+endDate);
+        BigDecimal init = new BigDecimal(0);
+        transData.put("money1", init);
+        transData.put("money2", init);
+        transData.put("money3", init);
+        transData.put("money4", init);
+        transData.put("elec1", init);
+        transData.put("elec2", init);
+        transData.put("elec3", init);
+        transData.put("elec4", init);
+        BigDecimal powerInt = new BigDecimal(power);
+        String str = billingModel.getTimeSlot();
+        String[] model = str.split("");
+        long nowTimeMillis = 0L;
+        if (endTime == 0) {
+            nowTimeMillis = System.currentTimeMillis();
+        } else {
+            nowTimeMillis = endTime;
+        }
+
+        //开始
+        LocalDateTime startDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(startTimeMillis), java.time.ZoneId.systemDefault());
+        LocalDate starlocalDate = Instant.ofEpochMilli(startTimeMillis).atZone(ZoneId.systemDefault()).toLocalDate();
+        int startHour = startDateTime.getHour();
+        int startMinute = startDateTime.getMinute();
+        LocalTime startTime = LocalTime.of(startHour, startMinute);
+
+        //现在
+        LocalDateTime nowDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(nowTimeMillis), java.time.ZoneId.systemDefault());
+        LocalDate nowlocalDate = Instant.ofEpochMilli(nowTimeMillis).atZone(ZoneId.systemDefault()).toLocalDate();
+        int nowHour = nowDateTime.getHour();
+        int nowMinute = nowDateTime.getMinute();
+        LocalTime nowTime = LocalTime.of(nowHour, nowMinute);
+
+        //开始时间点在的半小时时间段
+        int startTimeSlot = startTime.toSecondOfDay() / 1800;
+        String ss = model[startTimeSlot];
+        byte startTimePirce = Byte.parseByte(ss);
+
+        //结束时间点在的半小时时间段
+        int nowTimeSlot = nowTime.toSecondOfDay() / 1800;
+        String ns = model[nowTimeSlot];
+        byte nowTimePirce = Byte.parseByte(ns);
+
+
+        //当前时间和开始充电时间相差天数,防止跨天充电
+        long until = starlocalDate.until(nowlocalDate, ChronoUnit.DAYS);
+        //BigDecimal money = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        Map<String, BigDecimal> map = new HashMap<>();
+        if (until < 1) {
+            //相差0,同一天的充电
+            log.info("交易记录:同一天的充电,数据计算>>>>");
+            map = onDayMoney(powerInt, billingModel, startTimeMillis, nowTimeMillis);
+            //money = money.add(onDayMoney(powerInt, billingModel, startTimeMillis, nowTimeMillis));
+
+        } else if (until == 1) {
+            //跨一天 开始时间到晚上24:00  24:00到结束时间
+            log.info("交易记录:跨一天的充电,数据计算>>>>");
+            map = differentDayMoney(powerInt, billingModel, startTimeMillis, nowTimeMillis);
+        } else {
+            //跨多天
+            log.info("交易记录:跨多天的充电,数据计算>>>>");
+            Map<String, BigDecimal> mapall = allDayMoney(powerInt, billingModel);
+            map = differentDayMoney(powerInt, billingModel, startTimeMillis, nowTimeMillis);
+
+            BigDecimal elecAll = mapall.get("elec");
+            BigDecimal moneyAll = mapall.get("money");
+
+            BigDecimal elecDiff = map.get("elec");
+            BigDecimal moneyDiff = map.get("money");
+
+            BigDecimal elec = elecDiff.add(elecAll.multiply(new BigDecimal(until - 1L)).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal money = moneyDiff.add(moneyAll.multiply(new BigDecimal(until - 1L)).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+            map.put("elec", elec);
+            map.put("money", money);
+        }
+        transData.put("elec", map.get("elec"));
+        transData.put("money", map.get("money"));
+
+        Map<String, BigDecimal> finaltransData = transData;
+        finaltransData.keySet().stream().forEach(s -> {
+            log.info(s + ":" + finaltransData.get(s));
+        });
+        return transData;
+    }
+
+    /**
+     * 跨整天的费用数据
+     *
+     * @param power
+     * @param billingModel
+     * @return
+     */
+    private Map<String, BigDecimal> allDayMoney(BigDecimal power, BillingModel billingModel)  throws Exception{
+        BigDecimal money = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        BigDecimal elec = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        String str = billingModel.getTimeSlot();
+        String[] model = str.split("");
+        for (String s : model) {
+            byte b = Byte.parseByte(s);
+            money = money.add(money(b, new BigDecimal(30), power, billingModel));
+            BigDecimal chargTime = new BigDecimal(30).divide(hourToMin, 4, BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(chargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+        Map<String, BigDecimal> decimalHashMap = new HashMap<>();
+        decimalHashMap.put("money", money);
+        decimalHashMap.put("elec", elec);
+        return decimalHashMap;
+    }
+
+    /**
+     * 跨天数的费率计算
+     *
+     * @param power
+     * @param billingModel
+     * @param startTimeMillis
+     * @param nowTimeMillis
+     * @return
+     */
+    private Map<String, BigDecimal> differentDayMoney(BigDecimal power, BillingModel billingModel, Long startTimeMillis, Long nowTimeMillis) throws Exception {
+        BigDecimal money = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        BigDecimal elec = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        String str = billingModel.getTimeSlot();
+        String[] model = str.split("");
+
+        //开始
+        LocalDateTime startDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(startTimeMillis), java.time.ZoneId.systemDefault());
+        LocalDate starlocalDate = Instant.ofEpochMilli(startTimeMillis).atZone(ZoneId.systemDefault()).toLocalDate();
+        int startHour = startDateTime.getHour();
+        int startMinute = startDateTime.getMinute();
+        int startSecond = startDateTime.getSecond();
+        LocalTime startTime = LocalTime.of(startHour, startMinute);
+
+        //现在
+        LocalDateTime nowDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(nowTimeMillis), java.time.ZoneId.systemDefault());
+        LocalDate nowlocalDate = Instant.ofEpochMilli(nowTimeMillis).atZone(ZoneId.systemDefault()).toLocalDate();
+        int nowHour = nowDateTime.getHour();
+        int nowMinute = nowDateTime.getMinute();
+        int nowSecond = nowDateTime.getSecond();
+        LocalTime nowTime = LocalTime.of(nowHour, nowMinute);
+
+        //开始时间点在的半小时时间段
+        int startTimeSlot = startTime.toSecondOfDay() / 1800;
+        String ss = model[startTimeSlot];
+        byte startTimePirce = Byte.parseByte(ss);
+
+        //结束时间点在的半小时时间段
+        int nowTimeSlot = nowTime.toSecondOfDay() / 1800;
+        String ns = model[nowTimeSlot];
+        byte nowTimePirce = Byte.parseByte(ns);
+
+        if (startTimeSlot == 47) {
+            //特殊情况:开始时间段就在最后半小时实际那段,没有24:00 的时间,只能单独计算费用
+            BigDecimal bigDecimal = new BigDecimal(24 * 60 * 60 - (startHour * 60 * 60 + startMinute * 60 + startSecond));
+            BigDecimal time = bigDecimal.divide(hourToMin, 2, BigDecimal.ROUND_DOWN);
+            money = money.add(money(startTimePirce, time, power, billingModel));
+            BigDecimal startchargTime = time.divide(hourToMin, 4, BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(startchargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+        //47段不会进这个循环
+        for (int i = startTimeSlot + 1; i <= 47; i++) {
+            byte b = Byte.parseByte(model[i]);
+            money = money.add(money(b, new BigDecimal(30), power, billingModel));
+            BigDecimal chargTime = new BigDecimal(30).divide(hourToMin, 4, BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(chargTime.divide(hourToMin,2,BigDecimal.ROUND_DOWN).multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+        //计算00:00到当前时间的费用
+        //计算当前半小时段内充的电费
+        BigDecimal bigDecimal = new BigDecimal((nowHour * 60 * 60 + nowMinute * 60 + nowSecond) - (nowTimeSlot) * 30 * 60);
+        BigDecimal time = bigDecimal.divide(hourToMin, 2, BigDecimal.ROUND_DOWN);
+        money = money.add(money(nowTimePirce, time, power, billingModel));
+        BigDecimal nowchargTime = time.divide(hourToMin, 4, BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+        elec = elec.add(nowchargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+
+        //当前第一段时不会进循环
+        for (int i = 0; i < nowTimeSlot; i++) {
+            byte b = Byte.parseByte(model[i]);
+            money = money.add(money(b, new BigDecimal(30), power, billingModel));
+            BigDecimal chargTime = new BigDecimal(30).divide(hourToMin, 4, BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(chargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+        Map<String, BigDecimal> decimalHashMap = new HashMap<>();
+        decimalHashMap.put("money", money);
+        decimalHashMap.put("elec", elec);
+        return decimalHashMap;
+    }
+
+    /**
+     * 同一天的费率计算
+     *
+     * @param power
+     * @param billingModel
+     * @param startTimeMillis
+     * @param nowTimeMillis
+     * @return
+     */
+    private Map<String, BigDecimal> onDayMoney(BigDecimal power, BillingModel billingModel, Long startTimeMillis, Long nowTimeMillis)  throws Exception{
+        String str = billingModel.getTimeSlot();
+        String[] model = str.split("");
+        //时间段的模型
+        //String[] model = billingModel.getTimeSlot().split("");
+        LocalDateTime startDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(startTimeMillis), java.time.ZoneId.systemDefault());
+        // 获取日,小时和分钟
+        //int
+        int startHour = startDateTime.getHour();
+        int startMinute = startDateTime.getMinute();
+        int startSecond = startDateTime.getSecond();
+
+        // 当前时间戳创建LocalDateTime对象
+        LocalDateTime nowDateTime = LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(nowTimeMillis), java.time.ZoneId.systemDefault());
+        // 获取小时和分钟
+        int nowHour = nowDateTime.getHour();
+        int nowMinute = nowDateTime.getMinute();
+        int nowSecond = nowDateTime.getSecond();
+        //开始时间点
+        LocalTime startTime = LocalTime.of(startHour, startMinute);
+        //当前时间点
+        LocalTime nowTime = LocalTime.of(nowHour, nowMinute);
+        //开始时间点在的半小时时间段
+        int startTimeSlot = startTime.toSecondOfDay() / 1800;
+        String ss = model[startTimeSlot];
+        byte startTimePirce = Byte.parseByte(ss);
+
+        //结束时间点在的半小时时间段
+        int nowTimeSlot = nowTime.toSecondOfDay() / 1800;
+        String ns = model[nowTimeSlot];
+        byte nowTimePirce = Byte.parseByte(ns);
+
+        BigDecimal money = new BigDecimal(0);
+        BigDecimal elec = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+        if (startTimeSlot != nowTimeSlot) {
+            //1111.算出,同一天跨时段充电 电费
+            //启充时间到现在夸多个时段 19  31
+            for (int i = startTimeSlot + 1; i <= nowTimeSlot - 1; i++) {
+                byte b = Byte.parseByte(model[i]);
+                BigDecimal startchargTime = new BigDecimal(30).divide(hourToMin, 4, BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+                money = money.add(money(b, startchargTime, power, billingModel));
+                elec = elec.add(startchargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+            }
+            //开始段费率计算
+            BigDecimal start = new BigDecimal((startTimeSlot + 1) * 30 * 60 - (startHour * 60 * 60 + startMinute * 60 + startSecond));
+            BigDecimal startBigDecimal = start.divide(hourToMin, 2, BigDecimal.ROUND_DOWN);
+            money = money.add(money(startTimePirce, startBigDecimal, power, billingModel));
+            BigDecimal startchargTime = startBigDecimal.divide(hourToMin, 4, BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(startchargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+            //结束段费率计算
+            BigDecimal now = new BigDecimal((nowHour * 60 * 60 + nowMinute * 60 + nowSecond) - nowTimeSlot * 30 * 60);
+            BigDecimal nowBigDecimal = now.divide(hourToMin, 2, BigDecimal.ROUND_DOWN);
+            money = money.add(money(nowTimePirce, nowBigDecimal, power, billingModel));
+            BigDecimal nowchargTime = nowBigDecimal.divide(hourToMin, 4, BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(nowchargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        } else {
+            //2222.算出同一天的不跨时段充电的电费
+            BigDecimal charging = new BigDecimal((nowMinute * 60 + nowSecond) - (startMinute * 60 + startSecond)).setScale(0);
+            BigDecimal chargingTime = charging.divide(hourToMin, 2, BigDecimal.ROUND_DOWN);
+            //money=money(nowTimePirce,chargingTime,power);一样的计费还在同一个费率段内
+            money = money.add(money(startTimePirce, chargingTime, power, billingModel));
+            BigDecimal chargTime = chargingTime.divide(hourToMin, 4, BigDecimal.ROUND_DOWN).setScale(4, BigDecimal.ROUND_DOWN);
+            elec = elec.add(chargTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN)).setScale(4, BigDecimal.ROUND_DOWN);
+        }
+
+        Map<String, BigDecimal> decimalHashMap = new HashMap<>();
+        decimalHashMap.put("money", money);  //总金额
+        decimalHashMap.put("elec", elec);     //总电量
+        return decimalHashMap;
+    }
+
+    /**
+     * @param b            模型
+     * @param chargingTime 充电时间
+     * @param power        功率
+     * @return
+     */
+    private BigDecimal money(byte b, BigDecimal chargingTime, BigDecimal power, BillingModel billingModel)  throws Exception{
+        //电价  段电价+服务费
+        BigDecimal sharpPrice = billingModel.getSharpPrice().add(billingModel.getSharpServiceFee()).setScale(5, BigDecimal.ROUND_DOWN);
+        BigDecimal peakPrice = billingModel.getPeakPrice().add(billingModel.getPeakServiceFee()).setScale(5, BigDecimal.ROUND_DOWN);
+        BigDecimal flatPrice = billingModel.getFlatPrice().add(billingModel.getFlatServiceFee()).setScale(5, BigDecimal.ROUND_DOWN);
+        BigDecimal valleyPrice = billingModel.getValleyPrice().add(billingModel.getValleyServiceFee()).setScale(5, BigDecimal.ROUND_DOWN);
+        BigDecimal money = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+
+        BigDecimal bigDecimal = new BigDecimal(60);
+        BigDecimal divideTime = chargingTime.divide(bigDecimal, 4, BigDecimal.ROUND_DOWN);
+
+        BigDecimal elec = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+
+
+        if (b == BillingModelConst.SHARP) {
+            //段计费
+            money = divideTime.multiply(sharpPrice).multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal money1 = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+            if (transData.containsKey("money1")) {
+                money1 = transData.get("money1");
+            }
+            money1 = money1.add(money).setScale(4, BigDecimal.ROUND_DOWN);
+            transData.put("money1", money1);
+
+            //段电量
+            elec = divideTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal elec1 = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+            if (transData.containsKey("elec1")) {
+                elec1 = transData.get("elec1");
+            }
+            elec1 = elec1.add(elec).setScale(4, BigDecimal.ROUND_DOWN);
+            transData.put("elec1", elec1);
+
+        } else if (b == BillingModelConst.PEAK) {
+            //段计费
+            money = divideTime.multiply(peakPrice).multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal money2 = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+            if (transData.containsKey("money2")) {
+                money2 = transData.get("money2");
+            }
+            money2 = money2.add(money).setScale(4, BigDecimal.ROUND_DOWN);
+            transData.put("money2", money2);
+
+            //段电量
+            elec = divideTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal elec2 = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+            if (transData.containsKey("elec2")) {
+                elec2 = transData.get("elec2");
+            }
+            elec2 = elec2.add(elec).setScale(4, BigDecimal.ROUND_DOWN);
+            transData.put("elec2", elec2);
+
+        } else if (b == BillingModelConst.FLAT) {
+            //段计费
+            money = divideTime.multiply(flatPrice).multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal money3 = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+            if (transData.containsKey("money3")) {
+                money3 = transData.get("money3");
+            }
+            money3 = money3.add(money).setScale(4, BigDecimal.ROUND_DOWN);
+            transData.put("money3", money3);
+
+            //段电量
+            elec = divideTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal elec3 = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+            if (transData.containsKey("elec3")) {
+                elec3 = transData.get("elec3");
+            }
+            elec3 = elec3.add(elec).setScale(4, BigDecimal.ROUND_DOWN);
+            transData.put("elec3", elec3);
+
+        } else if (b == BillingModelConst.VALLEY) {
+            //段计费
+            money = divideTime.multiply(valleyPrice).multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal money4 = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+            if (transData.containsKey("money4")) {
+                money4 = transData.get("money4");
+            }
+            money4 = money4.add(money).setScale(4, BigDecimal.ROUND_DOWN);
+            transData.put("money4", money4);
+
+            //段电量
+            elec = divideTime.multiply(power).setScale(4, BigDecimal.ROUND_DOWN);
+            BigDecimal elec4 = new BigDecimal(0).setScale(4, BigDecimal.ROUND_DOWN);
+            if (transData.containsKey("elec4")) {
+                elec4 = transData.get("elec4");
+            }
+            elec4 = elec4.add(elec).setScale(4, BigDecimal.ROUND_DOWN);
+            transData.put("elec4", elec4);
+
+        }
+        return money;
+    }
+}

+ 121 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/BillingModelFrame.java

@@ -0,0 +1,121 @@
+package com.tmzn.devicelinkykc.frameMsg.frameType;
+
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.util.Encrytion;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+/**
+ * @author xp
+ * @date 2024/3/19
+ * @explain " 加密 "
+ */
+@Component
+@Slf4j
+public class BillingModelFrame {
+    @Autowired
+    private RedisCache redisCache;
+
+
+    /**
+     * 校验计费模型
+     * @param deviceConnectionMsg
+     */
+    public  void checkBillingModel(DeviceConnectionMsg deviceConnectionMsg){
+        byte[] params = checkModel(deviceConnectionMsg);
+        String key = redisCache.getCacheObject(RedisConstant.KEYS+deviceConnectionMsg.getDeviceId());
+
+        byte[] encrypt = new byte[0];
+        try {
+            encrypt = Encrytion.aesEncrypt(params, key.getBytes());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        byte[] spliceing = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.BILLING_MODEL_VALIDATE_REQUEST.getFrameType(), DeviceSendYkc.BILLING_MODEL_VALIDATE_REQUEST.getEncryptFlag(), encrypt, encrypt.length);
+
+        try {
+            deviceConnectionMsg.getOutputStream().write(spliceing);
+            deviceConnectionMsg.getOutputStream().flush();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        deviceConnectionMsg.incrementMessageCount();
+    }
+    private byte[] checkModel(DeviceConnectionMsg deviceConnectionMsg){
+        String deviceId = deviceConnectionMsg.getDeviceId();
+        byte[] pile_code = new byte[7];
+        pile_code = DataConversion.bcdToBytes(deviceId, 7);
+        byte[] msg = new byte[9];
+        System.arraycopy(pile_code, 0, msg, 0, pile_code.length);
+        byte[] num = new byte[]{0x10,0x00};
+
+        System.arraycopy(num, 0, msg, pile_code.length, num.length);
+        return msg;
+    }
+
+
+    /**
+     * 充电桩获取计费模型请求
+     * @param deviceConnectionMsg
+     */
+    public  void getBillingModel(DeviceConnectionMsg deviceConnectionMsg){
+        byte[] params = getModelParams(deviceConnectionMsg);
+        String key = redisCache.getCacheObject(RedisConstant.KEYS+deviceConnectionMsg.getDeviceId());
+
+        try {
+            byte[] encrypt = Encrytion.aesEncrypt(params, key.getBytes());
+            byte[] spliceing = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.BILLING_MODEL_REQUEST.getFrameType(), DeviceSendYkc.BILLING_MODEL_REQUEST.getEncryptFlag(), encrypt, encrypt.length);
+            deviceConnectionMsg.getOutputStream().write(spliceing);
+            deviceConnectionMsg.getOutputStream().flush();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        deviceConnectionMsg.incrementMessageCount();
+    }
+
+    private byte[] getModelParams(DeviceConnectionMsg deviceConnectionMsg){
+        String deviceId = deviceConnectionMsg.getDeviceId();
+        byte[] pile_code = new byte[7];
+        pile_code = DataConversion.bcdToBytes(deviceId, 7);
+        return pile_code;
+    }
+
+    /**
+     * 计费模型设置应答
+     * @param deviceConnectionMsg
+     * @param res
+     */
+    public void resp(DeviceConnectionMsg deviceConnectionMsg,byte res){
+        byte[] params = params(deviceConnectionMsg.getDeviceId(),res);
+        String key = redisCache.getCacheObject(RedisConstant.KEYS+deviceConnectionMsg.getDeviceId());
+        try {
+            byte[] encrypt = Encrytion.aesEncrypt(params, key.getBytes());
+            byte[] spliceing = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.BILLING_MODEL_SETTING_RESPONSE.getFrameType(), DeviceSendYkc.BILLING_MODEL_SETTING_RESPONSE.getEncryptFlag(), encrypt, encrypt.length);
+            deviceConnectionMsg.getOutputStream().write(spliceing);
+            deviceConnectionMsg.getOutputStream().flush();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        deviceConnectionMsg.incrementMessageCount();
+    }
+
+    private byte[] params(String pileCode,byte res){
+        byte[] pile_code = new byte[7];
+        pile_code = DataConversion.bcdToBytes(pileCode, 7);
+        byte[] msg = new byte[8];
+        System.arraycopy(pile_code, 0, msg, 0, pile_code.length);
+        byte[] num = new byte[]{res};
+        System.arraycopy(num, 0, msg, pile_code.length, num.length);
+        return msg;
+    }
+
+
+}

+ 126 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/CharngingPushFrame.java

@@ -0,0 +1,126 @@
+package com.tmzn.devicelinkykc.frameMsg.frameType;
+
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.util.Encrytion;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author xp
+ * @date 2024/3/20
+ * @explain "  "
+ */
+@Component
+@Slf4j
+public class CharngingPushFrame {
+
+    @Autowired
+    private RedisCache redisCache;
+
+
+    /**
+     * 启充结果
+     * @param deviceConnectionMsg
+     * @param trans
+     * @param port
+     * @param result
+     * @param reson
+     */
+    public void startStatus(DeviceConnectionMsg deviceConnectionMsg,byte[] trans,int port,int result,int reson){
+        byte[] params = startParams(deviceConnectionMsg.getDeviceId(), trans, port, result, reson);
+        log.info("startStatus>开始充电回复>>>"+DataConversion.bytesToHexString(params));
+        String key = redisCache.getCacheObject(RedisConstant.KEYS+deviceConnectionMsg.getDeviceId());
+        try {
+            byte[] encrypt = Encrytion.aesEncrypt(params, key.getBytes());
+            byte[] spliceing = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.START_CHARNGING_RESPONSE.getFrameType(), DeviceSendYkc.START_CHARNGING_RESPONSE.getEncryptFlag(), encrypt, encrypt.length);
+            deviceConnectionMsg.getOutputStream().write(spliceing);
+            deviceConnectionMsg.getOutputStream().flush();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        deviceConnectionMsg.incrementMessageCount();
+
+    }
+    private byte[] startParams(String pileCode,byte[] trans,int port,int result,int reson){
+
+
+        byte[] msg = new byte[26];
+        int index=0;
+        //流水号
+        byte[] transaction = new byte[16];
+        System.arraycopy(trans, 0, msg, index, transaction.length);
+        index += transaction.length;
+
+        //枪号
+        byte[] pile_code = new byte[7];
+        pile_code = DataConversion.bcdToBytes(pileCode, 7);
+        System.arraycopy(pile_code, 0, msg, index, pile_code.length);
+        index+=pile_code.length;
+
+        //枪号
+        byte[] guns = new byte[]{(byte) port};
+        System.arraycopy(guns, 0, msg, index, guns.length);
+        index++;
+
+        //启动结果
+        byte[] resltass = new byte[]{(byte) result};
+        System.arraycopy(resltass, 0, msg, index, resltass.length);
+        index++;
+
+        //失败原因
+        byte[] resonarr = new byte[]{(byte) reson};
+        log.info("启充成功或失败原因>>>reason>>>"+resonarr[0]+"???"+reson);
+        System.arraycopy(resonarr, 0, msg, index, resonarr.length);
+
+        return msg;
+    }
+
+    public void endStatus(DeviceConnectionMsg deviceConnectionMsg,int port,int result,int reson){
+        byte[] params = endParams(deviceConnectionMsg.getDeviceId(), port, result, reson);
+        log.info("endStatus>停止充电回复>>>"+DataConversion.bytesToHexString(params));
+        String key = redisCache.getCacheObject(RedisConstant.KEYS+deviceConnectionMsg.getDeviceId());
+        try {
+            byte[] encrypt = Encrytion.aesEncrypt(params, key.getBytes());
+            byte[] spliceing = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.STOP_CHARNGING_RESPONSE.getFrameType(), DeviceSendYkc.STOP_CHARNGING_RESPONSE.getEncryptFlag(), encrypt, encrypt.length);
+            deviceConnectionMsg.getOutputStream().write(spliceing);
+            deviceConnectionMsg.getOutputStream().flush();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        deviceConnectionMsg.incrementMessageCount();
+
+    }
+    private byte[] endParams(String pileCode,int port,int result,int reson){
+
+
+        byte[] msg = new byte[10];
+        int index=0;
+        //桩号
+        byte[] pile_code = new byte[7];
+        pile_code = DataConversion.bcdToBytes(pileCode, 7);
+        System.arraycopy(pile_code, 0, msg, index, pile_code.length);
+        index+=pile_code.length;
+
+        //枪号
+        byte[] guns = new byte[]{(byte) port};
+        System.arraycopy(guns, 0, msg, index, guns.length);
+        index++;
+
+        //启动结果
+        byte[] resltass = new byte[]{(byte) result};
+        System.arraycopy(resltass, 0, msg, index, resltass.length);
+        index++;
+
+        //失败原因
+        byte[] resonarr = new byte[]{(byte) reson};
+        System.arraycopy(resltass, 0, msg, index, resonarr.length);
+
+        return msg;
+    }
+}

+ 60 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/CheckTime.java

@@ -0,0 +1,60 @@
+package com.tmzn.devicelinkykc.frameMsg.frameType;
+
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.msgEnum.YkcSendDevice;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.util.CP56Time2a;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author xp
+ * @date 2024/3/19
+ * @explain " 不加密 "
+ */
+@Component
+@Slf4j
+public class CheckTime {
+    public void checkTimeSend(DeviceConnectionMsg deviceConnectionMsg) {
+        byte[] bytes = charngTime(deviceConnectionMsg);
+        log.info("checkTimeSend>设备校时上送>>>"+DataConversion.bytesToHexString(bytes));
+        byte[] pack = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.CHECKTIME_RESPONSE.getFrameType(), DeviceSendYkc.CHECKTIME_RESPONSE.getEncryptFlag(), bytes, bytes.length);
+        try {
+            deviceConnectionMsg.getOutputStream().write(pack);
+            deviceConnectionMsg.getOutputStream().flush();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        deviceConnectionMsg.incrementMessageCount();
+    }
+
+    public byte[] charngTime(DeviceConnectionMsg deviceConnection) {
+        try {
+            byte[] pile_code = new byte[7];
+            pile_code = DataConversion.bcdToBytes(deviceConnection.getDeviceId(), 7);
+            byte[] time = new byte[7];
+            String format = new SimpleDateFormat("SS-ss-mm-HH-dd-MM-yy").format(new Date());
+            String substring = format.substring(1);
+            byte[] byteArray = CP56Time2a.timeCP56Time2a(substring);
+            for (int i = 0; i < byteArray.length; i++) {
+                time[i] = byteArray[i];
+            }
+            byte[] msg = new byte[14];
+            System.arraycopy(pile_code, 0, msg, 0, pile_code.length);
+            System.arraycopy(time, 0, msg, pile_code.length, time.length);
+            for (byte b : msg) {
+                System.out.printf("%02X ", b);
+            }
+            return msg;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 53 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/HeartFrameSend.java

@@ -0,0 +1,53 @@
+package com.tmzn.devicelinkykc.frameMsg.frameType;
+
+import com.tmzn.devicelinkykc.constant.ykc.StatusConstant;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+/**
+ * @author xp
+ * @date 2024/3/14
+ * @explain " 充电桩心跳包 不加密"
+ */
+@Component
+@Slf4j(topic = "HeartFrameSend")
+public class HeartFrameSend {
+
+    public void heartSend(DeviceConnectionMsg deviceConnectionMsg, DeviceStatus device){
+        byte[] send = heartSend(device.getPileCode(), device.getGunPort(), device.getGunStatus()==StatusConstant.FAULT?StatusConstant.HEART_GUNS_STATUS_FAULT:StatusConstant.HEART_GUNS_STATUS_OK);
+        //log.info("heartSend>设备心跳消息上送>>>"+DataConversion.bytesToHexString(send));
+        byte[] bytes = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.HEART_SEND.getFrameType(), DeviceSendYkc.HEART_SEND.getEncryptFlag(), send, send.length);
+        try {
+            deviceConnectionMsg.getOutputStream().write(bytes);
+            deviceConnectionMsg.getOutputStream().flush();
+        } catch (IOException e) {
+            log.info("pileCode:"+device.getPileCode()+" ,heart push frame Exception");
+            e.printStackTrace();
+        }
+        deviceConnectionMsg.incrementMessageCount();
+        log.info("pileCode:"+device.getPileCode()+" ,heart push frame over");
+    }
+
+    private   byte[] heartSend(String deviceId , int guns, byte port) {
+        byte[] msg = new byte[9];//消息体
+
+        //1.枪编码
+        byte[] pile_code = new byte[7];
+        pile_code= DataConversion.bcdToBytes(deviceId, pile_code.length);
+        System.arraycopy(pile_code, 0, msg, 0, pile_code.length);
+        //2.枪号
+        msg[7] = (byte) guns;
+        // 3.充电枪状态
+        msg[8] = port;
+        return msg;
+    }
+
+
+}

+ 141 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/LoginFrame.java

@@ -0,0 +1,141 @@
+package com.tmzn.devicelinkykc.frameMsg.frameType;
+
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.entity.GdDispostion;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.service.GdDispostionService;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import com.tmzn.devicelinkykc.util.Encrytion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.Base64;
+import java.util.Random;
+
+/**
+ * @author xp
+ * @date 2024/3/14
+ * @explain " 登录认证 不加密"
+ */
+@Component
+public class LoginFrame {
+
+    @Autowired
+    private SocketHandle socketHandle;
+
+    private static final Logger log = LoggerFactory.getLogger(LoginFrame.class);
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    private GdDispostionService gdDispostionService;
+
+    public void loginMsgSend(DeviceConnectionMsg deviceConnectionMsg,Device device){
+        byte[] send = new byte[0];
+        try {
+            send = loginMsg(device);
+        } catch (Exception e) {
+            log.info("pileCode:"+device.getPileCode()+" login params Exception:");
+            e.printStackTrace();
+        }
+        if (send.length>0) {
+            log.info("loginMsgSend>设备登录消息上送>>>"+DataConversion.bytesToHexString(send));
+            byte[] bytes = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.LOGIN.getFrameType(), DeviceSendYkc.LOGIN.getEncryptFlag(), send, send.length);
+            try {
+                deviceConnectionMsg.getOutputStream().write(bytes);
+                deviceConnectionMsg.getOutputStream().flush();
+                deviceConnectionMsg.incrementMessageCount();
+            } catch (IOException e) {
+                log.info("pileCode:" + device.getPileCode() + " ,login push frame Exception");
+                e.printStackTrace();
+                //发送异常直接停止
+                socketHandle.removeDeviceConnection(device.getPileCode());
+            }
+
+        }else {
+            log.info("pileCode:"+device.getPileCode()+" login params null");
+        }
+        log.info("pileCode:"+device.getPileCode()+" login over");
+    }
+
+
+    private byte[] loginMsg( Device device) throws Exception {
+        Random random = new Random();
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < 16; i++) {
+            sb.append(random.nextInt(10));
+        }
+        byte[] msg = new byte[127];//消息体
+        // 密钥
+        String str_key = sb.toString();
+        //当前通讯的密钥缓存到redis
+        //20240627:要求对接其他按云快充对接的
+        GdDispostion dispostionServiceById = gdDispostionService.getById(device.getOperatorsId());
+        redisCache.setCacheObject(RedisConstant.KEYS +device.getPileCode(), str_key);
+        log.info("LOGIN>>>>16weikey>>>>>>>"+str_key);
+        byte[] bytes = Encrytion.rsaEncrypt(str_key.getBytes(),dispostionServiceById.getCode());
+        byte[] base_encode = Base64.getEncoder().encode(bytes);
+        //1.随机密钥
+        byte[] random_key = new byte[88];
+        System.arraycopy(base_encode, 0, random_key, 0, base_encode.length);
+        int index = 0;
+        System.arraycopy(random_key, 0, msg, index, random_key.length);
+        index += random_key.length;
+        //2.桩编码
+        byte[] pile_code = new byte[7];
+        byte[] bcd = DataConversion.bcdToBytes(device.getPileCode(), pile_code.length);
+        System.arraycopy(bcd, 0, pile_code, 0, bcd.length);
+        System.arraycopy(pile_code, 0, msg, index, pile_code.length);
+        index += pile_code.length;
+        //3. 充电桩类型,0:直流,1:交流
+        byte[] pile_type = new byte[]{device.getPileType()};
+        System.arraycopy(pile_type, 0, msg, index, pile_type.length);
+        index += pile_type.length;
+        // 4.充电枪连接数
+        byte[] charger_gun_num = new byte[]{device.getChargerGunNum()};
+        System.arraycopy(charger_gun_num, 0, msg, index, charger_gun_num.length);
+        index += charger_gun_num.length;
+        // 5.通讯协议版本
+        byte[] comm_protocol_ver = new byte[3]; // 通讯协议版本
+        String[] split = device.getCommProtocolVer().split("\\.");
+        for (int i = 0; i < split.length; i++) {
+            comm_protocol_ver[i] = Byte.parseByte(split[i]);
+        }
+        System.arraycopy(comm_protocol_ver, 0, msg, index, comm_protocol_ver.length);
+        index += comm_protocol_ver.length;
+
+        //6.程序版本
+        byte[] program_version = new byte[8];
+        byte[] program_versions = device.getProgramVersion().getBytes();
+        System.arraycopy(program_versions, 0, program_version, 0, program_versions.length);
+        System.arraycopy(program_version, 0, msg, index, program_version.length);
+        index += program_version.length;
+        // 7.网络链接类型,0:sim 卡
+        byte[] net_link_type = new byte[]{device.getNetLinkType()};
+        System.arraycopy(net_link_type, 0, msg, index, net_link_type.length);
+        index += net_link_type.length;
+        //8.SIM卡
+        byte[] sim_card = new byte[10];
+        sim_card = DataConversion.bcdToBytes(device.getSimCard(), sim_card.length);
+        System.arraycopy(sim_card, 0, msg, index, sim_card.length);
+        index += sim_card.length;
+        // 9.运营商,0:移动
+        byte[] operators = new byte[]{device.getOperators()};
+        System.arraycopy(operators, 0, msg, index, operators.length);
+        index += operators.length;
+        //10 token
+        byte[] token = new byte[7];
+        token = DataConversion.bcdToBytes(device.getToken(), token.length);
+        System.arraycopy(token, 0, msg, index, token.length);
+        index += token.length;
+        return msg;
+    }
+}

+ 166 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/RealTimeStatusPushFrame.java

@@ -0,0 +1,166 @@
+package com.tmzn.devicelinkykc.frameMsg.frameType;
+
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.util.Encrytion;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author xp
+ * @date 2024/3/16
+ * @explain " 加密 "
+ */
+@Component
+@Slf4j(topic = "RealTimeStatusPushFrame")
+public class RealTimeStatusPushFrame {
+    @Autowired
+    private RedisCache redisCache;
+
+    public void deviceStatusPush(DeviceConnectionMsg deviceConnectionMsg, byte[] trans, String pileCode, byte guns, byte gunsStatus, byte gunsInsert, double voltage, double power, BigDecimal elec, BigDecimal money, int time) {
+        byte[] encrypt = new byte[0];
+        try {
+            byte[] send = upStatusDevice(trans, pileCode, guns, gunsStatus, gunsInsert, voltage, power, elec, money, time);
+            log.info("deviceStatusPush>设备实时状态上报>>>"+DataConversion.bytesToHexString(send));
+            String key = redisCache.getCacheObject(RedisConstant.KEYS+deviceConnectionMsg.getDeviceId());
+           // log.info("deviceStatusPush>设备实时状态key>>>"+key);
+            encrypt = Encrytion.aesEncrypt(send, key.getBytes());
+            log.info("deviceStatusPush>设备实时状态encrypt msg::>>>"+DataConversion.bytesToHexString(encrypt));
+        } catch (Exception e) {
+            log.info("pileCode:" + pileCode + " deviceStatusPush params Exception");
+            e.printStackTrace();
+        }
+        if (encrypt.length > 0) {
+            byte[] bytes = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.UPLOAD_DEVICE_STATUS_RESPONSE.getFrameType(), DeviceSendYkc.UPLOAD_DEVICE_STATUS_RESPONSE.getEncryptFlag(), encrypt, encrypt.length);
+            try {
+                deviceConnectionMsg.getOutputStream().write(bytes,0,bytes.length);
+                deviceConnectionMsg.getOutputStream().flush();
+            } catch (IOException e) {
+                log.info("pileCode:" + pileCode + " ,realTimeStatus push frame Exception");
+                e.printStackTrace();
+            }
+            deviceConnectionMsg.incrementMessageCount();
+        } else {
+            log.info("pileCode:" + pileCode + " deviceStatusPush params null");
+        }
+
+    }
+
+    private byte[] upStatusDevice(byte[] trans, String pileCode, byte guns, byte gunsStatus, byte gunsInsert, double voltage, double power, BigDecimal elec, BigDecimal money, int time) throws Exception {
+
+        byte[] msg = new byte[60];
+//1.交易流水号,,空闲自己生成上报状态,充电拿到交易流水
+        int index = 0;
+        byte[] transaction = new byte[16];
+        System.arraycopy(trans, 0, msg, index, transaction.length);
+        index += transaction.length;
+        //2.桩编码
+        byte[] pileCodebytes = new byte[7];
+        pileCodebytes = DataConversion.bcdToBytes(pileCode, pileCodebytes.length);
+        System.arraycopy(pileCodebytes, 0, msg, index, pileCodebytes.length);
+        index += pileCodebytes.length;
+//3.枪号
+        byte[] gunsBytes = new byte[1];
+        gunsBytes = new byte[]{guns};
+
+        System.arraycopy(gunsBytes, 0, msg, index, gunsBytes.length);
+        index += gunsBytes.length;
+//4.状态
+        byte[] guns_status = {gunsStatus};
+        System.arraycopy(guns_status, 0, msg, index, guns_status.length);
+        index += guns_status.length;
+//5.枪是否归位,默认归位
+        byte[] guns_return = {0x01};
+        System.arraycopy(guns_return, 0, msg, index, guns_return.length);
+        index += guns_return.length;
+//6.是否插枪
+        byte[] guns_insert = {gunsInsert};
+        System.arraycopy(guns_insert, 0, msg, index, guns_insert.length);
+        index += guns_insert.length;
+//7.输出电压
+        byte[] out_V = new byte[2];
+
+        out_V = DataConversion.bin2ToOne(new BigDecimal(voltage));
+        System.arraycopy(out_V, 0, msg, index, out_V.length);
+        index += out_V.length;
+
+        if (guns == 10) {
+            power = 9000;
+        }
+        byte[] out_A = new byte[2];           //8.输出电流
+        if (voltage == 0) {
+            out_A = new byte[]{0x00, 0x00};
+        } else {
+            out_A = DataConversion.bin2ToOne(new BigDecimal(power).divide(new BigDecimal(voltage),1,BigDecimal.ROUND_DOWN));
+        }
+        System.arraycopy(out_A, 0, msg, index, out_A.length);
+        index += out_A.length;
+
+        byte[] guns_line_temp = new byte[1];
+        //9.枪线温度
+        guns_line_temp[0] = 0x32;
+        System.arraycopy(guns_line_temp, 0, msg, index, guns_line_temp.length);
+        index += guns_line_temp.length;
+//10.枪线编码
+        byte[] guns_line_num = new byte[8];
+        guns_line_num[0] = 0x00;
+        guns_line_num[1] = 0x00;
+        guns_line_num[2] = 0x00;
+        guns_line_num[3] = 0x00;
+        guns_line_num[4] = 0x00;
+        guns_line_num[5] = 0x00;
+        guns_line_num[6] = 0x00;
+        guns_line_num[7] = 0x00;
+        System.arraycopy(guns_line_num, 0, msg, index, guns_line_num.length);
+        index += guns_line_num.length;
+        //11.SOC
+        byte[] soc = new byte[]{0x00};
+        System.arraycopy(soc, 0, msg, index, soc.length);
+        index += soc.length;
+//12.电池组最高温度
+        byte[] battery_temp = {0x32};
+        System.arraycopy(battery_temp, 0, msg, index, battery_temp.length);
+        index += battery_temp.length;
+//13. 累计充电时间 BIN 码 2 单位:min;待机置零
+        byte[] charnging_Time = new byte[2];
+        charnging_Time[1] = (byte) ((time >> 8) & 0xFF);
+        charnging_Time[0] = (byte) (time & 0xFF);
+        System.arraycopy(charnging_Time, 0, msg, index, charnging_Time.length);
+        index += charnging_Time.length;
+        //14. 剩余时间 BIN 码 2 单位:min;待机置零、交流桩置零
+        byte[] remain_time = {0x00, 0x00};
+        System.arraycopy(remain_time, 0, msg, index, remain_time.length);
+        index += remain_time.length;
+
+        //15 充电度数
+        byte[] charnging_kwh = new byte[4];
+        charnging_kwh = DataConversion.bin4Tofour(elec);
+        System.arraycopy(charnging_kwh, 0, msg, index, charnging_kwh.length);
+        index += charnging_kwh.length;
+        //16. 计损充电度数 BIN 码 4 精确到小数点后四位;待机置零未设置计损比例时等于充电度数
+        byte[] charnging_loss_kwh = new byte[4];
+        charnging_loss_kwh = DataConversion.bin4Tofour(new BigDecimal("0"));
+        System.arraycopy(charnging_loss_kwh, 0, msg, index, charnging_loss_kwh.length);
+        index += charnging_loss_kwh.length;
+//17. 已充金额 BIN 码 4 精确到小数点后四位;待机置零(电费+服务费)*计损充电度数
+        byte[] charnging_rmb = new byte[4];
+        charnging_rmb = DataConversion.bin4Tofour(money);
+        System.arraycopy(charnging_rmb, 0, msg, index, charnging_rmb.length);
+        index += charnging_rmb.length;
+
+        byte[] charnging_damage = {0x00, 0x00};//18. 硬件故障
+        System.arraycopy(charnging_damage, 0, msg, index, charnging_damage.length);
+        index += charnging_damage.length;
+        return msg;
+    }
+}

+ 72 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/RemoteBalanceUpdatePushFrame.java

@@ -0,0 +1,72 @@
+package com.tmzn.devicelinkykc.frameMsg.frameType;
+
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.util.Encrytion;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+/**
+ * @author xp
+ * @date 2024/3/26
+ * @explain "  "
+ */
+@Component
+@Slf4j
+public class RemoteBalanceUpdatePushFrame {
+
+    @Autowired
+    private RedisCache redisCache;
+
+    public void updateBalance(DeviceConnectionMsg deviceConnectionMsg,byte[] card,byte reason){
+        byte[] encrypt = new byte[0];
+        try {
+            byte[] send = params(deviceConnectionMsg.getDeviceId(), card,reason);
+            log.info("updateBalance>更新余额回复>>>"+DataConversion.bytesToHexString(send));
+            String key = redisCache.getCacheObject(RedisConstant.KEYS+deviceConnectionMsg.getDeviceId());
+            encrypt = Encrytion.aesEncrypt(send, key.getBytes());
+        } catch (Exception e) {
+            log.info("pileCode:" + deviceConnectionMsg.getDeviceId() + " updateBalance params Exception");
+            e.printStackTrace();
+        }
+        if (encrypt.length > 0) {
+            byte[] bytes = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.UPDATE_BALANCE_RESPONSE.getFrameType(), DeviceSendYkc.UPDATE_BALANCE_RESPONSE.getEncryptFlag(), encrypt, encrypt.length);
+            try {
+                deviceConnectionMsg.getOutputStream().write(bytes);
+                deviceConnectionMsg.getOutputStream().flush();
+            } catch (IOException e) {
+                log.info("pileCode:" + deviceConnectionMsg.getDeviceId() + " ,updateBalance push frame Exception");
+                e.printStackTrace();
+            }
+            deviceConnectionMsg.incrementMessageCount();
+        } else {
+            log.info("pileCode:" + deviceConnectionMsg.getDeviceId() + " updateBalance params null");
+        }
+    }
+
+
+    private byte[] params(String pileCode,byte[] card,byte reason){
+        byte[] msg = new byte[16];
+        int index=0;
+        //桩号
+        byte[] pile_code = new byte[7];
+        pile_code = DataConversion.bcdToBytes(pileCode, 7);
+        System.arraycopy(pile_code, 0, msg, index, pile_code.length);
+        index+=pile_code.length;
+        //物理卡号
+        System.arraycopy(card,0,msg,index,card.length);
+        index+=card.length;
+        //修改结果
+        msg[15]= reason;
+
+        return msg;
+    }
+
+}

+ 355 - 0
src/main/java/com/tmzn/devicelinkykc/frameMsg/frameType/TransactionFlowPushFrame.java

@@ -0,0 +1,355 @@
+package com.tmzn.devicelinkykc.frameMsg.frameType;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.constant.ykc.TransConstant;
+import com.tmzn.devicelinkykc.entity.BillingModel;
+import com.tmzn.devicelinkykc.entity.TransOrder;
+import com.tmzn.devicelinkykc.entity.param.TransCheck;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.service.BillingModelService;
+import com.tmzn.devicelinkykc.service.OrderStatusService;
+import com.tmzn.devicelinkykc.service.TransOrderService;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.util.CP56Time2a;
+import com.tmzn.devicelinkykc.util.Encrytion;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.sql.Time;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author xp
+ * @date 2024/3/20
+ * @explain "  "
+ */
+
+@Component
+@Slf4j
+public class TransactionFlowPushFrame {
+
+    private static final BigDecimal zero=new BigDecimal("0");
+
+    @Autowired
+    private OrderStatusService orderStatusService;
+    @Autowired
+    private BillingModelService billingModelService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    //TODO:模拟存订单信息
+    @Autowired
+    private TransOrderService transOrderService;
+
+    public byte[] sendTrans(DeviceConnectionMsg deviceConnectionMsg,byte[] transOrder, String pileCode, byte gunPort, long createTime, long endTime, BillingModel billingModel, byte[] card, Map<String, BigDecimal> map,int reason)  {
+
+        byte[] encrypt = new byte[0];
+        // 缓存上报的订单,上报失败30秒重试,
+        TransCheck transCheck = new TransCheck();
+        try {
+            //密钥变化这里存订单明文
+            byte[] params = params(transOrder, pileCode, gunPort, createTime, endTime, billingModel, card, map,reason);
+            transCheck.setTrans(params);
+            transCheck.setCheck_time(0);
+            transCheck.setTime(System.currentTimeMillis());
+
+            //log.info("sendTrans>订单上送>>>"+DataConversion.bytesToHexString(params));
+            String key = redisCache.getCacheObject(RedisConstant.KEYS+deviceConnectionMsg.getDeviceId());
+            log.info("key>订单消息加密密钥>>>"+key);
+            encrypt = Encrytion.aesEncrypt(params, key.getBytes());
+            log.info("msg>订单加密消息>>>"+DataConversion.bytesToHexString(encrypt));
+        } catch (Exception e) {
+            log.info("pileCode:" + pileCode + " TransactionFlowPush params Exception");
+            e.printStackTrace();
+            return pileCode.getBytes();
+        }
+        if (encrypt.length > 0) {
+            byte[] bytes = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.TRANSACTION_RECORDS_REQUEST.getFrameType(), DeviceSendYkc.TRANSACTION_RECORDS_REQUEST.getEncryptFlag(), encrypt, encrypt.length);
+            try {
+                //outputStream..write(bytes)
+                //log.info("bytes>上送订单加密消息>>>"+DataConversion.bytesToHexString(bytes));
+               // log.info("bytes.length>上送订单加密length>>>"+bytes.length);
+                    deviceConnectionMsg.getOutputStream().write(bytes);
+                    deviceConnectionMsg.getOutputStream().flush();
+
+                //只有发送了才将校验订单写到redis里
+                redisCache.setCacheObject(RedisConstant.NO_RESPONSE_WAS_RECEIVED+pileCode,transCheck,6,TimeUnit.MINUTES);
+            } catch (IOException e) {
+                log.info("pileCode:" + pileCode + " ,TransactionFlow push frame Exception");
+                e.printStackTrace();
+            }
+            deviceConnectionMsg.incrementMessageCount();
+        } else {
+            log.info("pileCode:" + pileCode + " transactionFlowPush params null");
+        }
+        return encrypt;
+    }
+
+    public byte[] params(byte[] transOrder, String pileCode, byte gunPort, long createTime, long endTime, BillingModel billingModel, byte[] card, Map<String, BigDecimal> map,int reason) {
+        //订单创建时间0和预留原因不存库
+        if (createTime > 0||reason!=TransConstant.START_FAIL) {
+        try {
+                //TODO:测试存订单
+                byte[] transOrders = new byte[8];
+                System.arraycopy(transOrder, 8, transOrders, 0, transOrders.length);
+                QueryWrapper<TransOrder> transOrderQueryWrapper = new QueryWrapper<>();
+                transOrderQueryWrapper.eq("trans", DataConversion.bytesToHexString(transOrders)).eq("pile_code", pileCode);
+                TransOrder transOrderServiceOne = transOrderService.getOne(transOrderQueryWrapper);
+                transOrderServiceOne.setStartTime(new Date(createTime));
+                transOrderServiceOne.setEndTime(new Date(endTime));
+                transOrderServiceOne.setSharpElec(map.get("elec1"));
+                transOrderServiceOne.setSharpMoney(map.get("money1"));
+                transOrderServiceOne.setPeakElec(map.get("elec2"));
+                transOrderServiceOne.setPeakMoney(map.get("money2"));
+                transOrderServiceOne.setFlatElec(map.get("elec3"));
+                transOrderServiceOne.setFlatMoney(map.get("money3"));
+                transOrderServiceOne.setValleyElec(map.get("elec4"));
+                transOrderServiceOne.setValleyMoney(map.get("money4"));
+                transOrderServiceOne.setElec(map.get("elec"));
+                transOrderServiceOne.setMoney(map.get("money"));
+                transOrderServiceOne.setReasonStopCharging(reason);
+                transOrderService.updateById(transOrderServiceOne);
+                //TODO:测试存订单
+            }catch(Exception e){
+                e.printStackTrace();
+            }
+            redisCache.deleteObject(RedisConstant.DEVICE_CHARNGING_INFO + pileCode + gunPort);
+        }
+        int index = 0;
+        byte[] msg = new byte[201];
+        //1.交易流水
+        byte[] trans = new byte[16];
+        System.arraycopy(transOrder, 0, trans, 0, transOrder.length);
+        System.arraycopy(trans, 0, msg, index, trans.length);
+        index += trans.length;
+
+        //2. 桩编码 BCD
+        byte[] pile_code = new byte[7];
+        System.arraycopy(DataConversion.bcdToBytes(pileCode, 7), 0, msg, index, pile_code.length);
+        index += pile_code.length;
+//3.枪号
+        byte[] guns = {gunPort};
+        System.arraycopy(guns, 0, msg, index, guns.length);
+        index += guns.length;
+
+        //4.开始时间
+        Date sdate = new Date(createTime);
+        byte[] startTime = new byte[7];
+        SimpleDateFormat se = new SimpleDateFormat("SS-ss-mm-HH-dd-MM-yy");
+        String sformat = se.format(sdate).substring(1);
+        startTime = CP56Time2a.timeCP56Time2a(sformat);
+        System.arraycopy(startTime, 0, msg, index, startTime.length);
+        index += startTime.length;
+
+        //5.结束时间
+        Date date = new Date(endTime);
+        byte[] time = new byte[7];
+        String format = se.format(date).substring(1);
+        time = CP56Time2a.timeCP56Time2a(format);
+        System.arraycopy(time, 0, msg, index, time.length);
+        index += time.length;
+        //   6 电表表号 BCD 码 6
+        byte[] bytes = new byte[6];
+        bytes = DataConversion.temp(bytes);   //DataConversion.bcdToBytes(transaction.getElecMeterNumber(),bytes.length);
+        System.arraycopy(bytes, 0, msg, index, bytes.length);
+        index += bytes.length;
+        //   7 电表密文 BIN 34
+        byte[] bytes1 = new byte[34];
+        bytes1 = DataConversion.temp(bytes1);//transaction.getElecMeterCiphertext().getBytes();
+        System.arraycopy(bytes1, 0, msg, index, bytes1.length);
+        index += bytes1.length;
+
+        //   8 电表协议版本号 BIN 2
+        byte[] bytes2 = new byte[2];
+        bytes2 = DataConversion.temp(bytes2);//transaction.getElecMeterProtocolNumber().getBytes();
+        System.arraycopy(bytes2, 0, msg, index, bytes2.length);
+        index += bytes2.length;
+
+        //   9 加密方式 BIN 1
+        byte[] bytes3 = new byte[1];
+        bytes3 = DataConversion.temp(bytes3);//transaction.getEncrypMethod();
+        System.arraycopy(bytes3, 0, msg, index, bytes3.length);
+        index += bytes3.length;
+
+        //   10 尖单价 BIN 4 精确到小数点后五位(尖电费 + 尖 服务费,见费率帧)
+        byte[] bytes4 = DataConversion.bin4Tofiv((billingModel.getSharpPrice().add(billingModel.getSharpServiceFee())).setScale(5));
+        //bytes4 = Transaction.temp(bytes4);
+        System.arraycopy(bytes4, 0, msg, index, bytes4.length);
+        index += bytes4.length;
+
+        //   11 尖电量 BIN 4 精确到小数点后四位
+        byte[] bytes5 = DataConversion.bin4Tofour(map.get("elec1").setScale(4));
+        //bytes5 = Transaction.temp(bytes5);
+        System.arraycopy(bytes5, 0, msg, index, bytes5.length);
+        index += bytes5.length;
+
+        //   12 计损尖电量 BIN 4 精确到小数点后四位
+        byte[] bytes6 = DataConversion.bin4Tofour(zero);
+        //  bytes6 = Transaction.temp(bytes6);
+        System.arraycopy(bytes6, 0, msg, index, bytes6.length);
+        index += bytes6.length;
+
+        //   13 尖金额 BIN 4 精确到小数点后四位
+        byte[] bytes7 = new byte[4];
+        bytes7 = DataConversion.bin4Tofour(map.get("money1").setScale(4));
+        System.arraycopy(bytes7, 0, msg, index, bytes7.length);
+        index += bytes7.length;
+
+        //   14 峰单价 BIN 4 精确到小数点后五位(峰电费 + 峰 服务费)
+        byte[] bytes8 = DataConversion.bin4Tofiv((billingModel.getPeakPrice().add(billingModel.getPeakServiceFee())).setScale(5));
+        // bytes8 = Transaction.temp(bytes8);
+        System.arraycopy(bytes8, 0, msg, index, bytes8.length);
+        index += bytes8.length;
+
+        //   15 峰电量 BIN 4 精确到小数点后四位
+        byte[] bytes9 = DataConversion.bin4Tofour(map.get("elec2").setScale(4));
+        //bytes9 = Transaction.temp(bytes9);
+        System.arraycopy(bytes9, 0, msg, index, bytes9.length);
+        index += bytes9.length;
+
+        //   16 计损峰电量 BIN 4 精确到小数点后四位
+        byte[] bytesa = DataConversion.bin4Tofour(zero);
+        //bytesa = Transaction.temp(bytesa);
+        System.arraycopy(bytesa, 0, msg, index, bytesa.length);
+        index += bytesa.length;
+
+        //   17 峰金额 BIN 4 精确到小数点后四位
+        byte[] bytesb = DataConversion.bin4Tofour(map.get("money2").setScale(4));
+        // bytesb = Transaction.temp(bytesb);
+        System.arraycopy(bytesb, 0, msg, index, bytesb.length);
+        index += bytesb.length;
+
+        //   18 平单价 BIN 4 精确到小数点后五位(平电费 + 平 服务费)
+
+        byte[] bytesc = DataConversion.bin4Tofiv((billingModel.getFlatPrice().add(billingModel.getFlatServiceFee())).setScale(5));
+        //bytesc = Transaction.temp(bytesc);
+        System.arraycopy(bytesc, 0, msg, index, bytesc.length);
+        index += bytesc.length;
+
+        //   19 平电量 BIN 4 精确到小数点后四位
+        byte[] bytesd = new byte[4];
+
+        bytesd = DataConversion.bin4Tofour(map.get("elec3").setScale(4));
+        // bytesd = Transaction.temp(bytesd);
+        System.arraycopy(bytesd, 0, msg, index, bytesd.length);
+        index += bytesd.length;
+
+        //   20 计损平电量 BIN 4 精确到小数点后四位
+        byte[] bytese = DataConversion.bin4Tofour(zero);
+        //bytese = Transaction.temp(bytese);
+        System.arraycopy(bytese, 0, msg, index, bytese.length);
+        index += bytese.length;
+
+        //   21 平金额 BIN 4 精确到小数点后四位
+        byte[] bytesf = new byte[4];
+
+        bytesf = DataConversion.bin4Tofour(map.get("money3").setScale(4).setScale(4));
+        // bytesf = Transaction.temp(bytesf);
+        System.arraycopy(bytesf, 0, msg, index, bytesf.length);
+        index += bytesf.length;
+
+        //   22 谷单价 BIN 4 精确到小数点后五位(谷电费 + 谷 服务费)
+        byte[] bytesg = DataConversion.bin4Tofiv((billingModel.getValleyPrice().add(billingModel.getValleyServiceFee())).setScale(5));
+        //  bytesg = Transaction.temp(bytesg);
+        System.arraycopy(bytesg, 0, msg, index, bytesg.length);
+        index += bytesg.length;
+
+        //   23 谷电量 BIN 4 精确到小数点后四位
+        byte[] bytesh = new byte[4];
+        bytesh = DataConversion.bin4Tofour(map.get("elec4").setScale(4));//测试
+
+        //byte[] bytesh = DataConversion.four(transaction.getValleyElec());
+        System.arraycopy(bytesh, 0, msg, index, bytesh.length);
+        index += bytesh.length;
+
+        //   24 计损谷电量 BIN 4 精确到小数点后四位
+        byte[] bytesi = DataConversion.bin4Tofour(zero);
+        // bytesi = Transaction.temp(bytesi);
+        System.arraycopy(bytesi, 0, msg, index, bytesi.length);
+        index += bytesi.length;
+
+        //   25 谷金额 BIN 4 精确到小数点后四位
+        byte[] bytesj = new byte[4];
+        bytesj = DataConversion.bin4Tofour(map.get("money4").setScale(4));
+        //byte[] bytesj = DataConversion.four(transaction.getValleyMoney());
+        System.arraycopy(bytesj, 0, msg, index, bytesj.length);
+        index += bytesj.length;
+
+        //   26 电表总起值 BIN 5 精确到小数点后四位
+        byte[] bytesk = DataConversion.bin5Tofour(new BigDecimal("0.0001").setScale(4)); //测试
+        System.arraycopy(bytesk, 0, msg, index, bytesk.length);
+        index += bytesk.length;
+
+
+        //   27 电表总止值 BIN 5 精确到小数点后四位
+        byte[] bytesl = new byte[5];
+        bytesl = DataConversion.bin5Tofour(new BigDecimal("0.0001").add(map.get("elec")).setScale(4)); //测试
+        System.arraycopy(bytesl, 0, msg, index, bytesl.length);
+        index += bytesl.length;
+
+        //   28 总电量 BIN 4 精确到小数点后四位
+        byte[] bytesm = new byte[4];
+            bytesm = DataConversion.bin4Tofour(map.get("elec").setScale(4));//测试
+        System.arraycopy(bytesm, 0, msg, index, bytesm.length);
+        index += bytesm.length;
+
+
+        //   29 计损总电量 BIN 4 精确到小数点后四位
+        byte[] bytesn = DataConversion.bin4Tofour(zero);
+        //  bytesn = Transaction.temp(bytesn);
+        System.arraycopy(bytesn, 0, msg, index, bytesn.length);
+        index += bytesn.length;
+
+        //   30 消费金额 BIN 4 精确到小数点后四位,包含电费、服务费
+
+        byte[] byteso = DataConversion.bin4Tofour(map.get("money").setScale(4));
+        System.arraycopy(byteso, 0, msg, index, byteso.length);
+        index += byteso.length;
+
+//   31 电动汽车唯一标识 ASCII 17 VIN 码,此处 VIN 码和充电时 VIN 码不同,正序直接上传,无需补 0 和反序
+        byte[] bytesp = new byte[17];
+        bytesp = DataConversion.temp(bytesp);
+        System.arraycopy(bytesp, 0, msg, index, bytesp.length);
+        index += bytesp.length;
+
+        //   32 交易标识 BIN 1 0x01:app 启动 0x02:卡启动 0x04:离线卡启动 0x05:vin 码启动充电
+        byte[] bytesq = new byte[]{TransConstant.APP_START};
+        System.arraycopy(bytesq, 0, msg, index, bytesq.length);
+        index += bytesq.length;
+
+        //   33 交易日期、时间 BIN 7 CP56Time2a 格式
+        byte[] timej = new byte[7];
+//        SimpleDateFormat ses = new SimpleDateFormat("yy-MM-dd-HH-mm-ss-SS");
+//        String format1 = ses.format(new Date(System.currentTimeMillis()));
+//        timej = CP56Time2a.timeCP56Time2a(format1.substring(0, format1.length() - 1));
+        System.arraycopy(time, 0, msg, index, timej.length);
+        index += timej.length;
+
+        //   34 停止原因 BIN 1 见附录 11.1
+        byte[] bytesr = new byte[]{(byte) reason};
+        log.info("订单>>>>停止原因>>>"+reason);
+        System.arraycopy(bytesr, 0, msg, index, bytesr.length);
+        index += bytesr.length;
+
+        //    35 物理卡号 BIN 码 8 不足 8 位补 0
+        byte[] bytess = new byte[8];
+        System.arraycopy(card, 0, bytess, 0, bytess.length);
+        System.arraycopy(bytess, 0, msg, index, bytess.length);
+        index += bytess.length;
+        return msg;
+    }
+
+
+}

+ 14 - 0
src/main/java/com/tmzn/devicelinkykc/mapper/BillingModelMapper.java

@@ -0,0 +1,14 @@
+package com.tmzn.devicelinkykc.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.tmzn.devicelinkykc.entity.BillingModel;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author xp
+ * @date 2024/3/19
+ * @explain "  "
+ */
+@Mapper
+public interface BillingModelMapper extends BaseMapper<BillingModel> {
+}

+ 14 - 0
src/main/java/com/tmzn/devicelinkykc/mapper/DeviceMapper.java

@@ -0,0 +1,14 @@
+package com.tmzn.devicelinkykc.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.tmzn.devicelinkykc.entity.Device;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author xp
+ * @date 2024/3/14
+ * @explain "  "
+ */
+@Mapper
+public interface DeviceMapper extends BaseMapper<Device> {
+}

+ 14 - 0
src/main/java/com/tmzn/devicelinkykc/mapper/DeviceStatusMapper.java

@@ -0,0 +1,14 @@
+package com.tmzn.devicelinkykc.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author xp
+ * @date 2024/3/14
+ * @explain "  "
+ */
+@Mapper
+public interface DeviceStatusMapper extends BaseMapper<DeviceStatus> {
+}

+ 15 - 0
src/main/java/com/tmzn/devicelinkykc/mapper/GdDispostionMapper.java

@@ -0,0 +1,15 @@
+package com.tmzn.devicelinkykc.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.tmzn.devicelinkykc.entity.GdDispostion;
+import org.apache.ibatis.annotations.Mapper;
+
+
+/**
+ * @author xp
+ * @date 2024/6/26
+ * @explain "  "
+ */
+@Mapper
+public interface GdDispostionMapper extends BaseMapper<GdDispostion> {
+}

+ 14 - 0
src/main/java/com/tmzn/devicelinkykc/mapper/OrderStatusMapper.java

@@ -0,0 +1,14 @@
+package com.tmzn.devicelinkykc.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.tmzn.devicelinkykc.entity.OrderStatus;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author xp
+ * @date 2024/3/14
+ * @explain "  "
+ */
+@Mapper
+public interface OrderStatusMapper extends BaseMapper<OrderStatus> {
+}

+ 14 - 0
src/main/java/com/tmzn/devicelinkykc/mapper/TransOrderMapper.java

@@ -0,0 +1,14 @@
+package com.tmzn.devicelinkykc.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.tmzn.devicelinkykc.entity.TransOrder;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author xp
+ * @date 2024/4/11
+ * @explain "  "
+ */
+@Mapper
+public interface TransOrderMapper extends BaseMapper<TransOrder> {
+}

+ 29 - 0
src/main/java/com/tmzn/devicelinkykc/mapstruct/BillingModelMapping.java

@@ -0,0 +1,29 @@
+package com.tmzn.devicelinkykc.mapstruct;
+
+import com.tmzn.devicelinkykc.entity.BillingModel;
+import com.tmzn.devicelinkykc.entity.param.dto.BillingModelDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+
+/**
+ * @author xp
+ * @date 2024/4/24
+ * @explain "  "
+ */
+@Mapper(componentModel = "spring")
+public interface BillingModelMapping {
+
+    @Mappings({
+            @Mapping(target = "sharpPrice",expression = "java(billingModel.getSharpPrice().floatValue())"),
+            @Mapping(target = "sharpServiceFee",expression = "java(billingModel.getSharpServiceFee().floatValue())"),
+            @Mapping(target = "peakPrice",expression = "java(billingModel.getPeakPrice().floatValue())"),
+            @Mapping(target = "peakServiceFee",expression = "java(billingModel.getPeakServiceFee().floatValue())"),
+            @Mapping(target = "flatPrice",expression = "java(billingModel.getFlatPrice().floatValue())"),
+            @Mapping(target = "flatServiceFee",expression = "java(billingModel.getFlatServiceFee().floatValue())"),
+            @Mapping(target = "valleyPrice",expression = "java(billingModel.getValleyPrice().floatValue())"),
+            @Mapping(target = "valleyServiceFee",expression = "java(billingModel.getValleyServiceFee().floatValue())")
+    })
+    BillingModelDTO billingModelToBillingModelDto(BillingModel billingModel);
+}
+

+ 24 - 0
src/main/java/com/tmzn/devicelinkykc/mapstruct/DeviceMapping.java

@@ -0,0 +1,24 @@
+package com.tmzn.devicelinkykc.mapstruct;
+
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import com.tmzn.devicelinkykc.entity.param.dto.DeviceDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+
+/**
+ * @author xp
+ * @date 2024/4/10
+ * @explain "  "
+ */
+@Mapper(componentModel = "spring")
+public interface DeviceMapping {
+
+    @Mappings({
+            @Mapping(target = "createTime",expression = "java(System.currentTimeMillis())"),
+            @Mapping(target = "updateTime",expression = "java(System.currentTimeMillis())")
+    })
+    Device deviceDTOTOdevice(DeviceDTO deviceDTO);
+
+}

+ 28 - 0
src/main/java/com/tmzn/devicelinkykc/mapstruct/TransMapping.java

@@ -0,0 +1,28 @@
+package com.tmzn.devicelinkykc.mapstruct;
+
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.entity.OrderStatus;
+import com.tmzn.devicelinkykc.entity.TransOrder;
+import com.tmzn.devicelinkykc.entity.param.dto.DeviceDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+
+/**
+ * @author xp
+ * @date 2024/4/11
+ * @explain "  "
+ */
+@Mapper(componentModel = "spring")
+public interface TransMapping {
+
+    @Mappings({
+            @Mapping(target = "trans" ,ignore = true),
+            @Mapping(target = "id" ,ignore = true),
+            @Mapping(target = "endTime" ,ignore = true),
+            @Mapping(target = "reasonStopCharging" ,ignore = true),
+            @Mapping(target = "startTime",expression = "java(new java.util.Date(orderStatus.getCreateTime()))")
+    })
+    TransOrder orderStatusToTransOrder(OrderStatus orderStatus);
+
+}

+ 833 - 0
src/main/java/com/tmzn/devicelinkykc/message/DeviceMsgHandle.java

@@ -0,0 +1,833 @@
+package com.tmzn.devicelinkykc.message;
+
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
+import com.tmzn.devicelinkykc.constant.DeviceOnlineStatus;
+import com.tmzn.devicelinkykc.constant.PortStatusConstant;
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.constant.ykc.BillingModelConst;
+import com.tmzn.devicelinkykc.constant.ykc.StatusConstant;
+import com.tmzn.devicelinkykc.constant.ykc.TransConstant;
+import com.tmzn.devicelinkykc.entity.*;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.frameMsg.TransMoney;
+import com.tmzn.devicelinkykc.frameMsg.frameType.CharngingPushFrame;
+import com.tmzn.devicelinkykc.frameMsg.frameType.LoginFrame;
+import com.tmzn.devicelinkykc.frameMsg.frameType.RealTimeStatusPushFrame;
+import com.tmzn.devicelinkykc.frameMsg.frameType.TransactionFlowPushFrame;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.service.*;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import com.tmzn.devicelinkykc.transdata.constant.NormalChargeConstant;
+import com.tmzn.devicelinkykc.transdata.entity.DeviceParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 设备消息处理 "
+ * //TODO:考虑设备上来的消息只走数据库查询不走redis订阅数据
+ */
+@Component
+public class DeviceMsgHandle {
+
+    @Autowired
+    private DeviceStatusService deviceStatusService;
+    @Autowired
+    private SocketHandle socketHandle;
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private RealTimeStatusPushFrame realTimeStatusPushFrame;
+    @Autowired
+    private TransactionFlowPushFrame transactionFlowPushFrame;
+    @Autowired
+    private LoginFrame loginFrame;
+    @Autowired
+    private CharngingPushFrame charngingPushFrame;
+    @Autowired
+    private DeviceService deviceService;
+    @Autowired
+    private OrderStatusService orderStatusService;
+
+    @Autowired
+    private DeviceControlerService deviceControlerService;
+
+    @Autowired
+    private BillingModelService billingModelService;
+
+    @Autowired
+    private TransMoney transMoney;
+
+    private static final Logger logger = LoggerFactory.getLogger(DeviceMsgHandle.class);
+    private Long lastLogTime;
+    private static final Long gap = 1000 * 30L;
+    private static final BigDecimal zero = new BigDecimal("0");
+
+    public void deviceMsg(String msg)  throws Exception{
+        //必须过滤调非云快充设备
+
+
+        //logger.info("redis中msg>>>" + msg);
+        msg = msg.substring(1, msg.length() - 1);
+        msg = msg.replace("\\", "");
+        checkActive(msg);
+
+        JSONObject jsonObject = null;
+        try {
+
+            jsonObject = JSONObject.parseObject(msg);
+
+        } catch (Exception e) {
+            //这里正常格式才走,不正常的格式不打印报错了;可能有其他格式的数据过来;正常要解析的数据是能走到JSON中的
+            logger.info("device msg conversion exception not processed!!!" + msg);
+
+            return;
+            //e.printStackTrace();
+        }
+        if (!redisCache.hasKey(RedisConstant.DEVICE_INFO)) {
+            return;
+        }
+
+        Set<String> setImei = redisCache.getCacheObject(RedisConstant.DEVICE_INFO);
+        String imei = jsonObject.getString("imei");
+        if (!(imei != null && setImei.contains(imei))) {
+            return;
+        }
+/*        JSONArray data = jsonObject.getJSONArray("data");
+        Integer[] a = new Integer[data.size()];
+        for (int i = 0; i < data.size(); i++) {
+            a[i] = data.getIntValue(i);
+        }
+        List<Integer> ints = Arrays.asList(a);
+        if (ints.contains(113)) {
+            //特殊情况113,{"port":1,"paytype":1,"reason":1,"balance":0,"elec":52},
+            //113的位置
+            System.out.println("113index>" + ints.indexOf(113));
+            int port = a[ints.indexOf(113) + 1];
+            int paytype = a[ints.indexOf(113) + 2];
+            int reason = a[ints.indexOf(113) + 3];
+            int elec1 = a[ints.indexOf(113) + 7];
+            int elec12 = a[ints.indexOf(113) + 8] << 8;
+            int elec = elec1 + elec12;
+            String aaa = "{\"real_data\":{\"port\":" + port + ",\"paytype\":" + paytype + ",\"reason\":" + reason + ",\"balance\":0,\"elec\":" + elec + "},\"type\":113}";
+            JSONObject realdata = JSONObject.parseObject(aaa);
+            System.out.println(realdata);
+//            jsonObject.remove("real_data");
+//            jsonObject.put("real_data");
+            jsonObject = realdata;
+        }*/
+        logger.info("msg>>>" + msg);
+        //设备状态更新,true:没有type不是设备上送类型不往云快充处理 false:需要根据设备消息类型往下处理是不是需要上报云快充
+        if (checkDeviceStatus(jsonObject)) {
+            return;
+        }
+    }
+
+    private void checkActive(String s) {
+
+        Long now = System.currentTimeMillis();
+        if (lastLogTime == null) {
+            lastLogTime = now;
+        }
+        Long gap = now - lastLogTime;
+        if (gap > gap) {
+            //logger.info("message:,{}", s);
+            lastLogTime = now;
+        }
+    }
+
+    /**
+     * 设备状态按照设备上报和设备心跳上送时间校验修改,这里就必须更新状态的修改时间;通过定时任务判断在线设备接收心跳包超10分钟改为离线
+     *
+     * @param jsonObject
+     * @return
+     */
+    private boolean checkDeviceStatus(JSONObject jsonObject) throws Exception {
+        String imei = jsonObject.getString("imei");
+
+        Integer cmd = jsonObject.getInteger("cmd");
+        if (!jsonObject.containsKey("type")) {
+            //TODO:离线判断:设备上报+?设备心跳时间校验?
+            //没有type但是cmd指令是离线时37896=========
+            if (cmd == 37896) {
+                QueryWrapper<DeviceStatus> deviceStatusQueryWrapper = new QueryWrapper<>();
+                deviceStatusQueryWrapper.eq("device_imei", imei);
+                List<DeviceStatus> list = deviceStatusService.list(deviceStatusQueryWrapper);
+                if (list.size() > 0) {
+                    for (DeviceStatus deviceStatus : list) {
+                        //deviceStatus.setInsertGunStatus(StatusConstant.INSERT_GUNS_NO);
+                        //deviceStatus.setGunStatus(StatusConstant.OFFLINE);
+                        deviceStatus.setOnlineStatus(DeviceOnlineStatus.OFFLINE);
+                        deviceStatus.setUpdateTime(System.currentTimeMillis());
+                        deviceStatusService.updateById(deviceStatus);
+                        if (socketHandle.existDeviceConnection(deviceStatus.getPileCode())) {
+                            String pilecode = deviceStatus.getPileCode();
+                            DeviceConnectionMsg deviceConnection = socketHandle.getDeviceConnection(pilecode);
+                            byte[] transactionNum = new byte[16]; //FrameDataSplicing.transactionNum(pilecode, deviceConnection.getMessageCount());
+                            if (deviceStatus.getGunStatus() == PortStatusConstant.EMERGENCY_STOP) {
+                                deviceStatus.setGunStatus(StatusConstant.FREE);
+                            }
+                            realTimeStatusPushFrame.deviceStatusPush(deviceConnection, transactionNum, pilecode, (byte) deviceStatus.getGunPort(), deviceStatus.getGunStatus(), deviceStatus.getInsertGunStatus(), 0, 0, zero, zero, 0);
+                            socketHandle.removeDeviceConnection(deviceStatus.getPileCode());
+                            redisCache.deleteObject(RedisConstant.DEVICE_PORT_STATUS + imei);
+                        }
+                    }
+                }
+            }
+            return true;
+        }
+
+        Integer type = jsonObject.getInteger("type");
+        //TODO:设备状态的修改这里进行监听变化 ,?? 1.设备消息cmd:75960设备上报消息是的命令,只要设备报的不是离线当做设备在线 2.设备上报指令后根据type类型来分类处理消息包括:设备设为在线 是否插枪状态 枪状态(端口状态)
+        if (NormalChargeConstant.CMD_SET_MAINBOARD.equals(cmd)) {
+
+            if (NormalChargeConstant.KEY_PORT_DETAIL.equals(type)) {
+                //103状态是带电压功率等信息的,所以需要对该信息进行处理操作
+                JSONObject data = jsonObject.getJSONObject("real_data");
+                Integer port_first_status = data.getInteger("port_first_status");
+                Integer port_second_status = data.getInteger("port_second_status");
+                Integer power = data.getInteger("power");
+
+                //功率为0时,还是充电中状态,将状态转换成空闲,结束充电
+//              20240608现场使用时出现充电状态第一次103上来就是power是0情况
+//                if (port_first_status != null&&power==0&&port_first_status==PortStatusConstant.CHARGING){
+//                    port_first_status=PortStatusConstant.FREE;
+//                }
+//                if (port_second_status != null&&power==0&&port_second_status==PortStatusConstant.CHARGING){
+//                    port_second_status=PortStatusConstant.FREE;
+//                }
+                redisCache.setCacheObject(RedisConstant.DEVICE_PORT_STATUS + imei, data, 15, TimeUnit.MINUTES);
+                if (port_first_status != null) {
+                    checkPort(port_first_status, 1, imei, type);
+                }
+                if (port_second_status != null) {
+                    checkPort(port_second_status, 2, imei, type);
+                }
+
+            } else if (NormalChargeConstant.PORT_STATUS.equals(type)) {
+                //116端口详情也会变化
+                JSONArray data = jsonObject.getJSONArray("data");
+                checkPort(data.getInteger(4), data.getInteger(3), imei, type);
+            } else if (NormalChargeConstant.KEY_PORT_STATUS.equals(type)) {
+                //状态查询101
+                JSONObject data = jsonObject.getJSONObject("real_data");
+                Integer port_first_status = data.getInteger("port_first_status");
+                Integer port_second_status = data.getInteger("port_second_status");
+                if (port_first_status != null) {
+                    checkPort(port_first_status, 1, imei, type);
+                }
+                if (port_second_status != null) {
+                    checkPort(port_second_status, 2, imei, type);
+                }
+            } else if (NormalChargeConstant.KEY_STARTCHARGE.equals(type)) {
+                //开启充电设备上报结果
+                JSONObject data = jsonObject.getJSONObject("real_data");
+                Integer result = data.getInteger("result");
+                Integer port = data.getInteger("port");
+                //TODO:上报云快充启动充电结果
+//                QueryWrapper<OrderStatus> orderStatusQueryWrapper = new QueryWrapper<>();
+//                orderStatusQueryWrapper.eq("imei",imei).eq("guns_code",port);
+//                OrderStatus one = orderStatusService.getOne(orderStatusQueryWrapper);
+//                List<OrderStatus> collect = orderStatuses.stream().filter(orderStatus -> orderStatus.getGunsCode() == port).collect(Collectors.toList());
+//                OrderStatus one = new OrderStatus();
+//                if (collect.size()>0){
+//                    one=collect.get(0);
+//                } else {
+//                    logger.info("开启充电未查到设备订单");
+//                    return false;
+//                }
+                QueryWrapper<OrderStatus> orderStatusQueryWrapper = new QueryWrapper<>();
+                orderStatusQueryWrapper.eq("device_imei", imei).eq("guns_code", port).orderByDesc("create_time").last("limit 1");
+                OrderStatus statusServiceOne = orderStatusService.getOne(orderStatusQueryWrapper);
+                byte[] bytes = statusServiceOne.getTransOrder();
+                logger.info(statusServiceOne.getPileCode() + ":设备开启充电流水号:" + DataConversion.bytesToHexString(bytes));
+                DeviceConnectionMsg deviceConnection = socketHandle.getDeviceConnection(statusServiceOne.getPileCode());
+                int reson = 0x00;
+                if (result == 0x01) {
+                    //启充成功上报充电开启成功
+                    charngingPushFrame.startStatus(deviceConnection, bytes, port, result, 0x00);
+                } else {
+                    //启机不成功,根据设备状态判断,并且要结束充电并上报订单
+                    if (port == 1) {
+                        if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_ONE)) {
+                            DeviceStatus oneStatus = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE, statusServiceOne.getPileCode());
+                            if (StatusConstant.FAULT == oneStatus.getGunStatus()) {
+                                reson = 0x03;
+                            } else if (StatusConstant.OFFLINE == oneStatus.getGunStatus()) {
+                                reson = 0x04;
+                            } else if (StatusConstant.CHARGING == oneStatus.getGunStatus()) {
+                                reson = 0x02;
+                            }
+                            charngingPushFrame.startStatus(deviceConnection, bytes, port, result, reson);
+                        }
+                    } else if (port == 2) {
+                        if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_TWO)) {
+                            DeviceStatus oneStatus = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_TWO, statusServiceOne.getPileCode());
+                            if (StatusConstant.FAULT == oneStatus.getGunStatus()) {
+                                reson = 0x03;
+                            } else if (StatusConstant.OFFLINE == oneStatus.getGunStatus()) {
+                                reson = 0x04;
+                            } else if (StatusConstant.CHARGING == oneStatus.getGunStatus()) {
+                                reson = 0x02;
+                            }
+                            charngingPushFrame.startStatus(deviceConnection, bytes, port, result, reson);
+                        }
+                    }
+                }
+            } else if (NormalChargeConstant.KEY_END_NOTICE.equals(type)) {
+                //停止充电通知触发情况:1.急停,收到停充时主动要去发停充,所以这里不能处理急停的交易订单;2.手动停充,正常的远程停止指令
+                // 1.设备已经在接收到云快充指令的时候进行了停充操作,2.结算订单,上报云快充停止回复
+
+
+                try {
+
+                    JSONObject data = jsonObject.getJSONObject("real_data");
+                    logger.info("接收到设备上送113停止充电msg>>" + jsonObject.toString());
+                    int port = data.getIntValue("port");
+                    byte reson = data.getByte("reason");
+                    QueryWrapper<OrderStatus> orderStatusQueryWrapper = new QueryWrapper<>();
+                    orderStatusQueryWrapper.eq("device_imei", imei).eq("guns_code", port).orderByDesc("create_time").last("limit 1");
+                    OrderStatus statusServiceOne = orderStatusService.getOne(orderStatusQueryWrapper);
+                    if (NormalChargeConstant.KEY_END_NOTICE.equals(type)&&statusServiceOne.getReasonStopCharging()!=TransConstant.APP_REMOTE_STOP) {
+                        DeviceParam dataParam = new DeviceParam();
+                        dataParam.setDeviceId(imei);
+                        dataParam.setCcid(imei);
+                        deviceControlerService.sendPortDetailCmd(dataParam);
+                        return false;
+                    }
+                    if (statusServiceOne.getTransactionOrderReportingActionStatus()==StatusConstant.TRANSACTION_ORDER_REPORTING_ACTION_STATUS_OK){
+                        return false;
+                    }
+                    //查询计费模板
+                    QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+                    billingModelQueryWrapper.eq("device_imei", imei);
+                    BillingModel model = billingModelService.getOne(billingModelQueryWrapper);
+                    //收到停充回复
+                    DeviceConnectionMsg deviceConnection = socketHandle.getDeviceConnection(statusServiceOne.getPileCode());
+                    if (reson == 0x01) {
+                        if (redisCache.hasKey(RedisConstant.DEVICE_PORT_STATUS + imei)) {
+                            //拿缓存的103状态看现在的端口状态是急停不处理113停止充电
+                            JSONObject dataJson = redisCache.getCacheObject(RedisConstant.DEVICE_PORT_STATUS + imei);
+                            Integer port_first_status = dataJson.getInteger("port_first_status");
+                            Integer port_second_status = dataJson.getInteger("port_second_status");
+                            if (port_first_status == PortStatusConstant.EMERGENCY_STOP || port_first_status == PortStatusConstant.EMERGENCY_STOP) {
+                                return false;
+                            }
+                        }
+                        if (statusServiceOne.getReasonStopCharging() == TransConstant.EMERGENCY_STOP_EXCEPTION_STOP || statusServiceOne.getStopChargingReply() == 1) {
+                            //1.急停停充的逻辑不处理停止充电记录  2.一个订单多次停止也不再次上报 3.充满不拔枪113结束通知会延迟所以充满时已经处理过交易上送
+                            return false;
+                        }
+                        //113进到这里说明需要记录结束时间(最优时间,尽量能让后台计费的103或113落库)
+                        statusServiceOne.setEndTime(System.currentTimeMillis());
+                        byte[] encrypt = new byte[0];
+                        //订单充电结束但是原因是0的一般是当手动停充处理
+                        if (statusServiceOne.getReasonStopCharging() == 0) {
+                            statusServiceOne.setReasonStopCharging(TransConstant.MANUAL_STOP);
+                        }
+                        ////计算电量费用后上报,手动停止和余额不足的情况
+                        // DeviceStatus deviceStatus = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE, statusServiceOne.getPileCode());
+                        Map<String, BigDecimal> map = transMoney.compute(port, model, statusServiceOne.getCreateTime(), statusServiceOne.getEndTime(), true);
+                        logger.info(statusServiceOne.getPileCode() + ":停止充电上送交易记录" + DataConversion.bytesToHexString(statusServiceOne.getTransOrder()));
+                        encrypt = transactionFlowPushFrame.sendTrans(deviceConnection, statusServiceOne.getTransOrder(), statusServiceOne.getPileCode(), (byte) 1, statusServiceOne.getCreateTime(), statusServiceOne.getEndTime(), model, statusServiceOne.getCard(), map, statusServiceOne.getReasonStopCharging());
+                        statusServiceOne.setOriginalText(encrypt);
+                        statusServiceOne.setStopChargingReply(StatusConstant.STOP_CHARGING_REPLY_OK);
+                        statusServiceOne.setNowOrderStatus(StatusConstant.NOW_ORDER_STATUS_CHARGING_ENDING);
+                        statusServiceOne.setTransactionOrderReportingActionStatus(StatusConstant.TRANSACTION_ORDER_REPORTING_ACTION_STATUS_OK);
+                        //statusServiceOne.setEndTime(endTime);
+                        orderStatusService.updateById(statusServiceOne);
+                        charngingPushFrame.endStatus(deviceConnection, port, 0x01, 0x00);
+                    } else if (reson == 0x00) {
+                        //余额用完停充
+                        logger.info(statusServiceOne.getPileCode() + ":设备余额不足>>>>>>停充>>>>");
+                        DeviceParam deviceParam = new DeviceParam();
+                        deviceParam.setDeviceId(statusServiceOne.getDeviceImei());
+                        deviceParam.setCcid(statusServiceOne.getDeviceImei());
+                        //deviceControlerService.sendPortDetailCmd(deviceParam);
+                        //deviceControlerService.stopCharge(statusServiceOne.getDeviceImei(), statusServiceOne.getDeviceImei(), 1);
+                        statusServiceOne.setEndTime(System.currentTimeMillis());
+                        byte[] encrypt = new byte[0];
+                        statusServiceOne.setReasonStopCharging(TransConstant.INSUFFICIENT_BALANCE_EXCEPTION_STOP);
+                        //计算电量
+                        Map<String, BigDecimal> map = transMoney.compute(port, model, statusServiceOne.getCreateTime(), statusServiceOne.getEndTime(), true);
+                        encrypt = transactionFlowPushFrame.sendTrans(deviceConnection, statusServiceOne.getTransOrder(), statusServiceOne.getPileCode(), (byte) 2, statusServiceOne.getCreateTime(), statusServiceOne.getEndTime(), model, statusServiceOne.getCard(), map, statusServiceOne.getReasonStopCharging());
+                        statusServiceOne.setOriginalText(encrypt);
+                        statusServiceOne.setStopChargingReply(StatusConstant.STOP_CHARGING_REPLY_OK);
+                        statusServiceOne.setNowOrderStatus(StatusConstant.NOW_ORDER_STATUS_CHARGING_ENDING);
+                        statusServiceOne.setTransactionOrderReportingActionStatus(StatusConstant.TRANSACTION_ORDER_REPORTING_ACTION_STATUS_OK);
+                        orderStatusService.updateById(statusServiceOne);
+                        //return false;
+                    }
+                    //deviceControlerService.reset(imei, imei);
+                    DeviceParam dataParam = new DeviceParam();
+                    dataParam.setDeviceId(imei);
+                    dataParam.setCcid(imei);
+                    deviceControlerService.sendPortDetailCmd(dataParam);
+                } catch (Exception e) {
+                    //TODO:测试不上传订单,报个空闲设备状态
+                    DeviceConnectionMsg deviceConnection = socketHandle.getDeviceConnection(imei);
+                    realTimeStatusPushFrame.deviceStatusPush(deviceConnection, FrameDataSplicing.transactionNum(deviceConnection.getDeviceId(), deviceConnection.getMessageCount()), deviceConnection.getDeviceId(), (byte) 1, StatusConstant.FREE, StatusConstant.CHARGING_INIT_STATUS_OK, 0, 0, zero, zero, 0);
+                    e.printStackTrace();
+                }
+            } else if (NormalChargeConstant.EMERGENCY_STOP_CHARGING.equals(type)) {
+                //急停停充
+                JSONArray data = jsonObject.getJSONArray("data");
+                int port = data.getInteger(2);
+                int status = data.getInteger(3);
+                //关闭急停和急停完,停止充电,获取设备状态
+                DeviceParam dataParam = new DeviceParam();
+                dataParam.setDeviceId(imei);
+                dataParam.setCcid(imei);
+                deviceControlerService.sendPortDetailCmd(dataParam);
+                deviceControlerService.stopCharge(imei, imei, port);
+                //查询计费模板
+                QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+                billingModelQueryWrapper.eq("device_imei", imei);
+                BillingModel model = billingModelService.getOne(billingModelQueryWrapper);
+                QueryWrapper<OrderStatus> orderStatusQueryWrapper = new QueryWrapper<>();
+                orderStatusQueryWrapper.eq("device_imei", imei)
+                        .eq("guns_code", port)
+                        .orderByDesc("create_time"
+                        ).last("limit 1");
+                OrderStatus statusServiceOne = orderStatusService.getOne(orderStatusQueryWrapper);
+                statusServiceOne.setEndTime(System.currentTimeMillis());
+                DeviceConnectionMsg deviceConnection = socketHandle.getDeviceConnection(statusServiceOne.getPileCode());
+                if (status == 1) {
+                    byte[] encrypt = new byte[0];
+                    if (port == 1) {
+                        //急停停充:急停状态下:1.向设备发起结束充电,结算交易订单(交给结束充电处理;但是要考虑是急停的原因),2.上报的充电结束的订单式
+
+                        Map<String, BigDecimal> map = transMoney.compute(1, model, statusServiceOne.getCreateTime(), statusServiceOne.getEndTime(), true);
+
+                        logger.info(statusServiceOne.getPileCode() + ":急停停充>>>上报交易记录>>>");
+                        encrypt = transactionFlowPushFrame.sendTrans(deviceConnection, statusServiceOne.getTransOrder(), statusServiceOne.getPileCode(), (byte) 1, statusServiceOne.getCreateTime(), statusServiceOne.getEndTime(), model, statusServiceOne.getCard(), map, TransConstant.EMERGENCY_STOP_EXCEPTION_STOP);
+                    } else if (port == 2) {
+                        //TODO:这里还是模拟数据上报
+                        DeviceStatus deviceStatus = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_TWO, statusServiceOne.getPileCode());
+
+                        Map<String, BigDecimal> map = transMoney.compute(2, model, statusServiceOne.getCreateTime(), statusServiceOne.getEndTime(), true);
+                        //模拟3.5千瓦
+                        encrypt = transactionFlowPushFrame.sendTrans(deviceConnection, statusServiceOne.getTransOrder(), statusServiceOne.getPileCode(), (byte) 2, statusServiceOne.getCreateTime(), statusServiceOne.getEndTime(), model, statusServiceOne.getCard(), map, TransConstant.EMERGENCY_STOP_EXCEPTION_STOP);
+                    }
+                    statusServiceOne.setOriginalText(encrypt);
+                    statusServiceOne.setReasonStopCharging(TransConstant.EMERGENCY_STOP_EXCEPTION_STOP);
+                    statusServiceOne.setStopChargingReply(StatusConstant.STOP_CHARGING_REPLY_OK);
+                    statusServiceOne.setNowOrderStatus(StatusConstant.NOW_ORDER_STATUS_CHARGING_ENDING);
+                    statusServiceOne.setTransactionOrderReportingActionStatus(StatusConstant.TRANSACTION_ORDER_REPORTING_ACTION_STATUS_OK);
+                    orderStatusService.updateById(statusServiceOne);
+                }
+
+
+            } else if (type == NormalChargeConstant.REPORT_PORT_STATUS) {
+                //114设备主动上报
+                JSONArray data = jsonObject.getJSONArray("data");
+                Integer integer = data.getInteger(2);
+                if (integer > 5) {
+                    //双枪
+                    checkPort(data.getInteger(7), 1, imei, type);
+                    checkPort(data.getInteger(8), 2, imei, type);
+                } else {
+                    checkPort(data.getInteger(7), 1, imei, type);
+                }
+
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 设备端口状态转成云快充识别状态
+     *
+     * @param portStatus
+     * @param port
+     * @param imei
+     */
+    private void checkPort(Integer portStatus, int port, String imei, int type)  throws Exception{
+        if (portStatus.equals(PortStatusConstant.FREE)) {
+            if (type == NormalChargeConstant.PORT_STATUS) {
+                //这里116状态是1可能是直接拔枪时的操作,直接拔枪时结束充电消息会直接上送,这里只记录停止原因不做交易订单上送
+                deviceOnline(imei, port, (byte) PortStatusConstant.FREE, StatusConstant.INSERT_GUNS_NO, type);
+            }
+            //处理特殊情况之后还要看枪状态变位上送
+            deviceOnline(imei, port, StatusConstant.FREE, StatusConstant.INSERT_GUNS_NO);
+        } else if (portStatus.equals(PortStatusConstant.CHARGING)) {
+            deviceOnline(imei, port, StatusConstant.CHARGING, StatusConstant.INSERT_GUNS_YES);
+        } else if (portStatus.equals(PortStatusConstant.DISABLED) || portStatus.equals(PortStatusConstant.FAULT)) {
+            //禁用或故障上报云快充为故障信息
+            deviceOnline(imei, port, StatusConstant.FAULT, StatusConstant.NO);
+        } else if (portStatus.equals(PortStatusConstant.INSERT_GUN) || portStatus.equals(PortStatusConstant.BOOKED)) {
+            deviceOnline(imei, port, StatusConstant.FREE, StatusConstant.INSERT_GUNS_YES);
+        } else if (portStatus.equals(PortStatusConstant.CHARGING_END)) {
+            //充电完成状态是7,先处理一波
+            logger.info("charnging Portstatus>>" + portStatus + ";type:" + type);
+
+                if (deviceOnline(imei, port, (byte) PortStatusConstant.CHARGING_END, StatusConstant.INSERT_GUNS_YES, type)){
+                    //状态是7时,处理过订单返回才是true
+                    return;
+                }
+
+            logger.info("charnging Port>>b>>>>");
+            //再去保证设备状态变化处理7为空闲插枪
+            deviceOnline(imei, port, (byte) StatusConstant.FREE, StatusConstant.INSERT_GUNS_NO);
+
+        } else if (portStatus.equals(PortStatusConstant.EMERGENCY_STOP)) {
+            //急停中按要求需要上报空闲状态;急停保存原始状态
+            deviceOnline(imei, port, (byte) PortStatusConstant.EMERGENCY_STOP, StatusConstant.INSERT_GUNS_NO);
+        }
+    }
+
+    /**
+     * 端口状态是7时的特殊处理充满;端口状态是1时特殊处理直接拔枪
+     *
+     * @param imei
+     * @param port
+     * @param gunsStatus
+     * @param insertGunStatus
+     * @param type            指令类型
+     */
+    private boolean deviceOnline(String imei, int port, byte gunsStatus, byte insertGunStatus, int type)  throws Exception{
+        logger.info("处理7状态>>>>>>");
+        QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+        billingModelQueryWrapper.eq("device_imei", imei);
+        BillingModel model = billingModelService.getOne(billingModelQueryWrapper);
+
+        if (model == null) {
+            //说明设备不是云快充设备或者还没登录,获取一次103?
+            DeviceParam deviceParam = new DeviceParam();
+            deviceParam.setDeviceId(imei);
+            deviceParam.setCcid(imei);
+            deviceControlerService.sendPortDetailCmd(deviceParam);
+            return false;
+        }
+
+        //只有指令116时进入处理
+        //20240620:所有7状态当充满,116不处理
+        if (type == NormalChargeConstant.PORT_STATUS) {
+            return true;
+        }
+        if (gunsStatus == PortStatusConstant.CHARGING_END) {
+            //特殊处理端口状态是7充电已完成(充满),TODO:但是116的端口状态拔枪和充满都是7;103上报是7的话会一只走状态7的处理>就是一只查库看订单状态
+            QueryWrapper<OrderStatus> orderStatusQueryWrapper = new QueryWrapper<>();
+            orderStatusQueryWrapper.eq("device_imei", imei).eq("guns_code", port).orderByDesc("create_time").last("limit 1");
+            OrderStatus one = orderStatusService.getOne(orderStatusQueryWrapper);
+
+            //7状态小于30秒内且订单原因不是远程停充
+            if((System.currentTimeMillis()- one.getCreateTime())<30*1000&&one.getReasonStopCharging()!=TransConstant.APP_REMOTE_STOP){
+                logger.info("启动状态还没变化--->当前7状态不处理");
+                return true;
+            }
+            //7状态:充电完成未拔枪会一直上报,只有当订单完成并回复解析订单成功,7状态当空闲状态上报
+            if (one.getTransactionOrderReportingActionStatus()==StatusConstant.TRANSACTION_ORDER_REPORTING_ACTION_STATUS_OK&&one.getTransactionOrderReportingActionStatus()==StatusConstant.TRANSACTION_ORDER_REPLY_STATUS_SUCC){
+                return false;
+            }
+
+
+            //如果当前设备的最新订单状态已经是'已经充满'的原因并且订单现在的情况是'充电结束'说明不需要再处理,已经处理过了,
+            if (one.getReasonStopCharging() == TransConstant.SOC_FULL_OF_STOP || one.getNowOrderStatus() == StatusConstant.NOW_ORDER_STATUS_CHARGING_ENDING) {
+                return false;
+            }
+            //订单充电中,103端口状态2变7:认为充满.//充满情况,因为可能没拔枪113结束充电通知还没上来;手动发停充;在113中处理交易记录上送
+//            DeviceParam deviceParam = new DeviceParam();
+//            deviceParam.setDeviceId(imei);
+//            deviceParam.setCcid(imei);
+//            deviceControlerService.sendPortDetailCmd(deviceParam);
+            deviceControlerService.stopCharge(imei, imei, port);
+            DeviceConnectionMsg deviceConnection = socketHandle.getDeviceConnection(model.getPileCode());
+
+            QueryWrapper<DeviceStatus> deviceStatusQueryWrapper = new QueryWrapper<>();
+            deviceStatusQueryWrapper.eq("device_imei", imei).eq("gun_port", port);
+            DeviceStatus statusServiceOne = deviceStatusService.getOne(deviceStatusQueryWrapper);
+            one.setEndTime(System.currentTimeMillis());
+
+            //后台计费
+            //Map<String, BigDecimal> start = transMoney.compute(port, model, one.getCreateTime(), one.getEndTime(),true);
+            //byte[] encrypt = transactionFlowPushFrame.sendTrans(deviceConnection, one.getTransOrder(), one.getPileCode(), (byte) port, one.getCreateTime(), one.getEndTime(), model, one.getCard(), start, TransConstant.SOC_FULL_OF_STOP);
+            //one.setOriginalText(encrypt);
+            if (one.getReasonStopCharging()==0) {
+                one.setReasonStopCharging(TransConstant.SOC_FULL_OF_STOP);//充满
+            }
+            one.setNowOrderStatus(StatusConstant.NOW_ORDER_STATUS_CHARGING_ENDING);//已结束
+            one.setStopChargingReply(StatusConstant.STOP_CHARGING_REPLY_NO);//记录为停充回复
+            one.setTransactionOrderReportingActionStatus(StatusConstant.TRANSACTION_ORDER_REPORTING_ACTION_STATUS_NO);//未送交易
+            orderStatusService.updateById(one);
+
+        } else if (gunsStatus == PortStatusConstant.FREE) {
+            //特俗处理:校验是不是最新订单是充电中原因没有等等,也就是订单未结束时直接状态成了未插枪状态
+            QueryWrapper<OrderStatus> orderStatusQueryWrapper = new QueryWrapper<>();
+            orderStatusQueryWrapper.eq("device_imei", imei).eq("guns_code", port).orderByDesc("create_time").last("limit 1");
+            OrderStatus one = orderStatusService.getOne(orderStatusQueryWrapper);
+            if (one == null) {
+                return false;
+            }
+            if (one.getReasonStopCharging() == 0 && one.getNowOrderStatus() == StatusConstant.NOW_ORDER_STATUS_CHARGING) {
+                //校验通过,说明是直接拔枪了,拔枪设备会主动上报113结束充电通知
+                one.setEndTime(System.currentTimeMillis());
+                //手动停充
+                one.setReasonStopCharging(TransConstant.MANUAL_STOP);
+                one.setNowOrderStatus(StatusConstant.NOW_ORDER_STATUS_CHARGING_ENDING);
+                one.setStopChargingReply(StatusConstant.STOP_CHARGING_REPLY_OK);//已停充
+                orderStatusService.updateById(one);
+            }
+        }
+        return false;
+
+    }
+
+
+    /**
+     * 1.这里接收到消息就是设备在线;2.对状态进行处理,不同类型消息的枪状态不同进行落库;3.并且状态变化的一个变位上送
+     *
+     * @param imei       设备imei码
+     * @param port       设备port(枪号)
+     * @param gunsStatus 枪状态
+     */
+    private void deviceOnline(String imei, int port, byte gunsStatus, byte insertGunStatus)  throws Exception{
+        QueryWrapper<DeviceStatus> deviceStatusQueryWrapper = new QueryWrapper<>();
+        deviceStatusQueryWrapper.eq("device_imei", imei).eq("gun_port", port);
+        DeviceStatus statusServiceOne = deviceStatusService.getOne(deviceStatusQueryWrapper);
+        DeviceStatus statusServiceOneTemp = new DeviceStatus();
+        if (statusServiceOne != null) {
+            BeanUtils.copyProperties(statusServiceOne, statusServiceOneTemp);
+        } else {
+            //禁用的设备不再上线,只看正常设备
+
+//            if ("864606063793920".equals(imei)){
+//                QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+//                QueryWrapper<Device> device_imei = deviceQueryWrapper.eq("device_imei", imei);
+//                Device a = deviceService.getOne(device_imei);
+//                logger.info("a>>"+a.toString());
+//            }
+
+            QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+            QueryWrapper<Device> device_imei = deviceQueryWrapper.eq("device_imei", imei).eq("disabled", DeviceOnlineStatus.NORMAL);
+            Device one = deviceService.getOne(device_imei);
+            if (one == null) {
+                logger.info("当前设备imei:" + imei + ",没有绑定ykc桩编码!!!!!!!!!!!!!!!!!!");
+                return;
+            }
+            statusServiceOne = new DeviceStatus();
+            statusServiceOne.setDeviceImei(imei);
+            statusServiceOne.setDeviceSn(one.getDeviceSn());
+            statusServiceOne.setPileCode(one.getPileCode());
+            statusServiceOne.setGunPort((byte) port);
+            statusServiceOne.setCreateTime(System.currentTimeMillis());
+            statusServiceOne.setUpdateTime(System.currentTimeMillis());
+            deviceStatusService.saveOrUpdate(statusServiceOne);
+        }
+        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+        deviceQueryWrapper.eq("pile_code", statusServiceOne.getPileCode()).eq("disabled", DeviceOnlineStatus.NORMAL);
+        Device device = deviceService.getOne(deviceQueryWrapper);
+
+        if (!socketHandle.existDeviceConnection(statusServiceOne.getPileCode())) {
+            try {
+
+                socketHandle.addDeviceConnection(device.getIp(),device.getPort(),statusServiceOne.getPileCode(), statusServiceOne.getDeviceImei(), statusServiceOne.getDeviceSn());
+                loginFrame.loginMsgSend(socketHandle.getDeviceConnection(statusServiceOne.getPileCode()), device);
+                //从新登录可能造成通讯上的问题,这里不在往下进行状态操作
+                return;
+            } catch (IOException e) {
+                logger.info("device:" + statusServiceOne.getDeviceSn() + " open socket Exception!!!");
+                e.printStackTrace();
+            }
+        } else {
+            //存在连接校验socket连接是否正常
+            DeviceConnectionMsg deviceConnection = socketHandle.getDeviceConnection(statusServiceOne.getPileCode());
+            if (!deviceConnection.getSocket().isConnected()) {
+                //不正常,移除再连接
+                socketHandle.removeDeviceConnection(statusServiceOne.getPileCode());
+                try {
+                    socketHandle.addDeviceConnection(device.getIp(),device.getPort(),statusServiceOne.getPileCode(), statusServiceOne.getDeviceImei(), statusServiceOne.getDeviceSn());
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        logger.info("设备原状态>>>" + statusServiceOneTemp.toString());
+        statusServiceOne.setGunStatus(gunsStatus);
+        statusServiceOne.setInsertGunStatus(insertGunStatus);
+        statusServiceOne.setOnlineStatus(DeviceOnlineStatus.ONLINE);
+        statusServiceOne.setUpdateTime(System.currentTimeMillis());
+        deviceStatusService.updateById(statusServiceOne);
+        logger.info("设备状态持久化>>>" + statusServiceOne.toString());
+
+        //1.redis也更新掉
+
+        if (port == 1) {
+            if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_ONE)) {
+                DeviceStatus ds = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE, statusServiceOne.getPileCode());
+                if (ds != null) {
+                    redisCache.deleteCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE, statusServiceOne.getPileCode());
+                }
+            }
+        } else if (port == 2) {
+            if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_TWO)) {
+                DeviceStatus ds = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_TWO, statusServiceOne.getPileCode());
+                if (ds != null) {
+                    redisCache.deleteCacheMapValue(RedisConstant.ONLINE_DEVICE_TWO, statusServiceOne.getPileCode());
+                }
+            }
+        }
+        redisCache.setCacheMapValue(port == 1 ? RedisConstant.ONLINE_DEVICE_ONE : RedisConstant.ONLINE_DEVICE_TWO, statusServiceOne.getPileCode(), statusServiceOne);
+        redisCache.expire(port == 1 ? RedisConstant.ONLINE_DEVICE_ONE : RedisConstant.ONLINE_DEVICE_TWO, 60 * 1001 * 20, TimeUnit.MILLISECONDS);
+
+        //2.状态变位上送,状态变化需要上送一个实时状态帧;前提是已经在云快充登录成功
+        if (statusServiceOneTemp.getGunStatus() != gunsStatus || statusServiceOneTemp.getInsertGunStatus() != insertGunStatus) {
+            if (!socketHandle.existDeviceConnection(statusServiceOne.getPileCode())) {
+                try {
+                    socketHandle.addDeviceConnection(device.getIp(),device.getPort(),statusServiceOne.getPileCode(), statusServiceOne.getDeviceImei(), statusServiceOne.getDeviceSn());
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            DeviceConnectionMsg deviceConnectionMsg = socketHandle.getDeviceConnection(statusServiceOne.getPileCode());
+            if (deviceConnectionMsg.getLoginStatus() == 1) { //设备登录成功才会有其他的操作,登录没成功无法变位上送
+                QueryWrapper<OrderStatus> orderStatusQueryWrapper = new QueryWrapper<>();
+                orderStatusQueryWrapper.eq("device_imei", imei).eq("guns_code", port).orderByDesc("create_time").last("limit 1");
+                OrderStatus one = orderStatusService.getOne(orderStatusQueryWrapper);
+
+                if (one==null){
+                    logger.info("orderStatus is null ,status ↑↑↑↑");
+                    realTimeStatusPushFrame.deviceStatusPush(deviceConnectionMsg, FrameDataSplicing.transactionNum(null,0), statusServiceOne.getPileCode(), (byte) port, gunsStatus, insertGunStatus, 0, 0, zero, zero, 0);
+                    return;
+                }
+                //判断区分是因为充电中流水和待机状态流水号不同
+                if (gunsStatus == StatusConstant.CHARGING) {
+                    //这里的变位是变为充电中需要注意交易流水号的变更问题,流水号在云快充启充指令时获得并已经持久化到数据库中
+                    byte[] transactionNum = one.getTransOrder();
+
+                    if (one.getChargingInitStatus() == StatusConstant.CHARGING_INIT_STATUS_NO) {
+                        logger.info("变位为充电中且当前订单还没首次上报过初始状态:" + DataConversion.bytesToHexString(transactionNum));
+                        realTimeStatusPushFrame.deviceStatusPush(deviceConnectionMsg, transactionNum, statusServiceOneTemp.getPileCode(), (byte) port, gunsStatus, insertGunStatus, 0, 0, zero, zero, 0);
+                    }
+                    //这里上报开始充电初始化状态并记录已经上报首次状态
+                    one.setNowOrderStatus(StatusConstant.NOW_ORDER_STATUS_CHARGING);
+                    one.setChargingInitStatus(StatusConstant.CHARGING_INIT_STATUS_OK);
+                    orderStatusService.updateById(one);
+                } else {
+                    logger.info(statusServiceOne.getPileCode() + ":除了充电变位的其他状态>>>>>>" + gunsStatus);
+                    //这里的状态变位是除了充电中以外的情况
+                    byte[] transactionNum = FrameDataSplicing.transactionNum(statusServiceOneTemp.getPileCode(), deviceConnectionMsg.getMessageCount());
+                    if (gunsStatus == PortStatusConstant.EMERGENCY_STOP) {
+                        //  急停后库里记录的是急停状态,但是上报的时候需上报云快充一次故障状态;所以临时为故障上报
+                        gunsStatus = StatusConstant.FAULT;
+                        logger.info(statusServiceOne.getPileCode() + ":状态变位为急停时需要一次故障状态:" + gunsStatus);
+                    } else if (gunsStatus == PortStatusConstant.CHARGING_END&&one.getReasonStopCharging()==0) {
+                        //状态是7充电完成
+                        gunsStatus = StatusConstant.FREE;
+                    }else if (one.getReasonStopCharging()==0){
+                        one.setReasonStopCharging(TransConstant.MANUAL_STOP);
+                    }
+                    //状态未知按手动停充
+
+//                    如果断网后在断网时进行的停充,那么现在情况->1.设备状态离线2.订单记录还是 : 充电中0,没有停止指令上送0,交易记录也没有上送0,停止原因也是0,结束时间是null
+                    //TODO:停止充电时间怎么算:目前考虑的是检测到离线的时间就说device中的updateTime来当结束充电时间
+
+                    String transOrder = "";
+                    if (redisCache.hasKey(RedisConstant.NO_INSERT_GUN_YKC + statusServiceOneTemp.getPileCode())) {
+                        //未插枪启充的操作会保存订单,但是订单号不是因为断网中停充造成的订单结束原因是0,所以redis有当前设备的订单值,就不是结束充电
+                        transOrder = redisCache.getCacheObject(RedisConstant.NO_INSERT_GUN_YKC + statusServiceOneTemp.getPileCode());
+                    }
+                    //急停状态下不管是断网时急停还是直接按的急停都按急停处理,不按手动处理;
+                    //TODO:急停情况已经上报交易,其他从充电变成非已充电全部上报交易记录
+                    //原枪状态是充电中,现在不是根据状态上报
+                    logger.info("原本状态>>>>>>>>>>>>>>>>>"+statusServiceOneTemp.toString());
+                    logger.info("当前状态状态>>>>>>>>>>>>>>>>>"+statusServiceOne.toString());
+                    if (statusServiceOneTemp.getGunStatus()==StatusConstant.CHARGING&&statusServiceOne.getGunStatus()!=StatusConstant.CHARGING){
+                        QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+                        billingModelQueryWrapper.eq("device_imei", imei);
+                        BillingModel model = billingModelService.getOne(billingModelQueryWrapper);
+
+                        byte[] encrypt = new byte[0];
+                        Long endTime=System.currentTimeMillis();
+                        if (port == 1) {
+                            logger.info("订单状态orderStatus>>"+one.toString());
+                            Map<String, BigDecimal> map = transMoney.compute(1, model, one.getCreateTime(), endTime + 3 * 60 * 1000, true);
+                            //logger.info("断网中停充>>>上报交易记录>>>");
+                            encrypt = transactionFlowPushFrame.sendTrans(deviceConnectionMsg, one.getTransOrder(), statusServiceOne.getPileCode(), (byte) 1, one.getCreateTime(), statusServiceOneTemp.getUpdateTime() + 3 * 60 * 1000, model, one.getCard(), map, one.getReasonStopCharging());
+                        } else if (port == 2) {
+
+                            Map<String, BigDecimal> map = transMoney.compute(2, model, one.getCreateTime(), endTime + 3 * 60 * 1000, true);
+                            BigDecimal elec = map.get("elec");
+                            BigDecimal money = map.get("money");
+                            encrypt = transactionFlowPushFrame.sendTrans(deviceConnectionMsg, one.getTransOrder(), statusServiceOne.getPileCode(), (byte) 2, one.getCreateTime(), statusServiceOneTemp.getUpdateTime() + 3 * 60 * 1000, model, one.getCard(), map, statusServiceOneTemp.getFlage() == 2 ? TransConstant.CHARGING_STATION_POWER_OUTAGE : TransConstant.EMERGENCY_STOP_EXCEPTION_STOP);
+                        }
+                    /*    if (port == 1) {
+                            Map<String, BigDecimal> map = transMoney.compute(1, model, one.getCreateTime(), endTime + 3 * 60 * 1000, true);
+                            //logger.info("断网中停充>>>上报交易记录>>>");
+                            encrypt = transactionFlowPushFrame.sendTrans(deviceConnectionMsg, one.getTransOrder(), statusServiceOne.getPileCode(), (byte) 1, one.getCreateTime(), statusServiceOneTemp.getUpdateTime() + 3 * 60 * 1000, model, one.getCard(), map, statusServiceOneTemp.getFlage() == 2 ? TransConstant.CHARGING_STATION_POWER_OUTAGE : TransConstant.EMERGENCY_STOP_EXCEPTION_STOP);
+                        } else if (port == 2) {
+
+                            Map<String, BigDecimal> map = transMoney.compute(2, model, one.getCreateTime(), endTime + 3 * 60 * 1000, true);
+                            BigDecimal elec = map.get("elec");
+                            BigDecimal money = map.get("money");
+                            encrypt = transactionFlowPushFrame.sendTrans(deviceConnectionMsg, one.getTransOrder(), statusServiceOne.getPileCode(), (byte) 2, one.getCreateTime(), statusServiceOneTemp.getUpdateTime() + 3 * 60 * 1000, model, one.getCard(), map, statusServiceOneTemp.getFlage() == 2 ? TransConstant.CHARGING_STATION_POWER_OUTAGE : TransConstant.EMERGENCY_STOP_EXCEPTION_STOP);
+                        }*/
+                        one.setEndTime(endTime+ 3 * 60 * 1000);
+                        //手动停充
+                        one.setOriginalText(encrypt);
+                        //one.setReasonStopCharging(one);
+                        one.setNowOrderStatus(StatusConstant.NOW_ORDER_STATUS_CHARGING_ENDING);
+                        one.setStopChargingReply(StatusConstant.STOP_CHARGING_REPLY_OK);//停止指令回复云快充(模拟的)
+                        one.setTransactionOrderReportingActionStatus(StatusConstant.TRANSACTION_ORDER_REPORTING_ACTION_STATUS_OK);
+                        orderStatusService.updateById(one);
+                    }
+
+            /*        if (statusServiceOne.getGunStatus() != PortStatusConstant.EMERGENCY_STOP && one != null && !(transOrder.equals(DataConversion.bytesToHexString(one.getTransOrder()))) && one.getReasonStopCharging() == 0 && one.getNowOrderStatus() == StatusConstant.NOW_ORDER_STATUS_CHARGING && one.getStopChargingReply() == StatusConstant.STOP_CHARGING_REPLY_NO && one.getTransactionOrderReportingActionStatus() == StatusConstant.TRANSACTION_ORDER_REPORTING_ACTION_STATUS_NO ) {
+                        //这里需要查下103因为断网中停充可能103没有上来
+                        DeviceParam deviceParam = new DeviceParam();
+                        deviceParam.setDeviceId(imei);
+                        deviceParam.setCcid(imei);
+                        deviceControlerService.sendPortDetailCmd(deviceParam);
+                        *//*QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+                        billingModelQueryWrapper.eq("device_imei", imei);
+                        BillingModel model = billingModelService.getOne(billingModelQueryWrapper);
+                        byte[] encrypt = new byte[0];
+                        if (port == 1) {
+                            Map<String, BigDecimal> map = transMoney.compute(1, model, one.getCreateTime(), statusServiceOneTemp.getUpdateTime() + 3 * 60 * 1000, true);
+                            //logger.info("断网中停充>>>上报交易记录>>>");
+                            encrypt = transactionFlowPushFrame.sendTrans(deviceConnectionMsg, one.getTransOrder(), statusServiceOne.getPileCode(), (byte) 1, one.getCreateTime(), statusServiceOneTemp.getUpdateTime() + 3 * 60 * 1000, model, one.getCard(), map, statusServiceOneTemp.getFlage() == 2 ? TransConstant.CHARGING_STATION_POWER_OUTAGE : TransConstant.EMERGENCY_STOP_EXCEPTION_STOP);
+                        } else if (port == 2) {
+
+                            Map<String, BigDecimal> map = transMoney.compute(2, model, one.getCreateTime(), statusServiceOneTemp.getUpdateTime() + 3 * 60 * 1000, true);
+                            BigDecimal elec = map.get("elec");
+                            BigDecimal money = map.get("money");
+                            encrypt = transactionFlowPushFrame.sendTrans(deviceConnectionMsg, one.getTransOrder(), statusServiceOne.getPileCode(), (byte) 2, one.getCreateTime(), statusServiceOneTemp.getUpdateTime() + 3 * 60 * 1000, model, one.getCard(), map, statusServiceOneTemp.getFlage() == 2 ? TransConstant.CHARGING_STATION_POWER_OUTAGE : TransConstant.EMERGENCY_STOP_EXCEPTION_STOP);
+                        }*//*
+                        //订单断网停充上送交易订单
+
+
+                    }
+                  */  //插枪状态变化需要看是不是有启充时没插枪的情况
+                    if (insertGunStatus == StatusConstant.INSERT_GUNS_YES && gunsStatus == StatusConstant.FREE) {
+                        if (redisCache.hasKey(RedisConstant.NO_INSERT_GUN_YKC + statusServiceOneTemp.getPileCode())) {
+                            //有择发启充设备,然后移除掉redis
+                            logger.info(statusServiceOne.getPileCode() + ":云快充启动时未插枪>检测到插枪启动>>>订单>>" + DataConversion.bytesToHexString(one.getTransOrder()));
+                            deviceControlerService.startCharge(statusServiceOneTemp.getDeviceImei(), statusServiceOneTemp.getDeviceImei(), port, 0);
+                            charngingPushFrame.startStatus(deviceConnectionMsg, one.getTransOrder(), port, 0x01, 0x00);
+                            redisCache.deleteObject(RedisConstant.NO_INSERT_GUN_YKC + statusServiceOneTemp.getPileCode());
+                        }
+                    }
+                    logger.info(statusServiceOne.getPileCode() + ":设备变位状态上送枪状态>>>" + gunsStatus + ";插枪状态:" + insertGunStatus);
+                    realTimeStatusPushFrame.deviceStatusPush(deviceConnectionMsg, transactionNum, statusServiceOne.getPileCode(), (byte) port, gunsStatus, insertGunStatus, 0, 0, zero, zero, 0);
+                }
+            }
+        }
+    }
+}

+ 582 - 0
src/main/java/com/tmzn/devicelinkykc/message/YkcMsgHandle.java

@@ -0,0 +1,582 @@
+package com.tmzn.devicelinkykc.message;
+
+import cn.hutool.db.sql.Order;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.tmzn.devicelinkykc.constant.Constant;
+import com.tmzn.devicelinkykc.constant.DeviceOnlineStatus;
+import com.tmzn.devicelinkykc.constant.PortStatusConstant;
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.constant.ykc.BillingModelConst;
+import com.tmzn.devicelinkykc.constant.ykc.StatusConstant;
+import com.tmzn.devicelinkykc.constant.ykc.TransConstant;
+import com.tmzn.devicelinkykc.entity.BillingModel;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import com.tmzn.devicelinkykc.entity.OrderStatus;
+import com.tmzn.devicelinkykc.entity.TransOrder;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.TransMoney;
+import com.tmzn.devicelinkykc.frameMsg.frameType.*;
+import com.tmzn.devicelinkykc.mapstruct.TransMapping;
+import com.tmzn.devicelinkykc.msgEnum.YkcSendDevice;
+import com.tmzn.devicelinkykc.openfeign.MsgService;
+import com.tmzn.devicelinkykc.openfeign.transdata.DataParam;
+import com.tmzn.devicelinkykc.openfeign.transdata.RpcResult;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.service.*;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import com.tmzn.devicelinkykc.test.SendMsgToDevice;
+import com.tmzn.devicelinkykc.transdata.entity.DeviceParam;
+import com.tmzn.devicelinkykc.transdata.entity.MainBoard;
+import com.tmzn.devicelinkykc.transdata.entity.opertype.OperEnum;
+import com.tmzn.devicelinkykc.util.Encrytion;
+import com.tmzn.devicelinkykc.util.ResultUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain "  "
+ */
+@Component
+public class YkcMsgHandle {
+
+    private static final Logger logger = LoggerFactory.getLogger(YkcMsgHandle.class);
+    private static final BigDecimal zero=new BigDecimal("0");
+
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private OrderStatusService orderStatusService;
+    @Autowired
+    private DeviceStatusService deviceStatusService;
+    @Autowired
+    private DeviceControlerService deviceControlerService;
+    @Autowired
+    private CheckTime checkTime;
+    @Autowired
+    private BillingModelFrame billingModelFrame;
+    @Autowired
+    private MsgService msgService;
+    @Autowired
+    private BillingModelService billingModelService;
+    @Autowired
+    private RealTimeStatusPushFrame realTimeStatusPushFrame;
+    @Autowired
+    private CharngingPushFrame charngingPushFrame;
+    @Autowired
+    private RemoteBalanceUpdatePushFrame remoteBalanceUpdatePushFrame;
+    @Autowired
+    private TransMoney transMoney;
+    @Autowired
+    private TransactionFlowPushFrame transactionFlowPushFrame;
+
+    @Autowired
+    private TransOrderService transOrderService;
+    @Autowired
+    private TransMapping transMapping;
+
+    public void startListening(DeviceConnectionMsg deviceConnectionMsg) {
+
+        Runnable listener = () -> {
+            boolean temp=true;
+            while (temp) {
+                // 接收数据帧
+                byte[] receiveData = new byte[1024];
+                int length = 0;
+                try {
+                    if (deviceConnectionMsg.getSocket().isConnected()) {
+                        length = deviceConnectionMsg.getSocket().getInputStream().read(receiveData);
+                    } else {
+                        logger.info("receiveYKCData socket no>>>>");
+                    }
+                    if (length < 0) {
+                        return;
+                    }
+                } catch (IOException e) {
+                    //e.printStackTrace();
+                    String message = e.getMessage();
+                    logger.info(deviceConnectionMsg.getDeviceId()+":Receive YKC dataMsg IOException !!!!!!!:" + message+"!!!!!!!!"+ message+"!!!!!!!!"+ message+"!!!!!!!!"+ message+"!!!!!!!!");
+                    //异常后操作删除所有的缓存,重新获取
+                    DataParam dataParam = new DataParam();
+                    dataParam.setDeviceId(deviceConnectionMsg.getImei());
+                    dataParam.setCcid(deviceConnectionMsg.getImei());
+                    JSONObject object1 = new JSONObject();
+                    dataParam.setData(object1);
+                    dataParam.setType(OperEnum.PortDetail.getType());
+                    msgService.sendMsg(dataParam);
+                    temp=false;
+                    continue;
+                }
+                byte[] response = new byte[length];
+                //将接收到的消息类型拿到进行判断
+                System.arraycopy(receiveData, 0, response, 0, response.length);
+                String s = Integer.toHexString(response[5] & 0xFF);
+                int framType = response[5] & 0xFF;//Integer.parseInt(s);
+                int encry = response[4] & 0xFF;
+
+                //消息体
+                byte[] respone_msg = Arrays.copyOfRange(response, 6, response.length - 2);
+                logger.info("framType:" + framType + "||" + response[5] + "应答消息类型>>>" + s);
+                if (encry == 0) {
+                    logger.info("respone_msg>" + DataConversion.bytesToHexString(respone_msg));
+                } else {
+                    try {
+                        String key = redisCache.getCacheObject(RedisConstant.KEYS+deviceConnectionMsg.getDeviceId());
+                        respone_msg = Encrytion.decrypt(respone_msg,key.getBytes());
+                        logger.info("respone_msg_enc>" + DataConversion.bytesToHexString(respone_msg));
+                    } catch (Exception e) {
+                        logger.info("ykc->msg decrypt Exception");
+                        e.printStackTrace();
+                    }
+                }
+                if (framType == YkcSendDevice.LOGIN_RESPONSE.getFrameType()) {
+                    logger.info("ykc->dev:"+deviceConnectionMsg.getDeviceId()+"↓↓↓↓↓↓↓↓↓↓↓↓↓登录认证应答消息↓↓↓↓↓↓↓↓↓↓↓↓↓"+framType);
+                    //处理登录成功与否
+                    loginResponse(deviceConnectionMsg, respone_msg);
+                    //计费模型请求
+                    billingModelFrame.checkBillingModel(deviceConnectionMsg);
+                    logger.info("===========登录认证应答============over");
+
+                } else if (framType == YkcSendDevice.HEART_RESPONSE.getFrameType()) {
+                    //TODO:个人理解,枪不管故障还是正常都会有心跳,心跳应答正常说明设备是正常的状态,所以这里不管是同一设备的几号枪心跳都缓存下来心跳应答时间
+                    logger.info("ykc->dev"+deviceConnectionMsg.getDeviceId()+"↓↓↓↓↓↓↓↓↓↓↓↓↓心跳包应答消息↓↓↓↓↓↓↓↓↓↓↓↓↓"+framType);
+                    deviceConnectionMsg.setHeartTime(System.currentTimeMillis());
+                    logger.info("===========心跳包应答============over");
+
+                } else if (framType == YkcSendDevice.BILLING_MODEL_VALIDATE_RESPONSE.getFrameType()) {
+                    logger.info("ykc->dev↓↓↓↓↓↓↓↓↓↓↓↓↓计费模型验证请求应答↓↓↓↓↓↓↓↓↓↓↓↓↓"+framType);
+                    billingModelHandle(deviceConnectionMsg,respone_msg);
+
+                } else if (framType == YkcSendDevice.BILLING_MODEL_RESPONSE.getFrameType()) {
+                    logger.info("ykc->dev"+deviceConnectionMsg.getDeviceId()+"↓↓↓↓↓↓↓↓↓↓↓↓↓计费模型请求应答↓↓↓↓↓↓↓↓↓↓↓↓↓"+framType);
+                    try {
+                        getBillingModelHandle(deviceConnectionMsg,respone_msg);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+
+                } else if (framType == YkcSendDevice.TRANSACTION_RECORDS_RESPONSE.getFrameType()) {
+                    logger.info("ykc->dev"+deviceConnectionMsg.getDeviceId()+"↓↓↓↓↓↓↓↓↓↓↓↓↓交易记录确认↓↓↓↓↓↓↓↓↓↓↓↓↓");
+                    byte[] trans = Arrays.copyOfRange(respone_msg, 0, 16);
+                    byte[] reason = Arrays.copyOfRange(respone_msg, 16, 17);
+                    QueryWrapper<OrderStatus> orderStatusQueryWrapper = new QueryWrapper<>();
+                    orderStatusQueryWrapper.eq("trans_order",trans);
+                    OrderStatus one = orderStatusService.getOne(orderStatusQueryWrapper);
+
+                    QueryWrapper<TransOrder> transOrderQueryWrapper = new QueryWrapper<>();
+                    transOrderQueryWrapper.eq("trans_order",trans);
+                    TransOrder transOrderServiceOne = transOrderService.getOne(transOrderQueryWrapper);
+
+                    if (reason[0]==0x00){
+                        one.setTransactionOrderReplyStatus(StatusConstant.TRANSACTION_ORDER_REPLY_STATUS_SUCC);
+                        transOrderServiceOne.setFlage(StatusConstant.TRANSACTION_ORDER_REPLY_STATUS_SUCC);
+                    }else if (reason[0]==0x01){
+                        one.setTransactionOrderReplyStatus(StatusConstant.TRANSACTION_ORDER_REPLY_STATUS_FAIL);
+                    }else if (reason[0]==0x02){
+                        one.setTransactionOrderReplyStatus(StatusConstant.TRANSACTION_ORDER_REPLY_STATUS_ILLEGAL);
+                    }
+                    //接收到后将订单校验从redis剔除
+                    redisCache.deleteObject(RedisConstant.NO_RESPONSE_WAS_RECEIVED+one.getPileCode());
+                    orderStatusService.updateById(one);
+                    transOrderService.updateById(transOrderServiceOne);
+                } else if (framType == YkcSendDevice.DEVICE_STATUS_REQUEST.getFrameType()) {
+                    logger.info("ykc->dev↓↓↓↓↓↓↓↓↓↓↓↓↓读取实时监测数据↓↓↓↓↓↓↓↓↓↓↓↓↓");
+
+                } else if (framType == YkcSendDevice.START_CHARNGING_REQUEST.getFrameType()) {
+                    logger.info("ykc->dev↓↓↓↓↓↓↓↓↓↓↓↓↓运营平台远程控制启机↓↓↓↓↓↓↓↓↓↓↓↓↓"+framType);
+                    startChargingRequest(deviceConnectionMsg, respone_msg);
+
+                } else if (framType == YkcSendDevice.STOP_CHARNGING_REQUEST.getFrameType()) {
+                    logger.info("ykc->dev↓↓↓↓↓↓↓↓↓↓↓↓↓运营平台远程停机↓↓↓↓↓↓↓↓↓↓↓↓↓");
+                    stopChargingRequest(deviceConnectionMsg,respone_msg);
+
+                } else if (framType == YkcSendDevice.UPDATE_BALANCE.getFrameType()) {
+                    logger.info("ykc->dev↓↓↓↓↓↓↓↓↓↓↓↓↓远程更新余额↓↓↓↓↓↓↓↓↓↓↓↓↓");
+                    try {
+                        remoteBalanceUpdate(deviceConnectionMsg,respone_msg);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+
+                } else if (framType == YkcSendDevice.CHECKTIME.getFrameType()) {
+                    logger.info("ykc->dev↓↓↓↓↓↓↓↓↓↓↓↓↓对时设置↓↓↓↓↓↓↓↓↓↓↓↓↓"+framType);
+                    checkTime.checkTimeSend(deviceConnectionMsg);
+
+                } else if (framType == YkcSendDevice.BILLING_MODEL_SETTING.getFrameType()) {
+                    logger.info("ykc->dev↓↓↓↓↓↓↓↓↓↓↓↓↓计费模型设置↓↓↓↓↓↓↓↓↓↓↓↓↓");
+                    try {
+                        //有异常就是失败
+                        getBillingModelHandle(deviceConnectionMsg,respone_msg);
+                        billingModelFrame.resp(deviceConnectionMsg,(byte) 1);
+                    } catch (Exception e) {
+                        billingModelFrame.resp(deviceConnectionMsg,(byte) 0);
+                        e.printStackTrace();
+                    }
+
+                } else if (framType == YkcSendDevice.REMOTE_REBOOT.getFrameType()) {
+                    logger.info("ykc->dev↓↓↓↓↓↓↓↓↓↓↓↓↓远程重启↓↓↓↓↓↓↓↓↓↓↓↓↓");
+
+                } else if (framType == YkcSendDevice.UPLOAD_FILE_UPDATE.getFrameType()) {
+                    logger.info("ykc->dev↓↓↓↓↓↓↓↓↓↓↓↓↓远程更新↓↓↓↓↓↓↓↓↓↓↓↓↓");
+
+                }
+            }
+        };
+        Thread thread = new Thread(listener);
+        thread.start();
+    }
+
+    /**
+     * 登录回复消息处理
+     *
+     * @param deviceConnectionMsg
+     * @param respone_msg
+     */
+    private void loginResponse(DeviceConnectionMsg deviceConnectionMsg, byte[] respone_msg) {
+        int login_result = respone_msg[7] & 0xFF;
+        if (ResultUtil.isSuccess(login_result)) {
+            //登录成功,相当于初始化心跳包接收时间更新,初始化心跳包时间相当于登录成功,心跳包能正常上传
+            deviceConnectionMsg.setHeartTime(System.currentTimeMillis());
+            deviceConnectionMsg.setLoginStatus(Constant.DEVICE_LOGIN_STATUS);
+        } else {
+            logger.info("pileCode:" + deviceConnectionMsg.getDeviceId() + " login error!!!waiting for retry");
+            //登录失败等心跳包10秒后进行重试
+        }
+    }
+
+    /**
+     * 接收到云快充启机指令的处理
+     *
+     * @param deviceConnectionMsg
+     * @param respone_msg
+     */
+    private void startChargingRequest(DeviceConnectionMsg deviceConnectionMsg, byte[] respone_msg){
+
+        try {
+            //1.解析出启动充电流水号
+            //查询设备sn和imie
+
+            QueryWrapper<DeviceStatus> deviceStatusQueryWrapper = new QueryWrapper<>();
+            deviceStatusQueryWrapper.eq("pile_code", deviceConnectionMsg.getDeviceId());
+            List<DeviceStatus> list = deviceStatusService.list(deviceStatusQueryWrapper);
+            //可能双枪
+            DeviceStatus deviceStatus = list.get(0);
+            //切出订单号,保存在连接的该设备中
+            byte[] transOrder = Arrays.copyOfRange(respone_msg, 0, 16);
+            //测试
+            t=transOrder;
+            byte[] guns = Arrays.copyOfRange(respone_msg, 23, 24);
+            byte[] card = Arrays.copyOfRange(respone_msg, 32, 40);
+            byte[] logCard = Arrays.copyOfRange(respone_msg, 24, 32);
+            byte[] startMoney = Arrays.copyOfRange(respone_msg, respone_msg.length-6, respone_msg.length-2);
+            boolean temp=false;
+            int result=0x00;    //失败
+            int reason=0x00;   //无
+            if (guns[0]==1){
+                DeviceStatus oneDeviceStatus = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE, deviceConnectionMsg.getDeviceId());
+                logger.info(oneDeviceStatus.toString());
+                if (oneDeviceStatus.getOnlineStatus()==StatusConstant.OFFLINE||StatusConstant.OFFLINE==oneDeviceStatus.getGunStatus()){
+                    //离线上报
+                    reason=0x04;
+                    temp=true;
+                    logger.info("reson>"+reason);
+                }else if (StatusConstant.CHARGING==oneDeviceStatus.getGunStatus()){
+                    //枪已在充电中
+                    reason=0x02;
+                    temp=true;
+                    logger.info("reson>"+reason);
+                }else if (StatusConstant.FAULT==oneDeviceStatus.getGunStatus()){
+                    //枪故障
+                    reason=0x03;
+                    temp=true;
+                    logger.info("reson>"+reason);
+                }else if (StatusConstant.INSERT_GUNS_NO==oneDeviceStatus.getInsertGunStatus()){
+                    //未插枪
+                    reason=0x05;
+                    temp=false;
+                    logger.info("reson>"+reason);
+                }else if (PortStatusConstant.EMERGENCY_STOP==oneDeviceStatus.getGunStatus()){
+                    //急停没复位,也是启充失败
+                    temp=true;
+                }
+            }else if(guns[0]==2){
+                DeviceStatus twoDeviceStatus = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_TWO, deviceConnectionMsg.getDeviceId());
+                if (twoDeviceStatus.getOnlineStatus()==StatusConstant.OFFLINE||StatusConstant.OFFLINE==twoDeviceStatus.getGunStatus()){
+                    //离线上报
+                    reason=0x04;
+                    temp=true;
+                    logger.info("reson>"+reason);
+                }else if (StatusConstant.CHARGING==twoDeviceStatus.getGunStatus()){
+                    //枪已在充电中
+                    reason=0x02;
+                    temp=true;
+                    logger.info("reson>"+reason);
+                }else if (StatusConstant.FAULT==twoDeviceStatus.getGunStatus()){
+                    //枪故障
+                    reason=0x03;
+                    temp=true;
+                    logger.info("reson>"+reason);
+                }else if (StatusConstant.INSERT_GUNS_NO==twoDeviceStatus.getInsertGunStatus()){
+                    //未插枪
+                    reason=0x05;
+                    temp=false;
+                    logger.info("reson>"+reason);
+                }else if (PortStatusConstant.EMERGENCY_STOP==twoDeviceStatus.getGunStatus()){
+                    //急停没复位,也是启充失败
+                    temp=true;
+                }
+            }
+            logger.info("reson>"+reason);
+        if (temp){
+            charngingPushFrame.startStatus(deviceConnectionMsg,transOrder,guns[0],result,reason );
+            Map<String, BigDecimal> compute = transMoney.compute(guns[0], new BillingModel(), 0L, 0L, false);
+            transactionFlowPushFrame.sendTrans(deviceConnectionMsg,transOrder, deviceStatus.getPileCode(),guns[0],System.currentTimeMillis(),System.currentTimeMillis(),new BillingModel(),card,compute,TransConstant.START_FAIL);
+            return;
+        }
+            BigDecimal bigDecimalmoney = DataConversion.arrToBigDec(startMoney, 2,2).setScale(2,BigDecimal.ROUND_UP);
+            logger.info("startMoney"+bigDecimalmoney+"sbyt+"+DataConversion.bytesToHexString(startMoney));
+            OrderStatus orderStatus = new OrderStatus();
+            orderStatus.setDeviceSn(deviceStatus.getDeviceSn());
+            orderStatus.setDeviceImei(deviceConnectionMsg.getImei());
+            orderStatus.setPileCode(deviceStatus.getPileCode());
+            orderStatus.setGunsCode(guns[0]);
+            orderStatus.setStartMoney(bigDecimalmoney);
+            orderStatus.setCard(card);
+            orderStatus.setTransOrder(transOrder);
+            orderStatus.setCreateTime(System.currentTimeMillis());
+            //2.保存交易流水号并记录本次交易流水状态
+            orderStatusService.save(orderStatus);
+            this.saveOrder(orderStatus);
+            //setMoney(orderStatus);//让设备记余额不足情况,启充时要传启充金额money,传0就是启充默认值
+            //3.向设备发送启机指令
+            if (reason==0x05){
+                //未插枪不启充设备,但是返回未插枪
+                logger.info("未插枪等待充电订单:"+DataConversion.bytesToHexString(transOrder));
+                charngingPushFrame.startStatus(deviceConnectionMsg,transOrder,guns[0],result,reason );
+                redisCache.setCacheObject(RedisConstant.NO_INSERT_GUN_YKC+deviceStatus.getPileCode(),DataConversion.bytesToHexString(transOrder),60*1000, TimeUnit.MILLISECONDS);
+            }else {
+                deviceControlerService.startCharge(deviceStatus.getDeviceImei(), deviceStatus.getDeviceImei(), (int) guns[0],0);
+            }
+
+
+            //TODO:在处理设备消息的地方需要处理设备启动充电是否成功
+            //TODO:保存交易流水时保存一个交易订单
+
+        } catch (Exception e) {
+            realTimeStatusPushFrame.deviceStatusPush(deviceConnectionMsg, t, deviceConnectionMsg.getDeviceId(), (byte) 1, (byte)2, (byte)1, 0,0 , zero , zero , 0);
+            e.printStackTrace();
+        }
+    }
+private  static  byte[] t=new byte[16];
+    /**
+     * 计费模型校验应答处理
+     *
+     * @param deviceConnectionMsg
+     * @param respone_msg
+     */
+    private void billingModelHandle(DeviceConnectionMsg deviceConnectionMsg, byte[] respone_msg) {
+        byte[] modelNum = Arrays.copyOfRange(respone_msg, 7, 9);
+        byte[] result = Arrays.copyOfRange(respone_msg, 9, 10);
+        logger.info("登录成功计费模型验证应答"+deviceConnectionMsg.getDeviceId()+">>modelNum:"+DataConversion.bytesToHexString(modelNum) +";result:"+DataConversion.bytesToHexString(result));
+        if (result[0] == BillingModelConst.DIFFERENT) {
+            //不一致请求计费模型,再向平台请求计费模型0x09
+            billingModelFrame.getBillingModel(deviceConnectionMsg);
+        }
+    }
+
+    /**
+     * 计费模型请求处理,需要将获取到的云快充计费模型保存到数据库中
+     * @param deviceConnectionMsg
+     * @param respone_msg
+     */
+    private void getBillingModelHandle(DeviceConnectionMsg deviceConnectionMsg, byte[] respone_msg)  throws Exception{
+        int index=7;
+        int a=0;
+        byte[] modelNum = Arrays.copyOfRange(respone_msg, index, index+2);
+        index+=2;
+        byte[] j = Arrays.copyOfRange(respone_msg, index, index+4);
+        index+=4;
+        byte[] jf = Arrays.copyOfRange(respone_msg, index, index+4);
+        index+=4;
+        byte[]  f= Arrays.copyOfRange(respone_msg, index, index+4);
+        index+=4;
+        byte[] ff = Arrays.copyOfRange(respone_msg, index, index+4);
+        index+=4;
+        byte[] p = Arrays.copyOfRange(respone_msg, index, index+4);
+        index+=4;
+        byte[] pf = Arrays.copyOfRange(respone_msg, index, index+4);
+        index+=4;
+        byte[] g = Arrays.copyOfRange(respone_msg, index, index+4);
+        index+=4;
+        byte[] gf = Arrays.copyOfRange(respone_msg, index, index+4);
+        index+=4;
+        byte[] solt = Arrays.copyOfRange(respone_msg, index, index+1);
+        index++;
+        byte[] shiduan  = Arrays.copyOfRange(respone_msg, index, respone_msg.length);
+        QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+        billingModelQueryWrapper.eq("pile_code",deviceConnectionMsg.getDeviceId());
+        BillingModel one = billingModelService.getOne(billingModelQueryWrapper);
+        if (one==null){
+            one = new BillingModel();
+        }
+        BigDecimal bigDecimal = DataConversion.arrToBigDec(modelNum, 0,0);
+        int intValue = bigDecimal.intValue();
+        one.setDeviceImei(deviceConnectionMsg.getImei());
+        one.setDeviceSn(deviceConnectionMsg.getDeviceSn());
+        one.setPileCode(deviceConnectionMsg.getDeviceId());
+        one.setModelNo(intValue);
+        one.setSharpPrice(DataConversion.arrToBigDec(j,5,5));         //尖
+        one.setSharpServiceFee(DataConversion.arrToBigDec(jf,5,5));   //尖服
+        one.setPeakPrice(DataConversion.arrToBigDec(f,5,5));          //峰
+        one.setPeakServiceFee(DataConversion.arrToBigDec(ff,5,5));    //峰服
+        one.setFlatPrice(DataConversion.arrToBigDec(p,5,5));         //平
+        one.setFlatServiceFee(DataConversion.arrToBigDec(pf,5,5));   //平服
+        one.setValleyPrice(DataConversion.arrToBigDec(g,5,5));          //谷
+        one.setValleyServiceFee(DataConversion.arrToBigDec(gf,5,5));    //谷服
+        int integer= Integer.valueOf(Byte.toString(solt[0]));  //计损比例
+        one.setLossRatio(integer);                                               //时间段
+        StringBuilder stringBuilder = new StringBuilder();
+        for (byte b : shiduan) {
+            stringBuilder.append(String.valueOf(b));
+        }
+        String s = stringBuilder.toString();
+        one.setTimeSlot(s);
+        one.setUpdateTime(System.currentTimeMillis());//更新时间
+        billingModelService.saveOrUpdate(one);
+    }
+
+    /**
+     * 停机指令处理
+     * @param deviceConnectionMsg
+     * @param respone_msg
+     */
+    private void stopChargingRequest(DeviceConnectionMsg deviceConnectionMsg,byte[] respone_msg){
+        byte[] guns = Arrays.copyOfRange(respone_msg, 7, 8);
+        //查询设备sn和imie
+        QueryWrapper<DeviceStatus> deviceStatusQueryWrapper = new QueryWrapper<>();
+        deviceStatusQueryWrapper.eq("pile_code", deviceConnectionMsg.getDeviceId());
+        List<DeviceStatus> list = deviceStatusService.list(deviceStatusQueryWrapper);
+        DeviceStatus deviceStatus = list.get(0);
+
+        try {
+            TimeUnit.MILLISECONDS.sleep(300);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        RpcResult rpcResult = deviceControlerService.stopCharge(deviceStatus.getDeviceImei(), deviceStatus.getDeviceImei(), (int) guns[0]);
+
+        if (rpcResult.isOk()){
+            //发送停充成功,一般没有指令发送成功设备不执行
+         /*   //deviceStatus.setGunStatus((byte) PortStatusConstant.INSERT_GUN);
+            deviceStatus.setOnlineStatus(DeviceOnlineStatus.ONLINE);
+            deviceStatus.setUpdateTime(System.currentTimeMillis());
+            deviceStatusService.updateById(deviceStatus);*/
+            QueryWrapper<OrderStatus> orderStatusQueryWrapper = new QueryWrapper<>();
+            orderStatusQueryWrapper.eq("device_imei", deviceStatus.getDeviceImei()).eq("guns_code", guns[0]).orderByDesc("create_time").last("limit 1");
+            OrderStatus statusServiceOne = orderStatusService.getOne(orderStatusQueryWrapper);
+            statusServiceOne.setEndTime(System.currentTimeMillis());
+            statusServiceOne.setReasonStopCharging(TransConstant.APP_REMOTE_STOP);
+            orderStatusService.updateById(statusServiceOne);
+            if (guns[0] == 1) {
+                if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_ONE)) {
+                    DeviceStatus ds= redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE,deviceStatus.getPileCode());
+                    if (ds!=null){
+                        redisCache.deleteCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE,deviceStatus.getPileCode());
+                    }
+                }
+            } else if (guns[0] == 2) {
+                if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_TWO)) {
+                    DeviceStatus ds= redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_TWO,deviceStatus.getPileCode());
+                    if (ds!=null){
+                        redisCache.deleteCacheMapValue(RedisConstant.ONLINE_DEVICE_TWO,deviceStatus.getPileCode());
+                    }
+                }
+            }
+            redisCache.setCacheMapValue(guns[0] == 1 ? RedisConstant.ONLINE_DEVICE_ONE : RedisConstant.ONLINE_DEVICE_TWO, deviceStatus.getPileCode(),deviceStatus);
+        }else {
+            logger.info("stop charging  device:"+deviceStatus.getDeviceSn()+";port:"+guns[0]+"stop charnging fail");
+        }
+        //发出结束充电指令
+        DeviceParam deviceParam = new DeviceParam();
+        deviceParam.setDeviceId(deviceStatus.getDeviceImei());
+        deviceParam.setCcid(deviceStatus.getDeviceImei());
+        deviceControlerService.sendPortDetailCmd(deviceParam);
+    }
+
+    /***
+     * 远程余额更新
+     * @param deviceConnectionMsg
+     * @param respone_msg
+     */
+    private void remoteBalanceUpdate(DeviceConnectionMsg deviceConnectionMsg,byte[] respone_msg)  throws Exception{
+        byte[] pileCode = Arrays.copyOfRange(respone_msg, 0, 7);
+        byte[] guns = Arrays.copyOfRange(respone_msg, 7, 8);
+        byte[] card = Arrays.copyOfRange(respone_msg, 8, 16);
+        byte[] balance=Arrays.copyOfRange(respone_msg, 16, 20);
+        BigDecimal bigDecimal = DataConversion.arrToBigDec(balance, 2, 2);
+
+        String pile=DataConversion.bytesToHexString(pileCode);
+
+        logger.info("更新余额>>"+bigDecimal+";pileCode:"+pile);
+        //TODO:1.这里更新的余额需要针对充电桩使用的人员进行修改;(考虑后台计费);2.更新操作完成时候响应云快充更新结果
+        //拿当前的订单去更新余额
+        QueryWrapper<OrderStatus> orderStatusQueryWrapper = new QueryWrapper<>();
+        orderStatusQueryWrapper.eq("pile_code", pile).eq("guns_code", guns[0]).eq("card",card).orderByDesc("create_time").last("limit 1");
+        OrderStatus statusServiceOne = orderStatusService.getOne(orderStatusQueryWrapper);
+
+        QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+        billingModelQueryWrapper.eq("pile_code",pile);
+        BillingModel model = billingModelService.getOne(billingModelQueryWrapper);
+
+        //后台计费
+        Map<String, BigDecimal> map = transMoney.compute(1, model, statusServiceOne.getCreateTime(), System.currentTimeMillis(),true);
+        BigDecimal money = statusServiceOne.getStartMoney().subtract(map.get("money")).setScale(4, BigDecimal.ROUND_DOWN);
+        BigDecimal add = money.add(bigDecimal);
+        statusServiceOne.setStartMoney(add);
+        orderStatusService.updateById(statusServiceOne);
+        //这里测试默认更新成功
+        remoteBalanceUpdatePushFrame.updateBalance(deviceConnectionMsg,card, (byte) 0x00);
+
+    }
+
+    //保存订单开始状态,记录充电的交易流水
+    private void saveOrder(OrderStatus orderStatus){
+        TransOrder transOrder = transMapping.orderStatusToTransOrder(orderStatus);
+        byte[] transOrders = new byte[8];
+        System.arraycopy(orderStatus.getTransOrder(), 8, transOrders, 0, transOrders.length);
+        //为了云快充app显示的订单号一致,将订单号格式转化一下
+        transOrder.setTrans(DataConversion.bytesToHexString(transOrders));
+        boolean b = transOrderService.saveOrUpdate(transOrder);
+        logger.info("保存订单>>>>>"+DataConversion.bytesToHexString(orderStatus.getTransOrder())+"结果:"+b);
+    }
+    //余额不足解决办法,将下发的余额和尖峰平谷中最贵的价格发送到主板参数中,配合主板参数进行余额不足判定
+    private void setMoney(OrderStatus orderStatus){
+
+        QueryWrapper<BillingModel> orderStatusQueryWrapper = new QueryWrapper<>();
+        orderStatusQueryWrapper.eq("pile_code",orderStatus.getPileCode());
+        BillingModel billingModel = billingModelService.getOne(orderStatusQueryWrapper);
+        MainBoard mainBoard = new MainBoard();
+        mainBoard.setBottomPrice(billingModel.getSharpPrice().add(billingModel.getSharpServiceFee()).intValue());
+        mainBoard.setBottomServicePrice(0);
+        mainBoard.setPeakPrice(billingModel.getSharpPrice().add(billingModel.getSharpServiceFee()).intValue());
+        mainBoard.setPeakServicePrice(0);
+        mainBoard.setBottomStart(0);
+        mainBoard.setBottomEnd(24);
+        mainBoard.setFeeType(0x00);
+        //设置主板参数
+        deviceControlerService.setMainBoard(orderStatus.getDeviceImei(),orderStatus.getDeviceImei(),mainBoard);
+
+    }
+
+}

+ 66 - 0
src/main/java/com/tmzn/devicelinkykc/msgEnum/DeviceSendYkc.java

@@ -0,0 +1,66 @@
+package com.tmzn.devicelinkykc.msgEnum;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 数据传输方向:充电桩=>运营平台 "
+ */
+public enum DeviceSendYkc {
+    LOGIN(0x01,0x00,"登录"),
+    HEART_SEND(0x03,0x00,"充电桩心跳包"),
+    BILLING_MODEL_VALIDATE_REQUEST(0x05,0x01,"计费模型验证请求"),
+    BILLING_MODEL_REQUEST(0x09,0x01,"充电桩计费模型请求"),
+    TRANSACTION_RECORDS_REQUEST(0x3D,0x01,"交易记录"),
+
+    UPLOAD_DEVICE_STATUS_RESPONSE(0x13,0x01,"设备上报平台状态"),
+    START_CHARNGING_RESPONSE(0xA7,0x01,"运营平台远程控制启机回复"),
+    STOP_CHARNGING_RESPONSE(0x35,0x01,"远程停机命令回复"),
+    UPDATE_BALANCE_RESPONSE(0x41,0x01,"远程更新余额应答"),
+    CHECKTIME_RESPONSE(0x55,0x00,"对时应答"),
+    BILLING_MODEL_SETTING_RESPONSE(0x57,0x01," 计费模型设置应答"),
+    REMOTE_REBOOT_RESPONSE(0x93,0x01,"远程重启应答"),
+    UPLOAD_FILE_UPDATE_RESPONSE(0x93,0x01,"远程更新应答"),
+    ;
+
+    private int frameType;          //帧类型
+    private int encryptFlag;        //加密类型
+    private String alias;           //帧类型名称
+
+    public int getFrameType() {
+        return frameType;
+    }
+
+    public void setFrameType(int frameType) {
+        this.frameType = frameType;
+    }
+
+    public int getEncryptFlag() {
+        return encryptFlag;
+    }
+
+    public void setEncryptFlag(int encryptFlag) {
+        this.encryptFlag = encryptFlag;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    DeviceSendYkc(int frameType, int encryptFlag, String alias) {
+        this.frameType = frameType;
+        this.encryptFlag = encryptFlag;
+        this.alias = alias;
+    }
+    public static String getNameByframeType(int ft) {
+        for (DeviceSendYkc item : DeviceSendYkc.values()) {
+            if (item.getFrameType() == ft) {
+                return item.getAlias();
+            }
+        }
+        return null; // 如果没有找到对应的 ID
+    }
+}

+ 57 - 0
src/main/java/com/tmzn/devicelinkykc/msgEnum/YkcSendDevice.java

@@ -0,0 +1,57 @@
+package com.tmzn.devicelinkykc.msgEnum;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 数据传输方向:运营平台=>充电桩  "
+ */
+public enum YkcSendDevice {
+    LOGIN_RESPONSE(0x02,0x00,"登录应答"),
+    HEART_RESPONSE(0x04,0x00," 心跳包应答"),
+    BILLING_MODEL_VALIDATE_RESPONSE(0x06,0x01,"计费模型验证请求应答"),
+    BILLING_MODEL_RESPONSE(0x0A,0x01,"计费模型请求应答"),
+    TRANSACTION_RECORDS_RESPONSE(0x40,0x01,"交易记录确认"),
+
+    DEVICE_STATUS_REQUEST(0x12,0x01,"读取实时监测数据"),
+    START_CHARNGING_REQUEST(0xA8,0x01,"运营平台远程控制启机"),
+    STOP_CHARNGING_REQUEST(0x36,0x01,"运营平台远程停机"),
+    UPDATE_BALANCE(0x42,0x01,"远程更新余额"),
+    CHECKTIME(0x56,0x00," 对时设置"),
+    BILLING_MODEL_SETTING(0x58,0x01," 计费模型设置"),
+    REMOTE_REBOOT(0x92,0x01," 远程重启"),
+    UPLOAD_FILE_UPDATE(0x94,0x01," 远程更新")
+    ;
+    private int frameType;          //帧类型
+    private int encryptFlag;        //加密类型
+    private String alias;           //帧类型名称
+
+    public int getFrameType() {
+        return frameType;
+    }
+
+    public void setFrameType(int frameType) {
+        this.frameType = frameType;
+    }
+
+    public int getEncryptFlag() {
+        return encryptFlag;
+    }
+
+    public void setEncryptFlag(int encryptFlag) {
+        this.encryptFlag = encryptFlag;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    YkcSendDevice(int frameType, int encryptFlag, String alias) {
+        this.frameType = frameType;
+        this.encryptFlag = encryptFlag;
+        this.alias = alias;
+    }
+}

+ 36 - 0
src/main/java/com/tmzn/devicelinkykc/openfeign/FeginClientFactory.java

@@ -0,0 +1,36 @@
+package com.tmzn.devicelinkykc.openfeign;
+
+import feign.Feign;
+import feign.jackson.JacksonDecoder;
+import feign.jackson.JacksonEncoder;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 缓存
+ */
+public class FeginClientFactory {
+	
+	/**
+	 * 缓存所有的Fegin客户端
+	 */
+	private volatile static Map<String, Object> feginClientCache = new HashMap<>();
+	
+	/**
+	 * 从Map中获取数据
+	 * @return 
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> T getFeginClient(Class<T> clazz, String baseUrl){
+		if(!feginClientCache.containsKey(baseUrl)) {
+			synchronized (FeginClientFactory.class) {
+				if(!feginClientCache.containsKey(baseUrl)) {
+					T feginClient = Feign.builder().decoder(new JacksonDecoder()).encoder(new JacksonEncoder()).target(clazz, baseUrl);
+					feginClientCache.put(baseUrl, feginClient);
+				}
+			}
+		}
+		return (T)feginClientCache.get(baseUrl);
+	}
+}

+ 13 - 0
src/main/java/com/tmzn/devicelinkykc/openfeign/FeginClientProxy.java

@@ -0,0 +1,13 @@
+package com.tmzn.devicelinkykc.openfeign;
+
+import com.tmzn.devicelinkykc.openfeign.transdata.DataParam;
+import com.tmzn.devicelinkykc.openfeign.transdata.RpcResult;
+import feign.Headers;
+import feign.RequestLine;
+
+//feign远程代理
+public interface FeginClientProxy {
+	@Headers("Content-Type:application/json;charset=UTF-8")
+	@RequestLine("POST /sendMsg")
+	RpcResult sendMsg(DataParam dataParam);
+}

+ 23 - 0
src/main/java/com/tmzn/devicelinkykc/openfeign/MsgService.java

@@ -0,0 +1,23 @@
+package com.tmzn.devicelinkykc.openfeign;
+
+import com.tmzn.devicelinkykc.openfeign.transdata.DataParam;
+import com.tmzn.devicelinkykc.openfeign.transdata.RpcResult;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+
+/**
+ *  利用feign 远程调用
+ */
+@Service
+public class MsgService {
+
+    @Value("${msgService}")
+    private String url;
+
+    public RpcResult sendMsg(DataParam dataParam){
+        RpcResult rpcResult = FeginClientFactory.getFeginClient(FeginClientProxy.class, url).sendMsg(dataParam);
+        return rpcResult;
+    }
+
+}

+ 13 - 0
src/main/java/com/tmzn/devicelinkykc/openfeign/OpenFeignConfig.java

@@ -0,0 +1,13 @@
+package com.tmzn.devicelinkykc.openfeign;
+
+import feign.Contract;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class OpenFeignConfig {
+	@Bean
+	public Contract useFeignAnnotations() {
+		return new Contract.Default();
+	}
+}

+ 16 - 0
src/main/java/com/tmzn/devicelinkykc/openfeign/transdata/DataParam.java

@@ -0,0 +1,16 @@
+package com.tmzn.devicelinkykc.openfeign.transdata;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.Data;
+
+@Data
+public class DataParam {
+
+    private JSONObject data;
+
+    private String ccid;
+
+    private String deviceId;
+
+    private Integer type;
+}

+ 42 - 0
src/main/java/com/tmzn/devicelinkykc/openfeign/transdata/RpcResult.java

@@ -0,0 +1,42 @@
+package com.tmzn.devicelinkykc.openfeign.transdata;
+
+import lombok.Data;
+
+@Data
+public class RpcResult {
+
+    private boolean ok = false;
+
+    private String msg;
+
+    private Object data;
+
+    public static RpcResult ok(){
+        RpcResult rpcResult = new RpcResult();
+        rpcResult.setOk(true);
+        return rpcResult;
+    }
+    public static RpcResult success(){
+        return ok();
+    }
+
+    public static RpcResult ok(Object data){
+        RpcResult rpcResult = new RpcResult();
+        rpcResult.setOk(true);
+        rpcResult.setData(data);
+        return rpcResult;
+    }
+
+    public static RpcResult success(Object data){
+
+        return ok(data);
+    }
+
+    public static RpcResult fail(String msg){
+        RpcResult rpcResult = new RpcResult();
+        rpcResult.setOk(false);
+        rpcResult.setMsg(msg);
+        return rpcResult;
+    }
+
+}

+ 296 - 0
src/main/java/com/tmzn/devicelinkykc/redis/RedisCache.java

@@ -0,0 +1,296 @@
+package com.tmzn.devicelinkykc.redis;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.BoundSetOperations;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * spring redis 工具类
+ *
+ * @author ruoyi
+ **/
+@SuppressWarnings(value = { "unchecked", "rawtypes" })
+@Component
+public class RedisCache
+{
+    @Autowired
+    public RedisTemplate redisTemplate;
+
+    /**
+     * 缓存基本的对象,Integer、String、实体类等
+     *
+     * @param key 缓存的键值
+     * @param value 缓存的值
+     */
+    public <T> void setCacheObject(final String key, final T value)
+    {
+        redisTemplate.opsForValue().set(key, value);
+    }
+
+    /**
+     * 缓存基本的对象,Integer、String、实体类等
+     *
+     * @param key 缓存的键值
+     * @param value 缓存的值
+     * @param timeout 时间
+     * @param timeUnit 时间颗粒度
+     */
+    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
+    {
+        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
+    }
+
+    /**
+     * 设置有效时间
+     *
+     * @param key Redis键
+     * @param timeout 超时时间
+     * @return true=设置成功;false=设置失败
+     */
+    public boolean expire(final String key, final long timeout)
+    {
+        return expire(key, timeout, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 设置有效时间
+     *
+     * @param key Redis键
+     * @param timeout 超时时间
+     * @param unit 时间单位
+     * @return true=设置成功;false=设置失败
+     */
+    public boolean expire(final String key, final long timeout, final TimeUnit unit)
+    {
+        return redisTemplate.expire(key, timeout, unit);
+    }
+
+    /**
+     * 获取有效时间
+     *
+     * @param key Redis键
+     * @return 有效时间
+     */
+    public long getExpire(final String key)
+    {
+        return redisTemplate.getExpire(key);
+    }
+
+    /**
+     * 判断 key是否存在
+     *
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public Boolean hasKey(String key)
+    {
+        return redisTemplate.hasKey(key);
+    }
+
+    /**
+     * 获得缓存的基本对象。
+     *
+     * @param key 缓存键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> T getCacheObject(final String key)
+    {
+        ValueOperations<String, T> operation = redisTemplate.opsForValue();
+        return operation.get(key);
+    }
+
+    /**
+     * 获得缓存的基本对象。
+     *
+     * @param key 缓存键值
+     * @return 缓存键值对应的数据
+     */
+    public synchronized  <T> T getAndDel(final String key)
+    {
+        ValueOperations<String, T> operation = redisTemplate.opsForValue();
+        T t = operation.get(key);
+        redisTemplate.delete(key);
+        return t;
+    }
+
+    /**
+     * 删除单个对象
+     *
+     * @param key
+     */
+    public boolean deleteObject(final String key)
+    {
+        return redisTemplate.delete(key);
+    }
+
+    /**
+     * 删除集合对象
+     *
+     * @param collection 多个对象
+     * @return
+     */
+    public boolean deleteObject(final Collection collection)
+    {
+        return redisTemplate.delete(collection) > 0;
+    }
+
+    /**
+     * 缓存List数据
+     *
+     * @param key 缓存的键值
+     * @param dataList 待缓存的List数据
+     * @return 缓存的对象
+     */
+    public <T> long setCacheList(final String key, final List<T> dataList)
+    {
+        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
+        return count == null ? 0 : count;
+    }
+
+    /**
+     * 获得缓存的list对象
+     *
+     * @param key 缓存的键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> List<T> getCacheList(final String key)
+    {
+        return redisTemplate.opsForList().range(key, 0, -1);
+    }
+
+    /**
+     * 缓存Set
+     *
+     * @param key 缓存键值
+     * @param dataSet 缓存的数据
+     * @return 缓存数据的对象
+     */
+    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
+    {
+        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
+        Iterator<T> it = dataSet.iterator();
+        while (it.hasNext())
+        {
+            setOperation.add(it.next());
+        }
+        return setOperation;
+    }
+
+    /**
+     * 获得缓存的set
+     *
+     * @param key
+     * @return
+     */
+    public <T> Set<T> getCacheSet(final String key)
+    {
+        return redisTemplate.opsForSet().members(key);
+    }
+
+    /**
+     * 缓存Map
+     *
+     * @param key
+     * @param dataMap
+     */
+    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
+    {
+        if (dataMap != null) {
+            redisTemplate.opsForHash().putAll(key, dataMap);
+        }
+    }
+
+    /**
+     * 获得缓存的Map
+     *
+     * @param key
+     * @return
+     */
+    public <T> Map<String, T> getCacheMap(final String key)
+    {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * 往Hash中存入数据
+     *
+     * @param key Redis键
+     * @param hKey Hash键
+     * @param value 值
+     */
+    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
+    {
+        redisTemplate.opsForHash().put(key, hKey, value);
+    }
+
+    /**
+     * 获取Hash中的数据
+     *
+     * @param key Redis键
+     * @param hKey Hash键
+     * @return Hash中的对象
+     */
+    public <T> T getCacheMapValue(final String key, final String hKey)
+    {
+        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
+        return opsForHash.get(key, hKey);
+    }
+
+    /**
+     * 获取多个Hash中的数据
+     *
+     * @param key Redis键
+     * @param hKeys Hash键集合
+     * @return Hash对象集合
+     */
+    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
+    {
+        return redisTemplate.opsForHash().multiGet(key, hKeys);
+    }
+
+    /**
+     * 删除Hash中的某条数据
+     *
+     * @param key Redis键
+     * @param hKey Hash键
+     * @return 是否成功
+     */
+    public boolean deleteCacheMapValue(final String key, final String hKey)
+    {
+        return redisTemplate.opsForHash().delete(key, hKey) > 0;
+    }
+
+    /**
+     * 获得缓存的基本对象列表
+     *
+     * @param pattern 字符串前缀
+     * @return 对象列表
+     */
+    public Collection<String> keys(final String pattern)
+    {
+        return redisTemplate.keys(pattern);
+    }
+
+    /**
+     * Redis 分布式锁
+     * @param key 锁键
+     * @param value 锁值,可以为随机数或者 UUID 等唯一标识符
+     * @param expireTime 锁过期时间,单位为毫秒
+     * @return true:获取锁成功,false:获取锁失败
+     */
+    public boolean tryLock(String key, String value, long expireTime) {
+        Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS);
+        if (result != null && result) {
+            // 获取锁成功
+            return true;
+        }
+        return false;
+    }
+
+}

+ 52 - 0
src/main/java/com/tmzn/devicelinkykc/redis/RedisConfig.java

@@ -0,0 +1,52 @@
+package com.tmzn.devicelinkykc.redis;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.listener.ChannelTopic;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+
+@Configuration
+@EnableCaching
+public class RedisConfig extends CachingConfigurerSupport {
+
+    @Value("${redisMsgChanel}")
+    private String redisMsgChanel;
+    @Bean
+    public RedisTemplate<Object, Object> redisTemplate(
+            RedisConnectionFactory redisConnectionFactory)
+            throws UnknownHostException {
+        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
+        template.setConnectionFactory(redisConnectionFactory);
+
+
+        template.setKeySerializer(new StringRedisSerializer(StandardCharsets.UTF_8));
+        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
+        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
+        template.setHashKeySerializer(new StringRedisSerializer(StandardCharsets.UTF_8));
+        return template;
+    }
+
+    @Bean
+    public MessageListenerAdapter messageListenerAdapter(RedisMessageListener redisMessageListener) {
+        return new MessageListenerAdapter(redisMessageListener);
+    }
+
+    @Bean
+    public RedisMessageListenerContainer redisContainer(RedisConnectionFactory connectionFactory, MessageListenerAdapter messageListenerAdapter) {
+        final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
+        container.setConnectionFactory(connectionFactory);
+        container.addMessageListener(messageListenerAdapter, new ChannelTopic(redisMsgChanel));
+        return container;
+    }
+}

+ 25 - 0
src/main/java/com/tmzn/devicelinkykc/redis/RedisMessageListener.java

@@ -0,0 +1,25 @@
+package com.tmzn.devicelinkykc.redis;
+
+import com.tmzn.devicelinkykc.message.DeviceMsgHandle;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.connection.MessageListener;
+import org.springframework.stereotype.Component;
+
+@Component
+public class RedisMessageListener implements MessageListener {
+
+    @Autowired
+    private DeviceMsgHandle deviceMsgHandle;
+
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        String channel = new String(message.getChannel());
+        String msg = new String(message.getBody());
+        try {
+            deviceMsgHandle.deviceMsg(msg);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 12 - 0
src/main/java/com/tmzn/devicelinkykc/service/BillingModelService.java

@@ -0,0 +1,12 @@
+package com.tmzn.devicelinkykc.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.tmzn.devicelinkykc.entity.BillingModel;
+
+/**
+ * @author xp
+ * @date 2024/3/19
+ * @explain "  "
+ */
+public interface BillingModelService extends IService<BillingModel> {
+}

+ 181 - 0
src/main/java/com/tmzn/devicelinkykc/service/DeviceControlerService.java

@@ -0,0 +1,181 @@
+package com.tmzn.devicelinkykc.service;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.tmzn.devicelinkykc.openfeign.MsgService;
+import com.tmzn.devicelinkykc.openfeign.transdata.DataParam;
+import com.tmzn.devicelinkykc.openfeign.transdata.RpcResult;
+import com.tmzn.devicelinkykc.transdata.entity.*;
+import com.tmzn.devicelinkykc.transdata.entity.opertype.OperEnum;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+public class DeviceControlerService {
+    @Autowired
+    private MsgService msgService;
+
+
+    /**
+     * 获取主板参数
+     * @param deviceId
+     * @param ccid
+     */
+    public void getMainBoard(String deviceId,String ccid){
+        DataParam dataParam = new DataParam();
+        MainBoard mainBoard = new MainBoard();
+        JSONObject object1 = JSONObject.parseObject(JSON.toJSONString(mainBoard));
+        dataParam.setData(object1);
+        dataParam.setCcid(ccid);
+        dataParam.setDeviceId(deviceId);
+        dataParam.setType(OperEnum.GetMainBord.getType());
+        msgService.sendMsg(dataParam);
+        log.info("发送获取主板的消息,{}",deviceId);
+    }
+
+    /**
+     * 设置主板参数
+     * @param deviceId
+     * @param ccid
+     * @param mainBoard
+     */
+    public void setMainBoard(String deviceId, String ccid, MainBoard mainBoard){
+        DataParam dataParam = new DataParam();
+        JSONObject object1 = JSONObject.parseObject(JSON.toJSONString(mainBoard));
+        dataParam.setData(object1);
+        dataParam.setCcid(ccid);
+        dataParam.setDeviceId(deviceId);
+        dataParam.setType(OperEnum.SetMainBord.getType());
+        msgService.sendMsg(dataParam);
+        log.info("发送设置主板的消息,{}",deviceId);
+    }
+
+    /**
+     * 开始充电
+     * @param deviceId
+     */
+    public void startCharge(String deviceId,String ccid,Integer port,int money){
+        DataParam dataParam = new DataParam();
+        StartCharge mainBoard = new StartCharge();
+        mainBoard.setPort(port);
+        if (money!=0){
+            mainBoard.setMoney(money);
+        }
+
+        JSONObject object1 = JSONObject.parseObject(JSON.toJSONString(mainBoard));
+        dataParam.setData(object1);
+        dataParam.setCcid(ccid);
+        dataParam.setDeviceId(deviceId);
+        dataParam.setType(OperEnum.StartCharge.getType());
+        RpcResult rpcResult = msgService.sendMsg(dataParam);
+
+        log.info("发送开始充电的消息,{}",deviceId);
+    }
+
+    /**
+     * 结束充电
+     * @param deviceId
+     * @param ccid
+     */
+    public RpcResult stopCharge(String deviceId,String ccid,Integer port){
+        DataParam dataParam = new DataParam();
+        EndCharge mainBoard = new EndCharge();
+        mainBoard.setPort(port);
+        JSONObject object1 = JSONObject.parseObject(JSON.toJSONString(mainBoard));
+        dataParam.setData(object1);
+        dataParam.setCcid(ccid);
+        dataParam.setDeviceId(deviceId);
+        dataParam.setType(OperEnum.EndCharge.getType());
+        RpcResult rpcResult = msgService.sendMsg(dataParam);
+        log.info("发送停止充电的消息,{}",deviceId);
+        return rpcResult;
+    }
+
+    /**
+     * 重启设备
+     * @param deviceId
+     * @param ccid
+     */
+    public void restart(String deviceId,String ccid){
+        DataParam dataParam = new DataParam();
+        MainBoard mainBoard = new MainBoard();
+        JSONObject object1 = JSONObject.parseObject(JSON.toJSONString(mainBoard));
+        dataParam.setData(object1);
+        dataParam.setCcid(ccid);
+        dataParam.setDeviceId(deviceId);
+        dataParam.setType(OperEnum.RestartDevice.getType());
+        msgService.sendMsg(dataParam);
+        log.info("发送重启设备的消息,{}",deviceId);
+    }
+
+    /**
+     * 重置设备
+     * @param deviceId
+     * @param ccid
+     */
+    public void reset(String deviceId,String ccid){
+        DataParam dataParam = new DataParam();
+        MainBoard mainBoard = new MainBoard();
+        JSONObject object1 = JSONObject.parseObject(JSON.toJSONString(mainBoard));
+        dataParam.setData(object1);
+        dataParam.setCcid(ccid);
+        dataParam.setDeviceId(deviceId);
+        dataParam.setType(OperEnum.ResetDevice.getType());
+        msgService.sendMsg(dataParam);
+
+        log.info("发送重置设备的消息,{}",deviceId);
+    }
+
+
+    /**
+     * 发送获取端口状态指令
+     * @param deviceParam
+     */
+    public void sendPortDetailCmd(DeviceParam deviceParam) {
+        String deviceId = deviceParam.getDeviceId();
+        String ccid = deviceParam.getCcid();
+        DataParam dataParam = new DataParam();
+        JSONObject object1 = new JSONObject();
+        dataParam.setData(object1);
+        dataParam.setCcid(ccid);
+        dataParam.setDeviceId(deviceId);
+        dataParam.setType(OperEnum.PortDetail.getType());
+        RpcResult rpcResult = msgService.sendMsg(dataParam);
+        log.info("发送获取端口详情的消息,{}",deviceId);
+    }
+
+    /**
+     * 预约充电
+     */
+    public void planCharge(String deviceId, String ccid, PlanCharge planCharge) {
+        DataParam dataParam = new DataParam();
+        JSONObject object1 = JSONObject.parseObject(JSON.toJSONString(planCharge));
+        dataParam.setData(object1);
+        dataParam.setCcid(ccid);
+        dataParam.setDeviceId(deviceId.toString());
+        dataParam.setType(OperEnum.PlanCHarge.getType());
+        RpcResult rpcResult = msgService.sendMsg(dataParam);
+        log.info("发送预约充电的消息,{}",deviceId);
+    }
+
+
+    /**
+     * 取消预约充电
+     */
+    public void cancelChargePlan(String deviceId, String ccid, int port) {
+        DataParam dataParam = new DataParam();
+        JSONObject object1 = new JSONObject();
+        object1.put("port",port);
+        dataParam.setData(object1);
+        dataParam.setCcid(ccid);
+        dataParam.setDeviceId(deviceId.toString());
+        dataParam.setType(OperEnum.CancelPlanCHarge.getType());
+        RpcResult rpcResult = msgService.sendMsg(dataParam);
+        log.info("发送取消预约充电的消息,{},端口:{}",deviceId,port);
+    }
+
+
+}

+ 17 - 0
src/main/java/com/tmzn/devicelinkykc/service/DeviceService.java

@@ -0,0 +1,17 @@
+package com.tmzn.devicelinkykc.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import com.tmzn.devicelinkykc.entity.param.AjaxResult;
+
+/**
+ * @author xp
+ * @date 2024/3/15
+ * @explain "  "
+ */
+public interface DeviceService extends IService<Device> {
+
+    public AjaxResult deleteDevice(String pileCode);
+
+}

+ 12 - 0
src/main/java/com/tmzn/devicelinkykc/service/DeviceStatusService.java

@@ -0,0 +1,12 @@
+package com.tmzn.devicelinkykc.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+
+/**
+ * @author xp
+ * @date 2024/3/15
+ * @explain "  "
+ */
+public interface DeviceStatusService extends IService<DeviceStatus> {
+}

+ 14 - 0
src/main/java/com/tmzn/devicelinkykc/service/GdDispostionService.java

@@ -0,0 +1,14 @@
+package com.tmzn.devicelinkykc.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.tmzn.devicelinkykc.entity.GdDispostion;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author xp
+ * @date 2024/6/26
+ * @explain "  "
+ */
+
+public interface GdDispostionService extends IService<GdDispostion> {
+}

+ 16 - 0
src/main/java/com/tmzn/devicelinkykc/service/OrderStatusService.java

@@ -0,0 +1,16 @@
+package com.tmzn.devicelinkykc.service;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.tmzn.devicelinkykc.entity.OrderStatus;
+
+/**
+ * @author xp
+ * @date 2024/3/18
+ * @explain "  "
+ */
+public interface OrderStatusService extends IService<OrderStatus> {
+
+
+}

+ 17 - 0
src/main/java/com/tmzn/devicelinkykc/service/TransOrderService.java

@@ -0,0 +1,17 @@
+package com.tmzn.devicelinkykc.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.tmzn.devicelinkykc.entity.TransOrder;
+import com.tmzn.devicelinkykc.entity.param.vo.TransOrderVO;
+
+/**
+ * @author xp
+ * @date 2024/4/11
+ * @explain "  "
+ */
+public interface TransOrderService extends IService<TransOrder> {
+
+    public Page<TransOrder> selectAndPage(TransOrderVO transOrderVO);
+
+}

+ 16 - 0
src/main/java/com/tmzn/devicelinkykc/service/impl/BillingModelServiceImpl.java

@@ -0,0 +1,16 @@
+package com.tmzn.devicelinkykc.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.tmzn.devicelinkykc.entity.BillingModel;
+import com.tmzn.devicelinkykc.mapper.BillingModelMapper;
+import com.tmzn.devicelinkykc.service.BillingModelService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author xp
+ * @date 2024/3/19
+ * @explain "  "
+ */
+@Service
+public class BillingModelServiceImpl extends ServiceImpl<BillingModelMapper, BillingModel> implements BillingModelService {
+}

+ 110 - 0
src/main/java/com/tmzn/devicelinkykc/service/impl/DeviceServiceImpl.java

@@ -0,0 +1,110 @@
+package com.tmzn.devicelinkykc.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.constant.ykc.StatusConstant;
+import com.tmzn.devicelinkykc.entity.BillingModel;
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import com.tmzn.devicelinkykc.entity.param.AjaxResult;
+import com.tmzn.devicelinkykc.mapper.DeviceMapper;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.service.BillingModelService;
+import com.tmzn.devicelinkykc.service.DeviceService;
+import com.tmzn.devicelinkykc.service.DeviceStatusService;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * @author xp
+ * @date 2024/3/15
+ * @explain "  "
+ */
+@Service
+@Slf4j
+public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {
+
+    @Autowired
+    private BillingModelService billingModelService;
+
+    @Autowired
+    private DeviceStatusService deviceStatusService;
+
+    @Autowired
+    private SocketHandle socketHandle;
+    @Autowired
+    private RedisCache redisCache;
+
+    @Override
+    //@Transactional
+    public AjaxResult deleteDevice(String pileCode) {
+        QueryWrapper<DeviceStatus> deviceStatusQueryWrapper = new QueryWrapper<>();
+        deviceStatusQueryWrapper.eq("pile_code", pileCode);
+        List<DeviceStatus> deviceStatusList = deviceStatusService.list(deviceStatusQueryWrapper);
+        List<DeviceStatus> collect = deviceStatusList.stream().filter(deviceStatus ->
+                deviceStatus.getGunStatus() == StatusConstant.CHARGING
+        ).collect(Collectors.toList());
+        if (collect.size() > 0) {
+            //设备有正在充电的端口
+            return AjaxResult.error("设备有正在充电端口,不能进行删除!");
+        } else {
+            //断开socket
+            if (socketHandle.existDeviceConnection(pileCode)) {
+                socketHandle.removeDeviceConnection(pileCode);
+            }
+            //断开socket后,删除状态数据
+            try {
+                boolean b1 = deviceStatusService.removeBatchByIds(deviceStatusList);
+            } catch (Exception e) {
+                log.info("YKC-device:"+pileCode+">删除状态失败");
+                e.printStackTrace();
+            }
+
+            //和计费模板数据
+            QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+            billingModelQueryWrapper.eq("pile_code", pileCode);
+            BillingModel billingModel = billingModelService.getOne(billingModelQueryWrapper);
+            try {
+                boolean b2 = billingModelService.removeById(billingModel);
+            } catch (Exception e) {
+                log.info("YKC-device:"+pileCode+">删除计费模板失败");
+                e.printStackTrace();
+            }
+
+            //删除设备
+            QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+            deviceQueryWrapper.eq("pile_code", pileCode);
+            Device device = this.getOne(deviceQueryWrapper);
+            try {
+                boolean b3 = this.removeById(device);
+            } catch (Exception e) {
+                e.printStackTrace();
+                return AjaxResult.error("删除设备失败");
+            }
+            //通讯密钥也要删除掉
+            redisCache.deleteObject(RedisConstant.KEYS + pileCode);
+
+            if (redisCache.hasKey(RedisConstant.DEVICE_INFO)) {
+                Set<String> imeis = redisCache.getCacheObject(RedisConstant.DEVICE_INFO);
+                log.info("delete imeis>>>" + device.toString() + ">>>>>" + imeis.size());
+                imeis.stream().forEach(s -> log.info("s>>" + s));
+                if (imeis.contains(device.getDeviceImei())) {
+                    imeis.remove(device.getDeviceImei());
+                }
+                //redisCache.setCacheObject(RedisConstant.DEVICE_INFO, imeis, 6 * 1000 * 60, TimeUnit.MILLISECONDS);
+                redisCache.setCacheObject(RedisConstant.DEVICE_INFO,imeis);
+            }
+
+        }
+        return AjaxResult.success();
+    }
+}

+ 16 - 0
src/main/java/com/tmzn/devicelinkykc/service/impl/DeviceStatusServiceImpl.java

@@ -0,0 +1,16 @@
+package com.tmzn.devicelinkykc.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import com.tmzn.devicelinkykc.mapper.DeviceStatusMapper;
+import com.tmzn.devicelinkykc.service.DeviceStatusService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author xp
+ * @date 2024/3/15
+ * @explain "  "
+ */
+@Service
+public class DeviceStatusServiceImpl extends ServiceImpl<DeviceStatusMapper, DeviceStatus> implements DeviceStatusService {
+}

+ 17 - 0
src/main/java/com/tmzn/devicelinkykc/service/impl/GdDispostionServiceImpl.java

@@ -0,0 +1,17 @@
+package com.tmzn.devicelinkykc.service.impl;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.tmzn.devicelinkykc.entity.GdDispostion;
+import com.tmzn.devicelinkykc.mapper.GdDispostionMapper;
+import com.tmzn.devicelinkykc.service.GdDispostionService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author xp
+ * @date 2024/6/26
+ * @explain "  "
+ */
+@Service
+public class GdDispostionServiceImpl extends ServiceImpl<GdDispostionMapper, GdDispostion> implements GdDispostionService {
+}

+ 18 - 0
src/main/java/com/tmzn/devicelinkykc/service/impl/OrderStatusServiceImpl.java

@@ -0,0 +1,18 @@
+package com.tmzn.devicelinkykc.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.tmzn.devicelinkykc.entity.OrderStatus;
+import com.tmzn.devicelinkykc.mapper.OrderStatusMapper;
+import com.tmzn.devicelinkykc.service.OrderStatusService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author xp
+ * @date 2024/3/18
+ * @explain "  "
+ */
+@Service
+public class OrderStatusServiceImpl extends ServiceImpl<OrderStatusMapper, OrderStatus> implements OrderStatusService {
+
+}

+ 38 - 0
src/main/java/com/tmzn/devicelinkykc/service/impl/TransOrderServiceImpl.java

@@ -0,0 +1,38 @@
+package com.tmzn.devicelinkykc.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.tmzn.devicelinkykc.entity.TransOrder;
+import com.tmzn.devicelinkykc.entity.param.vo.TransOrderVO;
+import com.tmzn.devicelinkykc.mapper.TransOrderMapper;
+import com.tmzn.devicelinkykc.service.TransOrderService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author xp
+ * @date 2024/4/11
+ * @explain "  "
+ */
+@Service
+public class TransOrderServiceImpl extends ServiceImpl<TransOrderMapper, TransOrder> implements TransOrderService {
+
+    @Override
+    public Page<TransOrder> selectAndPage(TransOrderVO transOrderVO) {
+
+        LambdaQueryWrapper<TransOrder> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        LambdaQueryWrapper<TransOrder> ne = lambdaQueryWrapper
+                .like(transOrderVO.getDeviceImei() != null, TransOrder::getDeviceImei, transOrderVO.getDeviceImei())
+                .like(transOrderVO.getDeviceSn() != null, TransOrder::getDeviceSn, transOrderVO.getDeviceSn())
+                .like(transOrderVO.getPileCode() != null, TransOrder::getPileCode, transOrderVO.getPileCode())
+                .like(transOrderVO.getTrans() != null, TransOrder::getTrans, transOrderVO.getTrans())
+                .between(transOrderVO.getStartTime()!=null,TransOrder::getStartTime,transOrderVO.getStartTime(),transOrderVO.getEndTime())
+                .isNotNull(TransOrder::getTrans)
+                .orderByDesc(TransOrder::getStartTime);
+        String sqlSelect = ne.getSqlSelect();
+        log.debug(sqlSelect);
+        Page<TransOrder> transOrderPage = this.getBaseMapper().selectPage(new Page<>(transOrderVO.getPageCurrent(), transOrderVO.getPageSize()==0?10:transOrderVO.getPageSize()), ne);
+        return transOrderPage;
+    }
+}

+ 117 - 0
src/main/java/com/tmzn/devicelinkykc/socket/DeviceConnectionMsg.java

@@ -0,0 +1,117 @@
+package com.tmzn.devicelinkykc.socket;
+
+import org.springframework.stereotype.Component;
+
+import java.io.*;
+import java.net.Socket;
+
+/**
+ * @author xp
+ * @date 2024/3/14
+ * @explain " 设备连接信息 "
+ */
+
+public class DeviceConnectionMsg {
+    private Socket socket;
+    private String deviceId;
+    private String imei;
+    private String deviceSn;
+    private Integer messageCount;
+    private Long heartTime;
+    private BufferedOutputStream outputStream;
+    private BufferedInputStream inputStream;
+    private Integer loginStatus;
+
+    public DeviceConnectionMsg(Socket socket, String deviceId,String imei,String deviceSn) {
+        this.heartTime= 0L;
+        this.deviceId = deviceId;
+        this.socket = socket;
+        this.messageCount = 0;
+        this.loginStatus=0;
+        this.imei=imei;
+        this.deviceSn=deviceSn;
+        try {
+            this.inputStream = new BufferedInputStream(socket.getInputStream()) ;
+            this.outputStream =new BufferedOutputStream(socket.getOutputStream()) ;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public Socket getSocket() {
+        return socket;
+    }
+
+    public void setSocket(Socket socket) {
+        this.socket = socket;
+    }
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getImei() {
+        return imei;
+    }
+
+    public void setImei(String imei) {
+        this.imei = imei;
+    }
+
+    public String getDeviceSn() {
+        return deviceSn;
+    }
+
+    public void setDeviceSn(String deviceSn) {
+        this.deviceSn = deviceSn;
+    }
+
+    public Integer getMessageCount() {
+        return messageCount;
+    }
+
+    public synchronized void incrementMessageCount() {
+        this.messageCount++;
+    }
+
+
+    public Long getHeartTime() {
+        return heartTime;
+    }
+
+    public void setHeartTime(Long heartTime) {
+        this.heartTime = heartTime;
+    }
+
+    public void setMessageCount(Integer messageCount) {
+        this.messageCount = messageCount;
+    }
+
+    public BufferedOutputStream getOutputStream() {
+        return outputStream;
+    }
+
+    public void setOutputStream(BufferedOutputStream outputStream) {
+        this.outputStream = outputStream;
+    }
+
+    public BufferedInputStream getInputStream() {
+        return inputStream;
+    }
+
+    public void setInputStream(BufferedInputStream inputStream) {
+        this.inputStream = inputStream;
+    }
+
+    public Integer getLoginStatus() {
+        return loginStatus;
+    }
+
+    public void setLoginStatus(Integer loginStatus) {
+        this.loginStatus = loginStatus;
+    }
+}

+ 78 - 0
src/main/java/com/tmzn/devicelinkykc/socket/SocketHandle.java

@@ -0,0 +1,78 @@
+package com.tmzn.devicelinkykc.socket;
+
+import com.tmzn.devicelinkykc.message.DeviceMsgHandle;
+import com.tmzn.devicelinkykc.message.YkcMsgHandle;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author xp
+ * @date 2024/3/14
+ * @explain "  "
+ */
+@Component
+public class SocketHandle {
+    private  Map<String, DeviceConnectionMsg> deviceConnectionMsgMap = new ConcurrentHashMap<>();
+
+//    @Value("${ip}")
+//    private String ip;
+//    @Value("${port}")
+//    private int port;
+
+    @Autowired
+    private YkcMsgHandle ykcMsgHandle;
+
+    public synchronized void addDeviceConnection(String ip,int port,String deviceId,String imei,String deviceSn) throws IOException {
+        if (deviceConnectionMsgMap.containsKey(deviceId)) {
+            return;
+        }
+        //思考:这里的Socket的IP和端口从数据库查询到,云快充的device库中加字段保存IP和地址,根据桩后台传的设备SN码确定设备是对接的那个厂家的平台
+        //Socket socket = new Socket("114.55.7.88", 8781);
+        //集测
+        Socket socket = new Socket(ip, port);
+        socket.setKeepAlive(true);
+        DeviceConnectionMsg deviceConnectionMsg = new DeviceConnectionMsg(socket, deviceId,imei,deviceSn);
+        //每个设备连接后开启监听接收消息
+        ykcMsgHandle.startListening(deviceConnectionMsg);
+        //并将连接信息等保存再Map中
+        deviceConnectionMsgMap.put(deviceId, deviceConnectionMsg);
+    }
+
+    public void removeDeviceConnection(String deviceId) {
+        DeviceConnectionMsg deviceConnectionMsg = getDeviceConnection(deviceId);
+        try {
+            deviceConnectionMsg.getOutputStream().close();
+            deviceConnectionMsg.getInputStream().close();
+            deviceConnectionMsg.getSocket().close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        deviceConnectionMsgMap.remove(deviceId);
+    }
+
+    public DeviceConnectionMsg getDeviceConnection(String deviceId) {
+        return deviceConnectionMsgMap.get(deviceId);
+    }
+
+    public boolean existDeviceConnection(String deviceId) {
+        if (getDeviceConnection(deviceId) != null) {
+            return true;
+        }
+        return false;
+    }
+
+    public Map<String, DeviceConnectionMsg> getDeviceConnectionMsgMap() {
+        return deviceConnectionMsgMap;
+    }
+
+    //
+    public void updateDeviceConnectionMsgMap(DeviceConnectionMsg deviceConnectionMsg) {
+        deviceConnectionMsgMap.put(deviceConnectionMsg.getDeviceId(),deviceConnectionMsg);
+    }
+}

+ 168 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/DeviceOnlineTask.java

@@ -0,0 +1,168 @@
+package com.tmzn.devicelinkykc.taskQueue;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.tmzn.devicelinkykc.constant.Constant;
+import com.tmzn.devicelinkykc.constant.DeviceOnlineStatus;
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.constant.ykc.StatusConstant;
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import com.tmzn.devicelinkykc.frameMsg.frameType.LoginFrame;
+import com.tmzn.devicelinkykc.message.DeviceMsgHandle;
+import com.tmzn.devicelinkykc.openfeign.MsgService;
+import com.tmzn.devicelinkykc.openfeign.transdata.DataParam;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.service.DeviceControlerService;
+import com.tmzn.devicelinkykc.service.DeviceService;
+import com.tmzn.devicelinkykc.service.DeviceStatusService;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import com.tmzn.devicelinkykc.transdata.entity.DeviceParam;
+import com.tmzn.devicelinkykc.transdata.entity.opertype.OperEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * @author xp
+ * @date 2024/3/15
+ * @explain "  "
+ * //TODO:思考:1,长时间的宕机是不是会造成设备实际状态和数据库保存的状态不一致;2.设备的离线是否校验设备上报消息的时间间隔是否做定时任务校验(可能设备离线或断网时状态上报丢失)
+ */
+@Component
+public class DeviceOnlineTask {
+
+    private static final Logger logger = LoggerFactory.getLogger(DeviceOnlineTask.class);
+    @Autowired
+    private DeviceStatusService deviceStatusService;
+    @Autowired
+    private DeviceService deviceService;
+    @Autowired
+    private SocketHandle socketHandle;
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private MsgService msgService;
+    @Autowired
+    private DeviceControlerService deviceControlerService;
+
+    @Autowired
+    private LoginFrame loginFrame;
+
+    //设备的状态更新间隔时间
+    private static final long gapTime = 20 * 60 * 1000;
+
+    public void start() {
+        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    resetTheConnection();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        };
+        scheduler.scheduleAtFixedRate(task, 10, 5 * 60 * 1000, TimeUnit.MILLISECONDS);
+
+    }
+
+    /**
+     * 获取设备状态
+     */
+    private void getDevicePortDetail() {
+        logger.info("getDevicePortDetail>>>>>>>>>>>>>>>>>>>>>>>>");
+        List<DeviceStatus> list = deviceStatusService.list();
+        //双枪情况下,通过set集合减少循环次数,
+        Set<String> collect = list.stream().map(DeviceStatus::getDeviceImei).collect(Collectors.toSet());
+        collect.stream().forEach(imei -> {
+            DeviceParam dataParam = new DeviceParam();
+            dataParam.setDeviceId(imei);
+            dataParam.setCcid(imei);
+            deviceControlerService.sendPortDetailCmd(dataParam);
+        });
+    }
+
+    public void resetTheConnection(){
+try {
+        //差正常的设备,禁用设备不管
+        logger.info("设备连接检查");
+        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+        deviceQueryWrapper.eq("disabled", DeviceOnlineStatus.NORMAL);
+        List<Device> list = deviceService.list(deviceQueryWrapper);
+        logger.info("查询到设备"+list.size());
+        //云快充设备缓存过滤订阅消息
+        Set<String> imeiList = list.stream().map(device ->
+                device.getDeviceImei()
+        ).collect(Collectors.toSet());
+        //redisCache.setCacheObject(RedisConstant.DEVICE_INFO, imeiList, 10 * 1000 * 60, TimeUnit.MILLISECONDS);
+        redisCache.setCacheObject(RedisConstant.DEVICE_INFO,imeiList);
+
+
+        QueryWrapper<DeviceStatus> deviceStatusQueryWrapper = new QueryWrapper<>();
+        deviceStatusQueryWrapper.eq("online_status", DeviceOnlineStatus.ONLINE);
+        List<DeviceStatus> onlineList = deviceStatusService.list(deviceStatusQueryWrapper);
+
+        List<DeviceStatus> newOnlineList = onlineList.stream().map(deviceStatus -> {
+            long updateTime = deviceStatus.getUpdateTime();
+            long nowTime = System.currentTimeMillis();
+            if ((nowTime - updateTime) > gapTime) {
+                if (socketHandle.existDeviceConnection(deviceStatus.getPileCode())) {
+                    redisCache.deleteObject(RedisConstant.KEYS + deviceStatus.getPileCode());
+                    socketHandle.removeDeviceConnection(deviceStatus.getPileCode());
+                }
+                deviceStatus.setOnlineStatus(DeviceOnlineStatus.OFFLINE);
+                //deviceStatus.setGunStatus(StatusConstant.OFFLINE);
+            }
+            return deviceStatus;
+        }).collect(Collectors.toList());
+        if (redisCache.hasKey(RedisConstant.DEVICE_INFO)) {
+            Set<String> imeis = redisCache.getCacheObject(RedisConstant.DEVICE_INFO);
+            logger.info("redis-imei>>>" + imeis.size());
+        }
+        logger.info("dataBase device status change:" + newOnlineList.size());
+        deviceStatusService.updateBatchById(newOnlineList);
+
+        Map<String, Device> collect = list.stream().collect(Collectors.toMap(device -> device.getDeviceImei(), device -> device,(exis,repl)->exis));
+
+        newOnlineList.stream().forEach(deviceStatus -> {
+            //logger.info(" newOnlineList.stream().forEach");
+            if (!socketHandle.existDeviceConnection(deviceStatus.getPileCode())) {
+                try {
+                    Device device = collect.get(deviceStatus.getDeviceImei());
+                    socketHandle.addDeviceConnection(device.getIp(),device.getPort(),deviceStatus.getPileCode(), deviceStatus.getDeviceImei(), deviceStatus.getDeviceSn());
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            DeviceConnectionMsg deviceConnection = socketHandle.getDeviceConnection(deviceStatus.getPileCode());
+            //logger.info("deviceConnection");
+            if (deviceConnection.getLoginStatus()== Constant.DEVICE_NOT_LOGIN_STATUS&&deviceStatus.getOnlineStatus()==DeviceOnlineStatus.ONLINE){
+                //logger.info("DEVICE_NOT_LOGIN_STATUS");
+                List<Device> collect1 = list.stream().filter(device -> device.getPileCode() .equals(deviceConnection.getDeviceId())) .collect(Collectors.toList());
+                if (collect1.size()>0){
+                    logger.info("设备连接检查login>>>"+collect1.get(0).getDeviceSn());
+                    loginFrame.loginMsgSend(deviceConnection,collect1.get(0));
+                }
+            }
+        });
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 62 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/DeviceStatusPushTask.java

@@ -0,0 +1,62 @@
+package com.tmzn.devicelinkykc.taskQueue;
+
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import com.tmzn.devicelinkykc.taskQueue.queue.MsgCharngingQueue;
+import com.tmzn.devicelinkykc.taskQueue.queue.MsgFreeQueue;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author xp
+ * @date 2024/3/15
+ * @explain " 设备状态推送任务 "
+ */
+@Component
+public class DeviceStatusPushTask {
+
+    @Autowired
+    private MsgCharngingQueue msgCharngingQueue;
+    @Autowired
+    private MsgFreeQueue msgFreeQueue;
+    @Autowired
+    private SocketHandle socketHandle;
+
+    /**
+     * 充电中任务状态
+     */
+    public void chargingStatus(){
+        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+                Map<String, DeviceConnectionMsg> deviceConnectionMsgMap = socketHandle.getDeviceConnectionMsgMap();
+                msgCharngingQueue.add(deviceConnectionMsgMap);
+            }
+        };
+        scheduler.scheduleAtFixedRate(task,0,12*1000, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * 空闲状态上送
+     */
+    public void freeStatus(){
+        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+                Map<String, DeviceConnectionMsg> deviceConnectionMsgMap = socketHandle.getDeviceConnectionMsgMap();
+                msgFreeQueue.add(deviceConnectionMsgMap);
+            }
+        };
+        scheduler.scheduleAtFixedRate(task,100,3*60*1000, TimeUnit.MILLISECONDS);
+
+    }
+
+
+}

+ 54 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/HeartTask.java

@@ -0,0 +1,54 @@
+package com.tmzn.devicelinkykc.taskQueue;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import com.tmzn.devicelinkykc.entity.param.TransCheck;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.service.DeviceService;
+import com.tmzn.devicelinkykc.service.DeviceStatusService;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import com.tmzn.devicelinkykc.taskQueue.queue.MsgHeartQueue;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.*;
+
+/**
+ * @author xp
+ * @date 2024/3/15
+ * @explain " 心跳定时任务 "
+ */
+@Component
+@Slf4j
+public class HeartTask {
+
+    @Autowired
+    private SocketHandle socketHandle;
+    @Autowired
+    private DeviceStatusService deviceStatusService;
+    @Autowired
+    private MsgHeartQueue msgHeartQueue;
+
+    //理解:云快充的心跳30s未收到设备需要重新登录,这里直接拿连接socket的设备上报消息
+    public void start(){
+        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+                Map<String, DeviceConnectionMsg> deviceConnectionMsgMap = socketHandle.getDeviceConnectionMsgMap();
+                msgHeartQueue.add(deviceConnectionMsgMap);
+            }
+        };
+        scheduler.scheduleAtFixedRate(task,0,10*1000, TimeUnit.MILLISECONDS);
+
+    }
+}

+ 144 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/StartTask.java

@@ -0,0 +1,144 @@
+package com.tmzn.devicelinkykc.taskQueue;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import com.tmzn.devicelinkykc.openfeign.MsgService;
+import com.tmzn.devicelinkykc.openfeign.transdata.DataParam;
+import com.tmzn.devicelinkykc.service.DeviceStatusService;
+import com.tmzn.devicelinkykc.transdata.entity.opertype.OperEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.List;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 平台启动执行 "
+ */
+@Component
+public class StartTask {
+
+    private static final Logger logger = LoggerFactory.getLogger(StartTask.class);
+    @Autowired
+    private HeartTask heartTask;
+    @Autowired
+    private DeviceStatusPushTask deviceStatusPushTask;
+    @Autowired
+    private TranscationTask transcationTask;
+    @Autowired
+    private DeviceStatusService deviceStatusService;
+    @Autowired
+    private DeviceOnlineTask deviceOnlineTask;
+    @Autowired
+    private MsgService msgService;
+    @Autowired
+    private TransCheckTask transCheckTask;
+
+    //协议层启动后执行,连接前准备
+    @PostConstruct
+    public void initStart() {
+//        logger.info("Device online status task===========Starting============");
+//        //在线设备
+//        QueryWrapper<DeviceStatus> deviceStatusQueryWrapper = new QueryWrapper<>();
+//        deviceStatusQueryWrapper.eq("online_status", DeviceOnlineStatus.ONLINE);
+//        List<DeviceStatus> list = deviceStatusService.list(deviceStatusQueryWrapper);
+//        //双枪情况下,通过set集合减少循环校验次数,
+//        Set<String> listPileCode = list.stream().map(DeviceStatus::getPileCode).collect(Collectors.toSet());
+//        //拿到所有已经创建Socket的设备
+//        Map<String, DeviceConnectionMsg> deviceConnectionMsgMap = socketHandle.getDeviceConnectionMsgMap();
+//        Set<String> deviceConnectionSet = deviceConnectionMsgMap.keySet();
+//        Set<String> deviceConnectionSetTemp = deviceConnectionSet;//临时保存
+//        //在线没创建socket的设备进行连接
+//        listPileCode.stream().forEach(pileCode -> {
+//            if (deviceConnectionSet.contains(pileCode)) {
+//                //如果在线设备有连接则临时保存下来未校验到的在线设备,减少校验次数
+//                deviceConnectionSetTemp.remove(pileCode);
+//            } else {
+//                //在线设备没有连接则开启连接
+//                try {
+//                    socketHandle.addDeviceConnection(pileCode);
+//                } catch (IOException e) {
+//                    logger.info("pileCode " + pileCode + " device create socket Exception");
+//                    e.printStackTrace();
+//                }
+//            }
+//        });
+//        //socket连接状态但是设备不在线的进行剔除
+//        deviceConnectionSetTemp.stream().forEach(socketDev -> {
+//            if (list.contains(socketDev)) {
+//                //这里一般不会有数据进来
+//            } else {
+//                //设备在连接中但是非在线状态,当离线处理,剔除
+//                socketHandle.removeDeviceConnection(socketDev);
+//            }
+//        });
+//        //缓存设备状态,设备上送心跳不再查库;云快充注意问题:双枪心跳和实时数据报文都需要按枪发送,并且要1枪2枪错开发送,最好间隔1秒 ,不能在同一个毫秒级内上送;且1枪心跳第一次发送和第二次发送要间隔10s
+//        //分两个redis保存,尽量保证心跳包两只枪心跳间隔时间
+//
+//        List<DeviceStatus> collectOne = list.stream().filter(deviceStatus -> deviceStatus.getGunPort() == 1).collect(Collectors.toList());
+//        List<DeviceStatus> collectTwo = list.stream().filter(deviceStatus -> deviceStatus.getGunPort() == 2).collect(Collectors.toList());
+//        redisCache.setCacheObject(RedisConstant.ONLINE_DEVICE_ONE, collectOne, 15, TimeUnit.MINUTES);
+//        redisCache.setCacheObject(RedisConstant.ONLINE_DEVICE_TWO, collectTwo, 15, TimeUnit.MINUTES);
+//        logger.info("Device online status task===========ending============");
+
+        //获取所有设备端口,启动时执行一次
+
+
+
+
+        //开启所有定时任务队列
+        Timer timer = new Timer();
+        TimerTask task = new TimerTask() {
+            @Override
+            public void run() {
+                deviceOnlineTask.start();
+                try {
+                 TimeUnit.MILLISECONDS.sleep(2*1000);
+                //getDevicePortDetail();
+                logger.info("Starting four task queues=======================");
+                heartTask.start();
+                TimeUnit.MILLISECONDS.sleep(2*1000);
+                deviceStatusPushTask.freeStatus();
+                TimeUnit.MILLISECONDS.sleep(2*1000);
+                deviceStatusPushTask.chargingStatus();
+                TimeUnit.MILLISECONDS.sleep(2*1000);
+                transcationTask.start();
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                //transCheckTask.start();
+                //启动定时任务:登录,心跳,检查订单等,然后等心跳校验登录情况,
+            }
+        };
+        timer.schedule(task, 10000L);
+    }
+
+    /**
+     * todo:考虑压力问题
+     */
+    private void getDevicePortDetail(){
+        logger.info("getDevicePortDetail>>>>>>>>>>>>>>>>>>>>>>>>");
+        List<DeviceStatus> list = deviceStatusService.list();
+        //双枪情况下,通过set集合减少循环次数,
+        Set<String> collect = list.stream().map(DeviceStatus::getDeviceImei).collect(Collectors.toSet());
+        collect.stream().forEach(imei->{
+            DataParam dataParam = new DataParam();
+            dataParam.setDeviceId(imei);
+            dataParam.setCcid(imei);
+            JSONObject object1 = new JSONObject();
+            dataParam.setData(object1);
+            dataParam.setType(OperEnum.PortDetail.getType());
+            msgService.sendMsg(dataParam);
+        });
+    }
+}
+

+ 119 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/TransCheckTask.java

@@ -0,0 +1,119 @@
+package com.tmzn.devicelinkykc.taskQueue;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.entity.param.TransCheck;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.service.DeviceService;
+import com.tmzn.devicelinkykc.service.DeviceStatusService;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import com.tmzn.devicelinkykc.taskQueue.queue.MsgHeartQueue;
+import com.tmzn.devicelinkykc.util.Encrytion;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author xp
+ * @date 2024/7/23
+ * @explain "  "
+ */
+@Component
+@Slf4j
+public class TransCheckTask {
+    @Autowired
+    private SocketHandle socketHandle;
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private DeviceService deviceService;
+    @Autowired
+    private MsgHeartQueue msgHeartQueue;
+
+    //30s没收到订单回复再次上送,上送三次就结束将订单改为
+    public void start(){
+        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    log.info("check Trans>>>>>>>>>>>>>>>>>>>");
+                    Map<String, DeviceConnectionMsg> deviceConnectionMsgMap = socketHandle.getDeviceConnectionMsgMap();
+                    Set<String> imeis = redisCache.getCacheObject(RedisConstant.DEVICE_INFO);
+                    imeis.stream().forEach(s -> {
+                        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+                        QueryWrapper<Device> device_imei = deviceQueryWrapper.eq("device_imei", s);
+                        Device one = deviceService.getOne(device_imei);
+                        if (one != null) {
+                            if (deviceConnectionMsgMap.containsKey(one.getPileCode())) {
+                                //在线且链接,
+                                TransCheck transCheck = redisCache.getCacheObject(RedisConstant.NO_RESPONSE_WAS_RECEIVED + one.getPileCode());
+
+                                if (transCheck.getCheck_time() == 0) {
+                                    if ((System.currentTimeMillis() - transCheck.getTime()) > 30 * 1000) {
+                                        log.info(one.getPileCode() + "Transchek>>>>>>>checkTime:" + transCheck.getCheck_time());
+                                        upTrans(deviceConnectionMsgMap.get(one.getPileCode()), transCheck.getTrans());
+                                        transCheck.setCheck_time(1);
+                                        redisCache.setCacheObject(RedisConstant.NO_RESPONSE_WAS_RECEIVED+one.getPileCode(),transCheck);
+                                    }
+                                } else if (transCheck.getCheck_time() == 1) {
+                                    if ((System.currentTimeMillis() - transCheck.getTime()) > 60 * 1000) {
+                                        log.info(one.getPileCode() + "Transchek>>>>>>>checkTime:" + transCheck.getCheck_time());
+                                        upTrans(deviceConnectionMsgMap.get(one.getPileCode()), transCheck.getTrans());
+                                        transCheck.setCheck_time(2);
+                                        redisCache.setCacheObject(RedisConstant.NO_RESPONSE_WAS_RECEIVED+one.getPileCode(),transCheck);
+                                    }
+                                } else if (transCheck.getCheck_time() == 2) {
+                                    if ((System.currentTimeMillis() - transCheck.getTime()) > 90 * 1000) {
+                                        log.info(one.getPileCode() + "Transchek>>>>>>>checkTime:" + transCheck.getCheck_time());
+                                        upTrans(deviceConnectionMsgMap.get(one.getPileCode()), transCheck.getTrans());
+                                        transCheck.setCheck_time(3);
+                                        redisCache.setCacheObject(RedisConstant.NO_RESPONSE_WAS_RECEIVED+one.getPileCode(),transCheck);
+                                    }
+                                } else {
+                                    redisCache.deleteObject(RedisConstant.NO_RESPONSE_WAS_RECEIVED + one.getPileCode());
+                                }
+                            }
+                        }
+                    });
+                }catch (Exception e){
+
+                }
+            }
+        };
+        scheduler.scheduleAtFixedRate(task,1,5*1000, TimeUnit.MILLISECONDS);
+
+    }
+    private void upTrans(DeviceConnectionMsg deviceConnectionMsg,byte[] bytes){
+        try {
+            String key = redisCache.getCacheObject(RedisConstant.KEYS+deviceConnectionMsg.getDeviceId());
+            byte[] rsaEncrypt = Encrytion.aesEncrypt(bytes, key.getBytes());
+            byte[] msg = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.TRANSACTION_RECORDS_REQUEST.getFrameType(), DeviceSendYkc.TRANSACTION_RECORDS_REQUEST.getEncryptFlag(), rsaEncrypt, rsaEncrypt.length);
+            deviceConnectionMsg.getOutputStream().write(msg);
+            deviceConnectionMsg.getOutputStream().flush();
+            log.info(deviceConnectionMsg.getDeviceId() + "Trans_chek,key:"+key);
+            log.info(deviceConnectionMsg.getDeviceId() + "Trans_chek,rsaEncrypt:"+DataConversion.bytesToHexString(rsaEncrypt));
+            log.info(deviceConnectionMsg.getDeviceId() + "Trans_chek,↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑");
+        } catch (IOException e) {
+            log.info("TransCheck:>>pileCode:" + deviceConnectionMsg.getDeviceId() + " ,TransactionFlow push frame Exception");
+            e.printStackTrace();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        deviceConnectionMsg.incrementMessageCount();
+    }
+}

+ 38 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/TranscationTask.java

@@ -0,0 +1,38 @@
+package com.tmzn.devicelinkykc.taskQueue;
+
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import com.tmzn.devicelinkykc.taskQueue.queue.MsgTranscationQueue;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author xp
+ * @date 2024/3/15
+ * @explain "  "
+ */
+@Component
+public class TranscationTask {
+    @Autowired
+    private MsgTranscationQueue msgTranscationQueue;
+    @Autowired
+    private SocketHandle socketHandle;
+
+    public void start(){
+        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+                Map<String, DeviceConnectionMsg> deviceConnectionMsgMap = socketHandle.getDeviceConnectionMsgMap();
+                msgTranscationQueue.add(deviceConnectionMsgMap);
+            }
+        };
+        scheduler.scheduleAtFixedRate(task,10,30*1000, TimeUnit.MILLISECONDS);
+
+    }
+}

+ 54 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/MsgCharngingQueue.java

@@ -0,0 +1,54 @@
+package com.tmzn.devicelinkykc.taskQueue.queue;
+
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+@Component
+@Slf4j(topic = "MsgCharngingQueue")
+public class MsgCharngingQueue  extends Thread implements ApplicationRunner {
+
+      private BlockingQueue<Map<String, DeviceConnectionMsg>> charngingQueue = new ArrayBlockingQueue(1024);
+
+
+    @Autowired
+    private TaskRunner taskRunner;
+
+    public void add(Map<String, DeviceConnectionMsg> object){
+        try {
+               charngingQueue.put(object);
+
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void run() {
+        while (true){
+            try {
+                Map<String, DeviceConnectionMsg> charnging = charngingQueue.take();
+                    taskRunner.chargingMsg(charnging);
+            } catch (Exception e) {
+                e.printStackTrace();
+                log.error("执行异常",e);
+            }
+        }
+    }
+
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        this.start();
+        log.info("处理器启动");
+    }
+}

+ 54 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/MsgFreeQueue.java

@@ -0,0 +1,54 @@
+package com.tmzn.devicelinkykc.taskQueue.queue;
+
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+@Component
+@Slf4j(topic = "MsgFreeQueue")
+public class MsgFreeQueue extends Thread implements ApplicationRunner {
+
+    private BlockingQueue<Map<String, DeviceConnectionMsg>> freeQueue = new ArrayBlockingQueue(1024);
+
+
+    @Autowired
+    private TaskRunner taskRunner;
+
+    public void add(Map<String, DeviceConnectionMsg> object) {
+        try {
+            freeQueue.put(object);
+
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void run() {
+        while (true) {
+            try {
+                Map<String, DeviceConnectionMsg> free = freeQueue.take();
+                taskRunner.freeMsg(free);
+            } catch (Exception e) {
+                e.printStackTrace();
+                log.error("执行异常", e);
+            }
+        }
+    }
+
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        this.start();
+        log.info("处理器启动");
+    }
+}

+ 54 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/MsgHeartQueue.java

@@ -0,0 +1,54 @@
+package com.tmzn.devicelinkykc.taskQueue.queue;
+
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+@Component
+@Slf4j(topic = "MsgHeartQueue")
+public class MsgHeartQueue extends Thread implements ApplicationRunner {
+
+    private BlockingQueue<Map<String, DeviceConnectionMsg>> heartQueue = new ArrayBlockingQueue(1024);
+
+
+    @Autowired
+    private TaskRunner taskRunner;
+
+    public void add(Map<String, DeviceConnectionMsg> map){
+        try {
+                heartQueue.put(map);
+
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void run() {
+       while (true){
+           try {
+               Map<String, DeviceConnectionMsg> map= heartQueue.take();
+               taskRunner.heartMsg(map);
+           } catch (Exception e) {
+               e.printStackTrace();
+               log.error("执行异常",e);
+           }
+       }
+    }
+
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+            this.start();
+            log.info("处理器启动");
+    }
+}

+ 57 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/MsgTranscationQueue.java

@@ -0,0 +1,57 @@
+package com.tmzn.devicelinkykc.taskQueue.queue;
+
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * @author xp
+ * @date 2024/3/15
+ * @explain "  "
+ */
+@Component
+@Slf4j(topic = "MsgTranscationQueue")
+public class MsgTranscationQueue extends Thread implements ApplicationRunner {
+
+    private BlockingQueue<Map<String, DeviceConnectionMsg>> freeQueue = new ArrayBlockingQueue(1024);
+
+
+    @Autowired
+    private TaskRunner taskRunner;
+
+    public void add(Map<String, DeviceConnectionMsg> object){
+        try {
+            freeQueue.put(object);
+
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void run() {
+        while (true){
+            try {
+                Map<String, DeviceConnectionMsg> map = freeQueue.take();
+                taskRunner.transactionMsg(map);
+            } catch (Exception e) {
+                e.printStackTrace();
+                log.error("执行异常",e);
+            }
+        }
+    }
+
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        this.start();
+        log.info("处理器启动");
+    }
+}

+ 67 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/TaskExecutePool.java

@@ -0,0 +1,67 @@
+package com.tmzn.devicelinkykc.taskQueue.queue;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 自定义创建线程池
+ */
+@Configuration
+@EnableAsync
+public class TaskExecutePool {
+    @Bean
+    public Executor heartTaskAsyncPool() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(4); //核心线程数
+        executor.setMaxPoolSize(8);  //最大线程数
+        executor.setQueueCapacity(1000); //队列大小
+        executor.setKeepAliveSeconds(300); //线程最大空闲时间
+        executor.setThreadNamePrefix("async-heartExecutor-");
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略(一共四种,此处省略)
+        executor.initialize();
+        return executor;
+    }
+    @Bean
+    public Executor charngingTaskAsyncPool() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(4); //核心线程数
+        executor.setMaxPoolSize(8);  //最大线程数
+        executor.setQueueCapacity(1000); //队列大小
+        executor.setKeepAliveSeconds(300); //线程最大空闲时间
+        executor.setThreadNamePrefix("async-charngingExecutor-");
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略(一共四种,此处省略)
+        executor.initialize();
+        return executor;
+    }
+    @Bean
+    public Executor freeTaskAsyncPool() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(4); //核心线程数
+        executor.setMaxPoolSize(8);  //最大线程数
+        executor.setQueueCapacity(1000); //队列大小
+        executor.setKeepAliveSeconds(300); //线程最大空闲时间
+        executor.setThreadNamePrefix("async-freeExecutor-");
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略(一共四种,此处省略)
+        executor.initialize();
+        return executor;
+    }
+    @Bean
+    public Executor transactionTaskAsyncPool() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(4); //核心线程数
+        executor.setMaxPoolSize(8);  //最大线程数
+        executor.setQueueCapacity(1000); //队列大小
+        executor.setKeepAliveSeconds(300); //线程最大空闲时间
+        executor.setThreadNamePrefix("async-transactionExecutor-");
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略(一共四种,此处省略)
+        executor.initialize();
+        return executor;
+    }
+}
+
+

+ 590 - 0
src/main/java/com/tmzn/devicelinkykc/taskQueue/queue/TaskRunner.java

@@ -0,0 +1,590 @@
+package com.tmzn.devicelinkykc.taskQueue.queue;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.sun.org.apache.xpath.internal.operations.Or;
+import com.tmzn.devicelinkykc.constant.Constant;
+import com.tmzn.devicelinkykc.constant.DeviceOnlineStatus;
+import com.tmzn.devicelinkykc.constant.PortStatusConstant;
+import com.tmzn.devicelinkykc.constant.RedisConstant;
+import com.tmzn.devicelinkykc.constant.ykc.BillingModelConst;
+import com.tmzn.devicelinkykc.constant.ykc.StatusConstant;
+import com.tmzn.devicelinkykc.constant.ykc.TransConstant;
+import com.tmzn.devicelinkykc.entity.BillingModel;
+import com.tmzn.devicelinkykc.entity.Device;
+import com.tmzn.devicelinkykc.entity.DeviceStatus;
+import com.tmzn.devicelinkykc.entity.OrderStatus;
+import com.tmzn.devicelinkykc.frameMsg.DataConversion;
+import com.tmzn.devicelinkykc.frameMsg.FrameDataSplicing;
+import com.tmzn.devicelinkykc.frameMsg.TransMoney;
+import com.tmzn.devicelinkykc.frameMsg.frameType.HeartFrameSend;
+import com.tmzn.devicelinkykc.frameMsg.frameType.LoginFrame;
+import com.tmzn.devicelinkykc.frameMsg.frameType.RealTimeStatusPushFrame;
+import com.tmzn.devicelinkykc.frameMsg.frameType.TransactionFlowPushFrame;
+import com.tmzn.devicelinkykc.msgEnum.DeviceSendYkc;
+import com.tmzn.devicelinkykc.redis.RedisCache;
+import com.tmzn.devicelinkykc.service.*;
+import com.tmzn.devicelinkykc.socket.DeviceConnectionMsg;
+import com.tmzn.devicelinkykc.socket.SocketHandle;
+import com.tmzn.devicelinkykc.transdata.entity.DeviceParam;
+import com.tmzn.devicelinkykc.util.Encrytion;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.time.*;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * @author xp
+ * @date 2024/3/13
+ * @explain " 任务执行 "
+ */
+@Component
+@Slf4j(topic = "TaskRunner")
+public class TaskRunner {
+
+    @Autowired
+    private HeartFrameSend heartFrameSend;
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private LoginFrame loginFrame;
+    @Autowired
+    private DeviceService deviceService;
+    @Autowired
+    private RealTimeStatusPushFrame realTimeStatusPushFrame;
+    @Autowired
+    private OrderStatusService orderStatusService;
+    @Autowired
+    private BillingModelService billingModelService;
+    @Autowired
+    private TransMoney transMoney;
+    @Autowired
+    private DeviceStatusService deviceStatusService;
+    @Autowired
+    private SocketHandle socketHandle;
+
+    @Autowired
+    private DeviceControlerService deviceControlerService;
+    @Autowired
+    private TransactionFlowPushFrame transactionFlowPushFrame;
+
+    private static final BigDecimal zero = new BigDecimal("0");
+
+    @Async("heartTaskAsyncPool")
+    public void heartMsg(Map<String, DeviceConnectionMsg> map) throws Exception {
+        log.info("======Heart beat task starting=====");
+        //任务处理
+        //map拿出来进行心跳包上报,1:直接检查心跳时间是否大于三十秒
+        Set<String> devicePileCodes = map.keySet();
+        log.info("heart.deviceConnectionSize>>"+devicePileCodes.size());
+        devicePileCodes.stream().forEach(devicePileCode -> {
+            DeviceConnectionMsg deviceConnectionMsg = map.get(devicePileCode);
+            Long heartTime = deviceConnectionMsg.getHeartTime();
+            //log.info("heartTime>>>" + new Date(heartTime));
+
+            //拿到1枪设备状态
+            // log.info("↑↑↑↑↑↑↑↑↑↑");
+            if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_ONE)) {
+
+                //1:当上一次的心跳和本次的心跳相差大于30秒证明已经3次没收到心跳回复了,进行重新登录
+                //2:设备第一次上线时未触发过登录,设备连接集合中初始话的心跳时间还是0,符合该判断,进行登录
+                QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+                deviceQueryWrapper.eq("pile_code", devicePileCode).eq("disabled", DeviceOnlineStatus.NORMAL);
+                Device device = deviceService.getOne(deviceQueryWrapper);
+                DeviceStatus oneDs = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE, devicePileCode);
+                if (deviceConnectionMsg.getLoginStatus()== Constant.DEVICE_NOT_LOGIN_STATUS) {
+                    //
+                    if (oneDs != null&&oneDs.getOnlineStatus() == DeviceOnlineStatus.ONLINE && (System.currentTimeMillis() - heartTime) > 50 * 1000L) {
+                        log.info("heartTaskAsyncPool-1>not longin and heart normal>>" + devicePileCode);
+                        loginFrame.loginMsgSend(deviceConnectionMsg,device);
+                        return;
+                    }
+                }
+                //log.info("↑1↑1↑1↑1↑1↑1↑1↑1↑1↑1");
+                if (oneDs != null) {
+                  //  log.info("oneDes-heart>>>>>>>" + oneDs.toString());
+                    if (oneDs.getOnlineStatus() == DeviceOnlineStatus.ONLINE && (System.currentTimeMillis() - heartTime) > 50 * 1000L) {
+                        log.info("pileCode:" + devicePileCode + " loging... ..." + (System.currentTimeMillis() - heartTime));
+                        int a = 0;
+                        if (redisCache.hasKey(RedisConstant.DEVICE_LOGIN_YKC + devicePileCode)) {
+                            //如果登录次数30秒内登三次就算断开
+                            a = redisCache.getCacheObject(RedisConstant.DEVICE_LOGIN_YKC + devicePileCode);
+                            if (a > 3) {
+                                socketHandle.removeDeviceConnection(devicePileCode);
+                            } else {
+                                loginFrame.loginMsgSend(deviceConnectionMsg, device);
+                            }
+                            a++;
+                            redisCache.setCacheObject(RedisConstant.DEVICE_LOGIN_YKC + devicePileCode, a,32 * 1000, TimeUnit.MILLISECONDS);
+                        } else {
+                            redisCache.setCacheObject(RedisConstant.DEVICE_LOGIN_YKC + devicePileCode, a, 32 * 1000, TimeUnit.MILLISECONDS);
+                        }
+                    }else {
+                        //存在设备在线状态的,上送心跳
+                        log.info("heartSend-1>>>"+devicePileCode);
+                        heartFrameSend.heartSend(deviceConnectionMsg, oneDs);
+                    }
+                }
+            }
+            //拿到2枪设备状态
+            if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_TWO)) {
+                DeviceStatus twoDs = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_TWO, devicePileCode);
+                if (twoDs != null) {
+                    log.info("twoDes-heart>>>>>>>" + twoDs.toString());
+                    if (twoDs.getOnlineStatus() == DeviceOnlineStatus.ONLINE && (System.currentTimeMillis() - heartTime) > 50 * 1000L) {
+                        log.info("twoDs:pileCode:" + devicePileCode + " loging... ..." + (System.currentTimeMillis() - heartTime));
+                        //1:当上一次的心跳和本次的心跳相差大于30秒证明已经3次没收到心跳回复了,进行重新登录
+                        //2:设备第一次上线时未触发过登录,设备连接集合中初始话的心跳时间还是0,符合该判断,进行登录
+                        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+                        deviceQueryWrapper.eq("pile_code", devicePileCode).eq("disabled", DeviceOnlineStatus.NORMAL);
+                        Device device = deviceService.getOne(deviceQueryWrapper);
+
+                        loginFrame.loginMsgSend(deviceConnectionMsg, device);
+                    }
+                    //存在设备在线状态的,上送心跳
+                    log.info("heartSend-2>>>"+devicePileCode);
+                    heartFrameSend.heartSend(deviceConnectionMsg, twoDs);
+                }
+            }
+        });
+        log.info("======Heart beat task ending=====");
+    }
+
+    @Async("charngingTaskAsyncPool")
+    public void chargingMsg(Map<String, DeviceConnectionMsg> map) {
+        log.info("======Charging status push task starting=====");
+        //TODO:这边需要给充电中的状态进行查询流水号,流水号是由云快充启动充电时的下发指令带来存库的,
+        if (map.size()<1){
+            return;
+        }
+        //查询所有充电中设备的最新的订单记录,来上报设备状态消息.........?????????????????
+        List<OrderStatus> list = orderStatusService.list();
+        Map<String, OrderStatus> orderStatusMap = list.stream()
+                .collect(Collectors.toMap(OrderStatus::getPileCode, orderStatus -> orderStatus,
+                        (existing, replacement) -> (Instant.ofEpochMilli(existing.getCreateTime()).atZone(ZoneId.systemDefault()).toLocalDateTime()).isAfter(Instant.ofEpochMilli(replacement.getCreateTime()).atZone(ZoneId.systemDefault()).toLocalDateTime()) ? existing : replacement));
+        List<OrderStatus> orderStatuses = orderStatusMap.values().stream()
+                .collect(Collectors.toList());
+
+        //拿到当前计费模板
+        List<BillingModel> billingModels = billingModelService.list();
+
+        //redis取出在线设备集合
+        Set<String> devicePileCodes = map.keySet();
+        devicePileCodes.stream().forEach(devicePileCode -> {
+            if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_ONE)) {
+                DeviceStatus deviceStatus = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE, devicePileCode);
+                if (deviceStatus != null) {
+                    if(deviceStatus.getGunStatus() != StatusConstant.CHARGING){
+                        log.info("chargingMsg>>>deviceguns status>>"+deviceStatus.getGunStatus());
+                        return;
+                    }
+                    //存在设备上送充电状态
+                    DeviceConnectionMsg deviceConnectionMsg = map.get(devicePileCode);
+                    if (deviceConnectionMsg.getLoginStatus()== Constant.DEVICE_NOT_LOGIN_STATUS){
+                        //
+                        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+                        deviceQueryWrapper.eq("pile_code", devicePileCode).eq("disabled", DeviceOnlineStatus.NORMAL);
+                        Device device = deviceService.getOne(deviceQueryWrapper);
+                        Long heartTime = deviceConnectionMsg.getHeartTime();
+                        if (deviceStatus.getOnlineStatus() == DeviceOnlineStatus.ONLINE && (System.currentTimeMillis() - heartTime) > 50 * 1000L) {
+                            log.info("charngingTaskAsyncPool-1>not longin and heart normal>>" + devicePileCode);
+                            loginFrame.loginMsgSend(deviceConnectionMsg,device);
+                        }
+                        log.info("charngingTaskAsyncPool-1>not longin>>"+devicePileCode);
+                        return;
+                    }
+                    if (deviceStatus.getGunStatus() == StatusConstant.CHARGING) {
+                        if (deviceConnectionMsg.getLoginStatus() == 1) {
+                            if (redisCache.hasKey(RedisConstant.DEVICE_PORT_STATUS + deviceStatus.getDeviceImei())) {
+                                //TODO:充电时间   电量  金额   按计费模板进行计算后上报,不使用主板消息
+                                //设备当前状态筛选
+                                List<OrderStatus> orderStatuselist = orderStatuses.stream().filter(orderStatus -> orderStatus.getPileCode().equals(deviceStatus.getPileCode())).collect(Collectors.toList());
+                                if (orderStatuselist.size() > 0) {
+                                    orderStatuselist.stream().forEach(orderStatus -> {
+                                        //设备计费模板筛选
+                                        List<BillingModel> billingModelList = billingModels.stream().filter(billingModel -> orderStatus.getPileCode().equals(billingModel.getPileCode())).collect(Collectors.toList());
+                                        if (billingModelList.size() > 0) {
+                                            //费用计算
+                                            billingModelList.stream().forEach(billingModel -> {
+                                                JSONObject statusJSON = redisCache.getCacheObject(RedisConstant.DEVICE_PORT_STATUS + deviceStatus.getDeviceImei());
+                                                Integer voltage = statusJSON.getInteger("voltage");
+                                                Integer power = statusJSON.getInteger("power");
+
+                                                //计算实时数据
+                                                long endTime = System.currentTimeMillis();
+                                                //算电量
+                                                Map<String, BigDecimal> start = null;
+                                                try {
+                                                    start = transMoney.compute(1, billingModel, orderStatus.getCreateTime(), endTime, false);
+                                                } catch (Exception e) {
+                                                    e.printStackTrace();
+                                                }
+                                                //log.info(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 1 + ">>redis过期的时间" + redisCache.getExpire(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 1));
+                                                //log.info(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 1 + "redis的键是否存在" + redisCache.hasKey(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 1));
+                                                if (redisCache.hasKey(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 1) && redisCache.getExpire(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 1) > 1) {
+                                                    start = redisCache.getCacheObject(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 1);
+                                                } else {
+                                                    try {
+                                                        start = transMoney.compute(1, billingModel, orderStatus.getCreateTime(), endTime, true);
+                                                    } catch (Exception e) {
+                                                        e.printStackTrace();
+                                                    }
+                                                    redisCache.setCacheObject(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 1, start,  60, TimeUnit.SECONDS);
+                                                }
+                                                BigDecimal elec = start.get("elec");
+                                                BigDecimal money = start.get("money");
+                                                if (elec.equals(new BigDecimal("0.0000"))){
+                                                    elec=elec.add(new BigDecimal("0.0001").setScale(4,BigDecimal.ROUND_DOWN));
+                                                }
+                                                log.info(devicePileCode+"充电中-1=>实时elec>>>"+elec);
+                                                //????????????????????????????
+                                                if (money.compareTo(orderStatus.getStartMoney())>0){
+                                                    //余额没有了,停充
+                                                    log.info("实时状态校验时余额不足>>>>>>停充>>>>");
+                                                    DeviceParam deviceParam = new DeviceParam();
+                                                    deviceParam.setDeviceId(orderStatus.getDeviceImei());
+                                                    deviceParam.setCcid(orderStatus.getDeviceImei());
+                                                    orderStatus.setEndTime(System.currentTimeMillis()-1000*60*5);
+                                                    orderStatus.setReasonStopCharging(TransConstant.INSUFFICIENT_BALANCE_EXCEPTION_STOP);
+                                                    orderStatusService.updateById(orderStatus);
+
+                                                    deviceControlerService.sendPortDetailCmd(deviceParam);
+                                                    deviceControlerService.stopCharge(orderStatus.getDeviceImei(),orderStatus.getDeviceImei(),1);
+
+                                                    //这里为了保证余额不足的情况时:上报实时状态的最后电量费用不超过余额,上报的最后一帧为前一帧数据
+                                                    //Map<String, BigDecimal> map1 = realTimeMoney.start(power/1000, billingModel, orderStatus.getCreateTime(),orderStatus.getEndTime());
+                                                   //Map<String, BigDecimal> start = transMoney.compute(1, billingModel, orderStatus.getCreateTime(),orderStatus.getEndTime());
+
+                                                    //redisCache.setCacheObject(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode()+1, start, 5 * 60 , TimeUnit.SECONDS);
+                                                    //elec=start.get("elec");
+                                                    //money=start.get("money");
+                                                }
+                                                int mi = (int) (System.currentTimeMillis() - orderStatus.getCreateTime()) / 1000 / 60;
+
+                                                log.info("↑↑↑↑↑↑↑↑↑↑↑↑↑充电中实时状态上报-1↑↑↑↑↑↑↑↑↑↑↑↑↑mi " + mi);
+                                                realTimeStatusPushFrame.deviceStatusPush(deviceConnectionMsg, orderStatus.getTransOrder(), deviceStatus.getPileCode(), deviceStatus.getGunPort(), deviceStatus.getGunStatus(), deviceStatus.getInsertGunStatus(), voltage, power, elec, money, mi);
+                                            });
+                                        }
+                                    });
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_TWO)) {
+                DeviceStatus deviceStatus = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_TWO, devicePileCode);
+                if (deviceStatus != null) {
+                    //存在设备上送充电状态
+                    DeviceConnectionMsg deviceConnectionMsg = map.get(devicePileCode);
+                    if (deviceConnectionMsg.getLoginStatus()== Constant.DEVICE_NOT_LOGIN_STATUS){
+                        //
+                        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+                        deviceQueryWrapper.eq("pile_code", devicePileCode).eq("disabled", DeviceOnlineStatus.NORMAL);
+                        Device device = deviceService.getOne(deviceQueryWrapper);
+                        Long heartTime = deviceConnectionMsg.getHeartTime();
+                        if (deviceStatus.getOnlineStatus() == DeviceOnlineStatus.ONLINE && (System.currentTimeMillis() - heartTime) > 50 * 1000L) {
+                            log.info("charngingTaskAsyncPool-2>not longin and heart normal>>" + devicePileCode);
+                            loginFrame.loginMsgSend(deviceConnectionMsg,device);
+                        }
+                        log.info("charngingTaskAsyncPool-2>not longin>>"+devicePileCode);
+                        return;
+                    }
+                    if (deviceStatus.getGunStatus() == StatusConstant.CHARGING) {
+                        if (deviceConnectionMsg.getLoginStatus() == 1) {
+                            if (redisCache.hasKey(RedisConstant.DEVICE_PORT_STATUS + deviceStatus.getDeviceImei())) {
+                                //TODO:充电时间   电量  金额   按计费模板进行计算后上报,不使用主板消息
+                                //设备当前状态筛选
+                                List<OrderStatus> orderStatusList = orderStatuses.stream().filter(orderStatus -> orderStatus.getPileCode().equals(deviceStatus.getPileCode())).collect(Collectors.toList());
+                                if (orderStatusList.size() > 0) {
+                                    orderStatusList.stream().forEach(orderStatus -> {
+                                        //设备计费模板筛选
+                                        List<BillingModel> billingModelList = billingModels.stream().filter(billingModel -> orderStatus.getPileCode().equals(billingModel.getPileCode())).collect(Collectors.toList());
+                                        if (billingModelList.size() > 0) {
+                                            //费用计算
+                                            billingModelList.stream().forEach(billingModel -> {
+                                                JSONObject statusJSON = redisCache.getCacheObject(RedisConstant.DEVICE_PORT_STATUS + deviceStatus.getDeviceImei());
+                                                Integer voltage = statusJSON.getInteger("voltage");
+                                                Integer power = statusJSON.getInteger("power");
+                                                long endTime = 0L;
+                                                if (endTime == 0) {
+                                                    endTime = System.currentTimeMillis();
+                                                }
+                                                Map<String, BigDecimal> start = null;
+                                                try {
+                                                    start = transMoney.compute(1, billingModel, orderStatus.getCreateTime(), endTime, false);
+                                                } catch (Exception e) {
+                                                    e.printStackTrace();
+                                                }
+                                                if (redisCache.hasKey(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 2) && redisCache.getExpire(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 2) > 60 * 1000) {
+                                                    start = redisCache.getCacheObject(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 2);
+                                                } else {
+                                                    try {
+                                                        start = transMoney.compute(2, billingModel, orderStatus.getCreateTime(), endTime, true);
+                                                    } catch (Exception e) {
+                                                        e.printStackTrace();
+                                                    }
+                                                    redisCache.setCacheObject(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 2, start, 5 * 60 * 1000, TimeUnit.MILLISECONDS);
+                                                }
+                                                BigDecimal elec = start.get("elec");
+                                                BigDecimal money = start.get("money");
+                                                if (elec.equals(BigDecimal.ZERO)){
+                                                    elec=elec.add(new BigDecimal("0.0001"));
+                                                }
+                                                log.info(devicePileCode+"充电中-2=>实时elec>>>"+elec);
+                                                if (money.compareTo(orderStatus.getStartMoney()) > 0) {
+                                                    //余额没有了,停充
+                                                    log.info("实时状态校验时余额不足>>>>>>停充>>>>");
+                                                    DeviceParam deviceParam = new DeviceParam();
+                                                    deviceParam.setDeviceId(orderStatus.getDeviceImei());
+                                                    deviceParam.setCcid(orderStatus.getDeviceImei());
+                                                    orderStatus.setEndTime(System.currentTimeMillis()-1000*60*5);
+                                                    orderStatus.setReasonStopCharging(TransConstant.INSUFFICIENT_BALANCE_EXCEPTION_STOP);
+                                                    orderStatusService.updateById(orderStatus);
+
+                                                    deviceControlerService.sendPortDetailCmd(deviceParam);
+                                                    deviceControlerService.stopCharge(orderStatus.getDeviceImei(),orderStatus.getDeviceImei(),2);
+                                                    //余额没有了,停充
+//                                                    DeviceParam deviceParam = new DeviceParam();
+//                                                    deviceParam.setDeviceId(orderStatus.getDeviceImei());
+//                                                    deviceParam.setCcid(orderStatus.getDeviceImei());
+//                                                    deviceControlerService.sendPortDetailCmd(deviceParam);
+//                                                    deviceControlerService.stopCharge(orderStatus.getDeviceImei(), orderStatus.getDeviceImei(), 2);
+//
+//                                                    money = orderStatus.getStartMoney();
+//                                                    orderStatus.setReasonStopCharging(TransConstant.INSUFFICIENT_BALANCE_EXCEPTION_STOP);
+//                                                    orderStatusService.updateById(orderStatus);
+                                                    //这里为了保证余额不足的情况时:上报实时状态的最后电量费用不超过余额,上报的最后一帧为前一帧数据
+                                                    //Map<String, BigDecimal> map1 = realTimeMoney.start(power/1000, billingModel, orderStatus.getCreateTime(),orderStatus.getEndTime());
+                                                    redisCache.setCacheObject(RedisConstant.DEVICE_CHARNGING_INFO + orderStatus.getPileCode() + 2, start, 5 * 60 * 1000, TimeUnit.MILLISECONDS);
+                                                    elec = start.get("elec");
+                                                    money = start.get("money");
+                                                }
+                                                int mi = (int) (System.currentTimeMillis() - orderStatus.getCreateTime()) / 1000 / 60;
+                                                log.info("↑↑↑↑↑↑↑↑↑↑↑↑↑充电中实时状态上报-2↑↑↑↑↑↑↑↑↑↑↑↑↑mi " + mi);
+                                                realTimeStatusPushFrame.deviceStatusPush(deviceConnectionMsg, orderStatus.getTransOrder(), deviceStatus.getPileCode(), deviceStatus.getGunPort(), deviceStatus.getGunStatus(), deviceStatus.getInsertGunStatus(), voltage, power, elec, money, mi);
+                                            });
+                                        }
+                                    });
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        });
+/*
+
+        if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_ONE)) {
+            List<DeviceStatus> listOne = redisCache.getCacheObject(RedisConstant.ONLINE_DEVICE_ONE);
+            listOne.stream().forEach(deviceStatus -> {
+                //根据状态上报,15s的只报充电状态
+                DeviceConnectionMsg deviceConnectionMsg = map.get(deviceStatus.getPileCode());
+                if (deviceStatus.getGunStatus() == StatusConstant.CHARGING) {
+                    if (redisCache.hasKey(RedisConstant.DEVICE_PORT_STATUS+deviceStatus.getDeviceImei())){
+                        //++++ 20240319-15 这里费用计算需要用到计费模型
+                        JSONObject statusJSON = redisCache.getCacheObject(RedisConstant.DEVICE_PORT_STATUS + deviceStatus.getDeviceImei());
+                        Integer voltage = statusJSON.getInteger("voltage");
+                        Integer power = statusJSON.getInteger("power");
+
+                        //++++
+                        realTimeStatusPushFrame.deviceStatusPush(deviceConnectionMsg, FrameDataSplicing.transactionNum(deviceStatus.getPileCode(), deviceConnectionMsg.getMessageCount()), deviceStatus.getPileCode(), deviceStatus.getGunPort(), deviceStatus.getGunStatus(), deviceStatus.getInsertGunStatus(), 0, 0, 0, 0, 0);
+                    }
+                }
+            });
+        }
+        if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_TWO)) {
+            List<DeviceStatus> listTwo = redisCache.getCacheObject(RedisConstant.ONLINE_DEVICE_TWO);
+            listTwo.stream().forEach(deviceStatus -> {
+                //根据状态上报,15s的只报充电状态
+                DeviceConnectionMsg deviceConnectionMsg = map.get(deviceStatus.getPileCode());
+                if (deviceStatus.getGunStatus() == StatusConstant.CHARGING) {
+                    //realTimeStatusPushFrame.deviceStatusPush(deviceConnectionMsg, FrameDataSplicing.transactionNum(deviceStatus.getPileCode(), deviceConnectionMsg.getMessageCount()), deviceStatus.getPileCode(), deviceStatus.getGunPort(), deviceStatus.getGunStatus(), deviceStatus.getInsertGunStatus(), 0, 0, 0, 0, 0);
+                }
+            });
+        }*/
+        log.info("======Charging status push task ending=====");
+    }
+
+    @Async("freeTaskAsyncPool")
+    public void freeMsg(Map<String, DeviceConnectionMsg> map) throws Exception {
+        log.info("======Free status push task starting=====");
+
+        //任务处理
+        //redis取出在线设备集合
+        Set<String> devicePileCodes = map.keySet();
+        log.info("free.deviceConnectionSize>>"+devicePileCodes.size());
+        devicePileCodes.stream().forEach(devicePileCode -> {
+            if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_ONE)) {
+                DeviceStatus deviceStatus = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE, devicePileCode);
+                if (deviceStatus != null) {
+                    if (deviceStatus.getGunStatus() == StatusConstant.CHARGING) {
+                        log.info("freeMsg>>>deviceguns status>>"+deviceStatus.getGunStatus());
+                    }
+                    //存在设备在线状态的上送空闲状态
+                    DeviceConnectionMsg deviceConnectionMsg = map.get(devicePileCode);
+                    if (deviceConnectionMsg.getLoginStatus()== Constant.DEVICE_NOT_LOGIN_STATUS){
+                        //
+                        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+                        deviceQueryWrapper.eq("pile_code", devicePileCode).eq("disabled", DeviceOnlineStatus.NORMAL);
+                        Device device = deviceService.getOne(deviceQueryWrapper);
+                        Long heartTime = deviceConnectionMsg.getHeartTime();
+                        if (deviceStatus.getOnlineStatus() == DeviceOnlineStatus.ONLINE && (System.currentTimeMillis() - heartTime) > 50 * 1000L) {
+                            log.info("heartTaskAsyncPool-1>not longin and heart normal>>" + devicePileCode);
+                           // loginFrame.loginMsgSend(deviceConnectionMsg,device);
+                        }
+                        return;
+                    }
+                    if (deviceStatus.getGunStatus() != StatusConstant.CHARGING) {
+                        if (deviceConnectionMsg.getLoginStatus() == 1) {
+                            log.info("↑↑↑↑↑↑↑↑↑↑↑↑↑空闲实时状态上报1111↑↑↑↑↑↑↑↑↑↑↑↑↑ " + deviceStatus.getGunStatus() + "<>" + (deviceStatus.getGunStatus() == PortStatusConstant.EMERGENCY_STOP));
+                            if (deviceStatus.getGunStatus() == PortStatusConstant.EMERGENCY_STOP) {
+                                //急停中也是空闲状态上报
+                                deviceStatus.setGunStatus(StatusConstant.FREE);
+                            }
+                            realTimeStatusPushFrame.deviceStatusPush(deviceConnectionMsg, FrameDataSplicing.
+                                    transactionNum(deviceStatus.getPileCode(), deviceConnectionMsg.getMessageCount()), deviceStatus.getPileCode(), deviceStatus.getGunPort(), deviceStatus.getGunStatus(), deviceStatus.getInsertGunStatus(), 0, 0, zero, zero, 0);
+                        }
+                    }
+                }
+            }
+            if (redisCache.hasKey(RedisConstant.ONLINE_DEVICE_TWO)) {
+                DeviceStatus deviceStatus = redisCache.getCacheMapValue(RedisConstant.ONLINE_DEVICE_ONE, devicePileCode);
+                if (deviceStatus != null) {
+                    //存在设备在线状态的上送空闲状态
+                    DeviceConnectionMsg deviceConnectionMsg = map.get(devicePileCode);
+                    if (deviceConnectionMsg.getLoginStatus()== Constant.DEVICE_NOT_LOGIN_STATUS){
+                        //
+                        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+                        deviceQueryWrapper.eq("pile_code", devicePileCode).eq("disabled", DeviceOnlineStatus.NORMAL);
+                        Device device = deviceService.getOne(deviceQueryWrapper);
+                        Long heartTime = deviceConnectionMsg.getHeartTime();
+                        if (deviceStatus.getOnlineStatus() == DeviceOnlineStatus.ONLINE && (System.currentTimeMillis() - heartTime) > 50 * 1000L) {
+                            log.info("heartTaskAsyncPool-1>not longin and heart normal>>" + devicePileCode);
+                            loginFrame.loginMsgSend(deviceConnectionMsg,device);
+                        }
+                        return;
+                    }
+                    if (deviceStatus.getGunStatus() != StatusConstant.CHARGING) {
+                        if (deviceConnectionMsg.getLoginStatus() == 1) {
+                            if (deviceStatus.getGunStatus() == PortStatusConstant.EMERGENCY_STOP) {
+                                //急停中也是空闲状态上报
+                                deviceStatus.setGunStatus(StatusConstant.FREE);
+                            }
+                            log.info("↑↑↑↑↑↑↑↑↑↑↑↑↑空闲实时状态上报2222↑↑↑↑↑↑↑↑↑↑↑↑↑ ");
+                            realTimeStatusPushFrame.deviceStatusPush(deviceConnectionMsg, FrameDataSplicing.
+                                    transactionNum(deviceStatus.getPileCode(), deviceConnectionMsg.getMessageCount()), deviceStatus.getPileCode(), deviceStatus.getGunPort(), deviceStatus.getGunStatus(), deviceStatus.getInsertGunStatus(), 0, 0, zero, zero, 0);
+                        }
+                    }
+                }
+            }
+        });
+
+        log.info("======Free status push task ending=====");
+    }
+
+    @Async("transactionTaskAsyncPool")
+    public void transactionMsg(Map<String, DeviceConnectionMsg> map) throws Exception {
+        log.info("======transaction push task starting=====");
+        //任务处理 1.上报交易流水消息后没有响应回复的情况,30秒上送一次三次后停止,五分钟后上送最后一次,不管是否成功都不再上送:有结束时间,充电状态是结束充电的
+        List<OrderStatus> list = orderStatusService.list();
+        Map<String, OrderStatus> orderStatusMap = list.stream()
+                .collect(Collectors.toMap(OrderStatus::getPileCode, orderStatus -> orderStatus,
+                        (existing, replacement) -> (Instant.ofEpochMilli(existing.getCreateTime()).atZone(ZoneId.systemDefault()).toLocalDateTime()).isAfter(Instant.ofEpochMilli(replacement.getCreateTime()).atZone(ZoneId.systemDefault()).toLocalDateTime()) ? existing : replacement));
+        List<OrderStatus> orderStatuses = orderStatusMap.values().stream()
+                .collect(Collectors.toList());
+
+        //过滤充电状态是结束的且已经上送未收到订单恢复的,说明交易订单上报没收到云快充消息,如果时间充电结束时间到现在大于30秒上送
+        List<OrderStatus> collect = orderStatuses.stream().filter(orderStatus -> orderStatus.getNowOrderStatus() == 1 && orderStatus.getTransactionOrderReplyStatus() == 0 && orderStatus.getTransactionOrderReportingActionStatus() == 1).collect(Collectors.toList());
+        //看订单时间进行重新上送
+        if (collect.size() > 0) {
+            collect.stream().forEach(orderStatus -> {
+                //上送订单并且订单上送大于5分钟但是没有变成充电状态的订单,当成空上报一次
+
+               /* if ((((System.currentTimeMillis()-orderStatus.getEndTime())/1000) / 30 )==1||(((System.currentTimeMillis()-orderStatus.getEndTime()) /1000) / 60 )==1||(((System.currentTimeMillis()-orderStatus.getEndTime()) /1000) / 90)==1){
+                    //各上送一次订单记录
+                    DeviceConnectionMsg deviceConnectionMsg = map.get(orderStatus.getPileCode());
+                    try {
+                        String bytes = redisCache.getCacheObject(orderStatus.getPileCode());
+                        byte[] bytes1 = Encrytion.aesEncrypt(orderStatus.getOriginalText(), bytes.getBytes());
+                        deviceConnectionMsg.getOutputStream().write(bytes1);
+                        deviceConnectionMsg.getOutputStream().flush();
+                        deviceConnectionMsg.incrementMessageCount();
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }else*/
+                if ((System.currentTimeMillis() - orderStatus.getEndTime()) > 5 * 60 * 1000 && orderStatus.getTransactionOrderReplyStatus() != 5) {
+                    //五分钟上送最后一次结束,上送
+                    DeviceConnectionMsg deviceConnectionMsg = map.get(orderStatus.getPileCode());
+                    if (deviceConnectionMsg!=null&&deviceConnectionMsg.getLoginStatus()== Constant.DEVICE_NOT_LOGIN_STATUS){
+                        //wshizhengjuezhe zhewanyier meishajishuhanlian danshi zhedongxi  queshi xilan
+                        return;
+                    }
+                    try {
+                        //String bytes = redisCache.getCacheObject(orderStatus.getPileCode());
+                        //byte[] bytes1 = Encrytion.aesEncrypt(orderStatus.getOriginalText(), bytes.getBytes());
+
+                        //byte[] spliceing = FrameDataSplicing.spliceing(deviceConnectionMsg.getMessageCount(), DeviceSendYkc.TRANSACTION_RECORDS_REQUEST.getFrameType(), DeviceSendYkc.TRANSACTION_RECORDS_REQUEST.getEncryptFlag(), bytes1, bytes1.length);
+
+                        deviceConnectionMsg.getOutputStream().write(orderStatus.getOriginalText());
+                        deviceConnectionMsg.getOutputStream().flush();
+                        deviceConnectionMsg.incrementMessageCount();
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                    //将恢复结果状态转成5,记录为充电记录上传5分钟后失败
+                    orderStatus.setTransactionOrderReplyStatus((byte) 5);
+                    orderStatusService.updateById(orderStatus);
+                    log.info("最新未收到上报的订单list5分钟推送最后一次>>" + orderStatuses.toString());
+                }
+            });
+        }
+//        List<OrderStatus> collects = list.stream().filter(orderStatus -> orderStatus.getOriginalText()==null||orderStatus.getEndTime()==0).collect(Collectors.toList());
+//        if (collects.size()>0){
+//            collects.forEach(orderStatus -> {
+//            log.info(orderStatus.getPileCode()+":检查订单状态>orderStatus.getOriginalText()==null||orderStatus.getEndTime()==0>>"+orderStatus.getCreateTime());
+//            QueryWrapper<DeviceStatus> queryWrapper = new QueryWrapper<>();
+//            queryWrapper.eq("pile_code",orderStatus.getPileCode());
+//            DeviceStatus one = deviceStatusService.getOne(queryWrapper);
+//
+//            QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
+//            deviceQueryWrapper.eq("pile_code", orderStatus.getPileCode()).eq("disabled", DeviceOnlineStatus.NORMAL);
+//            Device device = deviceService.getOne(deviceQueryWrapper);
+//
+//            QueryWrapper<BillingModel> billingModelQueryWrapper = new QueryWrapper<>();
+//            billingModelQueryWrapper.eq("pile_code", orderStatus.getPileCode());
+//            BillingModel model = billingModelService.getOne(billingModelQueryWrapper);
+//
+//            if ((System.currentTimeMillis() - orderStatus.getCreateTime()) > 5 * 60 * 1000 && (one.getGunStatus()!=StatusConstant.CHARGING)) {
+//                log.info(orderStatus.getPileCode()+":上送订单并且订单上送大于5分钟但是没有变成充电状态的订单,当成空上报一次>"+orderStatus.getCreateTime());
+//                DeviceConnectionMsg deviceConnectionMsg = map.get(orderStatus.getPileCode());
+//                try {
+//                    Map<String, BigDecimal> compute = transMoney.compute(1, new BillingModel(), 0L, 0L, false);
+//                    byte[] e=transactionFlowPushFrame.sendTrans(deviceConnectionMsg, orderStatus.getTransOrder(), orderStatus.getPileCode(), (byte) 1, orderStatus.getCreateTime(), System.currentTimeMillis() + 3 * 60 * 1000, model, orderStatus.getCard(), compute, TransConstant.OTHER_STOP);
+//                    orderStatus.setOriginalText(e);
+//                    orderStatus.setEndTime(1);
+//                    orderStatus.setNowOrderStatus(StatusConstant.NOW_ORDER_STATUS_CHARGING_ENDING);
+//                    orderStatus.setReasonStopCharging(TransConstant.OTHER_STOP);
+//                    orderStatusService.updateById(orderStatus);
+//
+//                } catch (Exception e) {
+//                    e.printStackTrace();
+//                }
+//            }
+//            });
+//        }
+
+        log.info("======transaction push task ending=====");
+    }
+
+
+}

+ 16 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/DataParam.java

@@ -0,0 +1,16 @@
+package com.tmzn.devicelinkykc.transdata;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.Data;
+
+@Data
+public class DataParam {
+
+    private JSONObject data;
+
+    private String ccid;
+
+    private String deviceId;
+
+    private Integer type;
+}

+ 42 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/RpcResult.java

@@ -0,0 +1,42 @@
+package com.tmzn.devicelinkykc.transdata;
+
+import lombok.Data;
+
+@Data
+public class RpcResult {
+
+    private boolean ok = false;
+
+    private String msg;
+
+    private Object data;
+
+    public static RpcResult ok(){
+        RpcResult rpcResult = new RpcResult();
+        rpcResult.setOk(true);
+        return rpcResult;
+    }
+    public static RpcResult success(){
+        return ok();
+    }
+
+    public static RpcResult ok(Object data){
+        RpcResult rpcResult = new RpcResult();
+        rpcResult.setOk(true);
+        rpcResult.setData(data);
+        return rpcResult;
+    }
+
+    public static RpcResult success(Object data){
+
+        return ok(data);
+    }
+
+    public static RpcResult fail(String msg){
+        RpcResult rpcResult = new RpcResult();
+        rpcResult.setOk(false);
+        rpcResult.setMsg(msg);
+        return rpcResult;
+    }
+
+}

+ 4 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/constant/Constant.java

@@ -0,0 +1,4 @@
+package com.tmzn.devicelinkykc.transdata.constant;
+
+public class Constant {
+}

+ 121 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/constant/NormalChargeConstant.java

@@ -0,0 +1,121 @@
+package com.tmzn.devicelinkykc.transdata.constant;
+
+/**
+ * 汽车单双枪 充电常量
+ */
+public class NormalChargeConstant{
+    //发送主题指令
+    public static final String SEND_TOPIC = "d_wt1206_d2113/";
+    //获取主板消息
+    public static final String GET_MAINBOARD_MSG  = "[85,96,1,0,97]";
+
+    //重启设备
+    public static final String RESTART_DEVICE = "[85,117,1,0,118]";
+
+    //端口详情
+    public static final String PORT_DETAIL = "[85,103,1,0,104]";
+
+    //命令方为 85
+    public static final Integer CMD_SEND = 85;
+
+    //读取主板参数
+    public static final Integer CMD_SET_MAINBOARD = 75960;
+
+
+    //开始充电
+    public static final Integer CMD_START_CHARGE = 75960;
+
+    //结束充电
+    public static final Integer CMD_STOP_CHARGE = 75960;
+
+   //---------配置文件名称
+
+    public static final String CONFIG_DIR = "config";
+
+    //发送消息
+    public static final String CONFIG_SEND_SUFFIX = "send";
+
+    //返回消息
+    public static final String CONFIG_BACK_SUFFIX = "back";
+   //配置文件名称 主板
+   public static final String CONFIG_MAINBOARD_FILENAME = "mainboard";
+    //预约充电
+    public static final String CONFIG_PLANCHARGE_FILENAME = "plancharge";
+    //开始充电
+    public static final String CONFIG_STARTCHARTGE_FILENAME = "startcharge";
+
+    //结束充电
+    public static final String CONFIG_ENDCHARGE_FILENAME = "endcharge";
+
+    //结束充电通知
+    public static final String CONFIG_ENDNOTICE_FILENAME = "endchargenotice";
+
+    //重启设备
+    public static final String CONFIG_RESTART_DEVICE = "restartdeivce";
+
+    //端口状态
+    public static final String CONFIG_PORT_STATUS = "portstatus";
+
+    //双枪端口详情
+    public static final String CONFIG_PORT_DETAIL_DOUBLE = "portdetaildouble";
+
+    //单枪端口详情
+    public static final String CONFIG_PORT_DETAIL_SINGLE = "portdetailsingle";
+
+    //端口上报
+    public static final String CONFIG_PORT_REPORT = "portreport";
+
+    //远程禁用或者开启端口
+    public static final String CONFIG_PORT_OPER = "portoper";
+
+
+
+    //----------配置文件结束
+
+
+    //各个指令key
+
+
+    //主板
+    public static final Integer KEY_MAINBOARD = 96;
+
+
+    //开启充电
+    public static final Integer KEY_STARTCHARGE = 104;
+
+
+    //结束充电
+    public static final Integer KEY_ENDCHARGE = 105;
+
+    //结束充电通知
+    public static final Integer KEY_END_NOTICE = 113;
+
+
+    //重启充电桩
+    public static final Integer KEY_RESTART_DEVICE = 117;
+
+    //端口状态
+    public static final Integer KEY_PORT_STATUS = 101;
+
+    //端口详情
+    public static final Integer KEY_PORT_DETAIL = 103;
+
+    //主动上报详情
+    public static final Integer KEY_PORT_REPORT = 115;
+
+
+    //远程禁用或者开启端口
+    public static final Integer KEY_PORT_OPER = 112;
+    /**
+     * 急停停充
+     */
+    public static final Integer EMERGENCY_STOP_CHARGING = 123;
+
+    //上电1分钟后主动上报主板版本号和所有端口状态
+    public static final Integer REPORT_PORT_STATUS = 114;
+
+    //启充停充上报端口状态
+    public static final Integer PORT_STATUS = 116;
+    //各个指令key --- end
+
+}

+ 26 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/entity/BasicData.java

@@ -0,0 +1,26 @@
+package com.tmzn.devicelinkykc.transdata.entity;
+
+
+public abstract class BasicData {
+
+    public BasicData(){
+
+    }
+
+    public static void main(String[] args) {
+        BasicData endCharge = new StartCharge();
+
+    }
+
+    public abstract String getFileName();
+
+
+
+    /**
+     * 获取命令前缀的参数
+     * @return
+     */
+    public abstract Integer[] preCmds();
+
+
+}

+ 17 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/entity/DeviceParam.java

@@ -0,0 +1,17 @@
+package com.tmzn.devicelinkykc.transdata.entity;
+
+import lombok.Data;
+
+@Data
+public class DeviceParam {
+
+    private Integer devId;
+
+    private String deviceId;
+
+    private String ccid;
+
+    private String time;
+
+    private Integer port;
+}

+ 25 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/entity/EndCharge.java

@@ -0,0 +1,25 @@
+package com.tmzn.devicelinkykc.transdata.entity;
+
+import com.tmzn.devicelinkykc.transdata.constant.NormalChargeConstant;
+import lombok.Data;
+
+@Data
+public class EndCharge extends BasicData{
+
+    private Integer port = 1;
+    public EndCharge() {
+
+    }
+
+
+    @Override
+    public Integer[] preCmds() {
+        return new Integer[]{85,105};
+    }
+
+
+    @Override
+    public String getFileName() {
+        return NormalChargeConstant.CONFIG_ENDCHARGE_FILENAME;
+    }
+}

+ 90 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/entity/MainBoard.java

@@ -0,0 +1,90 @@
+package com.tmzn.devicelinkykc.transdata.entity;
+
+import com.tmzn.devicelinkykc.transdata.constant.NormalChargeConstant;
+import lombok.Data;
+
+
+/**
+ *      * CARD_AVAIL	是	是否启用刷卡,1启用、0关闭
+ *      * PEAK_PRICE	是	峰谷电价(单位角)
+ *      * COST	是	刷卡预扣费金额(0-100元) --2位
+ *      * TIME_LIMIT	是	最大充电时间、默认999分钟 --2位
+ *      * FEE_TYPE	是	按电价计费0x00、刷卡免费0x01、免费直接充电0x02
+ *      * MAX_POWER	是	最大充电功率、默认下发8400W --2位
+ *      * PEAK_SERVICE_PRICE	是	峰谷电价服务费(单位角)
+ *      * VOLUME	是	语音音量(1-9)
+ *      * BOTTOM_PRICE	是	峰谷电价(单位角)
+ *      * BOTTOM_START	是	峰谷开始时间点
+ *      * BOTTOM_END	是	峰谷结束时间点
+ *      * BOTTOM_SERVICE_PRICE	是	峰谷电价服务费(单位角)
+ *      * MAX_CURRENT	是	充电电流、可选8、10、13、16、32A --2位
+ *      * CP_CHECK_OFF	是	cp信号开启或者关闭,1启用、0关闭
+ *      * FEE_SHOW_OFF	是	屏幕费率显示开启或关闭,1启用、0关闭
+ */
+@Data
+public class MainBoard extends BasicData{
+    private Integer cardAvail ;
+    private Integer peakPrice ;
+    private Integer cost ;
+    private Integer timeLimit ;
+    private Integer feeType ;
+    private Integer maxPower ;
+    private Integer peakServicePrice ;
+    private Integer volume ;
+    private Integer bottomPrice;
+    private Integer bottomStart;
+    private Integer bottomEnd;
+    private Integer bottomServicePrice;
+    private Integer maxCurrent;
+    private Integer cpCheckOff;
+    private Integer feeShowOff;
+
+
+    public MainBoard(){
+        cardAvail = 1;
+        peakPrice = 8;
+        cost = 500;
+        timeLimit = 999;
+
+        feeType = 0;
+        maxPower = 8400;
+        peakServicePrice = 7;
+
+        volume = 9;
+        bottomPrice = 5;
+        bottomStart = 22;
+        bottomEnd = 8;
+
+        bottomServicePrice = 5;
+
+        maxCurrent = 3200;
+
+        cpCheckOff = 1;
+
+        feeShowOff = 1;
+
+    }
+
+
+    @Override
+    public String getFileName() {
+        return NormalChargeConstant.CONFIG_MAINBOARD_FILENAME;
+    }
+
+
+//    @Override
+//    public String[] getKeys() {
+//        String keyarr [] = {"cardAvail","peakPrice","cost","timeLimit","feeType","maxPower","peakServicePrice","volume","bottomPrice","bottomStart","bottomEnd","bottomServicePrice","maxCurrent","cpCheckOff","feeShowOff"};
+//        return keyarr;
+//    }
+
+    /**
+     * 主板的命令参数
+     *
+     * @return
+     */
+    @Override
+    public Integer[] preCmds() {
+        return new Integer[]{85,97};
+    }
+}

+ 23 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/entity/PlanCharge.java

@@ -0,0 +1,23 @@
+package com.tmzn.devicelinkykc.transdata.entity;
+
+import com.tmzn.devicelinkykc.transdata.constant.NormalChargeConstant;
+import lombok.Data;
+
+@Data
+//预约充电
+public class PlanCharge extends BasicData{
+
+    private Integer port = 1;
+    private Integer time;
+    private Integer money;
+    public PlanCharge(){}
+    @Override
+    public String getFileName() {
+        return NormalChargeConstant.CONFIG_PLANCHARGE_FILENAME;
+    }
+
+    @Override
+    public Integer[] preCmds() {
+            return new Integer[]{85,104};
+    }
+}

+ 20 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/entity/PortOper.java

@@ -0,0 +1,20 @@
+package com.tmzn.devicelinkykc.transdata.entity;
+
+import com.tmzn.devicelinkykc.transdata.constant.NormalChargeConstant;
+import lombok.Data;
+
+@Data
+public class PortOper extends BasicData{
+    private Integer port;
+    private Integer avail;
+
+    @Override
+    public String getFileName() {
+        return NormalChargeConstant.CONFIG_PORT_OPER;
+    }
+
+    @Override
+    public Integer[] preCmds() {
+        return new Integer[]{85,112};
+    }
+}

+ 23 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/entity/PortStatus.java

@@ -0,0 +1,23 @@
+package com.tmzn.devicelinkykc.transdata.entity;
+
+import com.tmzn.devicelinkykc.transdata.constant.NormalChargeConstant;
+import lombok.Data;
+
+@Data
+public class PortStatus extends  BasicData{
+
+    private Integer signal;
+    private Integer hour;
+    private Integer mimute;
+    private Integer second;
+
+    @Override
+    public String getFileName() {
+        return NormalChargeConstant.CONFIG_PORT_STATUS;
+    }
+
+    @Override
+    public Integer[] preCmds() {
+        return new Integer[]{85,101};
+    }
+}

+ 31 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/entity/StartCharge.java

@@ -0,0 +1,31 @@
+package com.tmzn.devicelinkykc.transdata.entity;
+
+import com.tmzn.devicelinkykc.transdata.constant.NormalChargeConstant;
+import lombok.Data;
+
+@Data
+public class StartCharge extends BasicData{
+
+    private Integer port = 1;
+
+    private Integer money = 50000;
+
+    public StartCharge() {
+    }
+
+//    @Override
+//    public String[] getKeys() {
+//        return new String []{"port","money"};
+//    }
+
+    @Override
+    public Integer[] preCmds() {
+        return new Integer[]{85,104};
+    }
+
+
+    @Override
+    public String getFileName() {
+        return NormalChargeConstant.CONFIG_STARTCHARTGE_FILENAME;
+    }
+}

+ 0 - 0
src/main/java/com/tmzn/devicelinkykc/transdata/entity/opertype/OperEnum.java


Деякі файли не було показано, через те що забагато файлів було змінено