1.Jar package download

jt705-sdk-1.0.0.jar Download
If you need Jar package development source code, please contact the business application


2.Integrated Development Instruction

2.1.Integrated development language and framework description

jt705-sdk-1.0.0.jar is based on the Java language,SpringBoot2.x frame,use netty,fastjson,lombok,commons-lang3

BaseEnum:base enumeration
Constant:custom constant
AlarmTypeEnum:Alarm enumeration
EventTypeEnum:event enumeration
LocationData:Location entity class
LockEvent:lock event entity class
Result:result entity class
SensorData:Slave data entity class
CommonUtil:public method class
NumberUtil:digital manipulation tools
ParserUtil:Parser method tool class
DataParser:Parser main method

2.2.Integration Instructions

Introduce jt705-sdk-1.0.0.jar into your gateway program as follows:
Introduce the jar package in pom.xml

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

Call jt705-sdk-1.0.0.jar, the receiveData() method in the DataParser class
receiveData() method is overloaded

package com.jointech.sdk.jt705;

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

/**
 * <p>Description: The body of the parsing method</p>
 *
 * @author lenny
 * @version 1.0.1
 */
public class DataParser {
    /**
     * Parse Hex string raw data
     * @param strData hexadecimal string
     * @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);
    }

    /**
     * Parse byte[] raw data
     * @param bytes
     * @return
     */
    private static Object receiveData(byte[] bytes)
    {
        ByteBuf msgBodyBuf =Unpooled.buffer(bytes.length);
        msgBodyBuf.writeBytes(bytes);
        return receiveData(msgBodyBuf);
    }

    /**
     * Parse ByteBuf raw data
     * @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.Core code

ParserUtil:Parsing method tool class ParserUtil

package com.jointech.sdk.jt705.utils;

import com.jointech.sdk.jt705.constants.Constant;
import com.jointech.sdk.jt705.model.AlarmTypeEnum;
import com.jointech.sdk.jt705.model.LocationData;
import com.jointech.sdk.jt705.model.LockEvent;
import com.jointech.sdk.jt705.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:  Analysis method tool class</p>
 * @author HyoJung
 */
public class ParserUtil {
    /**
     * Parse Positioning data 0x0200
     * @param in
     * @return
     */
    public static Result decodeBinaryMessage(ByteBuf in) {
        //unexcape rawdata
        ByteBuf msg = (ByteBuf) PacketUtil.decodePacket(in);
        //message length
        int msgLen = msg.readableBytes();
        //packet header
        msg.readByte();
        //message ID
        int msgId = msg.readUnsignedShort();
        //message body properties
        int msgBodyAttr = msg.readUnsignedShort();
        //message body length
        int msgBodyLen = msgBodyAttr & 0b00000011_11111111;
        //Whether to subcontract
        boolean multiPacket = (msgBodyAttr & 0b00100000_00000000) > 0;
        //Remove the base length of the message body
        int baseLen = Constant.BINARY_MSG_BASE_LENGTH;

        //The following packet length is obtained according to the length of the message body and whether it is subcontracted
        int ensureLen = multiPacket ? baseLen + msgBodyLen + 4 : baseLen + msgBodyLen;
        if (msgLen != ensureLen) {
            return null;
        }
        //array of deviceID
        byte[] terminalNumArr = new byte[6];
        msg.readBytes(terminalNumArr);
        //Device ID (remove leading 0)
        String terminalNumber = StringUtils.stripStart(ByteBufUtil.hexDump(terminalNumArr), "0");
        //message serial number
        int msgFlowId = msg.readUnsignedShort();
        //total number of message packets
        int packetTotalCount = 0;
        //package serial number
        int packetOrder = 0;
        //subcontract
        if (multiPacket) {
            packetTotalCount = msg.readShort();
            packetOrder = msg.readShort();
        }
        //message body
        byte[] msgBodyArr = new byte[msgBodyLen];
        msg.readBytes(msgBodyArr);
        if(msgId==0x0200) {
            //Parse the message body
            LocationData locationData=parseLocationBody(Unpooled.wrappedBuffer(msgBodyArr));
            locationData.setIndex(msgFlowId);
            locationData.setDataLength(msgBodyLen);
            //check code
            int checkCode = msg.readUnsignedByte();
            //packet end
            msg.readByte();
            //Get message response content
            String replyMsg= PacketUtil.replyBinaryMessage(terminalNumArr,msgFlowId);
            //Define the location data entity class
            Result model = new Result();
            model.setDeviceID(terminalNumber);
            model.setMsgType("Location");
            model.setDataBody(locationData);
            model.setReplyMsg(replyMsg);
            return model;
        }else {
            //Define the location data entity class
            Result model = new Result();
            model.setDeviceID(terminalNumber);
            model.setMsgType("heartbeat");
            model.setDataBody(null);
            return model;
        }
    }

    /**
     * Parse instruction data
     * @param in  raw data
     * @return
     */
    public static Result decodeTextMessage(ByteBuf in) {

        //The read pointer is set to the message header
        in.markReaderIndex();
        //Look for the end of the message, if not found, continue to wait for the next packet
        int tailIndex = in.bytesBefore(Constant.TEXT_MSG_TAIL);
        if (tailIndex < 0) {
            in.resetReaderIndex();
            return null;
        }
        //Define the location data entity class
        Result model = new Result();
        //packet header(
        in.readByte();
        //Field list
        List<String> itemList = new ArrayList<String>();
        while (in.readableBytes() > 0) {
            //Query the subscript of comma to intercept data
            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;
    }

    /**
     * Parse and locate message body
     * @param msgBodyBuf
     * @return
     */
    private static LocationData parseLocationBody(ByteBuf msgBodyBuf){
        //alarm sign
        long alarmFlag = msgBodyBuf.readUnsignedInt();
        //Device status
        long status = msgBodyBuf.readUnsignedInt();
        //latitude
        double lat = NumberUtil.multiply(msgBodyBuf.readUnsignedInt(), NumberUtil.COORDINATE_PRECISION);
        //longitude
        double lon = NumberUtil.multiply(msgBodyBuf.readUnsignedInt(), NumberUtil.COORDINATE_PRECISION);
        //Altitude, in meters
        int altitude = msgBodyBuf.readShort();
        //Speed
        double speed = NumberUtil.multiply(msgBodyBuf.readUnsignedShort(), NumberUtil.ONE_PRECISION);
        //directioin
        int direction = msgBodyBuf.readShort();
        //GPS time
        byte[] timeArr = new byte[6];
        msgBodyBuf.readBytes(timeArr);
        String bcdTimeStr = ByteBufUtil.hexDump(timeArr);
        ZonedDateTime gpsZonedDateTime = CommonUtil.parseBcdTime(bcdTimeStr);
        //Determine whether the south latitude and west longitude are based on the value of the status bit
        if (NumberUtil.getBitValue(status, 2) == 1) {
            lat = -lat;
        }
        if (NumberUtil.getBitValue(status, 3) == 1) {
            lon = -lon;
        }
        //Positioning status
        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;
        }
        //Alarm status
        int lockRope=NumberUtil.getBitValue(status, 20);
        //Lock status
        int lockMotor=NumberUtil.getBitValue(status, 21);
        //The lock state (judged by the combination of the lock button + motor;when lock button open (1) or motor unlock(1),lock state is unlocking(1);when lock button close(0) and motor locked(0),the lock state is locked
        int lockStatus=0;
        if(lockRope==1||lockMotor==1) {
            lockStatus=1;
        }
        int backCover=NumberUtil.getBitValue(status, 7);
        //Wake-up source
        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);
        //Handling additional information
        if (msgBodyBuf.readableBytes() > 0) {
            parseExtraInfo(msgBodyBuf, locationData);
        }
        return locationData;
    }

    /**
     * Parse additional information
     *
     * @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) {
                //lock event
                case 0x0B:
                    LockEvent event=new LockEvent();
                    //lock event
                    int type=extraInfoBuf.readUnsignedByte();
                    event.setType(type);
                    if(type==0x01||type==0x02||type==0x03||type==0x05||type==0x1E||type==0x1F){
                        //Unlock by password
                        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){
                        //Unlock by password
                        byte[] passwordArr = new byte[6];
                        extraInfoBuf.readBytes(passwordArr);
                        String password=new String(passwordArr);
                        event.setPassword(password);
                        event.setUnLockStatus(0);
                    }else if(type==0x22){
                        //RFID card No.
                        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){
                        //RFID card No.
                        long cardId = extraInfoBuf.readUnsignedInt();
                        if(cardId!=0) {
                            event.setCardNo(String.format("%010d", cardId));
                        }
                    }
                    location.setLockEvent(event);
                    break;
                //Gyroscope three-axis data
                case 0x0C:
                    int x=extraInfoBuf.readUnsignedShort();
                    int y=extraInfoBuf.readUnsignedShort();
                    int z=extraInfoBuf.readUnsignedShort();
                    x=x > 32768 ? (x - 65536) : x;
                    y=y > 32768 ? (y - 65536) : y;
                    z=z > 32768 ? (z - 65536) : z;
                    location.setPosture("x:"+x+";y:"+y+";z:"+z);
                    break;
                //Wireless communication network signal strength
                case 0x30:
                    int fCellSignal=extraInfoBuf.readByte();
                    location.setGSMSignal(fCellSignal);
                    break;
                //number of satellites
                case 0x31:
                    int fGPSSignal=extraInfoBuf.readByte();
                    location.setGpsSignal(fGPSSignal);
                    break;
                //battery percentage
                case 0xD4:
                    int fBattery=extraInfoBuf.readUnsignedByte();
                    location.setBattery(fBattery);
                    break;
                //battery voltage
                case 0xD5:
                    int fVoltage=extraInfoBuf.readUnsignedShort();
                    location.setVoltage(fVoltage*0.01);
                    break;
                case 0xF9:
                    //Protocol version
                    int version=extraInfoBuf.readUnsignedShort();
                    location.setProtocolVersion(version);
                    break;
                case 0xFD:
                    //LBS information
                    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;
            }
        }
    }

    /**
     * Alarm Parsing
     * @param alarmFlag
     * @return
     */
    private static int parseAlarm(long alarmFlag) {
        int alarm=-1;
        //Single trigger alarm
        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:Used to process preprocessed data and restore method encapsulation class

package com.jointech.sdk.jt705.utils;

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

/**
 * Parse package preprocessing (do unescaping)
 * @author HyoJung
 */
public class PacketUtil {
    /**
     * Parse message packets
     *
     * @param in
     * @return
     */
    public static Object decodePacket(ByteBuf in) {
        //The readable length cannot be less than the base length
        if (in.readableBytes() < Constant.BINARY_MSG_BASE_LENGTH) {
            return null;
        }

        //Prevent illegal code stream attacks, the data is too large to be abnormal data
        if (in.readableBytes() > Constant.BINARY_MSG_MAX_LENGTH) {
            in.skipBytes(in.readableBytes());
            return null;
        }

        //Look for the end of the message, if not found, continue to wait for the next packet
        in.readByte();
        int tailIndex = in.bytesBefore(Constant.BINARY_MSG_HEADER);
        if (tailIndex < 0) {
            in.resetReaderIndex();
            return null;
        }

        int bodyLen = tailIndex;
        //Create a ByteBuf to store the reversed data
        ByteBuf frame = ByteBufAllocator.DEFAULT.heapBuffer(bodyLen + 2);
        frame.writeByte(Constant.BINARY_MSG_HEADER);
        //The data between the header and the end of the message is escaped
        unescape(in, frame, bodyLen);
        in.readByte();
        frame.writeByte(Constant.BINARY_MSG_HEADER);

        //The length after the reverse escape cannot be less than the base length
        if (frame.readableBytes() < Constant.BINARY_MSG_BASE_LENGTH) {
            ReferenceCountUtil.release(frame);
            return null;
        }
        return frame;
    }

    /**
     * In the message header, message body and check code, 0x7D 0x02 is reversed to 0x7E, and 0x7D 0x01 is reversed to 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 {
                    //abnormal data
                    frame.writeByte(b);
                    frame.writeByte(nextByte);
                }
                i += 2;
            } else {
                frame.writeByte(b);
                i++;
            }
        }
    }

    /**
     * In the message header, message body and check code, 0x7E is escaped as 0x7D 0x02, and 0x7D is escaped as 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);
            }
        }
    }

    /**
     * Reply content
     * @param terminalNumArr
     * @param msgFlowId
     * @return
     */
    public static String replyBinaryMessage(byte[] terminalNumArr,int msgFlowId) {
        //Remove the length of the head and tail
        int contentLen = Constant.BINARY_MSG_BASE_LENGTH + 4;
        ByteBuf bodyBuf = ByteBufAllocator.DEFAULT.heapBuffer(contentLen-2);
        ByteBuf replyBuf = ByteBufAllocator.DEFAULT.heapBuffer(25);
        try {
            //Message ID
            bodyBuf.writeShort(0x8001);
            //Data Length
            bodyBuf.writeShort(0x0005);
            //Device ID
            bodyBuf.writeBytes(terminalNumArr);
            Random random = new Random();
            //Generate random numbers from 1-65534
            int index = random.nextInt() * (65534 - 1 + 1) + 1;
            //Current message serial number
            bodyBuf.writeShort(index);
            //Reply message serial number
            bodyBuf.writeShort(msgFlowId);
            //Reply message ID
            bodyBuf.writeShort(0x0200);
            //Response result
            bodyBuf.writeByte(0x00);
            //check code
            int checkCode = CommonUtil.xor(bodyBuf);
            bodyBuf.writeByte(checkCode);
            //packet header
            replyBuf.writeByte(Constant.BINARY_MSG_HEADER);
            //The read pointer is reset to the starting position
            bodyBuf.readerIndex(0);
            //escape
            PacketUtil.escape(replyBuf, bodyBuf);
            //packet end
            replyBuf.writeByte(Constant.BINARY_MSG_HEADER);
            return ByteBufUtil.hexDump(replyBuf);
        } catch (Exception e) {
            ReferenceCountUtil.release(replyBuf);
            return "";
        }
    }

    /**
     * Timing command reply
     * @param itemList
     */
    public static String replyBASE2Message(List<String> itemList) {
        try {
            //set date format
            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:digital manipulation tools

package com.jointech.sdk.jt705.utils;

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

/**
 * digital tools
 * @author HyoJung
 * @date 20210526
 */
public class NumberUtil {
    /**
     *  Coordinate accuracy
     */
    public static final BigDecimal COORDINATE_PRECISION = new BigDecimal("0.000001");

    /**
     *  Coordinate factor
     */
    public static final BigDecimal COORDINATE_FACTOR = new BigDecimal("1000000");

    /**
     * One decimal place precision
     */
    public static final BigDecimal ONE_PRECISION = new BigDecimal("0.1");

    private NumberUtil() {
    }

    /**
     *  Format message ID (convert to 0xXXXX)
     *
     * @param msgId Message ID
     * @return return format string
     */
    public static String formatMessageId(int msgId) {
        return String.format("0x%04x", msgId);
    }

    /**
     * format short numbers
     *
     * @param num number
     * @return format string
     */
    public static String formatShortNum(int num) {
        return String.format("0x%02x", num);
    }

    /**
     * Convert 4-digit hexadecimal string
     *
     * @param num digits
     * @return format string
     */
    public static String hexStr(int num) {
        return String.format("%04x", num).toUpperCase();
    }

    /**
     * Parse the value of type short and get the number of digits whose value is 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;
    }

    /**
     * Parse the value of type int and get the number of digits whose value is 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;
    }

    /**
     *  Get the value of the index-th bit in binary
     *
     * @param number
     * @param index
     * @return
     */
    public static int getBitValue(long number, int index) {
        return (number & (1 << index)) > 0 ? 1 : 0;
    }

    /**
     *  bit list to 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 list to 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 Multiplication
     *
     * @param longNum
     * @param precision
     * @return
     */
    public static double multiply(long longNum, BigDecimal precision) {
        return new BigDecimal(String.valueOf(longNum)).multiply(precision).doubleValue();
    }

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

CommonUtil:public method class

package com.jointech.sdk.jt705.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: Used to store some public methods encountered in parsing</p>
 *
 * @author lenny
 * @version 1.0.1
 * @date 20210328
 */
public class CommonUtil {
    /**
     * remove the last character of a string
     * @param inStr Input string
     * @param suffix characters to remove
     * @return
     */
    public static String trimEnd(String inStr, String suffix) {
        while(inStr.endsWith(suffix)){
            inStr = inStr.substring(0,inStr.length()-suffix.length());
        }
        return inStr;
    }

    /**
     * Hexadecimal to 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();
    }

    /**
     * Convert GPS time
     *
     * @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;
    }

    /**
     * Each byte is XORed
     *
     * @param buf
     * @return
     */
    public static int xor(ByteBuf buf) {
        int checksum = 0;
        while (buf.readableBytes() > 0) {
            checksum ^= buf.readUnsignedByte();
        }
        return checksum;
    }
}

BaseEnum:base enumeration

package com.jointech.sdk.jt705.base;

import java.io.Serializable;

/**
 * base enumeration
 * @author HyoJung
 */
public interface BaseEnum  <T> extends Serializable {
    T getValue();
}

Constant:custom constant

package com.jointech.sdk.jt705.constants;

/**
 * constant definition
 * @author HyoJung
 * @date 20210526
 */
public class Constant {
    private Constant(){}
    /**
     * binary message header
     */
    public static final byte BINARY_MSG_HEADER = 0x7E;
    /**
     * Base length without message body
     */
    public static final int BINARY_MSG_BASE_LENGTH = 15;

    /**
     * message length
     */
    public static final int BINARY_MSG_MAX_LENGTH = 102400;

    /**
     * text message header
     */
    public static final byte TEXT_MSG_HEADER = '(';

    /**
     * text message tail
     */
    public static final byte TEXT_MSG_TAIL = ')';

    /**
     * text message delimiter
     */
    public static final byte TEXT_MSG_SPLITER = ',';
}

AlarmTypeEnum:Device alarm type enumeration

package com.jointech.sdk.jt705.model;

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

/**
 * alarm type enumeration
 * @author HyoJung
 */

public enum AlarmTypeEnum implements BaseEnum<String> {

    ALARM_1("Speeding alarm", "1"),
    ALARM_2("Low Battery alarm", "2"),
    ALARM_3("Main unit Cover opening alarm", "3"),
    ALARM_4("Enter fence alarm", "4"),
    ALARM_5("Exit fence alarm", "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:Positiondata Entity Classes

package com.jointech.sdk.jt705.model;

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

import java.io.Serializable;

/**
 * <p>Description: Positiondata Entity Classes</p>
 *
 * @author lenny
 * @version 1.0.1
 */
@Data
public class LocationData implements Serializable {
    /**
     * Message body
     */
    @JSONField(name = "DataLength")
    public int DataLength;
    /**
     * Posintioning time
     */
    @JSONField(name = "GpsTime")
    public String GpsTime;
    /**
     * latitude
     */
    @JSONField(name = "Latitude")
    public double Latitude;
    /**
     * longitude
     */
    @JSONField(name = "Longitude")
    public double Longitude;
    /**
     * Positioning type
     */
    @JSONField(name = "LocationType")
    public int LocationType;
    /**
     * Speed
     */
    @JSONField(name = "Speed")
    public int Speed;
    /**
     * Direction(Header)
     */
    @JSONField(name = "Direction")
    public int Direction;
    /**
     * Mileage
     */
    @JSONField(name = "Mileage")
    public long Mileage;
    /**
     * Altitude
     */
    @JSONField(name = "Altitude")
    public int Altitude;
    /**
     * GPS signal
     */
    @JSONField(name = "GpsSignal")
    public int GpsSignal;
    /**
     * GSM signal quatity
     */
    @JSONField(name = "GSMSignal")
    public int GSMSignal;
    /**
     * Battery level 
     */
    @JSONField(name = "Battery")
    public int Battery;
    /**
     * Batttery voltage
     */
    @JSONField(name = "Voltage")
    public double Voltage;
    /**
     * Lock status
     */
    @JSONField(name = "LockStatus")
    public int LockStatus;
    /**
     * Lock rope status
     */
    @JSONField(name = "LockRope")
    public int LockRope;
    /**
     * Back Cover status
     */
    @JSONField(name = "BackCover")
    public int BackCover;
    /**
     * Protocol version
     */
    @JSONField(name = "ProtocolVersion")
    public int ProtocolVersion;
    /**
     * Fence 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;
    /**
     * Lock&unlock event
     */
    @JSONField(name = "LockEvent")
    public LockEvent LockEvent;
    /**
     * attitude
     */
    @JSONField(name = "Posture")
    public String Posture;
    /**
     * Data Serial number
     */
    @JSONField(name = "Index")
    public int Index;
}

LockEvent:lock event entity class

package com.jointech.sdk.jt705.model;

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

/**
 * lock event
 * @author HyoJung
 */
@Data
public class LockEvent {
    /**
     * Event type
     */
    @JSONField(name = "Type")
    public int Type;
    /**
     * Swipe RFID card number
     */
    @JSONField(name = "CardNo")
    public String CardNo;
    /**
     * Unlock password
     */
    @JSONField(name = "Password")
    public String Password;
    /**
     * Unlocking status(1:success;0:failed)
     */
    @JSONField(name = "UnLockStatus")
    public int UnLockStatus=0;
    /**
     * Fence ID related to unlocking
     */
    @JSONField(name = "FenceId")
    public int FenceId=-1;
}

Result:result entity class

package com.jointech.sdk.jt705.model;

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

import java.io.Serializable;

/**
 * result entity class
 * @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.Return messages and instructions

(1)heartbeat data
Rawdata:

7E000200007501804283100001267E

Return message:

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

Return message description

{"DeviceID":DeviceID,"MsgType":Messagetype(heartbeat:heartbeat)}

(2)Position data
Rawdata(without lock&unlock event):

7E020000428560090010140E56000000002234004201588F9506CA3D93000B00120000201210073024D40127D502017B30011E310106FE040000001BFD090000000000000000000C0600000000FFDFD47E

Return message:

{
    "DeviceID": "856009001014",
    "DataBody": {
        "GpsTime": "2020-12-10T07:30:24Z",
        "MNC": 0,
        "FenceId": 0,
        "BackCover": 0,
        "Index": 3670,
        "Latitude": 22.581141,
        "Awaken": 2,
        "ProtocolVersion": 0,
        "Direction": 0,
        "Posture": "x:0;y:0;z:-33",
        "Battery": 39,
        "GpsSignal": 6,
        "Voltage": 3.79,
        "Speed": 1,
        "LockStatus": 1,
        "Mileage": 27,
        "MCC": 0,
        "Longitude": 113.917331,
        "LAC": 0,
        "Alarm": -1,
        "DataLength": 66,
        "CELLID": 0,
        "LockRope": 1,
        "LocationType": 1,
        "Altitude": 11,
        "GSMSignal": 30
    },
    "ReplyMsg": "7e80010005856009001014d81b0e56020000f57e",
    "MsgType": "Location"
}

Return message description

{
    "DeviceID": "790011000094",
    "DataBody": {
        "GpsTime": Positioning time(UTC time),
        "Latitude": latitude(WGS84),
        "Longitude": longitude(WGS84),
        "MCC": MCC,
        "MNC": MNC,
        "LAC": LAC,
        "CELLID": CELLID,
        "FenceId": Geo-Fence ID,
        "BackCover": BackCover status(1:Open;0:Close),
        "Index": Data Serial number,
        "Awaken":Wake-up source (1: RTC alarm wake-up, 2: Gsens vibration wake-up 3: open back cover wake-up 4: rope-cut wake-up 5: charging wake-up 6: swipe card wake-up 7: Lora wake-up 8: VIP number wake-up 9: non-VIP wake-up 10: Bluetooth wake-up )
        "ProtocolVersion":Protocol version,
        "Direction": True North is 0, clockwise0-360,
        "Battery": Battery percentage(0~100%),
        "GpsSignal":Number of satellites currently received by GPS,
        "Voltage": Battery Voltage,unit: V,
        "Speed": Speed, unit:km/h,
        "LockStatus": DeviceLock Status(0:locked;1:unlock),
        "LockRope": Lock rope status(0:Inserted;1:Pull out),
        "Mileage": Mileage,Unit:km,
        "Alarm": Alarm type (1: Overspeed alarm; 2: Low power alarm; 3: Back cover open alarm; 4: Entering the fence alarm; 5: Exiting the fence alarm),
        "DataLength": Data length(Bytes),
        "LocationType": Positioning mode (0: no positioning; 1: GPS positioning; 2: base station positioning),
        "Altitude": Altitude,unit:km,
        "GSMSignal":GSM signal,
        "Posture":Attitude (x: plus 180 degrees, minus 180 degrees; y: plus 90 degrees, minus 90 degrees; z: plus 180 degrees, minus 180 degrees)
    },
    "ReplyMsg": The content of the device that needs to be replied to (empty means no reply is required)(platform response),
    "MsgType": Data type(Location:Position data)
}

Rawdata(data with lock&lock event):

7E0200004C8560090010140E54000000000224004201588F9506CA3D93000B00120000201210073017D40127D502017B30011E310106FE040000001BFD090000000000000000000B0805383838383838650C0600000000FFC5A27E

Return message:

{
    "DeviceID": "856009001014",
    "DataBody": {
        "GpsTime": "2020-12-10T07:30:17Z",
        "MNC": 0,
        "FenceId": 0,
        "BackCover": 0,
        "Index": 3668,
        "Latitude": 22.581141,
        "Awaken": 2,
        "ProtocolVersion": 0,
        "Direction": 0,
        "Posture": "x:0;y:0;z:-59",
        "Battery": 39,
        "GpsSignal": 6,
        "Voltage": 3.79,
        "LockEvent": {
            "Type": 5,
            "FenceId": -1,
            "UnLockStatus": 1,
            "Password": "888888"
        },
        "Speed": 1,
        "LockStatus": 1,
        "Mileage": 27,
        "MCC": 0,
        "Longitude": 113.917331,
        "LAC": 0,
        "Alarm": -1,
        "DataLength": 76,
        "CELLID": 0,
        "LockRope": 0,
        "LocationType": 1,
        "Altitude": 11,
        "GSMSignal": 30
    },
    "ReplyMsg": "7e80010005856009001014c8750e54020000897e",
    "MsgType": "Location"
}

Return message description

{
    "DeviceID": "790011000094",
    "DataBody": {
        "GpsTime": Positioning time(UTC time),
        "Latitude": latitude(WGS84),
        "Longitude": longitude(WGS84),
        "MCC": MCC,
        "MNC": MNC,
        "LAC": LAC,
        "CELLID": CELLID,
        "FenceId": Geo-Fence ID,
        "BackCover": BackCover status(1:Open;0:Close),
        "Index": Data Serial number,
        "Awaken":Wake-up source (1: RTC alarm wake-up, 2: Gsens vibration wake-up 3: open back cover wake-up 4: rope-cut wake-up 5: charging wake-up 6: swipe card wake-up 7: Lora wake-up 8: VIP number wake-up 9: non-VIP wake-up 10: Bluetooth wake-up )
        "ProtocolVersion":Protocol version,
        "Direction": True North is 0, clockwise0-360,
        "Battery": Battery percentage(0~100%),
        "GpsSignal":Number of satellites currently received by GPS,
        "Voltage": Battery Voltage,unit: V,
        "Speed": Speed, unit:km/h,
        "LockStatus": DeviceLock Status(0:locked;1:unlock),
        "LockRope": Lock rope status(0:Inserted;1:Pull out),
        "Mileage": Mileage,Unit:KM,
        "Alarm": Alarm type (1: Overspeed alarm; 2: Low power alarm; 3: Back cover open alarm; 4: Entering the fence alarm; 5: Exiting the fence alarm),
        "LockEvent": {
            "Type": Event type(Refer to below Table1),
            "FenceId": Fence ID associated with the event (-1: no fence related),
            "UnLockStatus": Unlock status (1: unlock successful; 0: unlock failed),
            "Password": Unlock password
            "CardNo":If the event type is swipe to unlock, here is the RFID card number
        },
        "DataLength": Data length(Bytes),
        "LocationType": Positioning mode (0: no positioning; 1: GPS positioning; 2: base station positioning),
        "Altitude": Altitude,unit:km,
        "GSMSignal":GSM signal,
        "Posture":Attitude (x: plus 180 degrees, minus 180 degrees; y: plus 90 degrees, minus 90 degrees; z: plus 180 degrees, minus 180 degrees)
    },
    "ReplyMsg": The content of the device that needs to be replied to (empty means no reply is required)(platform response),
    "MsgType": Data type(Location:Position data)
}

(3)Command data parse
Rawdata:

283835363030393030313035382C362C3030312C424153452C322C323032313037323430353531353529

Retrun message:

{
    "DeviceID": "856009001058",
    "DataBody": "(856009001058,6,001,BASE,2,20210724055155)",
    "ReplyMsg": "",
    "MsgType": "BASE2"
}

Retrun message description

{
    "DeviceID":DeviceID,
    "DataBody":Message content,
    "ReplyMsg": Reply to the device's message (empty means no reply is required)(platform response),
    "MsgType": command type
}

Table1

EventID(HEX) EventID Event description
0x01 1 Static password remote unlock
0x02 2 Dynamic password remote unlocking
0x03 3 Dynamic password on-site (Bluetooth or WIFI) APP unlock
0x05 5 Indicates static password on-site (Bluetooth or WIFI) APP unlocking
0x06 6 Wrong static password remote unlock
0x07 7 Wrong dynamic password remote unlock
0x08 8 Wrong dynamic password on-site (Bluetooth or WIFI) APP unlock
0x0B 11 Long unlock event
0x0C 12 Lock rope cutting event
0x0D 13 Lock events (automatic locked)
0x10 16 The remote execution of unlocking is abnormal, and the unlocking is not executed without positioning
0x11 17 The remote execution of unlocking is abnormal, and the unlocking will not be executed if it is positioned outside the fence
0x12 18 Abnormal motor
0x18 24 Unlocking is abnormal in Bluetooth execution, and unlocking is not performed without positioning
0x19 25 The Bluetooth unlocking is abnormal, and the unlocking is not performed if it is positioned outside the fence
0x1C 28 Unlock and pull out the lock rope
0x1E 30 SMS static password remote unlock
0x1F 31 SMS dynamic password remote unlock
0x20 32 Wrong SMS dynamic password
0x22 34 Swipe the authorization card to unlock the event
0x23 35 Swipe illegal card unlock event
0x28 40 Wrong static password on-site (Bluetooth or WIFI) APP unlock
0x29 41 Wrong SMS static password to unlock
0x2A 42 RFID performs unlocking abnormally, and does not perform unlocking without positioning
0x2B 43 RFID performs unlocking abnormally, if it is positioned outside the fence, it does not perform unlocking
文档更新时间: 2024-06-25 17:11   作者:admin