Android 串口开发——粘包解决方法,定时查询心跳数据,解析心跳数据。——持续更新中

粘包解决方法

 方法1

getXOR——是校验方法

 

/**
 * 最小数据包的长度(除开数据的N个字节)
 * 帧头      保留字节     协议控制字    地址字段     命令长度   命令码     命令数据     校验和
 * 2字节 3字节      1字节      2或8字节    2字节      2字节      0-1100字节 2字节
 * SYN      RES       PTROL     ADDR      SLEN      COMMAND   APPDATA       CRC16
 */
int MIN_PACK_LEN = 2 + 3 + 1 + 2 + 2 + 2 + 2;
/**
 * 数据:0-N字节,N小于等于250
 */
int SLEN = 1100;
int MAX_DATA_N = 255;

 

private final ByteBuffer mByteBuffer;

public SerialReadThread(InputStream is) {
    mInputStream = new BufferedInputStream(is);
    mByteBuffer = ByteBuffer.allocate(1024);
    mByteBuffer.clear();
}

 

private boolean isValid(byte[] recvCheck, byte[] myCheck) {

    //LgqLogutil.d("rr===  "+ MyByteUtil.bytes2HexStr(recvCheck)+",,,m==="+MyByteUtil.bytes2HexStr(myCheck));

    for (int i = 0, n = recvCheck.length; i < n; ++i) {
        if (recvCheck[i] != myCheck[i]) {
            return false;
        }
    }
    return true;
}
/**
 * 处理获取到的数据
 *
 * @param received
 * @param size
 */
private void onDataReceive(byte[] received, int size) {
    // TODO: 2018/3/22 解决粘包、分包等

    try {
        //LogPlus.e("Receiver", "接收数据=" + ByteUtil.bytes2HexStr(bytes, offset, length));
        byte b;
        int readable;
        mByteBuffer.put(received, 0, size);
        mByteBuffer.flip();
        out:
        while ((readable = mByteBuffer.remaining()) >= Protocol.MIN_PACK_LEN) { /*只处理数据帧, 不处理响应帧*/
            mByteBuffer.mark(); // 标记一下开始的位置
            int frameStart = mByteBuffer.position();
            byte[] frameHead = Protocol.FRAME_HEAD;
            for (byte h : frameHead) {
                b = mByteBuffer.get();
                if (b != h) { // 不满足帧头,就跳到第二位,重新开始
                    mByteBuffer.position(frameStart + 1);
                    continue out;
                }
            }

            byte[] dataLengthBytes = new byte[] { mByteBuffer.get(8), mByteBuffer.get(9) };//第9个字节 为 数据长度.两个字节,所以是8,9
            int dataLen = (int) ByteUtil.bytes2long(dataLengthBytes, 0, 2); /*dataLen已经包含了*/
            if (dataLen > Protocol.MAX_DATA_N) {
                // 如果data n超过最大允许长度,表示数据错乱了
                //则回到“第二位”,继续找到下一个 帧头
                mByteBuffer.position(frameStart + 2);
                continue;
            }

            int total = Protocol.MIN_PACK_LEN + dataLen - 2; /*-2 最小数据包长度中已经包含, dataLen中有最小数据包长度中的2个字节*/
            // 如果可读数据小于总数据长度,表示不够,还有数据没接收
            if (readable < total) {
                // 重置一下要处理的位置,并跳出循环
                mByteBuffer.reset();
                break;
            }
            mByteBuffer.reset();
            // 拿到整个包
            byte[] allPack = new byte[total];
            mByteBuffer.get(allPack);

            byte[] recvCheck = new byte[2];
            recvCheck[0] = allPack[allPack.length - 2];
            recvCheck[1] = allPack[allPack.length - 1];
            byte[] myCheck = ByteUtil.getXOR(allPack, allPack.length - 2);
            if (isValid(recvCheck, myCheck)) {// 校验通过
                /**/
                LgqLogutil.d("#####校验通过,allPack:" + ByteUtil.bytes2HexStr(allPack));
                //validData.add(allPack);
                LogManager.instance().post(new RecvMessage(ByteUtil.bytes2HexStr(allPack)));

            } else {
                mByteBuffer.position(frameStart + 2);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 最后清掉之前处理过的不合适的数据
        mByteBuffer.compact();
    }

    //String hexStr = ByteUtil.bytes2HexStr(received, 0, size);
    //String hexStr = ByteUtil.bytes2HexStr(allPack, 0, size);
    //LogManager.instance().post(new RecvMessage(hexStr));
}

 

 粘包解决方法2

 

 

try {
    //LogPlus.e("Receiver", "接收数据=" + ByteUtil.bytes2HexStr(bytes, offset, length));
    byte b;
    int readable;
    mByteBuffer.put(bytes, 0, length);
    mByteBuffer.flip();
    out:
    while ((readable = mByteBuffer.remaining()) >= Protocol.MIN_PACK_LEN) { /*只处理数据帧, 不处理响应帧*/
        mByteBuffer.mark(); // 标记一下开始的位置
        int frameStart = mByteBuffer.position();
        byte[] frameHead = Protocol.FRAME_HEAD;
        for (byte h : frameHead) {
            b = mByteBuffer.get();
            if (b != h) { // 不满足帧头,就跳到第二位,重新开始
                mByteBuffer.position(frameStart + 1);
                continue out;
            }
        }

        byte[] dataLengthBytes = new byte[]{mByteBuffer.get(4)};//第5个字节 为 数据长度
        int dataLen = (int) ByteUtil.bytes2long(dataLengthBytes, 0, 1); /*dataLen已经包含了*/
        if (dataLen > Protocol.MAX_DATA_N) {
            // 如果data n超过最大允许长度,表示数据错乱了
            //则回到“第二位”,继续找到下一个 帧头
            mByteBuffer.position(frameStart + 2);
            continue;
        }

        int total = Protocol.MIN_PACK_LEN + dataLen - 2; /*-2 最小数据包长度中已经包含, dataLen中有最小数据包长度中的2个字节*/
        // 如果可读数据小于总数据长度,表示不够,还有数据没接收
        if (readable < total) {
            // 重置一下要处理的位置,并跳出循环
            mByteBuffer.reset();
            break;
        }
        mByteBuffer.reset();
        // 拿到整个包
        byte[] allPack = new byte[total];
        mByteBuffer.get(allPack);

        byte recvCheck = allPack[allPack.length - 1];
        byte myCheck = ByteUtil.getXOR(allPack, 0, allPack.length - 1);
        if (myCheck == recvCheck) { // 校验通过
            /**/
            LogPlus.d("#####校验通过,allPack:" + ByteUtil.bytes2HexStr(allPack));
            validData.add(allPack);
        } else {
            mByteBuffer.position(frameStart + 2);
        }
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    // 最后清掉之前处理过的不合适的数据
    mByteBuffer.compact();
}

 

/**
 * 字节数组转换成对应的16进制表示的字符串
 *
 * @param src
 * @return
 */
public static String bytes2HexStr(byte[] src) {
    StringBuilder builder = new StringBuilder();
    if (src == null || src.length <= 0) {
        return "";
    }
    char[] buffer = new char[2];
    for (int i = 0; i < src.length; i++) {
        buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
        buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
        builder.append(buffer);
    }
    return builder.toString().toUpperCase();
}

 1、心跳数据强转为 byte[] 数据类型

@Override
public RecvCommand adaptReceive(byte[] allPack, Object... other) {
    int cmd = (int) other[0];
    LogPlus.e( "收到的命令:" +cmd);
    byte[] data = (byte[]) other[1];

 

2、开始解析——必须根据解析协议才能解析数据

public class RecvA3Status extends BaseRecvCommand {

    private final int mResult;

    private final float mPowerFactorState;
    public List<AuxiliaryPlate> auxiliaryPlates;

    public RecvA3Status(byte[] allPack, byte[] data) {
        super(allPack, data);
        mResult = 0xff & data[0];

        mPowerFactorState = ByteUtil.getFloat(data, 25);

        auxiliaryPlates = new ArrayList<>();
        for (int i = 0; i < (data.length - 29) / 107; i++) {
            byte[] bytes = new byte[107];
            System.arraycopy(data, 29 + i * 107, bytes, 0, bytes.length);
            if (i==0){
                LogPlus.e("几号门==="+i, "门状态11接收数据2仓门=" + ByteUtil.bytes2HexStr(bytes));
            }else {
                LogPlus.i("几号门==="+i, "门状态11接收数据2仓门=" + ByteUtil.bytes2HexStr(bytes));

            }

            auxiliaryPlates.add(new AuxiliaryPlate(bytes));
        }
    }

    /**
     * 仓控板状态表
     */
    public static class AuxiliaryPlate {
        private int onlineState;
        private int doorState;
        private int batteryState;
        private int fireState;
        private int urgentState;
        private int temperature;
        private int batteryVoltage;
        private int chargerOnlineState;
        private ChargerState chargerState;
        private OnOffAmount onOffAmount;
        private AnalogQuantity analogQuantity;

        private int BMSOnlineState;
        private String BMSID;

        /**type 0 = 正常电池样式  1 = 显示屏样式 */
        public int type = 0;

        public AuxiliaryPlate(byte[] bytes) {
            onlineState = 0xff & bytes[0];
            doorState = 0xff & bytes[1];
            batteryState = 0xff & bytes[2];
            fireState = 0xff & bytes[3];
            urgentState = 0xff & bytes[4];
            temperature = 0xff & bytes[5];
            batteryVoltage = ByteUtil.bytes2long(bytes, 6, 2) / 100;
            chargerOnlineState = 0xff & bytes[8];

            byte[] bytes1 = new byte[6];
            System.arraycopy(bytes, 9, bytes1, 0, bytes1.length);
            chargerState = new ChargerState(bytes1);
            BMSOnlineState = 0xff & bytes[15];

            byte[] bytes2 = new byte[26];
            System.arraycopy(bytes, 16, bytes2, 0, bytes2.length);
            BMSID = new String(bytes2, StandardCharsets.US_ASCII).trim();

            byte[] bytes3 = new byte[7];
            System.arraycopy(bytes, 42, bytes3, 0, bytes3.length);
            onOffAmount = new OnOffAmount(bytes3);

            byte[] bytes4 = new byte[58];
            System.arraycopy(bytes, 49, bytes4, 0, bytes4.length);
            analogQuantity = new AnalogQuantity(bytes4);
        }

 

付解析协议:

 

 

实现demo:https://download.csdn.net/download/meixi_android/12707609 

bug在线交流: QQ1085220040

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值