1.Jar包下载

jt704-sdk-1.0.0.jar 下载地址
如果需要Jar包开发源码,请与商务申请


2.集成开发说明

2.1.集成开发语言及框架说明

jt704-sdk-1.0.0.jar是基于Java语言,SpringBoot2.x框架,使用到了netty,fastjson,lombok,commons-lang3

BaseEnum:基础枚举
Constant:自定义常量
AlarmTypeEnum:终端报警类型枚举
LocationData:定位实体类
Result:结果实体类
CommonUtil:公共方法类
NumberUtil:数字操作工具类
PacketUtil:用来处理预处理数据以及分包方法封装类
ParserUtil:解析方法工具类
DataParser:解析主方法

2.2.集成说明

将jt704-sdk-1.0.0.jar引入到自己的网关程序中,引入方法如下:
在pom.xml引入jar包

        <dependency>
            <groupId>com.jointech.sdk</groupId>
            <artifactId>jt704-sdk</artifactId>
            <version>1.0.0</version>
        </dependency>

调用jt704-sdk-1.0.0.jar,DataParser类中receiveData()方法
receiveData()方法是重载方法

    /**
     * 解析Hex字符串原始数据
     * @param strData 16进制字符串
     * @return
     */
    public static Object receiveData(String strData) {
        int length=strData.length()%2>0?strData.length()/2+1:strData.length()/2;
        ByteBuf msgBodyBuf = Unpooled.buffer(length);
        msgBodyBuf.writeBytes(CommonUtil.hexStr2Byte(strData));
        return receiveData(msgBodyBuf);
    }

    /**
     * 解析byte[]原始数据
     * @param bytes
     * @return
     */
    private static Object receiveData(byte[] bytes)
    {
        ByteBuf msgBodyBuf =Unpooled.buffer(bytes.length);
        msgBodyBuf.writeBytes(bytes);
        return receiveData(msgBodyBuf);
    }

    /**
     * 解析ByteBuf原始数据
     * @param in
     * @return
     */
    private static Object receiveData(ByteBuf in)
    {
        Object decoded = null;
        in.markReaderIndex();
        int header = in.readByte();
        if (header == Constant.TEXT_MSG_HEADER) {
            in.resetReaderIndex();
            decoded = ParserUtil.decodeTextMessage(in);
        } else if (header == Constant.BINARY_MSG_HEADER) {
            in.resetReaderIndex();
            decoded = ParserUtil.decodeBinaryMessage(in);
        } else {
            return null;
        }
        return JSONArray.toJSON(decoded).toString();
    }

2.3.核心代码

ParserUtil:解析方法工具类

package com.jointech.sdk.jt704.utils;

import com.jointech.sdk.jt704.constants.Constant;
import com.jointech.sdk.jt704.model.AlarmTypeEnum;
import com.jointech.sdk.jt704.model.LocationData;
import com.jointech.sdk.jt704.model.Result;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import org.apache.commons.lang3.StringUtils;

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>Description: 解析方法工具类</p>
 * @author HyoJung
 * @date 20210526
 */
public class ParserUtil {
    /**
     * 定位数据解析0x0200
     * @param in
     * @return
     */
    public static Result decodeBinaryMessage(ByteBuf in) {
        //对数据进行反转移处理
        ByteBuf msg = (ByteBuf) PacketUtil.decodePacket(in);
        //消息长度
        int msgLen = msg.readableBytes();
        //包头
        msg.readByte();
        //消息ID
        int msgId = msg.readUnsignedShort();
        //消息体属性
        int msgBodyAttr = msg.readUnsignedShort();
        //消息体长度
        int msgBodyLen = msgBodyAttr & 0b00000011_11111111;
        //是否分包
        boolean multiPacket = (msgBodyAttr & 0b00100000_00000000) > 0;
        //去除消息体的基础长度
        int baseLen = Constant.BINARY_MSG_BASE_LENGTH;

        //根据消息体长度和是否分包得出后面的包长
        int ensureLen = multiPacket ? baseLen + msgBodyLen + 4 : baseLen + msgBodyLen;
        if (msgLen != ensureLen) {
            return null;
        }
        //终端号数组
        byte[] terminalNumArr = new byte[6];
        msg.readBytes(terminalNumArr);
        //终端号(去除前面的0)
        String terminalNumber = StringUtils.stripStart(ByteBufUtil.hexDump(terminalNumArr), "0");
        //消息流水号
        int msgFlowId = msg.readUnsignedShort();
        //消息总包数
        int packetTotalCount = 0;
        //包序号
        int packetOrder = 0;
        //分包
        if (multiPacket) {
            packetTotalCount = msg.readShort();
            packetOrder = msg.readShort();
        }
        //消息体
        byte[] msgBodyArr = new byte[msgBodyLen];
        msg.readBytes(msgBodyArr);
        if(msgId==0x0200) {
            //解析消息体
            LocationData locationData=parseLocationBody(Unpooled.wrappedBuffer(msgBodyArr));
            locationData.setIndex(msgFlowId);
            locationData.setDataLength(msgBodyLen);
            //校验码
            int checkCode = msg.readUnsignedByte();
            //包尾
            msg.readByte();
            //获取消息回复内容
            String replyMsg= PacketUtil.replyBinaryMessage(terminalNumArr,msgFlowId);
            //定义定位数据实体类
            Result model = new Result();
            model.setDeviceID(terminalNumber);
            model.setMsgType("Location");
            model.setDataBody(locationData);
            model.setReplyMsg(replyMsg);
            return model;
        }else {
            //定义定位数据实体类
            Result model = new Result();
            model.setDeviceID(terminalNumber);
            model.setMsgType("heartbeat");
            model.setDataBody(null);
            return model;
        }
    }

    /**
     * 解析指令数据
     * @param in 原始数据
     * @return
     */
    public static Result decodeTextMessage(ByteBuf in) {

        //读指针设置到消息头
        in.markReaderIndex();
        //查找消息尾,如果未找到则继续等待下一包
        int tailIndex = in.bytesBefore(Constant.TEXT_MSG_TAIL);
        if (tailIndex < 0) {
            in.resetReaderIndex();
            return null;
        }
        //定义定位数据实体类
        Result model = new Result();
        //包头(
        in.readByte();
        //字段列表
        List<String> itemList = new ArrayList<String>();
        while (in.readableBytes() > 0) {
            //查询逗号的下标截取数据
            int index = in.bytesBefore(Constant.TEXT_MSG_SPLITER);
            int itemLen = index > 0 ? index : in.readableBytes() - 1;
            byte[] byteArr = new byte[itemLen];
            in.readBytes(byteArr);
            in.readByte();
            itemList.add(new String(byteArr));
        }
        String msgType = "";
        if (itemList.size() >= 5) {
            msgType = itemList.get(3) + itemList.get(4);
        }
        Object dataBody=null;
        if(itemList.size()>0){
            dataBody="(";
            for(String item :itemList) {
                dataBody+=item+",";
            }
            dataBody=CommonUtil.trimEnd(dataBody.toString(),",");
            dataBody += ")";
        }
        String replyMsg="";
        if(msgType.equals("BASE2")&&itemList.get(5).toUpperCase().equals("TIME")) {
            replyMsg=PacketUtil.replyBASE2Message(itemList);
        }
        model.setDeviceID(itemList.get(0));
        model.setMsgType(msgType);
        model.setDataBody(dataBody);
        model.setReplyMsg(replyMsg);
        return model;
    }

    /**
     * 解析定位消息体
     * @param msgBodyBuf
     * @return
     */
    private static LocationData parseLocationBody(ByteBuf msgBodyBuf){
        //报警标志
        long alarmFlag = msgBodyBuf.readUnsignedInt();
        //状态
        long status = msgBodyBuf.readUnsignedInt();
        //纬度
        double lat = NumberUtil.multiply(msgBodyBuf.readUnsignedInt(), NumberUtil.COORDINATE_PRECISION);
        //经度
        double lon = NumberUtil.multiply(msgBodyBuf.readUnsignedInt(), NumberUtil.COORDINATE_PRECISION);
        //海拔高度,单位为米
        int altitude = msgBodyBuf.readShort();
        //速度
        double speed = NumberUtil.multiply(msgBodyBuf.readUnsignedShort(), NumberUtil.ONE_PRECISION);
        //方向
        int direction = msgBodyBuf.readShort();
        //定位时间
        byte[] timeArr = new byte[6];
        msgBodyBuf.readBytes(timeArr);
        String bcdTimeStr = ByteBufUtil.hexDump(timeArr);
        ZonedDateTime gpsZonedDateTime = CommonUtil.parseBcdTime(bcdTimeStr);
        //根据状态位的值判断是否南纬和西经
        if (NumberUtil.getBitValue(status, 2) == 1) {
            lat = -lat;
        }
        if (NumberUtil.getBitValue(status, 3) == 1) {
            lon = -lon;
        }
        //定位状态
        int locationType=NumberUtil.getBitValue(status, 18);
        if(locationType==0)
        {
            locationType = NumberUtil.getBitValue(status, 1);
        }
        if(locationType==0)
        {
            locationType = NumberUtil.getBitValue(status, 6) > 0 ? 2 : 0;
        }
        //报警状态
        int alarm=-1;
        if(NumberUtil.getBitValue(alarmFlag, 16) == 1)
        {
            alarm=Integer.parseInt(AlarmTypeEnum.ALARM_1.getValue());
        }
        //后盖状态
        int backCover=NumberUtil.getBitValue(status, 7);
        //唤醒源
        long awaken = (status>>24)&0b00001111;

        LocationData locationData=new LocationData();
        locationData.setGpsTime(gpsZonedDateTime.toString());
        locationData.setLatitude(lat);
        locationData.setLongitude(lon);
        locationData.setLocationType(locationType);
        locationData.setSpeed((int)speed);
        locationData.setDirection(direction);
        locationData.setAlarm(alarm);
        locationData.setBackCover(backCover);
        locationData.setAwaken((int)awaken);
        //处理附加信息
        if (msgBodyBuf.readableBytes() > 0) {
            parseExtraInfo(msgBodyBuf, locationData);
        }
        return locationData;
    }

    /**
     * 解析附加信息
     *
     * @param msgBody
     * @param location
     */
    private static void parseExtraInfo(ByteBuf msgBody, LocationData location) {
        ByteBuf extraInfoBuf = null;
        while (msgBody.readableBytes() > 1) {
            int extraInfoId = msgBody.readUnsignedByte();
            int extraInfoLen = msgBody.readUnsignedByte();
            if (msgBody.readableBytes() < extraInfoLen) {
                break;
            }
            extraInfoBuf = msgBody.readSlice(extraInfoLen);
            switch (extraInfoId) {
                case 0x0F:
                    //解析温度数据
                    double temperature = -1000.0;
                    temperature = parseTemperature(extraInfoBuf.readShort());
                    location.setTemperature((int)temperature);
                    break;
                //无线通信网络信号强度
                case 0x30:
                    int fCellSignal=extraInfoBuf.readByte();
                    location.setGSMSignal(fCellSignal);
                    break;
                //卫星数
                case 0x31:
                    int fGPSSignal=extraInfoBuf.readByte();
                    location.setGpsSignal(fGPSSignal);
                    break;
                //电池电量百分比
                case 0xD4:
                    int fBattery=extraInfoBuf.readUnsignedByte();
                    location.setBattery(fBattery);
                    break;
                //电池电压
                case 0xD5:
                    int fVoltage=extraInfoBuf.readUnsignedShort();
                    location.setVoltage(fVoltage*0.01);
                    break;
                case 0xFD:
                    //小区码信息
                    int mcc=extraInfoBuf.readUnsignedShort();
                    location.setMCC(mcc);
                    int mnc=extraInfoBuf.readUnsignedByte();
                    location.setMNC(mnc);
                    long cellId=extraInfoBuf.readUnsignedInt();
                    location.setCELLID((int)cellId);
                    int lac=extraInfoBuf.readUnsignedShort();
                    location.setLAC(lac);
                    break;
                case 0xFE:
                    long mileage = extraInfoBuf.readUnsignedInt();
                    location.setMileage(mileage);
                    break;
                default:
                    ByteBufUtil.hexDump(extraInfoBuf);
                    break;
            }
        }
    }

    /**
     * 温度解析
     * @param temperatureInt
     * @return
     */
    private static double parseTemperature(int temperatureInt) {
        if (temperatureInt == 0xFFFF) {
            return -1000;
        }
        double temperature = ((short) (temperatureInt << 4) >> 4) * 0.1;
        if ((temperatureInt >> 12) > 0) {
            temperature = -temperature;
        }
        return temperature;
    }
}

PacketUtil:用来处理预处理数据以及恢复方法封装类

package com.jointech.sdk.jt704.utils;

import com.jointech.sdk.jt704.constants.Constant;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.ReferenceCountUtil;

import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Random;

/**
 * 解析包预处理(进行反转义)
 * @author HyoJung
 */
public class PacketUtil {
    /**
     * 解析消息包
     *
     * @param in
     * @return
     */
    public static Object decodePacket(ByteBuf in) {
        //可读长度不能小于基本长度
        if (in.readableBytes() < Constant.BINARY_MSG_BASE_LENGTH) {
            return null;
        }

        //防止非法码流攻击,数据太大为异常数据
        if (in.readableBytes() > Constant.BINARY_MSG_MAX_LENGTH) {
            in.skipBytes(in.readableBytes());
            return null;
        }

        //查找消息尾,如果未找到则继续等待下一包
        in.readByte();
        int tailIndex = in.bytesBefore(Constant.BINARY_MSG_HEADER);
        if (tailIndex < 0) {
            in.resetReaderIndex();
            return null;
        }

        int bodyLen = tailIndex;
        //创建ByteBuf存放反转义后的数据
        ByteBuf frame = ByteBufAllocator.DEFAULT.heapBuffer(bodyLen + 2);
        frame.writeByte(Constant.BINARY_MSG_HEADER);
        //消息头尾之间的数据进行反转义
        unescape(in, frame, bodyLen);
        in.readByte();
        frame.writeByte(Constant.BINARY_MSG_HEADER);

        //反转义后的长度不能小于基本长度
        if (frame.readableBytes() < Constant.BINARY_MSG_BASE_LENGTH) {
            ReferenceCountUtil.release(frame);
            return null;
        }
        return frame;
    }

    /**
     * 消息头、消息体、校验码中0x7D 0x02反转义为0x7E,0x7D 0x01反转义为0x7D
     *
     * @param in
     * @param frame
     * @param bodyLen
     */
    public static void unescape(ByteBuf in, ByteBuf frame, int bodyLen) {
        int i = 0;
        while (i < bodyLen) {
            int b = in.readUnsignedByte();
            if (b == 0x7D) {
                int nextByte = in.readUnsignedByte();
                if (nextByte == 0x01) {
                    frame.writeByte(0x7D);
                } else if (nextByte == 0x02) {
                    frame.writeByte(0x7E);
                } else {
                    //异常数据
                    frame.writeByte(b);
                    frame.writeByte(nextByte);
                }
                i += 2;
            } else {
                frame.writeByte(b);
                i++;
            }
        }
    }

    /**
     * 消息头、消息体、校验码中0x7E转义为0x7D 0x02,0x7D转义为0x7D 0x01
     *
     * @param out
     * @param bodyBuf
     */
    public static void escape(ByteBuf out, ByteBuf bodyBuf) {
        while (bodyBuf.readableBytes() > 0) {
            int b = bodyBuf.readUnsignedByte();
            if (b == 0x7E) {
                out.writeShort(0x7D02);
            } else if (b == 0x7D) {
                out.writeShort(0x7D01);
            } else {
                out.writeByte(b);
            }
        }
    }

    /**
     * 回复内容
     * @param terminalNumArr
     * @param msgFlowId
     * @return
     */
    public static String replyBinaryMessage(byte[] terminalNumArr,int msgFlowId) {
        //去除包头包尾的长度
        int contentLen = Constant.BINARY_MSG_BASE_LENGTH + 4;
        ByteBuf bodyBuf = ByteBufAllocator.DEFAULT.heapBuffer(contentLen-2);
        ByteBuf replyBuf = ByteBufAllocator.DEFAULT.heapBuffer(25);
        try {
            //消息ID
            bodyBuf.writeShort(0x8001);
            //数据长度
            bodyBuf.writeShort(0x0005);
            //终端ID
            bodyBuf.writeBytes(terminalNumArr);
            Random random = new Random();
            //生成1-65534内的随机数
            int index = random.nextInt() * (65534 - 1 + 1) + 1;
            //当前消息流水号
            bodyBuf.writeShort(index);
            //回复消息流水号
            bodyBuf.writeShort(msgFlowId);
            //应答的消息ID
            bodyBuf.writeShort(0x0200);
            //应答结果
            bodyBuf.writeByte(0x00);
            //校验码
            int checkCode = CommonUtil.xor(bodyBuf);
            bodyBuf.writeByte(checkCode);
            //包头
            replyBuf.writeByte(Constant.BINARY_MSG_HEADER);
            //读指针重置到起始位置
            bodyBuf.readerIndex(0);
            //转义
            PacketUtil.escape(replyBuf, bodyBuf);
            //包尾
            replyBuf.writeByte(Constant.BINARY_MSG_HEADER);
            return ByteBufUtil.hexDump(replyBuf);
        } catch (Exception e) {
            ReferenceCountUtil.release(replyBuf);
            return "";
        }
    }

    /**
     * 授时指令回复
     * @param itemList
     */
    public static String replyBASE2Message(List<String> itemList) {
        try {
            //设置日期格式
            ZonedDateTime currentDateTime = ZonedDateTime.now(ZoneOffset.UTC);
            String strBase2Reply = String.format("(%s,%s,%s,%s,%s,%s)", itemList.get(0), itemList.get(1)
                    , itemList.get(2), itemList.get(3), itemList.get(4), DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(currentDateTime));
            return strBase2Reply;
        }catch (Exception e) {
            return "";
        }
    }
}

NumberUtil:数字操作工具类

package com.jointech.sdk.jt704.utils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 数字工具类
 * @author HyoJung
 * @date 20210526
 */
public class NumberUtil {
    /**
     * 坐标精度
     */
    public static final BigDecimal COORDINATE_PRECISION = new BigDecimal("0.000001");

    /**
     * 坐标因数
     */
    public static final BigDecimal COORDINATE_FACTOR = new BigDecimal("1000000");

    /**
     * 小数点一位精度
     */
    public static final BigDecimal ONE_PRECISION = new BigDecimal("0.1");

    private NumberUtil() {
    }

    /**
     * 格式化消息ID(转成0xXXXX)
     *
     * @param msgId 消息ID
     * @return 格式化字符串
     */
    public static String formatMessageId(int msgId) {
        return String.format("0x%04x", msgId);
    }

    /**
     * 格式化短数字
     *
     * @param num 数字
     * @return 格式化字符串
     */
    public static String formatShortNum(int num) {
        return String.format("0x%02x", num);
    }

    /**
     * 转4位的十六进制字符串
     *
     * @param num 数字
     * @return 格式化字符串
     */
    public static String hexStr(int num) {
        return String.format("%04x", num).toUpperCase();
    }

    /**
     * 解析short类型的值,获取值为1的位数
     *
     * @param number
     * @return
     */
    public static List<Integer> parseShortBits(int number) {
        List<Integer> bits = new ArrayList<>();
        for (int i = 0; i < 16; i++) {
            if (getBitValue(number, i) == 1) {
                bits.add(i);
            }
        }
        return bits;
    }

    /**
     * 解析int类型的值,获取值为1的位数
     *
     * @param number
     * @return
     */
    public static List<Integer> parseIntegerBits(long number) {
        List<Integer> bits = new ArrayList<>();
        for (int i = 0; i < 32; i++) {
            if (getBitValue(number, i) == 1) {
                bits.add(i);
            }
        }
        return bits;
    }

    /**
     * 获取二进制第index位的值
     *
     * @param number
     * @param index
     * @return
     */
    public static int getBitValue(long number, int index) {
        return (number & (1 << index)) > 0 ? 1 : 0;
    }

    /**
     * bit列表转int
     *
     * @param bits
     * @param len
     * @return
     */
    public static int bitsToInt(List<Integer> bits, int len) {
        if (bits == null || bits.isEmpty()) {
            return 0;
        }

        char[] chars = new char[len];
        for (int i = 0; i < len; i++) {
            char value = bits.contains(i) ? '1' : '0';
            chars[len - 1 - i] = value;
        }
        int result = Integer.parseInt(new String(chars), 2);
        return result;
    }

    /**
     * bit列表转long
     *
     * @param bits
     * @param len
     * @return
     */
    public static long bitsToLong(List<Integer> bits, int len) {
        if (bits == null || bits.isEmpty()) {
            return 0L;
        }

        char[] chars = new char[len];
        for (int i = 0; i < len; i++) {
            char value = bits.contains(i) ? '1' : '0';
            chars[len - 1 - i] = value;
        }
        long result = Long.parseLong(new String(chars), 2);
        return result;
    }

    /**
     * BigDecimal乘法
     *
     * @param longNum
     * @param precision
     * @return
     */
    public static double multiply(long longNum, BigDecimal precision) {
        return new BigDecimal(String.valueOf(longNum)).multiply(precision).doubleValue();
    }

    /**
     * BigDecimal乘法
     *
     * @param longNum
     * @param precision
     * @return
     */
    public static double multiply(int longNum, BigDecimal precision) {
        return new BigDecimal(String.valueOf(longNum)).multiply(precision).doubleValue();
    }
}

CommonUtil:公共方法类

package com.jointech.sdk.jt704.utils;

import io.netty.buffer.ByteBuf;

import java.nio.ByteBuffer;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

/**
 * <p>Description: 用来存储一些解析中遇到的公共方法</p>
 *
 * @author lenny
 * @version 1.0.1
 * @date 20210328
 */
public class CommonUtil {
    /**
     * 去掉字符串最后一个字符
     * @param inStr 输入的字符串
     * @param suffix 需要去掉的字符
     * @return
     */
    public static String trimEnd(String inStr, String suffix) {
        while(inStr.endsWith(suffix)){
            inStr = inStr.substring(0,inStr.length()-suffix.length());
        }
        return inStr;
    }

    /**
     * 16进制转byte[]
     * @param hex
     * @return
     */
    public static byte[] hexStr2Byte(String hex) {
        ByteBuffer bf = ByteBuffer.allocate(hex.length() / 2);
        for (int i = 0; i < hex.length(); i++) {
            String hexStr = hex.charAt(i) + "";
            i++;
            hexStr += hex.charAt(i);
            byte b = (byte) Integer.parseInt(hexStr, 16);
            bf.put(b);
        }
        return bf.array();
    }

    /**
     * 转换GPS时间
     *
     * @param bcdTimeStr
     * @return
     */
    public static ZonedDateTime parseBcdTime(String bcdTimeStr) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMddHHmmss");
        LocalDateTime localDateTime = LocalDateTime.parse(bcdTimeStr, formatter);
        ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneOffset.UTC);
        return zonedDateTime;
    }

    /**
     * 每个字节进行异或求值
     *
     * @param buf
     * @return
     */
    public static int xor(ByteBuf buf) {
        int checksum = 0;
        while (buf.readableBytes() > 0) {
            checksum ^= buf.readUnsignedByte();
        }
        return checksum;
    }
}

LocationData:定位实体类

package com.jointech.sdk.jt704.model;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;

import java.io.Serializable;

/**
 * <p>Description: 定位实体类</p>
 *
 * @author lenny
 * @version 1.0.1
 * @date 20210328
 */
@Data
public class LocationData implements Serializable {
    /**
     * 消息体
     */
    @JSONField(name = "DataLength")
    public int DataLength;
    /**
     * 定位时间
     */
    @JSONField(name = "GpsTime")
    public String GpsTime;
    /**
     * 纬度
     */
    @JSONField(name = "Latitude")
    public double Latitude;
    /**
     * 经度
     */
    @JSONField(name = "Longitude")
    public double Longitude;
    /**
     * 定位方式
     */
    @JSONField(name = "LocationType")
    public int LocationType;
    /**
     * 速度
     */
    @JSONField(name = "Speed")
    public int Speed;
    /**
     * 方向
     */
    @JSONField(name = "Direction")
    public int Direction;
    /**
     * 里程
     */
    @JSONField(name = "Mileage")
    public long Mileage;
    /**
     * GPS信号值
     */
    @JSONField(name = "GpsSignal")
    public int GpsSignal;
    /**
     * GSM信号质量
     */
    @JSONField(name = "GSMSignal")
    public int GSMSignal;
    /**
     * 电量值
     */
    @JSONField(name = "Battery")
    public int Battery;
    /**
     * 电压
     */
    @JSONField(name = "Voltage")
    public double Voltage;
    /**
     * 后盖状态
     */
    @JSONField(name = "BackCover")
    public int BackCover;
    /**
     * MCC
     */
    @JSONField(name = "MCC")
    public int MCC;
    /**
     * MNC
     */
    @JSONField(name = "MNC")
    public int MNC;
    /**
     * LAC
     */
    @JSONField(name = "LAC")
    public int LAC;
    /**
     * CELLID
     */
    @JSONField(name = "CELLID")
    public long CELLID;
    /**
     * Awaken
     */
    @JSONField(name = "Awaken")
    public int Awaken;
    /**
     * 报警类型
     */
    @JSONField(name = "Alarm")
    public int Alarm = -1;
    /**
     * 流水号
     */
    @JSONField(name = "Index")
    public int Index;
    /**
     * 温度值
     */
    @JSONField(name = "Temperature")
    public int Temperature=-1000;
}

Result:结果实体类

package com.jointech.sdk.jt704.model;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;

import java.io.Serializable;

/**
 * 结果实体类
 * @author HyoJung
 * @date 20210526
 */
@Data
public class Result implements Serializable {
    @JSONField(name = "DeviceID")
    private String DeviceID;
    @JSONField(name = "MsgType")
    private String MsgType;
    @JSONField(name = "DataBody")
    private Object DataBody;
    @JSONField(name = "ReplyMsg")
    private String ReplyMsg;
}

AlarmTypeEnum:终端报警类型枚举

package com.jointech.sdk.jt704.model;

import com.jointech.sdk.jt704.base.BaseEnum;
import lombok.Getter;

/**
 * 终端报警类型
 * @author HyoJung
 */
public enum AlarmTypeEnum implements BaseEnum<String> {

    ALARM_1("主机拆卸报警", "1");

    @Getter
    private String desc;

    private String value;

    AlarmTypeEnum(String desc, String value) {
        this.desc = desc;
        this.value = value;
    }

    @Override
    public String getValue() {
        return value;
    }

    public static AlarmTypeEnum fromValue(Integer value) {
        String valueStr = String.valueOf(value);
        for (AlarmTypeEnum alarmTypeEnum : values()) {
            if (alarmTypeEnum.getValue().equals(valueStr)) {
                return alarmTypeEnum;
            }
        }
        return null;
    }
}

2.4.返回消息及说明

(1)心跳数据
原始数据:

7E000200007501804283100001267E

返回消息:

{"DeviceID":"750180428310","MsgType":"heartbeat"}

返回消息描述

{"DeviceID":设备ID,"MsgType":消息类型(heartbeat:心跳)}

(2)定位数据
原始数据:

7E0200003B7501809250040102000000000004000201588F0F06CA3C52002700000000181106123212D4015AD502015C30011431010CFD0901CC00000010922866EF014A0F0201406E7E

返回消息:

{
    "DeviceID": "750180925004",
    "DataBody": {
        "Speed": 0,
        "GpsTime": "2018-11-06T12:32:12Z",
        "Temperature": 32,
        "MNC": 0,
        "Mileage": 0,
        "BackCover": 0,
        "Index": 258,
        "Latitude": 22.581007,
        "Awaken": 0,
        "MCC": 460,
        "Direction": 0,
        "Longitude": 113.91701,
        "LAC": 10342,
        "Alarm": -1,
        "Battery": 90,
        "GpsSignal": 12,
        "Voltage": 3.48,
        "DataLength": 59,
        "CELLID": 4242,
        "LocationType": 1,
        "GSMSignal": 20
    },
    "ReplyMsg": "7e800100057501809250040dbd0102020000077e",
    "MsgType": "Location"
}

返回消息描述

{
    "DeviceID": 终端号,
    "DataBody": {
        "Speed": 速度,
        "GpsTime": 定位时间(UTC),
        "Temperature": 温度,
        "MCC": 小区码信息MCC,
        "MNC": 小区码信息MNC,
        "LAC": 小区码信息LAC,
        "CELLID": 小区码信息CI,
        "Mileage": 里程值(km),
        "BackCover": 后盖状态(1:开;0:关),
        "Index": 流水号,
        "Latitude": 纬度(WGS84),
        "Longitude": 经度(WGS84),
        "Awaken": 唤醒源(0:RTC上报 3:开盖上报  4:关盖上报),
        "Direction": 方向(0~360,0表示正北),
        "Alarm": 报警类型(1:主机拆卸报警;-1:无报警),
        "Battery": 电量值(0~100%),
        "GpsSignal": 卫星颗数,
        "Voltage": 电压值(单位:V),
        "DataLength": 数据长度,
        "LocationType": 定位类型(1:GPS定位;0:不定位),
        "GSMSignal": GSM信号值
    },
    "ReplyMsg": 需要回复终端的指令,
    "MsgType": 数据类型(Location:定位数据)
}

(3)指令数据解析
原始数据:

283730303136303831383030302c312c3030312c424153452c312c312c32303135303431385f473330302c302c4265694875616e2c3131333742303353494d3930304d36345f53545f4d4d532c38393836303034323139313133303237323534392c3031323230373030353632303933322c3436302c30302c343234332c3638373729

返回消息:

{
    "DeviceID": "700160818000",
    "DataBody": "(700160818000,1,001,BASE,1,1,20150418_G300,0,BeiHuan,1137B03SIM900M64_ST_MMS,89860042191130272549,012207005620932,460,00,4243,6877)",
    "ReplyMsg": "",
    "MsgType": "BASE1"
}

返回消息描述

{
    "DeviceID": 设备ID,
    "DataBody": 消息体内容,
    "ReplyMsg": 回复设备的消息(为空则表示不需要回复),
    "MsgType": 指令类型
}

3.指令消息

3.1.查询终端基本信息

消息体内容(DataBody):

(700160818000,1,001,BASE,1,1,20150418_G300,0,BeiHuan,1137B03SIM900M64_ST_MMS,89860042191130272549,012207005620932,460,00,4243,6877)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,1 指令类型(BASE1)
3 20150418_G300 当前设备的版本号。
4 0,BeiHuan 前面的0表示英文,1表示其它语言的Unicode码,ASCII码表示,如:报警62A5 8B66上传是8个字节。此处为英语,名称为BeiHuan
5 1137B03SIM900M64_ST_MMS GSM模块版本。
6 89860042191130272549 SIM卡的ICCID。
7 012207005620932 GSM模块的IMEI号
8 460,00,4243,6877 网络信息:460 移动国家代码,即MCC信息,此处表示中国;00电信运营商网络号码,MNC信息(中国移动为00,中国联通为01);4243 基站编号CELL ID信息;6877 位置区域码LAC信息。CELL ID与LAC为十六进制,即4243转为十进制为16963。

3.2.授时(同步GMT时间)

消息体内容(DataBody):

(700160818000,1,001,BASE,2,20111018123820)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,2 指令类型(BASE2)
3 20111018123820 设置的时间(yyyyMMddHHmmss)

3.3.远程重启终端

消息体内容(DataBody):

(700160818000,1,001,BASE,3)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,3 指令类型(BASE3)

3.4.恢复出厂设置

消息体内容(DataBody):

(700160818000,1,001,BASE,4)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,4 指令类型(BASE4)

3.5.查询/设置上传间隔和休眠定时唤醒间隔

消息体内容(DataBody):

(700160818000,1,001,BASE,6,60,30)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,6 指令类型(BASE6)
3 60 上传间隔,以秒为单位,保留字段,暂无意义。
4 30 设备正常工作间隔,单位分钟,默认1440分钟,取值范围(5~1440)。

3.6.设置/查询设备本地时差

消息体内容(DataBody):

(2310915002,1,001,BASE,8, 480)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,8 指令类型(BASE8)
3 480 时差值,此处单位为分钟

3.7.查询/设置主从IP地址与端口号、APN及用户名与密码等参数

消息体内容(DataBody):

(700160818000,1,001,BASE,10,211.154.112.98,1088,211.154.112.98,1088,CMNET,abc,123456)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,10 指令类型(BASE10)
3 211.154.112.98 主IP(域名)
4 1088 主端口
5 211.154.112.98 从IP(域名)
6 1088 从端口
7 CMNET APN名称
8 abc 用户名
9 123456 密码

3.8.查询/设置/删除设备工作闹钟

消息体内容(DataBody):

(700160818000,1,001,BASE,32,1,480)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,32 指令类型(BASE32)
3 1 1则表明当前有效闹钟个数,闹钟时间点最多4个;返回0表示当前没有有效闹钟
4 480 设置一个设备闹钟的时间,以分钟为单位(范围为:0~1439)

3.9.设置/调试选项

消息体内容(DataBody):

(2310915002,1,001,DEBUG,2,2,1)

消息体内容描述:

序号 示例 说明
1 2310915002 设备ID
2 DEBUG,2 指令类型(DEBUG2)
3 2 2: 没意义,固定字段
4 1 1: 表示调试GPS模块原始输出信息 2表示调试GPRS原始输出AT指令信息

3.10.设置设备进入休眠的时间

消息体内容(DataBody):

(700160818000,1,001,DEBUG,15,10)

消息体内容描述:

序号 示例 说明
1 2310915002 设备ID
2 DEBUG,15 指令类型(DEBUG15)
3 10 10: 表示设备10秒钟后进入休眠

3.11.查询设备还可以存储多少条数据

消息体内容(DataBody):

(700160818000,1,001,DEBUG,19,1000)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 DEBUG,19 指令类型(DEBUG19)
3 1000 1000表示设备可以存储1000条数据

3.12.查询/设置/启用基站授时

消息体内容(DataBody):

(700160818000,1,001,DEBUG,26,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 DEBUG,26 指令类型(DEBUG26)
3 1 返回1则表明启用基站授时,0表示关闭基站授时。

3.13.查询卡信息

消息体内容(DataBody):

(700160818000,1,001,DEBUG,27,123456789,888888888)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 DEBUG,27 指令类型(DEBUG27)
3 123456789 123456789表示ICCID
4 888888888 888888888表示IMSI

3.14.通知设备MCU升级

消息体内容(DataBody):

(700160818000,1,001,OTA,1,1,20120102)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 OTA,1 指令类型(OTA1)
3 1 1:表示同意升级,只有当设备的当前版本号低于要升级的版本号的时候才返回1.若设备返回0表示拒绝升级,即设备当前的Firmware版本和升级的Firmware版本相同或者更高。服务器只有在收到同意升级以后才发送第一个Firmware数据包.
4 20120102 当前设备的Firmware版本号.

3.15.发送Firmware数据包

消息体内容(DataBody):

(700160818000,1,001,OTA,2,0,200,100)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 OTA,2 指令类型(OTA2)
3 0 1表示保存成功,0表示保存失败,如果失败重发.
4 200 接收到的Firmware数据包序号.
5 100 下一条需要发送的数据包序号.

3.16.取消Firmware升级

消息体内容(DataBody):

(700160818000,1,001,OTA,3,1,20120222)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 OTA,3 指令类型(OTA3)
3 1 1表示取消成功,0表示取消失败。如果该指令的版本号和正在升级的固件版本号不符的话就可能取消失败.
4 20120222 设备正在升级的版本号

3.17.完成Firmware传输

消息体内容(DataBody):

(700160818000,1,001,OTA,4,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 OTA,4 指令类型(OTA4)
3 1 该参数可以为1或者为0,1表示接受该指令,并根据指令执行。0表示:接受的数据不完全,拒绝升级.

3.18.查询发送下条Firmware数据包序号

消息体内容(DataBody):

(700160818000,1,001,OTA,5,1,200)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 OTA,5 指令类型(OTA5)
3 1 1:代表目前正在接收的固件和查询的相同,0表示目前接收的固件和查询的不同.
4 200 如果查询的固件版本号比设备的版本号新的话,该参数表示下一条需要发送的序号。如果查询的版本号和正在升级的版本号不同的话,则该参数为1.即代表需要重新下载现在查询的固件.

3.19.查询当前stm32固件的版本号

消息体内容(DataBody):

(700160818000,1,001,OTA,6,JT704_V103R001)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 OTA,6 指令类型(OTA6)
3 JT707A_V103R001 固件版本号
文档更新时间: 2021-08-05 12:20   作者:admin