1.Jar包下载

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


2.集成开发说明

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

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

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

2.2.集成说明

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

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

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

package com.jointech.sdk.jt709;

import com.alibaba.fastjson.JSONArray;
import com.jointech.sdk.jt709.constants.Constant;
import com.jointech.sdk.jt709.utils.CommonUtil;
import com.jointech.sdk.jt709.utils.ParserUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

/**
 * <p>Description: 解析方法的主体</p>
 *
 * @author lenny
 * @version 1.0.1
 */
public class DataParser {
    /**
     * 解析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.jt709.utils;

import com.jointech.sdk.jt709.constants.Constant;
import com.jointech.sdk.jt709.model.AlarmTypeEnum;
import com.jointech.sdk.jt709.model.LocationData;
import com.jointech.sdk.jt709.model.LockEvent;
import com.jointech.sdk.jt709.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
 */
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 lockRope=NumberUtil.getBitValue(status, 20);
        //锁电机状态
        int lockMotor=NumberUtil.getBitValue(status, 21);
        //锁状态(由锁绳/按钮+电机来组合判断,当锁绳/按钮为开(1)则锁状态必然为开(1);或者电机状态为开(1)则锁状态也为开(1);其他的则为关(0))
        int lockStatus=0;
        if(lockRope==1||lockMotor==1) {
            lockStatus=1;
        }
        int backCover=NumberUtil.getBitValue(status, 7);
        //唤醒源
        long awaken = (status>>24)&0b00001111;
        int alarm=parseAlarm(alarmFlag);
        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.setAltitude(altitude);
        locationData.setLockStatus(lockStatus);
        locationData.setLockRope(lockRope);
        locationData.setAwaken((int)awaken);
        locationData.setBackCover(backCover);
        locationData.setAlarm(alarm);
        //处理附加信息
        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 0x0B:
                    LockEvent event=new LockEvent();
                    //锁事件
                    int type=extraInfoBuf.readUnsignedByte();
                    event.setType(type);
                    if(type==0x01||type==0x02||type==0x03||type==0x05||type==0x1E||type==0x1F){
                        //凭密开锁
                        byte[] passwordArr = new byte[6];
                        extraInfoBuf.readBytes(passwordArr);
                        String password=new String(passwordArr);
                        event.setPassword(password);
                        int unlockStatus=extraInfoBuf.readUnsignedByte();
                        if(unlockStatus==0xff){
                            event.setUnLockStatus(0);
                        }else{
                            if(unlockStatus>0&&unlockStatus<100){
                                event.setFenceId(unlockStatus);
                            }
                            event.setUnLockStatus(1);
                        }
                    }else if(type==0x06||type==0x07||type==0x08||type==0x10||type==0x11||type==0x18||type==0x19||type==0x20||type==0x28||type==0x29){
                        //凭密开锁
                        byte[] passwordArr = new byte[6];
                        extraInfoBuf.readBytes(passwordArr);
                        String password=new String(passwordArr);
                        event.setPassword(password);
                        event.setUnLockStatus(0);
                    }else if(type==0x22){
                        //卡号
                        long cardId = extraInfoBuf.readUnsignedInt();
                        if(cardId!=0) {
                            event.setCardNo(String.format("%010d", cardId));
                        }
                        if(extraInfoBuf.readableBytes()>0) {
                            int unlockStatus = extraInfoBuf.readUnsignedByte();
                            if (unlockStatus == 0xff) {
                                event.setUnLockStatus(0);
                            } else {
                                if (unlockStatus > 0 && unlockStatus < 100) {
                                    event.setFenceId(unlockStatus);
                                }
                                event.setUnLockStatus(1);
                            }
                        }else{
                            event.setUnLockStatus(1);
                        }
                    }else if(type==0x23||type==0x2A||type==0x2B){
                        //卡号
                        long cardId = extraInfoBuf.readUnsignedInt();
                        if(cardId!=0) {
                            event.setCardNo(String.format("%010d", cardId));
                        }
                    }
                    location.setLockEvent(event);
                    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 0xF9:
                    //版本号
                    int version=extraInfoBuf.readUnsignedShort();
                    location.setProtocolVersion(version);
                    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 0xFC:
                    int fenceId = extraInfoBuf.readUnsignedByte();
                    location.setFenceId(fenceId);
                    break;
                case 0xFE:
                    long mileage = extraInfoBuf.readUnsignedInt();
                    location.setMileage(mileage);
                    break;
                default:
                    ByteBufUtil.hexDump(extraInfoBuf);
                    break;
            }
        }
    }

    /**
     * 报警解析
     * @param alarmFlag
     * @return
     */
    private static int parseAlarm(long alarmFlag) {
        int alarm=-1;
        //单次触发报警
        if(NumberUtil.getBitValue(alarmFlag, 1) == 1)
        {
            alarm = Integer.parseInt(AlarmTypeEnum.ALARM_1.getValue());
        }else if(NumberUtil.getBitValue(alarmFlag, 7) == 1)
        {
            alarm = Integer.parseInt(AlarmTypeEnum.ALARM_2.getValue());
        }else if(NumberUtil.getBitValue(alarmFlag, 16) == 1)
        {
            alarm = Integer.parseInt(AlarmTypeEnum.ALARM_3.getValue());
        }else if(NumberUtil.getBitValue(alarmFlag, 17) == 1)
        {
            alarm = Integer.parseInt(AlarmTypeEnum.ALARM_4.getValue());
        }else if(NumberUtil.getBitValue(alarmFlag, 18) == 1)
        {
            alarm = Integer.parseInt(AlarmTypeEnum.ALARM_5.getValue());
        }
        return alarm;
    }
}

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

package com.jointech.sdk.jt709.utils;

import com.jointech.sdk.jt709.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.jt709.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.jt709.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;
    }
}

BaseEnum:基础枚举

package com.jointech.sdk.jt709.base;

import java.io.Serializable;

/**
 * 基础枚举
 * @author HyoJung
 */
public interface BaseEnum  <T> extends Serializable {
    T getValue();
}

Constant:自定义常量

package com.jointech.sdk.jt709.constants;

/**
 * 常量定义
 * @author HyoJung
 * @date 20210526
 */
public class Constant {
    private Constant(){}
    /**
     * 二进制消息包头
     */
    public static final byte BINARY_MSG_HEADER = 0x7E;
    /**
     * 不包含消息体的基本长度
     */
    public static final int BINARY_MSG_BASE_LENGTH = 15;

    /**
     * 消息最大长度
     */
    public static final int BINARY_MSG_MAX_LENGTH = 102400;

    /**
     * 文本消息包头
     */
    public static final byte TEXT_MSG_HEADER = '(';

    /**
     * 文本消息包尾
     */
    public static final byte TEXT_MSG_TAIL = ')';

    /**
     * 文本消息分隔符
     */
    public static final byte TEXT_MSG_SPLITER = ',';
}

AlarmTypeEnum:终端报警类型枚举

package com.jointech.sdk.jt709.model;

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

/**
 * 报警枚举
 * @author HyoJung
 */

public enum AlarmTypeEnum implements BaseEnum<String> {

    ALARM_1("超速报警", "1"),
    ALARM_2("低电报警", "2"),
    ALARM_3("主机开盖报警", "3"),
    ALARM_4("进围栏报警", "4"),
    ALARM_5("出围栏报警", "5");

    @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;
    }
}

LocationData:定位实体类

package com.jointech.sdk.jt709.model;

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

import java.io.Serializable;

/**
 * <p>Description: 定位实体类</p>
 *
 * @author lenny
 * @version 1.0.1
 */
@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;
    /**
     * 海拔高度
     */
    @JSONField(name = "Altitude")
    public int Altitude;
    /**
     * 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 = "LockStatus")
    public int LockStatus;
    /**
     * 锁绳状态
     */
    @JSONField(name = "LockRope")
    public int LockRope;
    /**
     * 后盖状态
     */
    @JSONField(name = "BackCover")
    public int BackCover;
    /**
     * 协议版本号
     */
    @JSONField(name = "ProtocolVersion")
    public int ProtocolVersion;
    /**
     * 围栏ID
     */
    @JSONField(name = "FenceId")
    public int FenceId;
    /**
     * 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;
    /**
     * Alarm
     */
    @JSONField(name = "Alarm")
    public int Alarm;
    /**
     * 锁事件
     */
    @JSONField(name = "LockEvent")
    public LockEvent LockEvent;
    /**
     * 流水号
     */
    @JSONField(name = "Index")
    public int Index;
}

LockEvent:锁事件实体类

package com.jointech.sdk.jt709.model;

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

/**
 * 锁事件
 * @author HyoJung
 */
@Data
public class LockEvent {
    /**
     * 事件类型
     */
    @JSONField(name = "Type")
    public int Type;
    /**
     * 刷卡卡号
     */
    @JSONField(name = "CardNo")
    public String CardNo;
    /**
     * 开锁密码
     */
    @JSONField(name = "Password")
    public String Password;
    /**
     * 开锁状态(1:成功;0:失败)
     */
    @JSONField(name = "UnLockStatus")
    public int UnLockStatus=0;
    /**
     * 与开锁有关的围栏ID
     */
    @JSONField(name = "FenceId")
    public int FenceId=-1;
}

Result:结果实体类

package com.jointech.sdk.jt709.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;
}

2.4.返回消息及说明

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

7E000200007501804283100001267E

返回消息:

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

返回消息描述

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

(2)定位数据
原始数据(不带锁事件):

7E0200003A790011000094180C000000000234000201E807C80713B76A007800000000210804150651D4014AD502018C30011A310108FE0400000BCCFD0901CC000000308D51D0F27E

返回消息:

{
    "DeviceID": "790011000094",
    "DataBody": {
        "GpsTime": "2021-08-04T15:06:51Z",
        "MNC": 0,
        "FenceId": 0,
        "BackCover": 0,
        "Index": 6156,
        "Latitude": 31.98356,
        "Awaken": 2,
        "ProtocolVersion": 0,
        "Direction": 0,
        "Battery": 74,
        "GpsSignal": 8,
        "Voltage": 3.96,
        "Speed": 0,
        "LockStatus": 1,
        "Mileage": 3020,
        "MCC": 460,
        "Longitude": 118.73265,
        "LAC": 20944,
        "Alarm": -1,
        "DataLength": 58,
        "CELLID": 12429,
        "LockRope": 1,
        "LocationType": 1,
        "Altitude": 120,
        "GSMSignal": 26
    },
    "ReplyMsg": "7e80010005790011000094dae5180c020000517e",
    "MsgType": "Location"
}

返回消息描述

{
    "DeviceID": "790011000094",
    "DataBody": {
        "GpsTime": 定位时间(UTC时间),
        "Latitude": 纬度(WGS84),
        "Longitude": 经度(WGS84),
        "MCC": 小区码信息MCC,
        "MNC": 小区码信息MNC,
        "LAC": 小区码信息LAC,
        "CELLID": 小区码信息CI,
        "FenceId": 电子围栏Id,
        "BackCover": 后盖状态(1:开;0:关),
        "Index": 流水号,
        "Awaken": 唤醒源 (1:RTC闹钟唤醒,2:Gsens震动唤醒 3:开盖唤醒 4:剪绳唤醒 5:充电唤醒 6:刷卡唤醒7:Lora唤醒8:VIP号码唤醒9:非VIP唤醒10:蓝牙唤醒)
        "ProtocolVersion":协议版本号,
        "Direction": 正北为0,顺时针0-360,
        "Battery": 电量值(0~100%,255表示电池充电中),
        "GpsSignal": GPS当前接收到的卫星颗数,
        "Voltage": 电压值,单位V,
        "Speed": 速度,单位km/h,
        "LockStatus": 锁状态(0:关;1:开),
        "LockRope": 锁绳状态(0:插入;1:拔出),
        "Mileage": 里程值,单位km,
        "Alarm": 报警类型(1:超速报警;2:低电报警;3:主机开盖报警;4:进围栏报警;5:出围栏报警),
        "DataLength": 数据长度(字节数),
        "LocationType": 定位方式(0:不定位;1:GPS定位;2:基站定位),
        "Altitude": 海拔高度km,
        "GSMSignal": GSM信号值
    },
    "ReplyMsg": 需要回复终端的内容(为空则表示不需要回复),
    "MsgType": 数据类型(Location:定位数据)
}

原始数据(带锁事件):

7E020000477900110000941808000000000224000201E807AA0713B7EC009100000000210804150506D4014AD502018C30011E310108FE0400000BCCFD0901CC00000038CB51D0EF014A0B0801383838383838654B7E

返回消息:

{
    "DeviceID": "790011000094",
    "DataBody": {
        "GpsTime": "2021-08-04T15:05:06Z",
        "MNC": 0,
        "FenceId": 0,
        "BackCover": 0,
        "Index": 6152,
        "Latitude": 31.98353,
        "Awaken": 2,
        "ProtocolVersion": 0,
        "Direction": 0,
        "Battery": 74,
        "GpsSignal": 8,
        "Voltage": 3.96,
        "LockEvent": {
            "Type": 1,
            "FenceId": -1,
            "UnLockStatus": 1,
            "Password": "888888"
        },
        "Speed": 0,
        "LockStatus": 1,
        "Mileage": 3020,
        "MCC": 460,
        "Longitude": 118.73278,
        "LAC": 20944,
        "Alarm": -1,
        "DataLength": 71,
        "CELLID": 14539,
        "LockRope": 0,
        "LocationType": 1,
        "Altitude": 145,
        "GSMSignal": 30
    },
    "ReplyMsg": "7e80010005790011000094a8251808020000e77e",
    "MsgType": "Location"
}

返回消息描述

{
    "DeviceID": "790011000094",
    "DataBody": {
        "GpsTime": 定位时间(UTC时间),
        "Latitude": 纬度(WGS84),
        "Longitude": 经度(WGS84),
        "MCC": 小区码信息MCC,
        "MNC": 小区码信息MNC,
        "LAC": 小区码信息LAC,
        "CELLID": 小区码信息CI,
        "FenceId": 电子围栏Id,
        "BackCover": 后盖状态(1:开;0:关),
        "Index": 流水号,
        "Awaken": 唤醒源 (1:RTC闹钟唤醒,2:Gsens震动唤醒 3:开盖唤醒 4:剪绳唤醒 5:充电唤醒 6:刷卡唤醒7:Lora唤醒8:VIP号码唤醒9:非VIP唤醒10:蓝牙唤醒)
        "ProtocolVersion":协议版本号,
        "Direction": 正北为0,顺时针0-360,
        "Battery": 电量值(0~100%,255 表示电池充电中),
        "GpsSignal": GPS当前接收到的卫星颗数,
        "Voltage": 电压值,单位V,
        "Speed": 速度,单位km/h,
        "LockStatus": 锁状态(0:关;1:开),
        "LockRope": 锁绳状态(0:插入;1:拔出),
        "Mileage": 里程值,单位km,
        "Alarm": 报警类型(1:超速报警;2:低电报警;3:主机开盖报警;4:进围栏报警;5:出围栏报警),
        "LockEvent": {
            "Type": 事件类型(参见附件1),
            "FenceId": 事件关联的围栏ID(-1:与围栏无关),
            "UnLockStatus": 开锁状态(1:开锁成功;0:开锁失败),
            "Password": 开锁密码
            "CardNo":如果事件类型为刷卡开锁,这里表示的是卡号
        },
        "DataLength": 数据长度(字节数),
        "LocationType": 定位方式(0:不定位;1:GPS定位;2:基站定位),
        "Altitude": 海拔高度km,
        "GSMSignal": GSM信号值
    },
    "ReplyMsg": 需要回复终端的内容(为空则表示不需要回复),
    "MsgType": 数据类型(Location:定位数据)
}

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

283739313030353030303030332c312c3030312c424153452c312c4a54373039415f32303230303932325f48572d56312e305f4e6f524649445f53494d434f4d373630305f56325f312c302c4c45313142303353494d373630304d31315f412c38393836303431323130313843303733383535342c3836373538343033343631313131362c3436302c302c3138303535343736342c313033343229

返回消息:

{
    "DeviceID": "791005000003",
    "DataBody": "(791005000003,1,001,BASE,1,JT709A_20200922_HW-V1.0_NoRFID_SIMCOM7600_V2_1,0,LE11B03SIM7600M11_A,898604121018C0738554,867584034611116,460,0,180554764,10342)",
    "ReplyMsg": "",
    "MsgType": "BASE1"
}

返回消息描述

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

3.指令消息

3.1.查询终端基本信息

消息体内容(DataBody):

(791005000003,1,001,BASE,1,JT709A_20200922_HW-V1.0_NoRFID_SIMCOM7600_V2_1,0,LE11B03SIM7600M11_A,898604121018C0738554,867584034611116,460,0,180554764,10342)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,1 指令类型(BASE1)
3 JT709A_20200922_HW-V1.0_NoRFID_SIMCOM7600_V2_1 当前设备的版本号。
4 0 前面的0表示英文,1表示其它语言的Unicode码
5 LE11B03SIM7600M11_A GSM模块版本。
6 898604121018C0738554 SIM卡的ICCID。
7 867584034611116 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,5, 1,1,1,1,1,1,1,1,1,1,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,5 指令类型(BASE5)
3 1,1,1,1,1,1,1,1,1,1,1 从左至右依次为(1)锁挂绳剪断报警、(2)刷非法卡报警、(3)开锁状态保持一段时间报警、(4)指令开锁密码连续输错5次报警、(5)震动报警、(6)进区域报警、(7)出区域报警的开关、(8)低电报警(电量低于满电的30%)、(9)开后盖报警、(10)卡锁报警、(11)超速报警;(每个报警开关参数可以取值为0,1,2,3,并且可以任意组合,0表示GPRS和SMS报警都关闭,1表示只开启GPRS报警,2表示只开启SMS报警,3表示GPRS和SMS报警都开启.)

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

消息体内容(DataBody):

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

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,6 指令类型(BASE6)
3 60 上传间隔,以秒为单位,默认60。最长1小时,最短5秒
4 30 设备正常工作间隔,单位分钟,默认60分钟. 当该值为0时,启用持续追踪模式,设备不休眠。最长24小时,最短5分钟

3.7.查询/设置终端休眠模式

消息体内容(DataBody):

(700160818000,1,001,BASE,7,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,7 指令类型(BASE7)
3 1 0正常休眠模式任一中断、RTC可以唤醒,1表示在0模式上增加短信电话可以唤醒

3.8.查询/设置终端本地时差

消息体内容(DataBody):

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

消息体内容描述:

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

3.9.查询/设置终端本地时差

消息体内容(DataBody):

(700160818000,1,001,BASE,9,8613998765432,0,0,0,0)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,9 指令类型(BASE9)
3 8613998765432,0,0,0,0 5个监控手机号码,0表示没有设置

3.10.查询/设置主从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.11.查询/设置/删除设备工作闹钟

消息体内容(DataBody):

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

消息体内容描述:

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

3.12.查询/设置仅支持VIP号码

消息体内容(DataBody):

(700160818000,1,001,BASE,40,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,40 指令类型(BASE40)
3 1 1表示仅支持VIP号码进行设备配置和查询,0表示非vip号码也可以配置和查询。默认0

3.13.查询/设置里程统计相关参数

消息体内容(DataBody):

(700160818000,1,001,BASE,43,10,10)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,43 指令类型(BASE43)
3 10 表示里程统计的速度限制值操作(即速度小于改值不统计里程,默认10km/h, 参数0-100)
4 10 表示设置里程同步值为10公里

3.14.查询/设置静态飘移处理功能

消息体内容(DataBody):

(700160818000,1,001,BASE,44,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,44 指令类型(BASE44)
3 1 1打开这个静态漂移处理功能,0表示关闭静态漂移处理功能

3.15.查询/设置GPS追踪定位模式功能

消息体内容(DataBody):

(700160818000,1,001,BASE,45,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,45 指令类型(BASE45)
3 1 1表示节能定时追踪,0 表示节能定位追踪,2表示全速追踪

3.16.查询/设置GPS定位判定门限参数(防GPS定位漂移)

消息体内容(DataBody):

(700160818000,1,001,BASE,46,5,25,6)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,46 指令类型(BASE46)
3 5 HDOP门限值
4 25 单颗卫星信噪比门限值
5 6 联合定位卫星个数门限值

3.17.查询/设置GPS定位判定门限参数(防GPS定位漂移)

消息体内容(DataBody):

(700160818000,1,001,BASE,47,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,47 指令类型(BASE47)
3 1 1: 启用监控IP服务器,0关闭监控IP服务器

3.18.设置报警开关指令

消息体内容(DataBody):

(700160818000,1,001,BASE,48, 1,1,1,1,1,1,1,1,1,1,1,1,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 BASE,47 指令类型(BASE47)
3 1,1,1,1, 1,1,1,1, 1,1,1,1,1 从左至右依次为(1) 锁绳剪断报警、(2)刷非法卡报警、(3)长时间开锁报警、(4)开锁密码错误、(5)震动报警、(6)进区域报警、(7)出区域报警、(8)低电报警、(9)开后盖报警、(10)卡锁报警、(11)GSM信号低报警、(12)超速报警 (每个报警开关参数可以取值为0,1,2,3,并且可以任意组合,0表示GPRS和SMS报警都关闭,1表示只开启GPRS报警,2表示只开启SMS报警,3表示GPRS和SMS报警都开启.),(13)倾斜报警,其中红色的没有相应报警

3.19.查询/设置震动检测阈值

消息体内容(DataBody):

(700160818000,1,001,GSENS,1, 500)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 GSENS,1 指令类型(GSENS1)
3 500 500:单位是mg(gsensor芯片63mg为阶梯配置,实际芯片配置值为500/63取整=7),最小63,最大值8000,默认500。当震动达到这个值,会检测到一次震动。如果值等于0,关闭该功能

3.20.电池产品入库前进入休眠

消息体内容(DataBody):

(700160818000,1,001,DEBUG,7)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 DEBUG,7 指令类型(DEBUG7)

3.20.电池产品入库前进入休眠

消息体内容(DataBody):

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

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 DEBUG,15 指令类型(DEBUG15)
3 10 0表示马上进入睡眠,设置大于0的数表示相应时间后进入休眠

3.21.查询物联卡信息

消息体内容(DataBody):

(700160818000,1,001,DEBUG,27, ICCID, IMSI)

消息体内容描述:

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

3.22.查询/清除历史(盲区)数据

消息体内容(DataBody):

(700160818000,1,001,DEBUG,29, 0)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 DEBUG,29 指令类型(DEBUG29)
3 0 返回0则表明当前历史数据条数

3.23.查询/设置超速报警速度

消息体内容(DataBody):

(700160818000,1,001,DEBUG,30,120,10)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 DEBUG,30 指令类型(DEBUG30)
3 120 报警的速度临界值(默认值120),单位为:公里
4 10 超速此时间后报警(默认值10),单位:秒

3.24.查询/设置低电报警数值

消息体内容(DataBody):

(700160818000,1,001,DEBUG,31,5)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 DEBUG,31 指令类型(DEBUG31)
3 5 报警的电量临界值;5表明当前电量百分比

3.25.查询/设置关闭看门狗作用

消息体内容(DataBody):

(700160818000,1,001,DEBUG,38,0)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 DEBUG,38 指令类型(DEBUG38)
3 0 返回0表明已接收该指令,并生效。

3.26.设置/查询区域详细节点信息

消息体内容(DataBody):

(700160818000,1,001,GFCE,7,8,15,1,10,11323.1234…)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 GFCE,7 指令类型(GFCE7)
3 8 8表示第八个区域
4 15 15表示总点数
5 1 1表示当前页
6 10 10表示当前页的点数
7 11323.1234… 各个点的经度与纬度

3.27.清除设置的全部区域点信息

消息体内容(DataBody):

(700160818000,1,001,GFCE,8,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 GFCE,8 指令类型(GFCE8)
3 1 1表示删除围栏的ID

3.28.查询/设置没有关锁报警提醒时间间隔

消息体内容(DataBody):

(700160818000,1,001,ELOCK,2, 1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,2 指令类型(ELOCK2)
3 1 提醒时间,单位(分钟)。当开锁后没有关锁,如果此值不为0(为0无效)时间到就上报开锁提醒消息,最小值为1分钟,最大值为10分钟 。默认为1分钟

3.29.终端上报动态密码

消息体内容(DataBody):

(700160818000,1,001,ELOCK,3,123456)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,3 指令类型(ELOCK3)
3 123456 上报的动态密码

3.30.设置/修改远程开锁静态密码

消息体内容(DataBody):

(700160818000,1,001,ELOCK,4,666666)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,4 指令类型(ELOCK4)
3 666666 666666: 表示修改成功的密码。

3.31.凭静态或动态密码开锁

消息体内容(DataBody):

(700160818000,1,001,ELOCK,5,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,5 指令类型(ELOCK5)
3 1 0: 表示密码正确,大于0表示密码错误开锁失败

3.32.查询/设置围栏外禁止开锁

消息体内容(DataBody):

(700160818000,1,001,ELOCK,7,1,2,3,4)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,7 指令类型(ELOCK7)
3 1 1表示设置了栏外禁止开锁,必须在定位状下围栏内才能开锁。2表示设置了栏外禁止开锁,有定位必须在围栏内开锁,没有定位可以通过远程静态密码或动态密码开锁,如果设置了围栏外禁止开锁没有定位不能通过现场按键或蓝牙APP开锁。0表示没有开启围栏外禁止开锁功能,默认没有开启。
4 2 围栏ID个数:1~10有效,只支持区域围栏。其它类型的围栏不支持
5 3,4 围栏ID: 1~10有效,3,4表示只能在区域围栏ID为3,4的围栏可以开锁,1~10的ID存在对应的位置

3.33.动态密码权限处理(不对外公开)

消息体内容(DataBody):

(700160818000,1,001,ELOCK,7,1)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,7 指令类型(ELOCK7)
3 1 1表示使能产生动态密码;0表示关闭产生动态密码

3.34.查询/增删/开锁授权号

消息体内容(DataBody)实例1:

(700160818000,1,001, ELOCK,15,0,2,20,2,1,0013953759, 0013953758, 0013953757,,,,,,,,,,,,,)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,15 指令类型(ELOCK15)
3 0 操作模式, 0表示查询终端已存所有授权号,1表示要增加开锁授权号操作,2表示删除授权号,3表示删除所有的授权号。
4 2 2为查询第2组数据,一共分3组,分别为1~3
5 20 20表示有20个ID号
6 2 2表示总共有2应答包
7 1 1表示当前是第1个应答包
8 0013953759, 0013953758, 0013953757,,,,, 表示该组存储的授权号列表

消息体内容(DataBody)实例2:

(700160818000,1,001,ELOCK,15,1,3,200)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,15 指令类型(ELOCK15)
3 1 操作模式, 0表示查询终端已存所有授权号,1表示要增加开锁授权号操作,2表示删除授权号,3表示删除所有的授权号。
4 3 增加3个授权卡号成功
5 200 当前总共有200个卡

消息体内容(DataBody)实例3:

(700160818000,1,001,ELOCK,15,2,3)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,15 指令类型(ELOCK15)
3 2 操作模式, 0表示查询终端已存所有授权号,1表示要增加开锁授权号操作,2表示删除授权号,3表示删除所有的授权号。
4 3 表示删除3个授权卡号成功。(每次最多一次性删除20)

消息体内容(DataBody)实例4:

(700160818000,1,001, ELOCK,15,3,0)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,15 指令类型(ELOCK15)
3 3 操作模式, 0表示查询终端已存所有授权号,1表示要增加开锁授权号操作,2表示删除授权号,3表示删除所有的授权号。
4 0 表示删除全部授权卡号成功,目前剩余0个授权卡

3.35.现场刷卡授权模式配置指令

消息体内容(DataBody)实例1:

(700160818000,1,001, ELOCK,16,2,0013953759,0013953751)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,16 指令类型(ELOCK16)
3 2 授权的卡个数
4 0013953759,0013953751 授权的卡号

消息体内容(DataBody)实例2:

(700160818000,1,001, ELOCK,16,0)

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 ELOCK,16 指令类型(ELOCK16)
3 0 1表示打开批量增加终端开锁授权号功能, 0表示关闭批量增加终端开锁授权号功能。

3.36.通知设备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.37.发送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.38.取消Firmware升级

消息体内容(DataBody):

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

消息体内容描述:

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

3.39.完成Firmware传输

消息体内容(DataBody):

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

消息体内容描述:

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

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

消息体内容(DataBody):

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

消息体内容描述:

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

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

消息体内容(DataBody):

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

消息体内容描述:

序号 示例 说明
1 700160818000 设备ID
2 OTA,6 指令类型(OTA6)
3 JT709A_V103R001 固件版本号

附件1

事件ID(16进制) 事件ID 事件描述
0x01 1 静态密码远程开锁
0x02 2 动态密码远程开锁
0x03 3 动态密码现场(蓝牙或WIFI)APP开锁
0x05 5 表示静态密码现场(蓝牙或WIFI)APP开锁
0x06 6 错误的静态密码远程开锁
0x07 7 错误的动态密码远程开锁
0x08 8 错误的动态密码现场(蓝牙或WIFI)APP开锁
0x0B 11 长时间开锁事件
0x0C 12 锁绳剪断事件
0x0D 13 关锁事件(包括自动关锁和远程关锁)
0x10 16 远程执行开锁异常,没有定位不执行开锁
0x11 17 远程执行开锁异常,有定位在围栏外不执行开锁
0x12 18 电机异常
0x18 24 蓝牙执行开锁异常,没有定位不执行开锁
0x19 25 蓝牙执行开锁异常,有定位在围栏外不执行开锁
0x1C 28 开锁并拔出锁绳
0x1E 30 短信静态密码远程开锁
0x1F 31 短信动态密码远程开锁
0x20 32 表示错误的短信动态密码
0x22 34 刷授权卡开锁事件
0x23 35 刷非法卡开锁事件
0x28 40 错误的静态密码现场(蓝牙或WIFI)APP开锁
0x29 41 错误的短信静态密码开锁
0x2A 42 RFID执行开锁异常,没有定位不执行开锁
0x2B 43 RFID执行开锁异常,有定位在围栏外不执行开锁
文档更新时间: 2024-10-15 18:07   作者:admin