Преглед на файлове

1. 优化蓝牙扫描方式,单设备扫描,提高设备实时在线效率
2. 优化打印信息

bjb преди 1 месец
родител
ревизия
c2d5081deb
променени са 2 файла, в които са добавени 192 реда и са изтрити 143 реда
  1. 142 109
      app/src/main/java/com/iscs/bozzys/utils/ble/BleManager.kt
  2. 50 34
      app/src/main/java/com/iscs/bozzys/utils/ble/BleTask.kt

+ 142 - 109
app/src/main/java/com/iscs/bozzys/utils/ble/BleManager.kt

@@ -21,9 +21,12 @@ import com.iscs.bozzys.utils.byteArrayToHexString
 import com.iscs.bozzys.utils.hexToByteArray
 import kotlinx.coroutines.CancellableContinuation
 import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withTimeout
@@ -64,6 +67,11 @@ class BleManager(
     // 待响应式的请求数据接收池
     private val receiverPool = ConcurrentHashMap<String, CompletableDeferred<BleFrame>>()
 
+    // 是否已经连接
+    private var isConnected = false
+
+    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
+
     // 蓝牙扫描回调
     private val scanCallback = object : ScanCallback() {
 
@@ -88,108 +96,6 @@ class BleManager(
 
     }
 
-    /**
-     * 蓝牙Gatt连接回调
-     */
-    @OptIn(ExperimentalCoroutinesApi::class)
-    private val gattCallback = object : BluetoothGattCallback() {
-
-        @SuppressLint("MissingPermission")
-        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
-            if (newState == BluetoothProfile.STATE_CONNECTED) {
-                LogUtil.i(TAG, "gattCallback onConnectionStateChange() gatt connected")
-                gatt.discoverServices()
-            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
-                LogUtil.i(TAG, "gattCallback onConnectionStateChange() gatt disconnected")
-                if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
-                    this@BleManager.doneConnect?.resume(BleConnectResult(false), null)
-                    // 执行断开连接操作
-                    gatt.close()
-                    device = null
-                    this@BleManager.gatt = null
-                }
-            }
-        }
-
-        @SuppressLint("MissingPermission")
-        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
-            if (status == BluetoothGatt.GATT_SUCCESS) {
-                LogUtil.i(TAG, "gattCallback onServicesDiscovered() find services success")
-                // 执行设置mtu操作
-                gatt.requestMtu(mtu)
-            }
-        }
-
-        override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
-            super.onMtuChanged(gatt, mtu, status)
-            LogUtil.i(TAG, "gattCallback onMtuChanged() mtu $mtu")
-            Thread {
-                val indicateOk = enableIndicate()
-                SystemClock.sleep(1000)
-                if (needIndicate) {
-                    if (!(this@BleManager.doneConnect?.isCompleted ?: false)) this@BleManager.doneConnect?.resume(BleConnectResult(indicateOk), null)
-                } else {
-                    if (!(this@BleManager.doneConnect?.isCompleted ?: false)) this@BleManager.doneConnect?.resume(BleConnectResult(true), null)
-                }
-            }.start()
-        }
-
-        @SuppressLint("MissingPermission")
-        override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) {
-            super.onCharacteristicRead(gatt, characteristic, value, status)
-            LogUtil.i(TAG, "gattCallback onCharacteristicRead()")
-        }
-
-        override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {
-            super.onDescriptorRead(gatt, descriptor, status, value)
-            LogUtil.i(TAG, "gattCallback onDescriptorRead() data write success")
-        }
-
-        override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
-            super.onCharacteristicChanged(gatt, characteristic)
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) return
-            LogUtil.w(TAG, "gattCallback <--- ${characteristic?.value?.byteArrayToHexString(" ")}")
-            val data = characteristic?.value ?: byteArrayOf()
-            var key = ""
-            receiverPool.forEach { item ->
-                if (data.byteArrayToHexString().startsWith(item.key.split("_")[1])) {
-                    // 找到指定响应体
-                    key = item.key
-                    return@forEach
-                }
-            }
-            if (key.isNotEmpty()) {
-                val deferred = receiverPool.remove(key)
-                if (deferred != null && !deferred.isCompleted) {
-                    val spl = key.split("_")
-                    val rspCodeLen = spl[1].length / 2
-                    deferred.complete(BleFrame(spl[0].hexToByteArray(), data.copyOfRange(rspCodeLen, data.size), spl[1].hexToByteArray()))
-                }
-            }
-        }
-
-        override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {
-            super.onCharacteristicChanged(gatt, characteristic, value)
-            LogUtil.w(TAG, "gattCallback <--- ${value.byteArrayToHexString(" ")}")
-            var key = ""
-            receiverPool.forEach { item ->
-                if (value.byteArrayToHexString().startsWith(item.key.split("_")[1])) {
-                    // 找到指定响应体
-                    key = item.key
-                    return@forEach
-                }
-            }
-            if (key.isNotEmpty()) {
-                val deferred = receiverPool.remove(key)
-                if (deferred != null && !deferred.isCompleted) {
-                    val spl = key.split("_")
-                    val rspCodeLen = spl[1].length / 2
-                    deferred.complete(BleFrame(spl[0].hexToByteArray(), value.copyOfRange(rspCodeLen, value.size), spl[1].hexToByteArray()))
-                }
-            }
-        }
-    }
-
     /**
      * 协程方式的连接
      */
@@ -201,6 +107,23 @@ class BleManager(
         } else {
             this.device = device
             innerConnect()
+            timeoutDisconnect()
+        }
+    }
+
+    /**
+     * 超时未连接处理
+     */
+    private fun timeoutDisconnect() {
+        scope.launch {
+            delay(5000)
+            // 未连接成功执行断开连接操作
+            if (!isConnected) {
+                if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
+                    this@BleManager.doneConnect?.resume(BleConnectResult(false), null)
+                }
+                release()
+            }
         }
     }
 
@@ -241,10 +164,109 @@ class BleManager(
      */
     @SuppressLint("MissingPermission")
     private fun innerConnect() {
-        // context -> app
-        // autoConnect -> false 避免设备连接异常
         try {
-            gatt = device?.connectGatt(app, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
+            gatt = device?.connectGatt(app, false, object : BluetoothGattCallback() {
+
+                @SuppressLint("MissingPermission")
+                override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
+                    if (newState == BluetoothProfile.STATE_CONNECTED) {
+                        LogUtil.i(TAG, "$gatt gattCallback onConnectionStateChange() gatt connected")
+                        gatt.discoverServices()
+                    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+                        LogUtil.i(TAG, "$gatt gattCallback onConnectionStateChange() gatt disconnected")
+                        isConnected = false
+                        if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
+                            this@BleManager.doneConnect?.resume(BleConnectResult(false), null)
+                        }
+                        release()
+                    }
+                }
+
+                @SuppressLint("MissingPermission")
+                override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
+                    if (status == BluetoothGatt.GATT_SUCCESS) {
+                        LogUtil.i(TAG, "gattCallback onServicesDiscovered() find services success")
+                        // 执行设置mtu操作
+                        gatt.requestMtu(mtu)
+                    }
+                }
+
+                override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
+                    super.onMtuChanged(gatt, mtu, status)
+                    LogUtil.i(TAG, "gattCallback onMtuChanged() mtu $mtu")
+                    Thread {
+                        val indicateOk = enableIndicate()
+                        SystemClock.sleep(1000)
+                        if (needIndicate) {
+                            isConnected = indicateOk
+                            if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
+                                this@BleManager.doneConnect?.resume(BleConnectResult(indicateOk), null)
+                            }
+                        } else {
+                            isConnected = false
+                            if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
+                                this@BleManager.doneConnect?.resume(BleConnectResult(true), null)
+                            }
+                        }
+                    }.start()
+                }
+
+                @SuppressLint("MissingPermission")
+                override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) {
+                    super.onCharacteristicRead(gatt, characteristic, value, status)
+                    LogUtil.i(TAG, "gattCallback onCharacteristicRead()")
+                }
+
+                override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {
+                    super.onDescriptorRead(gatt, descriptor, status, value)
+                    LogUtil.i(TAG, "gattCallback onDescriptorRead() data write success")
+                }
+
+                override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
+                    super.onCharacteristicChanged(gatt, characteristic)
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) return
+                    LogUtil.w(TAG, "gattCallback <--- ${characteristic?.value?.byteArrayToHexString(" ")}")
+                    val data = characteristic?.value ?: byteArrayOf()
+                    var key = ""
+                    receiverPool.forEach { item ->
+                        if (data.byteArrayToHexString().startsWith(item.key.split("_")[1])) {
+                            // 找到指定响应体
+                            key = item.key
+                            return@forEach
+                        }
+                    }
+                    if (key.isNotEmpty()) {
+                        val deferred = receiverPool.remove(key)
+                        if (deferred != null && !deferred.isCompleted) {
+                            val spl = key.split("_")
+                            val rspCodeLen = spl[1].length / 2
+                            deferred.complete(BleFrame(spl[0].hexToByteArray(), data.copyOfRange(rspCodeLen, data.size), spl[1].hexToByteArray()))
+                        }
+                    }
+                }
+
+                override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {
+                    super.onCharacteristicChanged(gatt, characteristic, value)
+                    LogUtil.w(TAG, "gattCallback <--- ${value.byteArrayToHexString(" ")}")
+                    var key = ""
+                    receiverPool.forEach { item ->
+                        if (value.byteArrayToHexString().startsWith(item.key.split("_")[1])) {
+                            // 找到指定响应体
+                            key = item.key
+                            return@forEach
+                        }
+                    }
+                    if (key.isNotEmpty()) {
+                        val deferred = receiverPool.remove(key)
+                        if (deferred != null && !deferred.isCompleted) {
+                            val spl = key.split("_")
+                            val rspCodeLen = spl[1].length / 2
+                            deferred.complete(BleFrame(spl[0].hexToByteArray(), value.copyOfRange(rspCodeLen, value.size), spl[1].hexToByteArray()))
+                        }
+                    }
+                }
+            }, BluetoothDevice.TRANSPORT_LE)
+            LogUtil.d(TAG, "$gatt gatt created")
         } catch (e: Exception) {
             LogUtil.e(TAG, "connect() connect to device ${device?.address} failed $e")
         }
@@ -255,9 +277,20 @@ class BleManager(
      */
     @SuppressLint("MissingPermission")
     fun disconnect() {
-        device?.let {
-            gatt?.disconnect()
-        }
+        device?.let { gatt?.disconnect() }
+    }
+
+    /**
+     * 资源释放
+     */
+    @SuppressLint("MissingPermission")
+    private fun release() {
+        // 执行断开连接操作
+        gatt?.close()
+        gatt = null
+        device = null
+        doneConnect = null
+        isConnected = false
     }
 
     /**

+ 50 - 34
app/src/main/java/com/iscs/bozzys/utils/ble/BleTask.kt

@@ -55,9 +55,12 @@ object BleTask {
     // 设备上一次连接时间,用于防止设备频繁连接
     private val deviceLastConnectTimes: MutableMap<String, Long> = mutableMapOf()
 
-    // 正在使用中的设备
+    // 正在使用中的设备(在任务详情添加的设备)
     private val usedDevices: MutableMap<String, String> = mutableMapOf()
 
+    // 当前在队列中的设备
+    private val devices: MutableList<String> = mutableListOf()
+
     // 待处理的蓝牙设备
     private val deviceQueue = Channel<BluetoothDevice>(capacity = Channel.BUFFERED)
 
@@ -67,11 +70,15 @@ object BleTask {
     // 蓝牙扫描回调
     private val scanCallback = object : ScanCallback() {
 
-        override fun onBatchScanResults(results: List<ScanResult?>?) {
-            super.onBatchScanResults(results)
-            // 队列为空时,数据放入队列
-            if (!deviceQueue.tryReceive().isSuccess) results?.forEach {
-                if (it != null) deviceQueue.trySend(it.device)
+        override fun onScanResult(callbackType: Int, result: ScanResult?) {
+            super.onScanResult(callbackType, result)
+            result?.device?.let {
+                // 队列校验+同一设备5s内不进行处理操作
+                val times = System.currentTimeMillis() - deviceLastConnectTimes.getOrDefault(it.address, 0)
+                if (!devices.contains(it.address) && times >= 5000L) {
+                    devices += it.address
+                    deviceQueue.trySend(result.device)
+                }
             }
         }
 
@@ -86,7 +93,7 @@ object BleTask {
         bm = app.getSystemService(BluetoothManager::class.java)
         ba = bm.adapter
         scan()
-        scope.launch { for (device in deviceQueue) execTask(device) }
+        loop()
     }
 
     /**
@@ -119,23 +126,30 @@ object BleTask {
         val filters = ArrayList<ScanFilter>()
         filters.add(ScanFilter.Builder().setDeviceName("keyLock").build())
         val settings = ScanSettings.Builder()
-            .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
-            .setReportDelay(5000)
-            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
+            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
             .build()
         scan.startScan(filters, settings, scanCallback)
     }
 
+    /**
+     * 循环发现设备
+     */
+    private fun loop() {
+        scope.launch {
+            for (device in deviceQueue) {
+                execTask(device)
+                // 设备处理完成后,从列表中清除
+                devices.remove(device.address)
+                // 更新设备处理处理时间
+                deviceLastConnectTimes[device.address] = System.currentTimeMillis()
+            }
+        }
+    }
+
     /**
      * 执行设备的操作
      */
     private suspend fun execTask(device: BluetoothDevice) {
-        // 上一次处理过,间隔5s内的不再处理
-        val times = System.currentTimeMillis() - deviceLastConnectTimes.getOrDefault(device.address, 0)
-        if (times < 5000L) {
-            LogUtil.w("BleTask", "[${device.address}] 设备连接间隔短,等待下次执行连接")
-            return
-        }
         if (usedDevices.contains(device.address)) {
             if (usedDevices[device.address].isNullOrEmpty()) {
                 LogUtil.w("BleTask", "[${device.address}] 设备即将被使用,不做状态获取处理")
@@ -145,19 +159,19 @@ object BleTask {
             }
             return
         }
-        LogUtil.d("BleTask", "[${device.address}] 执行设备连接")
+        LogUtil.d("BleTask", "[${device.address}] 连接开始")
         val bm = BleManager(Entry.app, mac = device.address)
         val result = bm.connect(device)
         if (result.connected) {
             // 获取设备token
             val token = bm.writeByResponse(BleFrameExt.buildBLEGetTokenCMD()).getToken()
-            LogUtil.i("BleTask", "[${device.address}] 获取token:${token.byteArrayToHexString()}")
+            LogUtil.i("BleTask", "[${device.address}] 设备密钥 -> ${token.byteArrayToHexString()}")
             // 获取设备电量
             val power = bm.writeByResponse(token.buildBLEGetPowerCMD()).getPower()
-            LogUtil.i("BleTask", "[${device.address}] 当前电量:$power")
+            LogUtil.i("BleTask", "[${device.address}] 设备电量 -> $power")
             // 获取当前设备运行模式
             val runMode = bm.writeByResponse(token.buildBLEGetStatusCMD()).getRunMode()
-            LogUtil.i("BleTask", "[${device.address}] 当前模式:$runMode")
+            LogUtil.i("BleTask", "[${device.address}] 设备模式 -> $runMode")
             if (runMode == BleRunMode.WORK) {
                 // 执行作业票的获取
                 val pkgList = ArrayList<BleTicketDataPackage>()
@@ -171,33 +185,33 @@ object BleTask {
                 var datas = byteArrayOf()
                 pkgList.forEach { datas += it.pkgData }
                 val ticketJson = String(datas)
-                LogUtil.i("BleTask", "[${device.address}] 读取钥匙作业 -> $ticketJson")
+                LogUtil.i("BleTask", "[${device.address}] 设备数据 -> $ticketJson")
                 // 解析作业票
                 val json = Json { ignoreUnknownKeys = true }
                 try {
                     // 处理作业票完成情况
                     val ticket = json.decodeFromString<KeyTicket>(ticketJson)
-                    LogUtil.i("BleTask", "[${device.address}] 作业信息解析中")
+                    LogUtil.i("BleTask", "[${device.address}] 解析作业")
                     // 先找到蓝牙指定的设备NFC
                     val keys = ApiRequest.getKeyList(mutableMapOf("page" to 1, "pageSize" to -1)).getOrElse { it.getResponse() }
                     keys.data?.list?.find { it.macAddress == device.address }?.let { key ->
                         val params = ticket.getReturnParams(key.keyNfc)
                         // 执行归还数据核对
-                        LogUtil.i("BleTask", "[${device.address}] 归还钥匙,提交数据 -> $params")
+                        LogUtil.i("BleTask", "[${device.address}] 提交数据 -> $params")
                         val keyRsp = ApiRequest.returnKey(params).getOrElse { it.getResponse() }
-                        LogUtil.i("BleTask", "[${device.address}] 归还钥匙,返回的数据 -> ${keyRsp.data}")
+                        LogUtil.i("BleTask", "[${device.address}] 提交返回 -> ${keyRsp.data}")
                         // 归还钥匙接口调用结果验证
                         listener.forEach { it.onReadTicketSuccess(device.address, ticket) }
                     }
                 } catch (e: Exception) {
-                    LogUtil.i("BleTask", "[${device.address}] 解析作业异常 -> $e")
+                    LogUtil.i("BleTask", "[${device.address}] 解析异常 -> $e")
                 }
             }
             val disRet = bm.writeByResponse(token.buildBLEDisconnectCMD()).getDisconnectResult()
-            LogUtil.d("BleTask", "[${device.address}] 断开蓝牙连接:${disRet == 1}")
-            deviceLastConnectTimes[device.address] = System.currentTimeMillis()
+            LogUtil.i("BleTask", "[${device.address}] 设备断开 -> ${if(disRet == 1) "成功" else "失败"}")
         }
         bm.disconnect()
+        LogUtil.d("BleTask", "[${device.address}] 连接断开")
     }
 
     /**
@@ -205,19 +219,20 @@ object BleTask {
      */
     private suspend fun sendTicket2Device(device: BluetoothDevice) {
         val json = usedDevices[device.address] ?: "{}"
-        LogUtil.d("BleTask", "[${device.address}] 即将下发作业任务")
+        LogUtil.d("BleTask", "[${device.address}] 下发作业")
+        LogUtil.d("BleTask", "[${device.address}] 连接开始")
         val bm = BleManager(Entry.app, mac = device.address)
         val result = bm.connect(device)
         if (result.connected) {
             // 获取设备token
             val token = bm.writeByResponse(BleFrameExt.buildBLEGetTokenCMD()).getToken()
-            LogUtil.i("BleTask", "获取token:${token.byteArrayToHexString()}")
+            LogUtil.i("BleTask", "[${device.address}] 设备密钥 -> ${token.byteArrayToHexString()}")
             // 获取设备电量
             val power = bm.writeByResponse(token.buildBLEGetPowerCMD()).getPower()
-            LogUtil.i("BleTask", "当前电量:$power")
+            LogUtil.i("BleTask", "[${device.address}] 设备电量 -> $power")
             // 获取当前设备运行模式
             val runMode = bm.writeByResponse(token.buildBLEGetStatusCMD()).getRunMode()
-            LogUtil.i("BleTask", "当前模式:$runMode")
+            LogUtil.i("BleTask", "[${device.address}] 设备模式 -> $runMode")
             // 下发作业票
             val tickets = token.buildBLETicketDataCMDList(json)
             var ticketSendOk = true
@@ -229,17 +244,18 @@ object BleTask {
                     return@forEach
                 }
             }
-            LogUtil.i("BleTask", "下发作业票:$ticketSendOk")
+            LogUtil.i("BleTask", "[${device.address}] 下发作业 -> $ticketSendOk")
             // 作业票下发成功,修改设备运行模式为工作模式
             val switch = bm.writeByResponse(token.buildBLESwitchRunModeCMD(BleRunMode.WORK))
                 .getSwitchRunModeResult()
-            LogUtil.i("BleTask", "切换工作模式:$switch")
+            LogUtil.i("BleTask", "[${device.address}] 切换模式 -> $switch")
             val disRet = bm.writeByResponse(token.buildBLEDisconnectCMD()).getDisconnectResult()
-            LogUtil.d("BleTask", "断开蓝牙连接:${disRet == 1}")
+            LogUtil.i("BleTask", "[${device.address}] 设备断开 -> ${if (disRet == 1) "成功" else "失败"}")
             removeDeviceUsed(device.address)
             listener.forEach { it.onSendTicketSuccess(device.address) }
         }
         bm.disconnect()
+        LogUtil.d("BleTask", "[${device.address}] 断开连接")
     }
 
 }