Przeglądaj źródła

接入jt809协议

wenhongquan 2 lat temu
rodzic
commit
61a8dac515
32 zmienionych plików z 1872 dodań i 98 usunięć
  1. 7 0
      ruoyi-admin/src/main/resources/router/t.xml
  2. 6 0
      ruoyi-modules/ruoyi-khy/pom.xml
  3. 53 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/JT809Server.java
  4. 70 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/Const.java
  5. 220 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/CommonUtils.java
  6. 54 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/CrcUtil.java
  7. 38 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/JT809Encrypt.java
  8. 154 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/PacketDecoderUtils.java
  9. 21 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/PacketEncoderUtils.java
  10. 36 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/PropertiesUtil.java
  11. 31 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809AdapterHandle.java
  12. 27 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809HeartbeatHandle.java
  13. 31 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809IdleStateHandler.java
  14. 50 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809LoginHandle.java
  15. 42 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809Packet0x1202Handle.java
  16. 31 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809ServerInitialzer.java
  17. 173 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/packet/JT809BasePacket.java
  18. 15 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/packet/JT809HeartbeatResponse.java
  19. 70 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/packet/JT809LoginPacket.java
  20. 61 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/packet/JT809LoginResponsePacket.java
  21. 201 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/packet/JT809Packet0x1202.java
  22. 59 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/JT809DecoderAdapter.java
  23. 34 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/JT809EncodeAdapter.java
  24. 18 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/decoder/Decoder.java
  25. 31 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/decoder/DecoderFactory.java
  26. 21 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/decoder/HeartbeatDecoder.java
  27. 88 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/decoder/JT809Packet0x1202Decoder.java
  28. 61 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/decoder/LoginDecoder.java
  29. 16 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/encode/Encoder.java
  30. 21 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/encode/EncoderFactory.java
  31. 25 0
      ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/encode/LoginResponseEncoder.java
  32. 107 98
      ruoyi-modules/ruoyi-khy/src/main/java/org/dromara/util/CamelDataUtil.java

+ 7 - 0
ruoyi-admin/src/main/resources/router/t.xml

@@ -1,4 +1,11 @@
 <routes xmlns="http://camel.apache.org/schema/spring">
 <routes xmlns="http://camel.apache.org/schema/spring">
+
+    <route>
+        <from uri="timer://myTimer?repeatCount=1"></from>
+        <to uri="bean:jt809.JT809Server?method=initServer(20004)"/>
+
+    </route>
+
     <!--同步服务配置-->
     <!--同步服务配置-->
 
 
     <route>
     <route>

+ 6 - 0
ruoyi-modules/ruoyi-khy/pom.xml

@@ -24,6 +24,12 @@
         </dependency>
         </dependency>
 
 
         <dependency>
         <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+            <version>4.1.38.Final</version>
+        </dependency>
+
+        <dependency>
             <groupId>org.dromara</groupId>
             <groupId>org.dromara</groupId>
             <artifactId>ruoyi-common-doc</artifactId>
             <artifactId>ruoyi-common-doc</artifactId>
         </dependency>
         </dependency>

+ 53 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/JT809Server.java

@@ -0,0 +1,53 @@
+package jt809;
+
+import  jt809.common.util.PropertiesUtil;
+import  jt809.handle.JT809ServerInitialzer;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/21 20:26
+ * @Version 1.0
+ * @Describe: 启动类
+ */
+
+@Component
+public class JT809Server {
+    private static Logger log = LoggerFactory.getLogger(JT809Server.class);
+
+    private static int PORT;
+
+
+    public static void initServer(Integer port) {
+        PORT = port;
+        NioEventLoopGroup boosGroup = new NioEventLoopGroup();
+        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
+
+        final ServerBootstrap serverBootstrap = new ServerBootstrap();
+        serverBootstrap
+                .group(boosGroup, workerGroup)
+                .channel(NioServerSocketChannel.class)
+                .option(ChannelOption.SO_BACKLOG, 1024)
+                .childOption(ChannelOption.SO_KEEPALIVE, true)
+                .childOption(ChannelOption.TCP_NODELAY, true)
+                .childHandler(new JT809ServerInitialzer());
+        bind(serverBootstrap, PORT);
+    }
+
+    private static void bind(final ServerBootstrap serverBootstrap, int port) {
+        serverBootstrap.bind(port).addListener(future -> {
+            if (future.isSuccess()) {
+                log.info("JT809Server在端口:{}启动成功!",port);
+//                CommonUtils.delDataTimer(); // 定时任务,定时删除垃圾数据
+            } else {
+                log.error("JT809Server在端口:{}启动失败!",port);
+            }
+        });
+    }
+}

+ 70 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/Const.java

@@ -0,0 +1,70 @@
+package jt809.common;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/21 20:41
+ * @Version 1.0
+ * @Describe: 一些常量
+ */
+public class Const {
+    /** 报文序列号*/
+    private static int msg_sn = -1;
+    public static int getMsgSN(){
+        msg_sn += 1;
+        return msg_sn;
+    }
+
+    /** 业务数据类型标志*/
+     public interface BusinessDataType{
+        /** 主链路登录请求消息*/
+        short UP_CONNECT_REQ = 0x1001;
+        /** 主链路登录应答消息*/
+        short UP_CONNECT_RSP = 0x1002;
+        /** 主链路连接保持请求消息*/
+        short UP_LINKTEST_REQ = 0x1005;
+        /** 主链路连接保持应答消息*/
+        short UP_LINKTEST_RSP = 0x1006;
+        /** 主链路动态信息交换消息*/
+        short UP_EXG_MSG = 0x1200;
+    }
+
+    /** 子业务数据类型标志*/
+    public interface SubBusinessDataType{
+        /** 实时上传车辆定位信息*/
+        short UP_EXG_MSG_REAL_LOCATION = 0x1202;
+    }
+    /** 主链路登录应答*/
+    public interface LoginResponseCode{
+        /** 0x00:成功;*/
+        byte SUCCESS = 0x00;
+        /** 0x01:IP地址不正确;*/
+        byte IP_ERROR = 0x01;
+        /** 0x02:接入码不正确;*/
+        byte ACCESS_CODE_ERROR = 0x02;
+        /** 0x03:用户没用注册;*/
+        byte USERNAME_ERROR = 0x03;
+        /** 0x04:密码错误;*/
+        byte PASSWORD_ERROR = 0x04;
+        /** 0x05:资源紧张,稍后再连接(已经占用);*/
+        byte RESOURCE_CRISIS = 0x05;
+        /** 0x06:其他。*/
+        byte OTHER_ERROR = 0x06;
+    }
+    /** 报文数据体是否加密*/
+    public interface EncryptFlag{
+        /** 加密*/
+        byte YES = 0x01;
+        /** 不加密*/
+        byte NO = 0x00;
+    }
+
+    /** 用户信息,以下信息请自定义*/
+    public interface UserInfo{
+        int USER_ID = 6051;
+        String PASSWORD = "iot2yun";
+        int MSG_GNSSCENTERID = 1001; // 1001
+    }
+
+
+
+}

+ 220 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/CommonUtils.java

@@ -0,0 +1,220 @@
+package jt809.common.util;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.*;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/22 21:03
+ * @Version 1.0
+ * @Describe:
+ */
+public class CommonUtils {
+    private static Logger log = LoggerFactory.getLogger(CommonUtils.class);
+    /** 当前线程报文的缓存,以备出错时,打印或记录,方便后期定位*/
+    public static Map<String,String> PACKET_CACHE = new HashMap<>();
+
+    public static ByteBuf getByteBuf(byte[] bytes){
+        ByteBuf byteBuf = Unpooled.copiedBuffer(bytes);
+        return byteBuf;
+    }
+
+    public static byte[] getByteArray(ByteBuf byteBuf){
+        int num = byteBuf.readableBytes();
+        byte[] originalPacket = new byte[num];
+        byteBuf.readBytes(originalPacket);
+
+        return originalPacket;
+    }
+
+    /**
+     * byte数组拼接
+     * @param first
+     * @param back
+     * @return
+     */
+    public static byte[] append(byte[] first, byte[] back) {
+        if(null == first || null == back){
+            return null;
+        }
+        int length = first.length + back.length;
+        byte[] res = new byte[length];
+        System.arraycopy(first, 0, res, 0, first.length);
+        System.arraycopy(back, 0, res, first.length, back.length);
+        return res;
+
+    }
+
+    /**
+     * short转换为byte[]
+     * @param number
+     * @return byte[]
+     */
+    public static byte[] short2Bytes(short number) {
+        byte[] b = new byte[2];
+        b[0] = (byte) (number >> 8);
+        b[1] = (byte) (number & 0xff);
+        return b;
+    }
+
+    /**
+     * 大转小时,会截取低位,舍弃高位。
+     * int 32位
+     * byte 8位
+     * 当int转为1个byte时,会截取int最低的8位
+     * int to bytes
+     * @param n
+     * @return
+     */
+    public static byte[] int2bytes(int n) {
+        byte[] b = new byte[4];
+
+        for (int i = 0; i < 4; i++) {
+            b[i] = (byte) (n >> (24 - i * 8));
+        }
+        return b;
+    }
+
+    /**
+     * byte数组转时间字符串 格式 yyMMddHHmmss
+     * @return
+     */
+    public static String bytes2timeStr(byte[] array){
+        StringBuilder stringBuilder = new StringBuilder();
+        for(int i = 0; i < array.length; i ++){
+            int timeUnit = byte2UnsignedInt(array[i]);
+            if(timeUnit < 10){
+                stringBuilder.append(0);
+            }
+            stringBuilder.append(timeUnit);
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
+        return  sdf.format(new Date()).substring(0,2) + stringBuilder.toString();
+    }
+
+
+    /**
+     * byte转无符号整数
+     * @param value
+     * @return
+     */
+    public static int byte2UnsignedInt(byte value) {
+        return Byte.toUnsignedInt(value);
+    }
+
+    /**
+     *
+     *
+     * @描述 将一个long转换成8位的byte[]
+     * @param num
+     * 	long值
+     * @return
+     * 长度是8的byte[]
+     * @throws Exception
+     */
+    public static byte[] longToBytes(long num) {
+        byte[] b = new byte[8];
+        for (int i = 0; i < 8; i++) {
+            b[i] = (byte) (num >>> (56 - i * 8));
+        }
+        return b;
+    }
+    /**
+     *
+     *
+     * @描述 将一个数组转换成一个long值
+     * @param b
+     *  长度是8的byte[]
+     * @return
+     * 	long值
+     * @throws Exception
+     */
+    public static long bytesToLong(byte[] b) {
+        int mask = 0xff;
+        long temp = 0;
+        long res = 0;
+        for (int i = 0; i < 8; i++) {
+            res <<= 8;
+            temp = b[i] & mask;
+            res |= temp;
+        }
+        return res;
+    }
+
+    /**
+     *  将一个byte数组转换成二进制字符串
+     * @param bytes
+     * @return 二进制字符串
+     */
+    public static String bytes2bitStr(byte[] bytes){
+        StringBuilder stringBuilder = new StringBuilder();
+        for (byte b : bytes) {
+            stringBuilder.append(byte2bitStr(b));
+        }
+        return stringBuilder.toString();
+    }
+
+    /**
+     *  将一个byte转换成二进制字符串
+     * @param b
+     * @return 二进制字符串
+     */
+    public static String byte2bitStr(byte b) {
+        return "" + (byte) ((b >> 7) & 0x1) + (byte) ((b >> 6) & 0x1)
+                + (byte) ((b >> 5) & 0x1) + (byte) ((b >> 4) & 0x1)
+                + (byte) ((b >> 3) & 0x1) + (byte) ((b >> 2) & 0x1)
+                + (byte) ((b >> 1) & 0x1) + (byte) ((b >> 0) & 0x1);
+    }
+
+
+    /**
+     * byte[]转换为short
+     * @param bytes
+     * @return short
+     */
+    public static short bytes2Short(byte[] bytes){
+        short z = (short)((bytes[0] << 8) | (bytes[1] & 0xFF));
+        return z;
+    }
+
+    /**
+     * byte to int
+     *
+     * @param data
+     * @return
+     */
+    public static int bytes2int(byte[] data) {
+        int mask = 0xff;
+        int temp = 0;
+        int n = 0;
+        for (int i = 0; i < data.length; i++) {
+            n <<= 8;
+            temp = data[i] & mask;
+            n |= temp;
+        }
+        return n;
+    }
+
+//    public static void delDataTimer(){
+//        log.info("定时任务");
+//        Timer timer = new Timer();
+//        TimerTask task = new TimerTask() {
+//            @Override
+//            public void run() {
+//                int i = JT809Dao.delYesterdayData();
+//                log.info("删除{}条数据",i);
+//            }
+//        };
+//        ZonedDateTime zonedDateTime = LocalDateTime.now().plusSeconds(10).atZone(ZoneId.systemDefault());
+//        timer.scheduleAtFixedRate(task,Date.from(zonedDateTime.toInstant()),1000 * 6);
+//    }
+
+}

+ 54 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/CrcUtil.java

@@ -0,0 +1,54 @@
+package jt809.common.util;
+
+import io.netty.buffer.ByteBuf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static  jt809.common.util.CommonUtils.PACKET_CACHE;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/22 20:14
+ * @Version 1.0
+ * @Describe: 校验工具类
+ */
+public class CrcUtil {
+    private static Logger log = LoggerFactory.getLogger(CrcUtil.class);
+
+    public static boolean checkCRC(byte[] bytes){
+        ByteBuf byteBuf = CommonUtils.getByteBuf(bytes);
+        // 获取数据长度和crc标示
+        byteBuf.skipBytes(1);
+        int msgLength = CommonUtils.bytes2int(new byte[]{bytes[1],bytes[2],bytes[3],bytes[4]});
+        int crcLength = msgLength - 4;
+        byte[] crcBody = new byte[crcLength];
+        byteBuf.readBytes(crcBody);
+
+        short oldCRCcode = byteBuf.readShort();
+        short currentCRCcode = getCRC16(crcBody);
+        if (oldCRCcode == currentCRCcode) {
+            return true;
+        }
+        log.error("crc校验失败;报文信息:{}",PACKET_CACHE.get(Thread.currentThread().getName()));
+        return false;
+    }
+
+    public static short getCRC16(byte[] bytes) {
+        short crc = (short) 0xffff;
+        short polynomial = 0x1021;
+        for (int index = 0; index < bytes.length; index++) {
+            byte b = bytes[index];
+            for (int i = 0; i < 8; i++) {
+                boolean bit = ((b >> (7 - i) & 1) == 1);
+                boolean c15 = ((crc >> 15 & 1) == 1);
+                crc <<= 1;
+                if (c15 ^ bit) {
+                    crc ^= polynomial;
+                }
+            }
+        }
+        crc &= 0xffff;
+        byte[] short2Bytes = CommonUtils.short2Bytes(crc);
+        return crc;
+    }
+}

+ 38 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/JT809Encrypt.java

@@ -0,0 +1,38 @@
+package jt809.common.util;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 8:49
+ * @Version 1.0
+ * @Describe: 加密解密:只数据体加密
+ */
+public class JT809Encrypt {
+    private static final Long UINT32_MAX_VALUE = 4294967295L;
+
+    private static byte[] encrypt(long M1, long IA1, long IC1, long key, byte[] bytes) {
+        // 第一个传入的key默认认为小于等于UINT32_MAX_VALUE
+        int index = 0;
+        if (0 == key) {
+            key = 1;
+        }
+        int size = bytes.length;
+        if (M1 == 0) {
+            // 排除除以0的异常
+            return new byte[0];
+        }
+        while (index < size) {
+            key = IA1 * (key % M1) + IC1;
+
+            // 若key大于,取低位32位,java是有符号的所以unit32必须要用long类型类承接,而key为long类型时
+            // key = IA1 * (key % M1) + IC1很可能大于unit32的最大值,java实现里面是正常的,但是对比c#
+            // 加密后的数组字符串发现不一致,后来发现原始实现中用的uint32,当大于他的最大值是高位会被丢弃
+            // 也就是&UINT32_MAX_VALUE后的值才是正确的值
+            if (key > UINT32_MAX_VALUE) {
+                key &= UINT32_MAX_VALUE;
+            }
+            byte b = (byte) ((key >> 20) & 0xff);
+            bytes[index++] ^= b;
+        }
+        return bytes;
+    }
+}

+ 154 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/PacketDecoderUtils.java

@@ -0,0 +1,154 @@
+package jt809.common.util;
+
+import  jt809.packet.JT809BasePacket;
+import io.netty.buffer.ByteBuf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static  jt809.common.util.CommonUtils.PACKET_CACHE;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/22 16:55
+ * @Version 1.0
+ * @Describe: 报文解码工具包
+ */
+public class PacketDecoderUtils {
+    private static Logger log = LoggerFactory.getLogger(PacketDecoderUtils.class);
+
+    public static byte[] decoderEscape(ByteBuf buf){
+        byte[] originalPacket = CommonUtils.getByteArray(buf);
+        byte[] correctPacket = decoderEscape(originalPacket);
+        StringBuilder packetInfo = new StringBuilder();
+        packetInfo.append("原始报文:").append(bytes2HexStr(originalPacket)).append(";转义后的报文:").append(bytes2HexStr(correctPacket));
+        PACKET_CACHE.put(Thread.currentThread().getName(),packetInfo.toString());
+        return correctPacket;
+    }
+
+    public static byte[] decoderEscape(byte[] originalPacket){
+        String dataStr = bytes2FullHexStr(originalPacket);
+        dataStr = dataStr.replaceAll("0x5a0x01", "0x5b");
+        dataStr = dataStr.replaceAll("0x5a0x02", "0x5a");
+        dataStr = dataStr.replaceAll("0x5e0x01", "0x5d");
+        dataStr = dataStr.replaceAll("0x5e0x02", "0x5e");
+        byte[] bytes = fullHexStr2Bytes(dataStr);
+        return bytes;
+    }
+
+    /**
+     * 数组转换成十六进制字符串,不带0x
+     * @param array
+     * @return HexString
+     */
+    public static String bytes2HexStr(byte[] array) {
+        StringBuffer sb = new StringBuffer(array.length);
+        String sTemp;
+        for (int i = 0; i < array.length; i++) {
+            sTemp = Integer.toHexString(0xFF & array[i]);
+            if (sTemp.length() < 2){
+                sb.append(0);
+            }
+            sb.append(sTemp.toUpperCase());
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 数组转换成带0x的十六进制字符串
+     * @param array
+     * @return HexString
+     */
+    public static String bytes2FullHexStr(byte[] array) {
+        StringBuffer sb = new StringBuffer(array.length);
+        sb.append("0x");
+        String sTemp;
+        for (int i = 0; i < array.length; i++) {
+            sTemp = Integer.toHexString(0xFF & array[i]);
+            if (sTemp.length() < 2){
+                sb.append(0);
+            }
+            sb.append(sTemp);
+            if(i < array.length-1){
+                sb.append("0x");
+            }
+        }
+        return sb.toString().toLowerCase();
+    }
+
+    /**
+     * 把16进制字符串不带0x的转换成字节数组
+     * @param hex
+     * @return byte[]
+     */
+    public static byte[] hexStr2Bytes(String hex) {
+        int len = (hex.length() / 2);
+        byte[] result = new byte[len];
+        char[] achar = hex.toCharArray();
+        for (int i = 0; i < len; i++) {
+            int pos = i * 2;
+            result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
+        }
+        return result;
+    }
+
+    /**
+     * 把带0x的16进制字符串转换成字节数组
+     * @param hex
+     * @return byte[]
+     */
+    public static byte[] fullHexStr2Bytes(String hex){
+        hex = hex.toLowerCase().replaceAll("0x","").trim().toUpperCase();
+        return hexStr2Bytes(hex);
+    }
+
+    private static int toByte(char c) {
+        byte b = (byte) "0123456789ABCDEF".indexOf(c);
+        return b;
+    }
+
+    /**
+     * 基础解析,解析数据体之前的内容,公共的。
+     * @param bytes
+     * @param packet
+     */
+    public static ByteBuf baseDecoder(byte[] bytes, JT809BasePacket packet) throws Exception {
+        ByteBuf byteBuf = CommonUtils.getByteBuf(bytes);
+        byteBuf.skipBytes(1);
+        packet.setMsgLength(byteBuf.readInt());
+        packet.setMsgSn(byteBuf.readInt());
+        packet.setMsgId(byteBuf.readShort());
+        packet.setMsgGNSSCenterId(byteBuf.readInt());
+        byte[] versionFlag = new byte[3];
+        byteBuf.readBytes(versionFlag);
+        packet.setVersionFlag(versionFlag);
+        packet.setEncryptFlag(byteBuf.readByte());
+        packet.setEncryptKey(byteBuf.readInt());
+        return byteBuf;
+    }
+
+    /**
+     * 获取数据体的 ByteBuf
+     * @param byteBuf 是从数据体开始的ByteBuf
+     * @return
+     */
+    public static ByteBuf getMsgBodyBuf(ByteBuf byteBuf) {
+        byte[] msgBodyByteArr = getMsgBodyByteArr(byteBuf);
+        return CommonUtils.getByteBuf(msgBodyByteArr);
+    }
+
+    /**
+     * 获取数据体的 ByteArr
+     * @param byteBuf 是从数据体开始的ByteBuf
+     * @return
+     */
+    public static byte[] getMsgBodyByteArr(ByteBuf byteBuf) {
+        // 1、获取数据数组
+        int msgBodyLength = byteBuf.readableBytes()-3;
+        byte[] msgBodyByteArr = new byte[msgBodyLength];
+        byteBuf.readBytes(msgBodyByteArr);
+
+        return msgBodyByteArr;
+    }
+
+
+}

+ 21 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/PacketEncoderUtils.java

@@ -0,0 +1,21 @@
+package jt809.common.util;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 10:05
+ * @Version 1.0
+ * @Describe: 编码公共方法
+ */
+public class PacketEncoderUtils {
+
+    /** 编码转义*/
+    public static byte[] encoderEscape(byte[] dataBytesWithoutHeadAndEnd){
+        String dataStr = PacketDecoderUtils.bytes2FullHexStr(dataBytesWithoutHeadAndEnd);
+        dataStr = dataStr.replaceAll("0x5b", "0x5a0x01");
+        dataStr = dataStr.replaceAll("0x5a", "0x5a0x02");
+        dataStr = dataStr.replaceAll("0x5d", "0x5e0x01");
+        dataStr = dataStr.replaceAll("0x5e", "0x5e0x02");
+        byte[] bytes = PacketDecoderUtils.fullHexStr2Bytes(dataStr);
+        return bytes;
+    }
+}

+ 36 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/common/util/PropertiesUtil.java

@@ -0,0 +1,36 @@
+package jt809.common.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Properties;
+public class PropertiesUtil {
+    private static Logger log = LoggerFactory.getLogger(PropertiesUtil.class);
+    private static Properties props;
+    static {
+        String fileName = "application.properties";
+        props = new Properties();
+        try {
+            props.load(new InputStreamReader(PropertiesUtil.class.getClassLoader().getResourceAsStream(fileName),"UTF-8"));
+        } catch (IOException e) {
+            log.error("load configuration file failure!",e);
+        }
+    }
+    public static String getProperty(String key){
+        String value = props.getProperty(key.trim());
+        if(value.isEmpty()){
+            return null;
+        }
+        return value.trim();
+    }
+
+    public static String getProperty(String key,String defaultValue){
+        String value = props.getProperty(key.trim());
+        if(value.isEmpty()){
+            value = defaultValue;
+        }
+        return value.trim();
+    }
+}

+ 31 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809AdapterHandle.java

@@ -0,0 +1,31 @@
+package jt809.handle;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/22 21:21
+ * @Version 1.0
+ * @Describe: 适配处理器
+ */
+public class JT809AdapterHandle extends ChannelInboundHandlerAdapter {
+    private static Logger log = LoggerFactory.getLogger(JT809AdapterHandle.class);
+
+
+    @Override
+    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
+        log.info("{}客户端已连接",ctx.name());
+        super.channelRegistered(ctx);
+    }
+
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+        log.info("{}客户端关闭",ctx.name());
+        super.channelInactive(ctx);
+    }
+
+
+}

+ 27 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809HeartbeatHandle.java

@@ -0,0 +1,27 @@
+package jt809.handle;
+
+import  jt809.common.Const;
+import  jt809.packet.JT809BasePacket;
+import  jt809.packet.JT809HeartbeatResponse;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 14:42
+ * @Version 1.0
+ * @Describe:
+ */
+public class JT809HeartbeatHandle extends SimpleChannelInboundHandler<JT809HeartbeatResponse> {
+    @Override
+    protected void channelRead0(ChannelHandlerContext ctx, JT809HeartbeatResponse msg) throws Exception {
+        msg.setMsgLength(JT809BasePacket.getFixedByteLength());
+        msg.setMsgSn(Const.getMsgSN());
+        msg.setMsgId(Const.BusinessDataType.UP_LINKTEST_RSP);
+        msg.setMsgGNSSCenterId(Const.UserInfo.MSG_GNSSCENTERID);
+        msg.setVersionFlag(new byte[]{1,0,0});
+        msg.setEncryptFlag(Const.EncryptFlag.NO);
+        msg.setEncryptKey(0);
+        ctx.channel().writeAndFlush(msg);
+    }
+}

+ 31 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809IdleStateHandler.java

@@ -0,0 +1,31 @@
+package jt809.handle;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.handler.timeout.IdleStateHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 16:22
+ * @Version 1.0
+ * @Describe:
+ */
+public class JT809IdleStateHandler extends IdleStateHandler {
+    private static Logger log = LoggerFactory.getLogger(JT809IdleStateHandler.class);
+
+    private static final int READER_IDLE_TIME = 60;
+
+    public JT809IdleStateHandler() {
+        super(READER_IDLE_TIME, 0, 0, TimeUnit.SECONDS);
+    }
+
+    @Override
+    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) {
+        log.info(READER_IDLE_TIME + "秒内未读到数据,关闭连接");
+        ctx.channel().close();
+    }
+}

+ 50 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809LoginHandle.java

@@ -0,0 +1,50 @@
+package jt809.handle;
+
+import  jt809.common.Const;
+import  jt809.packet.JT809LoginPacket;
+import  jt809.packet.JT809LoginResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/22 20:06
+ * @Version 1.0
+ * @Describe:
+ */
+public class JT809LoginHandle extends SimpleChannelInboundHandler<JT809LoginPacket> {
+    private static Logger log = LoggerFactory.getLogger(JT809LoginHandle.class);
+
+    @Override
+    protected void channelRead0(ChannelHandlerContext ctx, JT809LoginPacket msg){
+        JT809LoginResponsePacket loginResponsePacket = new JT809LoginResponsePacket();
+        byte LoginResponseCode = valid(msg);
+        loginResponsePacket.setResul(LoginResponseCode);
+        loginResponsePacket.setVerifyCode(0);
+        // 登录响应
+        ctx.channel().writeAndFlush(loginResponsePacket);
+    }
+
+    /** 用户名密码校验*/
+    private byte valid(JT809LoginPacket msg) {
+        int userId =  msg.getUserId();
+        String password =  msg.getPassword();
+        log.info("接收到了登录的请求->用户名:{};密码:{};",userId,password);
+        if (Const.UserInfo.USER_ID == userId && Const.UserInfo.PASSWORD.equals(password.trim())) {
+            log.info("登录验证成功");
+            return Const.LoginResponseCode.SUCCESS;
+        } else if (Const.UserInfo.USER_ID != userId){
+            log.info("USER_ID不正确");
+            return Const.LoginResponseCode.USERNAME_ERROR;
+        } else if (!Const.UserInfo.PASSWORD.equals(password)){
+            log.info("PASSWORD_ERROR");
+            return Const.LoginResponseCode.PASSWORD_ERROR;
+        }else {
+            log.info("OTHER_ERROR");
+            return Const.LoginResponseCode.OTHER_ERROR;
+        }
+    }
+
+}

+ 42 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809Packet0x1202Handle.java

@@ -0,0 +1,42 @@
+package jt809.handle;
+
+import cn.hutool.json.JSONUtil;
+import jt809.common.util.CommonUtils;
+import  jt809.packet.JT809Packet0x1202;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import org.dromara.system.domain.GpsData;
+import org.dromara.util.CamelDataUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 16:04
+ * @Version 1.0
+ * @Describe:
+ */
+public class JT809Packet0x1202Handle extends SimpleChannelInboundHandler<JT809Packet0x1202> {
+    private static Logger log = LoggerFactory.getLogger(JT809Packet0x1202Handle.class);
+
+    @Autowired
+    private CamelDataUtil camelDataUtil;
+
+    @Override
+    protected void channelRead0(ChannelHandlerContext ctx, JT809Packet0x1202 msg) {
+        log.info("车辆定位信息:{}", msg.toString());
+        if (msg.getVec1() > 0 || msg.getVec2() > 0) {
+            GpsData gpsData = new GpsData();
+            gpsData.setDevicecode(msg.getVehicleNo().trim());
+            gpsData.setLat((double) (msg.getLat()/1000000));
+            gpsData.setLng((double) (msg.getLon()/1000000));
+            gpsData.setAlt(0.0);
+            gpsData.setSpeed((double) (msg.getVec1() > 0?msg.getVec1():msg.getVec2()));
+            camelDataUtil.processGpsData(gpsData);
+        }
+
+
+
+    }
+}

+ 31 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/handle/JT809ServerInitialzer.java

@@ -0,0 +1,31 @@
+package jt809.handle;
+
+import  jt809.protocol.JT809DecoderAdapter;
+import  jt809.protocol.JT809EncodeAdapter;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.DelimiterBasedFrameDecoder;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/21 20:29
+ * @Version 1.0
+ * @Describe:
+ */
+public class JT809ServerInitialzer extends ChannelInitializer<NioSocketChannel> {
+
+    @Override
+    protected void initChannel(NioSocketChannel ch) throws Exception {
+        ByteBuf delimiter = Unpooled.copiedBuffer(new byte[]{0x5d});
+        ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, false, delimiter));
+        ch.pipeline().addLast(new JT809IdleStateHandler());
+        ch.pipeline().addLast(new JT809AdapterHandle());
+        ch.pipeline().addLast(new JT809DecoderAdapter());
+        ch.pipeline().addLast(new JT809HeartbeatHandle());
+        ch.pipeline().addLast(new JT809LoginHandle());
+        ch.pipeline().addLast(new JT809Packet0x1202Handle());
+        ch.pipeline().addLast(new JT809EncodeAdapter());
+    }
+}

+ 173 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/packet/JT809BasePacket.java

@@ -0,0 +1,173 @@
+package jt809.packet;
+
+import  jt809.common.util.CommonUtils;
+import  jt809.common.util.CrcUtil;
+
+import java.util.Arrays;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/22 15:01
+ * @Version 1.0
+ * @Describe: 基础报文实体类
+ */
+public abstract class JT809BasePacket {
+    /**
+     *  不算消息体固定为26个字节长度
+     *  Head flag + Message Header + CRC Code + End Flag
+     *  1 + 22 + 2 + 1 = 26
+     */
+    public static final int FIXED_BYTE_LENGTH = 26;
+
+    /** 头标识*/
+    public static final byte HEAD_FLAG = 0x5b;
+    /** 数据长度(包括头标识、数据头、数据体和尾标识) 4字节*/
+    private int msgLength;
+    /** 报文序列号 4字节*/
+    private int msgSn;
+    /** 业务数据类型 2字节*/
+    private short msgId;
+    /** 下级平台接入码,上级平台给下级平台分配唯一标识码。4字节*/
+    private int msgGNSSCenterId;
+    /**
+     * 协议版本号标识,上下级平台之间采用的标准协议版
+     *  编号;长度为 3 个字节来表示,0x01 0x02 0x0F 标识
+     *  的版本号是 v1.2.15,以此类推。
+     *  Hex编码 ,这个是3个字节,需要注意
+     */
+    private byte[] versionFlag;
+    /** 报文加密标识位 b: 0 表示报文不加密,1 表示报文加密。0x00 0x01,这里默认不加密 1字节*/
+    private byte encryptFlag = 0x00;
+    /** 数据加密的密匙,长度为 4 个字节*/
+    private int encryptKey;
+
+    /////////////////
+    ////数据体在这///
+    public abstract byte[] getMsgBodyByteArr();
+    ///////////////
+
+    /** CRC 校验码 2字节*/
+    private int crcCode;
+    /** 尾标识*/
+    public static final byte END_FLAG = 0x5d;
+    /** 获取需要校验的部分*/
+    private byte[] getNeedCRCBody(){
+        byte[] msgLengthBytes = CommonUtils.int2bytes(this.msgLength);
+        byte[] msgSnBytes = CommonUtils.int2bytes(this.msgSn);
+        byte[] bytes1 = CommonUtils.append(msgLengthBytes, msgSnBytes);
+        byte[] msgIdBytes = CommonUtils.short2Bytes(this.msgId);
+        byte[] bytes2 = CommonUtils.append(bytes1, msgIdBytes);
+        byte[] msgGNSSCenterIdBytes = CommonUtils.int2bytes(this.msgGNSSCenterId);
+        byte[] bytes3 = CommonUtils.append(bytes2, msgGNSSCenterIdBytes);
+        byte[] bytes4 = CommonUtils.append(bytes3, this.versionFlag);
+        byte[] bytes5 = CommonUtils.append(bytes4, new byte[]{this.encryptFlag});
+        byte[] encryptKeyBytes = CommonUtils.int2bytes(this.encryptKey);
+        byte[] bytes6 = CommonUtils.append(bytes5, encryptKeyBytes);
+        byte[] bytes = CommonUtils.append(bytes6, getMsgBodyByteArr());
+        return bytes;
+    }
+
+    /**
+     * 没有头标志和尾标志
+     * @return
+     */
+    public byte[] getAllBody(){
+        byte[] needCRCBody = getNeedCRCBody();
+        short crc16 = CrcUtil.getCRC16(needCRCBody);
+        byte[] crcCode = CommonUtils.short2Bytes(crc16);
+        byte[] bytes = CommonUtils.append(needCRCBody, crcCode);
+        return bytes;
+    }
+
+
+    public static int getFixedByteLength() {
+        return FIXED_BYTE_LENGTH;
+    }
+
+    public static byte getHeadFlag() {
+        return HEAD_FLAG;
+    }
+
+    public static byte getEndFlag() {
+        return END_FLAG;
+    }
+
+    public int getMsgLength() {
+        return msgLength;
+    }
+
+    public void setMsgLength(int msgLength) {
+        this.msgLength = msgLength;
+    }
+
+    public int getMsgSn() {
+        return msgSn;
+    }
+
+    public void setMsgSn(int msgSn) {
+        this.msgSn = msgSn;
+    }
+
+    public short getMsgId() {
+        return msgId;
+    }
+
+    public void setMsgId(short msgId) {
+        this.msgId = msgId;
+    }
+
+    public int getMsgGNSSCenterId() {
+        return msgGNSSCenterId;
+    }
+
+    public void setMsgGNSSCenterId(int msgGNSSCenterId) {
+        this.msgGNSSCenterId = msgGNSSCenterId;
+    }
+
+    public byte[] getVersionFlag() {
+        return versionFlag;
+    }
+
+    public void setVersionFlag(byte[] versionFlag) {
+        this.versionFlag = versionFlag;
+    }
+
+    public byte getEncryptFlag() {
+        return encryptFlag;
+    }
+
+    public void setEncryptFlag(byte encryptFlag) {
+        this.encryptFlag = encryptFlag;
+    }
+
+    public int getEncryptKey() {
+        return encryptKey;
+    }
+
+    public void setEncryptKey(int encryptKey) {
+        this.encryptKey = encryptKey;
+    }
+
+
+    public int getCrcCode() {
+        return crcCode;
+    }
+
+    public void setCrcCode(int crcCode) {
+        this.crcCode = crcCode;
+    }
+
+    @Override
+    public String toString() {
+        return "JT809BasePacket{" +
+                "msgLength=" + msgLength +
+                ", msgSn=" + msgSn +
+                ", msgId=" + msgId +
+                ", msgGNSSCenterId=" + msgGNSSCenterId +
+                ", versionFlag=" + Arrays.toString(versionFlag) +
+                ", encryptFlag=" + encryptFlag +
+                ", encryptKey=" + encryptKey +
+                ", crcCode=" + crcCode +
+                '}';
+    }
+}

+ 15 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/packet/JT809HeartbeatResponse.java

@@ -0,0 +1,15 @@
+package jt809.packet;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/22 15:38
+ * @Version 1.0
+ * @Describe: 心跳应答包 数据体为空
+ */
+public class JT809HeartbeatResponse extends JT809BasePacket {
+
+    @Override
+    public byte[] getMsgBodyByteArr() {
+        return new byte[0];
+    }
+}

+ 70 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/packet/JT809LoginPacket.java

@@ -0,0 +1,70 @@
+package jt809.packet;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/22 15:35
+ * @Version 1.0
+ * @Describe: 登录请求包,此中的结尾数据体的消息。
+ */
+public class JT809LoginPacket extends JT809BasePacket {
+    private static Logger log = LoggerFactory.getLogger(JT809LoginPacket.class);
+    /** id 4字节*/
+    private int userId;
+    /** 密码 8字节*/
+    private String password;
+    /** 下级平台IP 32字节*/
+    private String downLinkIp;
+    /** 下级平台端口 2字节*/
+    private short downLinkPort;
+
+    public int getUserId() {
+        return userId;
+    }
+
+    public void setUserId(int userId) {
+        this.userId = userId;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getDownLinkIp() {
+        return downLinkIp;
+    }
+
+    public void setDownLinkIp(String downLinkIp) {
+        this.downLinkIp = downLinkIp;
+    }
+
+    public short getDownLinkPort() {
+        return downLinkPort;
+    }
+
+    public void setDownLinkPort(short downLinkPort) {
+        this.downLinkPort = downLinkPort;
+    }
+
+    @Override
+    public byte[] getMsgBodyByteArr() {
+        return new byte[0];
+    }
+
+    @Override
+    public String toString() {
+        return "JT809LoginPacket{" +
+                "userId='" + userId + '\'' +
+                ", password='" + password + '\'' +
+                ", downLinkIp='" + downLinkIp + '\'' +
+                ", downLinkPort='" + downLinkPort + '\'' +
+                super.toString() +
+                '}';
+    }
+}

+ 61 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/packet/JT809LoginResponsePacket.java

@@ -0,0 +1,61 @@
+package jt809.packet;
+
+import  jt809.common.Const;
+import  jt809.common.util.CommonUtils;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/22 15:37
+ * @Version 1.0
+ * @Describe: 登录应答包
+ */
+public class JT809LoginResponsePacket extends JT809BasePacket {
+
+    private static final int FIXED_LENGTH = 5;
+
+    public JT809LoginResponsePacket() {
+        setMsgLength(getFixedByteLength() + FIXED_LENGTH);
+        setMsgSn(Const.getMsgSN());
+        setMsgId(Const.BusinessDataType.UP_CONNECT_RSP);
+        setMsgGNSSCenterId(Const.UserInfo.MSG_GNSSCENTERID);
+        setVersionFlag(new byte[]{1,0,0});
+        setEncryptFlag(Const.EncryptFlag.NO);
+        setEncryptKey(0);
+    }
+
+    /** 标志 1位*/
+    private byte resul;
+    /** 校验码 4字节*/
+    private int verifyCode;
+
+    public byte getResul() {
+        return resul;
+    }
+
+    public void setResul(byte resul) {
+        this.resul = resul;
+    }
+
+    public int getVerifyCode() {
+        return verifyCode;
+    }
+
+    public void setVerifyCode(int verifyCode) {
+        this.verifyCode = verifyCode;
+    }
+
+    @Override
+    public byte[] getMsgBodyByteArr() {
+        byte[] verifyCodeBytes = CommonUtils.int2bytes(this.verifyCode);
+        return CommonUtils.append(new byte[]{this.resul},verifyCodeBytes);
+    }
+
+    @Override
+    public String toString() {
+        return "JT809LoginResponsePacket{" +
+                "resul=" + resul +
+                ", verifyCode=" + verifyCode +
+                super.toString() +
+                '}';
+    }
+}

+ 201 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/packet/JT809Packet0x1202.java

@@ -0,0 +1,201 @@
+package jt809.packet;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/22 15:41
+ * @Version 1.0
+ * @Describe: 实时上传车辆定位信息包
+ */
+public class JT809Packet0x1202 extends JT809BasePacket {
+    /** 车牌号 21字节*/
+    private String vehicleNo;
+    /** 车辆颜色 1字节*/
+    private byte vehicleColor;
+    /** 子业务类型标识 2字节*/
+    private short dataType;
+    /** 后续数据长度 4字节  后续长度为36,不足补0*/
+    private int dataLength;
+    /** 该字段标识传输的定位信息是否使用国家测绘局批准的地图保密插件进行加密。加密标识:1-已加密,0-未加密。1字节*/
+    private byte excrypt;
+    /** 日月年(dmyy),年的表示是先将年转换成2为十六进制数,如2009标识为0x070xD9.  4字节*/
+    private LocalDate date;
+    /** 时分秒(hms) 3字节*/
+    private LocalTime time;
+    /** 经度,单位为1*10^-6度。 4字节*/
+    private int lon;
+    /** 纬度,单位为1*10^-6度。 4字节*/
+    private int lat;
+    /** 2字节; 速度,指卫星定位车载终端设备上传的行车速度信息,为必填项。单位为千米每小时(km/h)。*/
+    private short vec1;
+    /** 2字节;行驶记录速度,指车辆行驶记录设备上传的行车速度信息,为必填项。单位为千米每小时(km/h)。*/
+    private short vec2;
+    /** 4字节;车辆当前总里程数,值车辆上传的行车里程数。单位单位为千米(km)。*/
+    private int vec3;
+    /** 2字节;方向,0-359,单位为度(。),正北为0,顺时针*/
+    private short direction;
+    /** 2字节;海拔高度,单位为米(m)。*/
+    private short altitude;
+    /** 4字节;车辆状态,二进制表示*/
+    private  int state;
+    /** 4字节;报警状态,二进制表示*/
+    private  int alarm;
+
+    public String getVehicleNo() {
+        return vehicleNo;
+    }
+
+    public void setVehicleNo(String vehicleNo) {
+        this.vehicleNo = vehicleNo;
+    }
+
+    public byte getVehicleColor() {
+        return vehicleColor;
+    }
+
+    public void setVehicleColor(byte vehicleColor) {
+        this.vehicleColor = vehicleColor;
+    }
+
+    public short getDataType() {
+        return dataType;
+    }
+
+    public void setDataType(short dataType) {
+        this.dataType = dataType;
+    }
+
+    public int getDataLength() {
+        return dataLength;
+    }
+
+    public void setDataLength(int dataLength) {
+        this.dataLength = dataLength;
+    }
+
+    public byte getExcrypt() {
+        return excrypt;
+    }
+
+    public void setExcrypt(byte excrypt) {
+        this.excrypt = excrypt;
+    }
+
+    public LocalDate getDate() {
+        return date;
+    }
+
+    public void setDate(LocalDate date) {
+        this.date = date;
+    }
+
+    public LocalTime getTime() {
+        return time;
+    }
+
+    public void setTime(LocalTime time) {
+        this.time = time;
+    }
+
+    public int getLon() {
+        return lon;
+    }
+
+    public void setLon(int lon) {
+        this.lon = lon;
+    }
+
+    public int getLat() {
+        return lat;
+    }
+
+    public void setLat(int lat) {
+        this.lat = lat;
+    }
+
+    public short getVec1() {
+        return vec1;
+    }
+
+    public void setVec1(short vec1) {
+        this.vec1 = vec1;
+    }
+
+    public short getVec2() {
+        return vec2;
+    }
+
+    public void setVec2(short vec2) {
+        this.vec2 = vec2;
+    }
+
+    public int getVec3() {
+        return vec3;
+    }
+
+    public void setVec3(int vec3) {
+        this.vec3 = vec3;
+    }
+
+    public short getDirection() {
+        return direction;
+    }
+
+    public void setDirection(short direction) {
+        this.direction = direction;
+    }
+
+    public short getAltitude() {
+        return altitude;
+    }
+
+    public void setAltitude(short altitude) {
+        this.altitude = altitude;
+    }
+
+    public int getState() {
+        return state;
+    }
+
+    public void setState(int state) {
+        this.state = state;
+    }
+
+    public int getAlarm() {
+        return alarm;
+    }
+
+    public void setAlarm(int alarm) {
+        this.alarm = alarm;
+    }
+
+    @Override
+    public byte[] getMsgBodyByteArr() {
+        return new byte[0];
+    }
+
+    @Override
+    public String toString() {
+        return "JT809Packet0x1202{" +
+                "vehicleNo='" + vehicleNo + '\'' +
+                ", vehicleColor='" + vehicleColor + '\'' +
+                ", dataType=" + dataType +
+                ", dataLength=" + dataLength +
+                ", excrypt=" + excrypt +
+                ", date=" + date +
+                ", time=" + time +
+                ", lon='" + lon + '\'' +
+                ", lat='" + lat + '\'' +
+                ", vec1='" + vec1 + '\'' +
+                ", vec2='" + vec2 + '\'' +
+                ", vec3='" + vec3 + '\'' +
+                ", direction='" + direction + '\'' +
+                ", altitude='" + altitude + '\'' +
+                ", state='" + state + '\'' +
+                ", alarm='" + alarm + '\'' +
+                super.toString() +
+                '}';
+    }
+}

+ 59 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/JT809DecoderAdapter.java

@@ -0,0 +1,59 @@
+package jt809.protocol;
+
+import jt809.common.util.CommonUtils;
+import jt809.common.util.CrcUtil;
+import jt809.common.util.PacketDecoderUtils;
+import jt809.packet.JT809BasePacket;
+import jt809.protocol.decoder.DecoderFactory;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+import static jt809.common.util.CommonUtils.PACKET_CACHE;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/21 20:34
+ * @Version 1.0
+ * @Describe: 解码器
+ */
+public class JT809DecoderAdapter extends ByteToMessageDecoder {
+    private static Logger log = LoggerFactory.getLogger(JT809DecoderAdapter.class);
+    @Override
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+        //判断是否有可读的字节
+        if (in.readableBytes() <= 0) {
+            return;
+        }
+        // 1、进行转义
+        byte[] bytes = PacketDecoderUtils.decoderEscape(in);
+        // 2、校验crc
+        if (!CrcUtil.checkCRC(bytes)){
+            return;
+        }
+        // 3、判断是那种类型的数据,交给具体的解码器类完成。
+        ByteBuf byteBuf = CommonUtils.getByteBuf(bytes);
+        byteBuf.skipBytes(9);
+        // 获取业务标志
+        short msgId = byteBuf.readShort();
+
+        // 交给具体的解码器
+        JT809BasePacket packet = null;
+        try {
+            packet = DecoderFactory.getDecoder(msgId).decoder(bytes);
+        } catch (Exception e) {
+            if (e instanceof NullPointerException) {
+                // log.info("没有可用的解析器,忽略这条信息!此信息不在业务范围内。");
+                // 没有可用的解析器,忽略这条信息!此信息不在业务范围内。
+            } else {
+                log.error("报文解析出错!错误信息:{};报文信息:{};",e.getMessage(),PACKET_CACHE.get(Thread.currentThread().getName()));
+            }
+            return;
+        }
+        out.add(packet);
+    }
+}

+ 34 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/JT809EncodeAdapter.java

@@ -0,0 +1,34 @@
+package jt809.protocol;
+
+import  jt809.common.util.CommonUtils;
+import  jt809.common.util.PacketDecoderUtils;
+import  jt809.common.util.PacketEncoderUtils;
+import  jt809.packet.JT809BasePacket;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 10:13
+ * @Version 1.0
+ * @Describe: 编码适配器
+ */
+public class JT809EncodeAdapter extends MessageToByteEncoder<JT809BasePacket> {
+    private static Logger log = LoggerFactory.getLogger(JT809EncodeAdapter.class);
+
+    @Override
+    protected void encode(ChannelHandlerContext ctx, JT809BasePacket packet, ByteBuf out) throws Exception {
+//        EncoderFactory.getEncoder(packet.getClass().getSimpleName()).encode(ctx,packet,out);
+        byte[] allBody = packet.getAllBody();
+        // 转义
+        byte[] dataBytes = PacketEncoderUtils.encoderEscape(allBody);
+        byte[] bytes1 = CommonUtils.append(new byte[]{JT809BasePacket.HEAD_FLAG}, dataBytes);
+        byte[] bytes = CommonUtils.append(bytes1, new byte[]{JT809BasePacket.END_FLAG});
+        String hexStr = PacketDecoderUtils.bytes2HexStr(bytes);
+        log.info("发出的报文为:{}",hexStr);
+        out.writeBytes(bytes);
+    }
+}

+ 18 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/decoder/Decoder.java

@@ -0,0 +1,18 @@
+package jt809.protocol.decoder;
+
+import  jt809.packet.JT809BasePacket;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/21 21:12
+ * @Version 1.0
+ * @Describe:
+ */
+public interface Decoder {
+    /**
+     *
+     * @param bytes
+     * @return
+     */
+    JT809BasePacket decoder(byte[] bytes) throws Exception;
+}

+ 31 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/decoder/DecoderFactory.java

@@ -0,0 +1,31 @@
+package jt809.protocol.decoder;
+
+
+import  jt809.common.Const;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/21 21:13
+ * @Version 1.0
+ * @Describe: 解码工厂类
+ */
+public class DecoderFactory {
+    private static Map<Short,Decoder> DECODER_FACTORY = new HashMap<>();
+    static {
+        DECODER_FACTORY.put(Const.BusinessDataType.UP_CONNECT_REQ,new LoginDecoder());
+        DECODER_FACTORY.put(Const.BusinessDataType.UP_LINKTEST_REQ,new HeartbeatDecoder());
+        DECODER_FACTORY.put(Const.BusinessDataType.UP_EXG_MSG,new JT809Packet0x1202Decoder());
+    }
+
+    /**
+     *
+     * @param businessDataType 业务数据类型标志
+     * @return 具体的解码器
+     */
+    public static Decoder getDecoder(short businessDataType){
+        return DECODER_FACTORY.get(businessDataType);
+    }
+}

+ 21 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/decoder/HeartbeatDecoder.java

@@ -0,0 +1,21 @@
+package jt809.protocol.decoder;
+
+import  jt809.packet.JT809BasePacket;
+import  jt809.packet.JT809HeartbeatResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 14:53
+ * @Version 1.0
+ * @Describe: 心跳解码器器
+ */
+public class HeartbeatDecoder implements Decoder{
+    private static Logger log = LoggerFactory.getLogger(HeartbeatDecoder.class);
+    @Override
+    public JT809BasePacket decoder(byte[] bytes) {
+        log.info("心跳解码器!");
+        return new JT809HeartbeatResponse();
+    }
+}

+ 88 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/decoder/JT809Packet0x1202Decoder.java

@@ -0,0 +1,88 @@
+package jt809.protocol.decoder;
+
+import  jt809.common.Const;
+import  jt809.common.util.PacketDecoderUtils;
+import  jt809.packet.JT809BasePacket;
+import  jt809.packet.JT809Packet0x1202;
+import io.netty.buffer.ByteBuf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.Charset;
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+import static  jt809.common.util.CommonUtils.PACKET_CACHE;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 15:21
+ * @Version 1.0
+ * @Describe:
+ */
+public class JT809Packet0x1202Decoder implements Decoder {
+    private static Logger log = LoggerFactory.getLogger(JT809Packet0x1202Decoder.class);
+    @Override
+    public JT809BasePacket decoder(byte[] bytes) throws Exception {
+        JT809Packet0x1202 jt809Packet0x1202 = new JT809Packet0x1202();
+        ByteBuf byteBuf = PacketDecoderUtils.baseDecoder(bytes, jt809Packet0x1202);
+        packetDecoder(byteBuf,jt809Packet0x1202);
+        return jt809Packet0x1202;
+    }
+
+    private void packetDecoder(ByteBuf byteBuf, JT809Packet0x1202 packet) throws Exception{
+        ByteBuf msgBodyBuf = null;
+        if (packet.getEncryptFlag() == Const.EncryptFlag.NO) {
+            msgBodyBuf = PacketDecoderUtils.getMsgBodyBuf(byteBuf);
+        } else {
+            log.error("报文已加密!未处理。报文信息:{}",PACKET_CACHE.get(Thread.currentThread().getName()));
+            msgBodyBuf = null;
+            return;
+        }
+        // 车牌号
+        byte [] vehicleNoBytes = new byte[21];
+        msgBodyBuf.readBytes(vehicleNoBytes);
+        packet.setVehicleNo(new String(vehicleNoBytes, Charset.forName("GBK")));
+        // 车辆颜色
+        packet.setVehicleColor(msgBodyBuf.readByte());
+        // 子业务类型标识
+        packet.setDataType(msgBodyBuf.readShort());
+        // 如果不是定位的数据,抛出空指针错误,解码适配器会对空指针错误做处理。
+        if (packet.getDataType() != Const.SubBusinessDataType.UP_EXG_MSG_REAL_LOCATION ) {
+            throw new NullPointerException();
+        }
+        // 后续数据长度
+        packet.setDataLength(msgBodyBuf.readInt());
+        // 经纬度信息是否按国标进行加密
+        packet.setExcrypt(msgBodyBuf.readByte());
+        if (packet.getExcrypt() == Const.EncryptFlag.YES ){
+            log.error("车辆经纬度已加密!报文信息:{}",PACKET_CACHE.get(Thread.currentThread().getName()));
+        }
+        // 跳过时间
+//        msgBodyBuf.skipBytes(7);
+        int day = Byte.toUnsignedInt(msgBodyBuf.readByte());
+        int month = Byte.toUnsignedInt(msgBodyBuf.readByte());
+        packet.setDate(LocalDate.of(msgBodyBuf.readShort(),month,day));
+        packet.setTime(LocalTime.of(Byte.toUnsignedInt(msgBodyBuf.readByte()),Byte.toUnsignedInt(msgBodyBuf.readByte()),Byte.toUnsignedInt(msgBodyBuf.readByte())));
+        // 经纬度
+        packet.setLon(msgBodyBuf.readInt());
+        packet.setLat(msgBodyBuf.readInt());
+        // 速度
+        packet.setVec1(msgBodyBuf.readShort());
+        // 行驶记录速度
+        packet.setVec2(msgBodyBuf.readShort());
+        // 车辆当前总里程数
+        packet.setVec3(msgBodyBuf.readInt());
+        // 方向
+        packet.setDirection(msgBodyBuf.readShort());
+        // 海拔
+        packet.setAltitude(msgBodyBuf.readShort());
+        // 车辆状态
+        packet.setState(msgBodyBuf.readInt());
+        // 报警状态
+        packet.setAlarm(msgBodyBuf.readInt());
+    }
+
+
+
+}

+ 61 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/decoder/LoginDecoder.java

@@ -0,0 +1,61 @@
+package jt809.protocol.decoder;
+
+import  jt809.common.Const;
+import  jt809.common.util.PacketDecoderUtils;
+import  jt809.packet.JT809BasePacket;
+import  jt809.packet.JT809LoginPacket;
+import io.netty.buffer.ByteBuf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.Charset;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/21 21:02
+ * @Version 1.0
+ * @Describe: 登录解码器
+ */
+public class LoginDecoder implements Decoder {
+    private static Logger log = LoggerFactory.getLogger(LoginDecoder.class);
+
+    @Override
+    public JT809BasePacket decoder(byte[] bytes) throws Exception {
+        JT809LoginPacket loginPacket = new JT809LoginPacket();
+        ByteBuf byteBuf = PacketDecoderUtils.baseDecoder(bytes, loginPacket);
+        loginPacketDecoder(byteBuf,loginPacket);
+        return loginPacket;
+    }
+
+    /**
+     *
+     * @param byteBuf
+     * @param loginPacket
+     */
+    private void loginPacketDecoder(ByteBuf byteBuf,JT809LoginPacket loginPacket) throws Exception{
+        ByteBuf msgBodyBuf = null;
+        if (loginPacket.getEncryptFlag() == Const.EncryptFlag.NO) {
+            log.info("报文未加密!继续处理。");
+            msgBodyBuf = PacketDecoderUtils.getMsgBodyBuf(byteBuf);
+        } else {
+            // TODO: 后续处理
+            log.info("报文已加密!未处理。");
+            msgBodyBuf = null;
+            return;
+        }
+
+        loginPacket.setUserId(msgBodyBuf.readInt());
+
+        byte[] passwordBytes = new byte[8];
+        msgBodyBuf.readBytes(passwordBytes);
+        loginPacket.setPassword(new String(passwordBytes, Charset.forName("GBK")));
+
+        // TODO ip和端口号的解析待确定
+        byte[] downLinkIpBytes = new byte[32];
+        msgBodyBuf.readBytes(downLinkIpBytes);
+        loginPacket.setDownLinkIp(new String(downLinkIpBytes));
+
+        loginPacket.setDownLinkPort(msgBodyBuf.readShort());
+
+    }
+}

+ 16 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/encode/Encoder.java

@@ -0,0 +1,16 @@
+package jt809.protocol.encode;
+
+import  jt809.packet.JT809BasePacket;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 10:56
+ * @Version 1.0
+ * @Describe:
+ */
+public interface Encoder {
+    /** 编码*/
+    void encode(ChannelHandlerContext ctx, JT809BasePacket packet, ByteBuf out);
+}

+ 21 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/encode/EncoderFactory.java

@@ -0,0 +1,21 @@
+package jt809.protocol.encode;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 10:58
+ * @Version 1.0
+ * @Describe:
+ */
+public class EncoderFactory {
+    private static Map<String,Encoder> ENCODER_FACTORY = new HashMap();
+    static {
+        ENCODER_FACTORY.put("JT809LoginResponsePacket",new LoginResponseEncoder());
+    }
+
+    public static Encoder getEncoder(String packetClassName){
+        return ENCODER_FACTORY.get(packetClassName);
+    }
+}

+ 25 - 0
ruoyi-modules/ruoyi-khy/src/main/java/jt809/protocol/encode/LoginResponseEncoder.java

@@ -0,0 +1,25 @@
+package jt809.protocol.encode;
+
+import  jt809.packet.JT809BasePacket;
+import  jt809.packet.JT809LoginResponsePacket;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Author: Xiuming Lee
+ * @Date: 2019/9/23 10:57
+ * @Version 1.0
+ * @Describe:
+ */
+public class LoginResponseEncoder implements Encoder {
+    private static Logger log = LoggerFactory.getLogger(LoginResponseEncoder.class);
+    @Override
+    public void encode(ChannelHandlerContext ctx, JT809BasePacket packet, ByteBuf out) {
+        log.info("登录响应开始编码!");
+        JT809LoginResponsePacket responsePacket = (JT809LoginResponsePacket) packet;
+
+    }
+
+}

+ 107 - 98
ruoyi-modules/ruoyi-khy/src/main/java/org/dromara/util/CamelDataUtil.java

@@ -67,117 +67,126 @@ public class CamelDataUtil {
         exchange.getMessage().setHeader("CamelPahoOverrideTopic","/dev/"+o.get("devtype")+"/"+o.get("devcode"));
         exchange.getMessage().setHeader("CamelPahoOverrideTopic","/dev/"+o.get("devtype")+"/"+o.get("devcode"));
         exchange.getMessage().setBody(body.getBytes("gb2312"));
         exchange.getMessage().setBody(body.getBytes("gb2312"));
     }
     }
-    public void ReciveData(String body, Exchange exchange){
-        if(exchange.getMessage().getHeaders().get("CamelMqttTopic").toString().contains("gps")){
-            String devicecode = exchange.getMessage().getHeaders().get("CamelMqttTopic").toString().split("/")[3];
-            GpsData gpsData = new GpsData();
-            gpsData.setDevicecode(devicecode);
-            gpsData.setLat(JSONUtil.parseObj(body).getDouble("LAT"));
-            gpsData.setLng(JSONUtil.parseObj(body).getDouble("LNG"));
-            gpsData.setAlt(JSONUtil.parseObj(body).getDouble("ALT"));
-            gpsData.setSpeed(JSONUtil.parseObj(body).getDouble("SPD"));
-            log.info("body:{}",JSONUtil.toJsonStr(gpsData));
-            gpsDataService.insertData(gpsData);
-
-            //缓存最新数据到redis
-            RedisUtils.setCacheObject(StringUtils.format("gps_{}", devicecode), gpsData);
-
-
-            threadPoolExecutor.execute(new Runnable() {
-                @Override
-                public void run() {
-
-                    TblCarInfoBo carInfoBo = new TblCarInfoBo();
-                    carInfoBo.setDeviceInfos(devicecode);
-                    List<TblCarInfoVo> carInfoVos =  tblCarInfoService.queryList(carInfoBo);
-                    if(carInfoVos.size()>0){
-                        TblCarInfoVo carInfoVo = carInfoVos.get(0);
-                        if(StringUtils.isNotEmpty(carInfoVo.getCarNum())){
-                            //查询车辆信息
-                            TblCarInfoBo carInfoBo1 = new TblCarInfoBo();
-                            carInfoBo1.setCarNum(carInfoVo.getCarNum());
-                            List<TblCarInfoVo> carInfoVos1 =  tblCarInfoService.queryList(carInfoBo1);
-                            if(carInfoVos1.size()>0){
-                                // 查询车辆信息
-                                TblCarInfoVo carInfoVo1 = carInfoVos1.get(0);
-                                RedisUtils.setCacheObject(StringUtils.format("gps_{}", carInfoVo1.getId()), gpsData);
-                                TblCarScheduleBo tblCarScheduleBo = new TblCarScheduleBo();
-                                tblCarScheduleBo.setCarNum(carInfoVo1.getCarNum());
-                                Map<String, Object> map = Map.of("scheduleDate", DateUtils.parseDate(DateUtils.getDate()+" 00:00:00") );
-                                tblCarScheduleBo.setParams(map);
-                                List<TblCarScheduleVo>  list =  tblCarScheduleService.queryList(tblCarScheduleBo);
-                                if(list.size()>0){
-                                    long t0 = 10;
-                                    TblCarScheduleVo scheduleVo = null;
-                                    for (TblCarScheduleVo vo : list) {
-                                        if(vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime()<0){
-                                            if(t0>0){
-                                                t0 = vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime();
-                                                scheduleVo = vo;
-                                            }
-                                            if(t0<vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime()){
-                                                t0 = vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime();
-                                                scheduleVo = vo;
-                                            }
+
+    public void processGpsData(GpsData gpsData){
+
+        log.info("body:{}",JSONUtil.toJsonStr(gpsData));
+        gpsDataService.insertData(gpsData);
+
+        //缓存最新数据到redis
+        RedisUtils.setCacheObject(StringUtils.format("gps_{}", gpsData.getDevicecode()), gpsData);
+
+
+        threadPoolExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+
+                TblCarInfoBo carInfoBo = new TblCarInfoBo();
+                carInfoBo.setDeviceInfos(gpsData.getDevicecode());
+                List<TblCarInfoVo> carInfoVos =  tblCarInfoService.queryList(carInfoBo);
+                if(carInfoVos.size()>0){
+                    TblCarInfoVo carInfoVo = carInfoVos.get(0);
+                    if(StringUtils.isNotEmpty(carInfoVo.getCarNum())){
+                        //查询车辆信息
+                        TblCarInfoBo carInfoBo1 = new TblCarInfoBo();
+                        carInfoBo1.setCarNum(carInfoVo.getCarNum());
+                        List<TblCarInfoVo> carInfoVos1 =  tblCarInfoService.queryList(carInfoBo1);
+                        if(carInfoVos1.size()>0){
+                            // 查询车辆信息
+                            TblCarInfoVo carInfoVo1 = carInfoVos1.get(0);
+                            RedisUtils.setCacheObject(StringUtils.format("gps_{}", carInfoVo1.getId()), gpsData);
+                            TblCarScheduleBo tblCarScheduleBo = new TblCarScheduleBo();
+                            tblCarScheduleBo.setCarNum(carInfoVo1.getCarNum());
+                            Map<String, Object> map = Map.of("scheduleDate", DateUtils.parseDate(DateUtils.getDate()+" 00:00:00") );
+                            tblCarScheduleBo.setParams(map);
+                            List<TblCarScheduleVo>  list =  tblCarScheduleService.queryList(tblCarScheduleBo);
+                            if(list.size()>0){
+                                long t0 = 10;
+                                TblCarScheduleVo scheduleVo = null;
+                                for (TblCarScheduleVo vo : list) {
+                                    if(vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime()<0){
+                                        if(t0>0){
+                                            t0 = vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime();
+                                            scheduleVo = vo;
+                                        }
+                                        if(t0<vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime()){
+                                            t0 = vo.getScheduleDate().getTime()-DateUtils.getNowDate().getTime();
+                                            scheduleVo = vo;
                                         }
                                         }
                                     }
                                     }
-                                    //  查询路线信息
-                                    if(scheduleVo!=null){
-                                        try{
-                                            JSONArray lines = JSONUtil.parseObj(scheduleVo.getPathInfo()).getJSONArray("pathLine");
-                                            for (JSONObject jsonObject : lines.toList(JSONObject.class)) {
-                                               double distance =  DistanceCalculator.calculateDistance(jsonObject.getStr("location"),StringUtils.format("{},{}", gpsData.getLng(),gpsData.getLat()));
-                                               String configdistance = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.car.distance");
-                                               if(Double.parseDouble(configdistance)>distance){
-                                                   //靠近站点
-                                                   String stationName =  RedisUtils.getCacheObject(StringUtils.format("car_{}_{}_tip",carInfoVo1.getCarNum(),DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS) ));
-                                                   if(stationName!=null && stationName.contains(jsonObject.getStr("stationName"))){
-                                                       //提报过的不再提报
-
-                                                   }else{
-                                                       stationName=stationName==null?"":stationName;
-                                                       RedisUtils.setCacheObject(StringUtils.format("car_{}_{}_tip",carInfoVo1.getCarNum(), DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS) ), stationName+","+jsonObject.getStr("stationName"),Duration.ofSeconds(3600*24));
-                                                       //开始播报
-                                                       //获取站点设备信息
-                                                       SysDictDataBo sysDictData =  new SysDictDataBo();
-                                                       sysDictData.setDictLabel(jsonObject.getStr("stationName"));
-                                                       sysDictData.setDictType("tbl_sites");
-                                                       List<SysDictDataVo> stations = sysDictDataService.selectDictDataList(sysDictData);
-                                                       if(stations.size()>0){
-                                                           //满足条件播报(led or 广播)
-                                                           String message = StringUtils.format("{}即将到达,请准备接车,请等待。。。", carInfoVo1.getCarNum());
-                                                           List<JSONObject> devicedatas = JSONUtil.parseArray( stations.get(0).getRemark()).toList(JSONObject.class);
-                                                           for (JSONObject i :devicedatas) {
-                                                               if(i.getStr("type").equals("led")){
-                                                                   MessageUtils.sendLed(i.getStr("code"), message);
-                                                               }
-                                                               if(i.getStr("type").equals("tts")){
-                                                                   MessageUtils.sendTTs(i.getStr("code"), message);
-                                                               }
-                                                           }
-                                                       }
-
-
-                                                   }
-
-                                               }
+                                }
+                                //  查询路线信息
+                                if(scheduleVo!=null){
+                                    try{
+                                        JSONArray lines = JSONUtil.parseObj(scheduleVo.getPathInfo()).getJSONArray("pathLine");
+                                        for (JSONObject jsonObject : lines.toList(JSONObject.class)) {
+                                            double distance =  DistanceCalculator.calculateDistance(jsonObject.getStr("location"),StringUtils.format("{},{}", gpsData.getLng(),gpsData.getLat()));
+                                            String configdistance = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.car.distance");
+                                            if(Double.parseDouble(configdistance)>distance){
+                                                //靠近站点
+                                                String stationName =  RedisUtils.getCacheObject(StringUtils.format("car_{}_{}_tip",carInfoVo1.getCarNum(),DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS) ));
+                                                if(stationName!=null && stationName.contains(jsonObject.getStr("stationName"))){
+                                                    //提报过的不再提报
+
+                                                }else{
+                                                    stationName=stationName==null?"":stationName;
+                                                    RedisUtils.setCacheObject(StringUtils.format("car_{}_{}_tip",carInfoVo1.getCarNum(), DateFormatUtils.format( scheduleVo.getScheduleDate(),DateUtils.YYYYMMDDHHMMSS) ), stationName+","+jsonObject.getStr("stationName"),Duration.ofSeconds(3600*24));
+                                                    //开始播报
+                                                    //获取站点设备信息
+                                                    SysDictDataBo sysDictData =  new SysDictDataBo();
+                                                    sysDictData.setDictLabel(jsonObject.getStr("stationName"));
+                                                    sysDictData.setDictType("tbl_sites");
+                                                    List<SysDictDataVo> stations = sysDictDataService.selectDictDataList(sysDictData);
+                                                    if(stations.size()>0){
+                                                        //满足条件播报(led or 广播)
+                                                        String message = StringUtils.format("{}即将到达,请准备接车,请等待。。。", carInfoVo1.getCarNum());
+                                                        List<JSONObject> devicedatas = JSONUtil.parseArray( stations.get(0).getRemark()).toList(JSONObject.class);
+                                                        for (JSONObject i :devicedatas) {
+                                                            if(i.getStr("type").equals("led")){
+                                                                MessageUtils.sendLed(i.getStr("code"), message);
+                                                            }
+                                                            if(i.getStr("type").equals("tts")){
+                                                                MessageUtils.sendTTs(i.getStr("code"), message);
+                                                            }
+                                                        }
+                                                    }
+
+
+                                                }
+
                                             }
                                             }
+                                        }
 
 
 
 
-                                        }catch (Exception e){
-                                            log.error("---------------------");
-                                            log.error(e.getMessage());
-                                            log.error("---------------------");
-                                        }
+                                    }catch (Exception e){
+                                        log.error("---------------------");
+                                        log.error(e.getMessage());
+                                        log.error("---------------------");
                                     }
                                     }
                                 }
                                 }
                             }
                             }
                         }
                         }
                     }
                     }
                 }
                 }
-            });
+            }
+        });
+
+    }
+    public void ReciveData(String body, Exchange exchange){
+        if(exchange.getMessage().getHeaders().get("CamelMqttTopic").toString().contains("gps")){
+            String devicecode = exchange.getMessage().getHeaders().get("CamelMqttTopic").toString().split("/")[3];
+            GpsData gpsData = new GpsData();
+            gpsData.setDevicecode(devicecode);
+            gpsData.setLat(JSONUtil.parseObj(body).getDouble("LAT"));
+            gpsData.setLng(JSONUtil.parseObj(body).getDouble("LNG"));
+            gpsData.setAlt(JSONUtil.parseObj(body).getDouble("ALT"));
+            gpsData.setSpeed(JSONUtil.parseObj(body).getDouble("SPD"));
+            if(JSONUtil.parseObj(body).getDouble("SPD")>0){
+                processGpsData(gpsData);
+            }
         }
         }
 
 
+
         log.info("body:{}",body);
         log.info("body:{}",body);
     }
     }
 }
 }