Browse Source

1. 串口工具类新增会话ID和流水ID的概念
2. 优化串口数据发送逻辑,添加数据和状态的同步锁以保证数据传输准确性

王育民 5 years ago
parent
commit
3b102ac5c5

+ 1 - 1
app/src/main/java/cn/minbb/serial/activities/MainActivity.java

@@ -93,7 +93,7 @@ public class MainActivity extends AppCompatActivity {
     public void onClick(View view) throws Exception {
         switch (view.getId()) {
             case R.id.open_serial_port:
-                if (serialPortManager.openDefaultSerialPort()) {
+                if (null != serialPortManager.openDefaultSerialPort()) {
                     Toast.makeText(getApplicationContext(), "串口打开成功", Toast.LENGTH_SHORT).show();
                     openSerial.setEnabled(false);
                     closeSerial.setEnabled(true);

+ 95 - 73
serialport/src/main/java/cn/minbb/serialport/SerialPortManager.java

@@ -13,9 +13,12 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.Serializable;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.UUID;
 
 import cn.minbb.serialport.app.DataType;
 import cn.minbb.serialport.listener.OnDataProgressListener;
@@ -24,6 +27,7 @@ import cn.minbb.serialport.listener.OnSerialPortDataListener;
 import cn.minbb.serialport.thread.SerialPortReadThread;
 import cn.minbb.serialport.utils.ByteUtil;
 import cn.minbb.serialport.utils.LogUtil;
+import cn.minbb.serialport.utils.PackageUtil;
 
 /**
  * 串口管理器 - 单例模式  防止 反射 克隆 序列化 破坏
@@ -39,7 +43,6 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
     private OnDataProgressListener onDataProgressListener;
 
     private HandlerThread mSendingHandlerThread;
-    private Handler mSendingHandler;
     private SerialPortReadThread mSerialPortReadThread;
 
     private SerialPortStatus serialPortStatus = SerialPortStatus.CLOSE;
@@ -48,6 +51,12 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
     private static int LENGTH = 64;
     // 超时时间
     private static int TIMEOUT = 5000;
+    // 会话 ID
+    private String sessionId = null;
+    // 流水 ID
+    private String serialId = null;
+    // 字节缓冲区列表
+    private List<byte[]> bytesBufferList = new ArrayList<>();
 
     // 默认是第一次创建
     private static volatile boolean isCreate = false;
@@ -76,10 +85,61 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
     }
 
     /**
-     * 获取当前串口状态
+     * 同步设置串口状态
      */
-    public SerialPortStatus getSerialPortStatus() {
-        return serialPortStatus;
+    private synchronized void setSerialPortStatus(SerialPortStatus serialPortStatus) {
+        this.serialPortStatus = serialPortStatus;
+    }
+
+    /**
+     * 同步获取当前串口状态
+     */
+    public synchronized SerialPortStatus getSerialPortStatus() {
+        return this.serialPortStatus;
+    }
+
+    /**
+     * 同步获取会话 ID
+     */
+    public synchronized String getSessionId() {
+        return sessionId;
+    }
+
+    /**
+     * 同步设置流水号
+     */
+    public synchronized void setSerialId(String serialId) {
+        this.serialId = serialId;
+    }
+
+    /**
+     * 同步获取流水号
+     */
+    public synchronized String getSerialId() {
+        return serialId;
+    }
+
+    /**
+     * 同步获取字节缓冲区列表
+     */
+    public synchronized List<byte[]> getBytesBufferList() {
+        return bytesBufferList;
+    }
+
+    /**
+     * 收到串口数据调用,同步锁
+     *
+     * @param bytes 收到的 Byte 数组
+     */
+    private synchronized void dataBytesReceived(byte[] bytes) {
+        bytesBufferList.add(bytes);
+    }
+
+    /**
+     * 清空字符缓冲区列表
+     */
+    private synchronized void dataBytesReceivedOver() {
+        bytesBufferList.clear();
     }
 
     /**
@@ -97,13 +157,20 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
      * 串口设备
      * 波特率
      *
-     * @return 打开是否成功
+     * @return 打开是否成功返回会话 ID,失败返回 null
      */
-    public boolean openDefaultSerialPort() {
+    public String openDefaultSerialPort() {
         return openCustomSerialPort(new File("/dev/ttyS1"), 115200);
     }
 
-    public boolean openCustomSerialPort(File device, int baudRate) {
+    /**
+     * 打开指定波特率的串口
+     *
+     * @param device   路径
+     * @param baudRate 波特率
+     * @return 打开是否成功返回会话 ID,失败返回 null
+     */
+    public String openCustomSerialPort(File device, int baudRate) {
         Log.i(TAG, "openSerialPort: " + String.format("打开串口 %s 波特率 %s", device.getPath(), baudRate));
         // 校验串口权限
         if (!device.canRead() || !device.canWrite()) {
@@ -113,7 +180,7 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
                 if (null != mOnOpenSerialPortListener) {
                     mOnOpenSerialPortListener.onFail(device, OnOpenSerialPortListener.Status.NO_READ_WRITE_PERMISSION);
                 }
-                return false;
+                return null;
             }
         }
 
@@ -129,15 +196,15 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
             startSendThread();
             // 开启接收消息的线程
             startReadThread();
-            serialPortStatus = SerialPortStatus.OPEN;
-            return true;
+            setSerialPortStatus(SerialPortStatus.OPEN);
+            return UUID.randomUUID().toString();
         } catch (Exception e) {
             e.printStackTrace();
             if (null != mOnOpenSerialPortListener) {
                 mOnOpenSerialPortListener.onFail(device, OnOpenSerialPortListener.Status.OPEN_FAIL);
             }
         }
-        return false;
+        return null;
     }
 
     /**
@@ -173,7 +240,8 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
 
         mOnOpenSerialPortListener = null;
         mOnSerialPortDataListener = null;
-        serialPortStatus = SerialPortStatus.CLOSE;
+        sessionId = null;
+        setSerialPortStatus(SerialPortStatus.CLOSE);
     }
 
     /**
@@ -205,54 +273,12 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
         // 开启发送消息的线程
         mSendingHandlerThread = new HandlerThread("mSendingHandlerThread");
         mSendingHandlerThread.start();
-        // Handler
-//        mSendingHandler = new Handler(mSendingHandlerThread.getLooper()) {
-//            @Override
-//            public void handleMessage(Message msg) {
-//                try {
-////                    byte[] sendBytes = new byte[64];
-//                    Data data = (Data) msg.obj;
-//                    switch (data.getDataType()) {
-//                        case BYTES:
-//                            SendDataBytesImpl sendDataBytesImpl = new SendDataBytesImpl();
-//                            SendDataContext<byte[]> contextBytes = new SendDataContext<>(mFileInputStream, mFileOutputStream, onDataProgressListener, sendDataBytesImpl);
-//                            contextBytes.doSend((byte[]) data.getData());
-//                            break;
-//                        case STRING:
-//                            SendDataStringImpl sendDataStringImpl = new SendDataStringImpl();
-//                            SendDataContext<String> contextString = new SendDataContext<>(mFileInputStream, mFileOutputStream, onDataProgressListener, sendDataStringImpl);
-//                            contextString.doSend((String) data.getData());
-//                            break;
-//                        case FILE:
-//                            SendDataFileImpl sendDataFileImpl = new SendDataFileImpl();
-//                            SendDataContext<File> contextFile = new SendDataContext<>(mFileInputStream, mFileOutputStream, onDataProgressListener, sendDataFileImpl);
-//                            contextFile.doSend((File) data.getData());
-//                            break;
-//                    }
-////                    if (null != mFileOutputStream && null != sendBytes && 0 < sendBytes.length) {
-////                        try {
-////                            mFileOutputStream.write(sendBytes);
-////                            if (null != mOnSerialPortDataListener) {
-////                                mOnSerialPortDataListener.onDataSent(sendBytes);
-////                            }
-////                        } catch (IOException e) {
-////                            e.printStackTrace();
-////                        }
-////                    }
-//                } catch (IOException e) {
-//                    throw new RuntimeException(e);
-//                } catch (Exception e) {
-//                    throw new RuntimeException(e);
-//                }
-//            }
-//        };
     }
 
     /**
      * 停止发送消息线程
      */
     private void stopSendThread() {
-        mSendingHandler = null;
         if (null != mSendingHandlerThread) {
             mSendingHandlerThread.interrupt();
             mSendingHandlerThread.quit();
@@ -263,21 +289,21 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
     /**
      * 开启接收消息的线程
      */
-    private static byte[] dataBytes = new byte[0];
-
     private void startReadThread() {
         mSerialPortReadThread = new SerialPortReadThread(mFileInputStream) {
             @Override
             public void onDataReceived(final byte[] bytes) {
+                // 设置接收流水号
+                if (null == getSerialId()) setSerialId(String.valueOf(System.currentTimeMillis()));
                 // 收到 EOF(-1) 或者超时则认为数据包接收完毕
                 // 串口空闲状态下收到数据,启动超时计时器,并修改串口状态为数据输入状态
-                if (SerialPortStatus.OPEN == serialPortStatus) {
-                    serialPortStatus = SerialPortStatus.DATA_IN;
+                if (SerialPortStatus.OPEN == getSerialPortStatus()) {
+                    setSerialPortStatus(SerialPortStatus.DATA_IN);
                     new Timer().schedule(new TimerTask() {
                         @Override
                         public void run() {
                             // 超时执行
-                            if (SerialPortStatus.DATA_IN == serialPortStatus) {
+                            if (SerialPortStatus.DATA_IN == getSerialPortStatus()) {
                                 // 解包收到的数据
                                 LogUtil.setAppendFile("接收超时");
                                 dataNotify();
@@ -370,6 +396,7 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
      */
     private void sendData(byte[] payload, DataType dataType) throws IOException {
         if (null != mFd && null != mFileInputStream && null != mFileOutputStream) {
+            setSerialPortStatus(SerialPortStatus.DATA_OUT);
             switch (dataType) {
                 case BYTES:
                     LogUtil.setAppendFile("发送字符数组:" + Arrays.toString(payload));
@@ -405,6 +432,7 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
                     }
                     break;
             }
+            setSerialPortStatus(SerialPortStatus.OPEN);
         } else {
             LogUtil.setAppendFile("数据流为空");
             throw new RuntimeException("数据流为空");
@@ -415,25 +443,19 @@ public class SerialPortManager extends SerialPort implements Serializable, Clone
      * 数据回调方法,当串口接收数据超时或者收到数据结束符时候调用,用于通知应用程序收到的数据
      */
     private void dataNotify() {
-        // 串口状态设置为空闲
-        serialPortStatus = SerialPortStatus.OPEN;
         if (null != mOnSerialPortDataListener) {
-            mOnSerialPortDataListener.onDataReceived(dataBytes, new String(dataBytes));
-            LogUtil.setAppendFile("Notify:" + new String(dataBytes) + Arrays.toString(dataBytes));
-            dataBytes = new byte[0];
+            // TODO: 2020-04-16 解包
+            String data = PackageUtil.analyze(getBytesBufferList(), getSerialId());
+            mOnSerialPortDataListener.onDataReceived(null, data);
         } else {
             LogUtil.setAppendFile("串口数据监听器未定义");
             throw new RuntimeException("串口数据监听器未定义");
         }
-    }
-
-    /**
-     * 收到串口数据调用,同步锁
-     *
-     * @param bytes 收到的 Byte 数组
-     */
-    private synchronized void dataBytesReceived(byte[] bytes) {
-        dataBytes = ByteUtil.addBytes(dataBytes, bytes);
+        dataBytesReceivedOver();
+        // 重置流水号
+        setSerialId(null);
+        // 串口状态设置为空闲
+        setSerialPortStatus(SerialPortStatus.OPEN);
     }
 
     @Override

+ 16 - 1
serialport/src/main/java/cn/minbb/serialport/utils/PackageUtil.java

@@ -3,7 +3,6 @@ package cn.minbb.serialport.utils;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.zip.CRC32;
 
 public class PackageUtil {
 
@@ -74,4 +73,20 @@ public class PackageUtil {
         }
         return bytesList;
     }
+
+    /**
+     * 数据包解析
+     *
+     * @param bytesList 数据包列表
+     * @param serialId  流水号
+     */
+    public static String analyze(List<byte[]> bytesList, String serialId) {
+        byte[] bytesBuffer = new byte[0];
+        for (byte[] bytes : bytesList) {
+            bytesBuffer = ByteUtil.addBytes(bytesBuffer, bytes);
+            LogUtil.setAppendFile("serialId:" + Arrays.toString(bytes));
+        }
+        LogUtil.setAppendFile("serialId:" + new String(bytesBuffer));
+        return serialId + ":" + new String(bytesBuffer);
+    }
 }