فهرست منبع

refactor(更新)
- 双路蓝牙

周文健 3 ماه پیش
والد
کامیت
067b51ed97
25فایلهای تغییر یافته به همراه653 افزوده شده و 487 حذف شده
  1. 5 3
      app/src/main/java/com/grkj/iscs/ISCSApplication.kt
  2. 7 8
      app/src/main/java/com/grkj/iscs/features/init/viewmodel/InitDeviceRegistrationKeyAndLockViewModel.kt
  3. 2 1
      app/src/main/java/com/grkj/iscs/features/main/activity/MainActivity.kt
  4. 31 37
      app/src/main/java/com/grkj/iscs/features/main/viewmodel/MainViewModel.kt
  5. 4 6
      app/src/main/java/com/grkj/iscs/features/main/viewmodel/home/HomeViewModel.kt
  6. 2 4
      app/src/main/java/com/grkj/iscs/features/main/viewmodel/job_manage/JobManageHomeViewModel.kt
  7. 6 10
      app/src/main/java/com/grkj/iscs/features/main/viewmodel/job_manage/MyTodoViewModel.kt
  8. 2 2
      gradle/libs.versions.toml
  9. 83 25
      ui-base/src/main/java/com/grkj/ui_base/business/BleBusinessManager.kt
  10. 7 11
      ui-base/src/main/java/com/grkj/ui_base/business/DataBusiness.kt
  11. 2 3
      ui-base/src/main/java/com/grkj/ui_base/business/ModbusBusinessManager.kt
  12. 6 72
      ui-base/src/main/java/com/grkj/ui_base/service/CheckKeyInfoTask.kt
  13. 1 1
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleBean.kt
  14. 38 6
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleCmdManager.kt
  15. 150 113
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleConnectionManager.kt
  16. 127 0
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleQueueDispatcher.kt
  17. 14 0
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleReturnDispatcher.kt
  18. 14 0
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleSendDispatcher.kt
  19. 104 72
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleUtil.kt
  20. 1 1
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/CustomBleGattCallback.kt
  21. 1 1
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/CustomBleIndicateCallback.kt
  22. 2 2
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/CustomBleScanCallback.kt
  23. 1 1
      ui-base/src/main/java/com/grkj/ui_base/utils/ble/CustomBleWriteCallback.kt
  24. 1 1
      ui-base/src/main/java/com/grkj/ui_base/utils/event/GetTicketStatusEvent.kt
  25. 42 107
      ui-base/src/main/java/com/grkj/ui_base/utils/modbus/ModBusController.kt

+ 5 - 3
app/src/main/java/com/grkj/iscs/ISCSApplication.kt

@@ -94,9 +94,11 @@ class ISCSApplication : Application() {
             }
 
             EventConstants.EVENT_START_MODBUS_COMPLETE -> {
-                val jobList = CronJobScanner.scanJobs(CheckKeyInfoTask())
-                logger.info("扫描任务结果:${jobList.toJson()},开始注册")
-                GlobalManager.cronJobManager.registerJobs(jobList)
+                if (ISCSConfig.isInit) {
+                    val jobList = CronJobScanner.scanJobs(CheckKeyInfoTask())
+                    logger.info("扫描任务结果:${jobList.toJson()},开始注册")
+                    GlobalManager.cronJobManager.registerJobs(jobList)
+                }
             }
         }
     }

+ 7 - 8
app/src/main/java/com/grkj/iscs/features/init/viewmodel/InitDeviceRegistrationKeyAndLockViewModel.kt

@@ -3,10 +3,9 @@ package com.grkj.iscs.features.init.viewmodel
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.liveData
-import com.clj.fastble.BleManager
 import com.grkj.data.data.MMKVConstants
-import com.grkj.data.model.dos.IsLockCabinetSlots
 import com.grkj.data.logic.IHardwareLogic
+import com.grkj.data.model.dos.IsLockCabinetSlots
 import com.grkj.ui_base.base.BaseViewModel
 import com.grkj.ui_base.business.ModbusBusinessManager
 import com.grkj.ui_base.config.ISCSConfig
@@ -47,6 +46,7 @@ class InitDeviceRegistrationKeyAndLockViewModel @Inject constructor(val hardware
             ModBusController.controlAllKeyBuckleOpen {
                 ModbusBusinessManager.registerInitListener {
                     if (!ISCSConfig.canInitDevice || isInDeviceInit) {
+                        logger.info("设备录入-正在初始化设备:${ISCSConfig.canInitDevice},${isInDeviceInit}")
                         return@registerInitListener
                     }
                     isInDeviceInit = true
@@ -77,7 +77,6 @@ class InitDeviceRegistrationKeyAndLockViewModel @Inject constructor(val hardware
                         .toMutableList()
                 })
             if (newHardwareKeyBean.isNotEmpty()) {
-                BleManager.getInstance().disconnectAllDevice()
                 BleConnectionManager.deviceList.clear()
                 logger.info("断开所有蓝牙设备")
             }
@@ -280,12 +279,12 @@ class InitDeviceRegistrationKeyAndLockViewModel @Inject constructor(val hardware
                 }
             }
             keyDevice.forEach { keyDevice ->
-                    val isBind =
-                        deviceInputKeySuspend(keyDevice.rfid.toString(), keyDevice.mac.toString())
-                    if (isBind) {
-                        keyDevice.newHardware = false
-                    }
+                val isBind =
+                    deviceInputKeySuspend(keyDevice.rfid.toString(), keyDevice.mac.toString())
+                if (isBind) {
+                    keyDevice.newHardware = false
                 }
+            }
             emit(
                 Triple(
                     true,

+ 2 - 1
app/src/main/java/com/grkj/iscs/features/main/activity/MainActivity.kt

@@ -24,6 +24,7 @@ import com.grkj.ui_base.utils.event.BottomNavVisibilityEvent
 import com.grkj.shared.utils.extension.toByteArrays
 import com.grkj.shared.utils.extension.toHexStrings
 import com.grkj.ui_base.utils.ble.BleConnectionManager
+import com.grkj.ui_base.utils.ble.BleSendDispatcher
 import com.grkj.ui_base.utils.event.FlashTipEvent
 import com.grkj.ui_base.utils.event.RFIDCardReadEvent
 import com.sik.sikcore.extension.file
@@ -212,7 +213,7 @@ class MainActivity() : BaseActivity<ActivityMainBinding>() {
      * 退出登录
      */
     private fun logout() {
-        BleConnectionManager.launchDisconnectAllJob()
+        BleSendDispatcher.disconnectAll(60_0000L)
         viewModel.removeBleIndicate()
         startActivity(Intent(this, LoginActivity::class.java).apply {
             flags = Intent.FLAG_ACTIVITY_NEW_TASK

+ 31 - 37
app/src/main/java/com/grkj/iscs/features/main/viewmodel/MainViewModel.kt

@@ -17,6 +17,8 @@ import com.grkj.ui_base.utils.ble.BleCmdManager
 import com.grkj.ui_base.utils.ble.BleConnectionManager
 import com.grkj.ui_base.utils.ble.BleConst
 import com.grkj.ui_base.utils.ble.BleIndicateListener
+import com.grkj.ui_base.utils.ble.BleReturnDispatcher
+import com.grkj.ui_base.utils.ble.BleSendDispatcher
 import com.grkj.ui_base.utils.event.LoadingEvent
 import com.grkj.ui_base.utils.modbus.DeviceConst
 import com.sik.sikcore.thread.ThreadUtils
@@ -51,20 +53,6 @@ class MainViewModel @Inject constructor(
                         isNeedLoading,
                         prepareDoneCallBack
                     )
-                    when {
-                        // 获取设备当前状态
-                        byteArray.startsWith(BleConst.RSP_CURRENT_STATUS) -> BleCmdManager.handleCurrentStatus(
-                            byteArray
-                        ) {
-                            when (it) {
-                                0x02.toByte() -> {
-                                    ThreadUtils.runOnIO {
-                                        DataBusiness.checkMyTodoForHandleKey(bleBean.bleDevice.mac)
-                                    }
-                                }
-                            }
-                        }
-                    }
                 }
             })
     }
@@ -104,31 +92,37 @@ class MainViewModel @Inject constructor(
                                     )
                                 }
                                 ThreadUtils.runOnIO {
-                                    suspend fun readJobTicket(mac: String, retryCount: Int = 3) {
-                                        val isConnect =
-                                            BleConnectionManager.tryConnectWithOptionalCharge(
-                                                mac
-                                            )
-                                        if (isConnect) {
-                                            val bleBean =
-                                                BleConnectionManager.getBleDeviceByMac(mac)
-                                            Executor.delayOnMain(300) {
-                                                bleBean?.let {
-                                                    LoadingEvent.sendLoadingEvent(
-                                                        CommonUtils.getStr(com.grkj.ui_base.R.string.loading_msg_get_ticket_status_start),
-                                                        true
-                                                    )
-                                                    BleConnectionManager.getCurrentStatus(
-                                                        4,
-                                                        it.bleDevice
-                                                    )
+                                    fun readJobTicket(mac: String, retryCount: Int = 3) {
+                                        ThreadUtils.runOnIO {
+                                            if (BleSendDispatcher.isConnecting(mac) || BleSendDispatcher.isConnected(
+                                                    mac
+                                                )
+                                            ) {
+                                                BleSendDispatcher.scheduleDisconnect(mac)
+                                                delay(300)
+                                                BleReturnDispatcher.submit(mac) { isConnect ->
+                                                    if (isConnect) {
+                                                        val bleBean =
+                                                            BleConnectionManager.getBleDeviceByMac(mac)
+                                                        Executor.delayOnMain(300) {
+                                                            bleBean?.let {
+                                                                LoadingEvent.sendLoadingEvent(
+                                                                    CommonUtils.getStr(com.grkj.ui_base.R.string.loading_msg_get_ticket_status_start),
+                                                                    true
+                                                                )
+                                                                BleConnectionManager.getCurrentStatus(
+                                                                    4,
+                                                                    it.bleDevice
+                                                                )
+                                                            }
+                                                        }
+                                                    } else if (retryCount > 0) {
+                                                        readJobTicket(mac, retryCount - 1)
+                                                    } else {
+                                                        LoadingEvent.sendLoadingEvent()
+                                                    }
                                                 }
                                             }
-                                        } else if (retryCount > 0) {
-                                            delay(1500)
-                                            readJobTicket(mac, retryCount - 1)
-                                        } else {
-                                            LoadingEvent.sendLoadingEvent()
                                         }
                                     }
                                     if (!ISCSConfig.isDeviceRegistration) {

+ 4 - 6
app/src/main/java/com/grkj/iscs/features/main/viewmodel/home/HomeViewModel.kt

@@ -2,21 +2,19 @@ package com.grkj.iscs.features.main.viewmodel.home
 
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.liveData
-import com.clj.fastble.BleManager
 import com.grkj.data.data.MainDomainData
-import com.grkj.data.model.dos.WorkflowMode
-import com.grkj.data.model.local.isMyTodo
-import com.grkj.data.model.vo.TodoItemVo
-import com.grkj.data.model.vo.WorkstationManageVo
 import com.grkj.data.logic.IHardwareLogic
 import com.grkj.data.logic.IJobTicketLogic
 import com.grkj.data.logic.IUserLogic
 import com.grkj.data.logic.IWorkflowLogic
 import com.grkj.data.logic.IWorkstationLogic
+import com.grkj.data.model.dos.WorkflowMode
 import com.grkj.data.model.extension.splitTodoSteps
 import com.grkj.data.model.extension.toTodoVo
+import com.grkj.data.model.local.isMyTodo
+import com.grkj.data.model.vo.TodoItemVo
+import com.grkj.data.model.vo.WorkstationManageVo
 import com.grkj.ui_base.base.BaseViewModel
-import com.grkj.ui_base.business.BleBusinessManager
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.Dispatchers
 import javax.inject.Inject

+ 2 - 4
app/src/main/java/com/grkj/iscs/features/main/viewmodel/job_manage/JobManageHomeViewModel.kt

@@ -2,15 +2,13 @@ package com.grkj.iscs.features.main.viewmodel.job_manage
 
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.liveData
-import com.clj.fastble.BleManager
 import com.grkj.data.data.MainDomainData
-import com.grkj.data.model.local.isMyTodo
-import com.grkj.data.model.vo.TodoItemVo
 import com.grkj.data.logic.IJobTicketLogic
 import com.grkj.data.model.extension.splitTodoSteps
 import com.grkj.data.model.extension.toTodoVo
+import com.grkj.data.model.local.isMyTodo
+import com.grkj.data.model.vo.TodoItemVo
 import com.grkj.ui_base.base.BaseViewModel
-import com.grkj.ui_base.business.BleBusinessManager
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.Dispatchers
 import javax.inject.Inject

+ 6 - 10
app/src/main/java/com/grkj/iscs/features/main/viewmodel/job_manage/MyTodoViewModel.kt

@@ -2,31 +2,27 @@ package com.grkj.iscs.features.main.viewmodel.job_manage
 
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.liveData
-import com.clj.fastble.BleManager
 import com.grkj.data.data.DictConstants
 import com.grkj.data.data.MainDomainData
 import com.grkj.data.enums.JobTicketStatusEnum
 import com.grkj.data.enums.OperationTypeEnum
 import com.grkj.data.enums.TodoStatusEnum
-import com.grkj.data.model.local.isMyTodo
-import com.grkj.data.model.res.CommonDictRes
-import com.grkj.data.model.vo.IsJobTicketPointsDataVo
-import com.grkj.data.model.vo.TodoItemVo
 import com.grkj.data.logic.IJobTicketLogic
 import com.grkj.data.logic.IUserLogic
-import com.grkj.iscs.R
 import com.grkj.data.model.extension.actionKey
 import com.grkj.data.model.extension.findPredecessors
 import com.grkj.data.model.extension.toTodoVo
+import com.grkj.data.model.local.isMyTodo
+import com.grkj.data.model.res.CommonDictRes
+import com.grkj.data.model.vo.IsJobTicketPointsDataVo
+import com.grkj.data.model.vo.TodoItemVo
+import com.grkj.iscs.R
 import com.grkj.ui_base.base.BaseViewModel
-import com.grkj.ui_base.business.BleBusinessManager
 import com.grkj.ui_base.business.DataBusiness
 import com.grkj.ui_base.utils.CommonUtils
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.Dispatchers
 import javax.inject.Inject
-import kotlin.collections.any
-import kotlin.collections.contains
 
 /**
  * 作业管理
@@ -79,7 +75,7 @@ class MyTodoViewModel @Inject constructor(
                 myTodoStepJoin.map { it.toTodoVo(todoItemData.filter { temp -> it.ticketId == temp.ticketId }) }
                     .toMutableList()
             splitTodoSteps(todoItemVos, 1)
-            if (BleManager.getInstance().allConnectedDevice.isEmpty() && (waitData.any { it.enableLock || it.enableUnlock } || todoData.any { it.enableLock || it.enableUnlock })) {
+            if (waitData.any { it.enableLock || it.enableUnlock } || todoData.any { it.enableLock || it.enableUnlock }) {
                 DataBusiness.checkMyTodoForHandleKey()
             }
             emit(true)

+ 2 - 2
gradle/libs.versions.toml

@@ -23,7 +23,7 @@ lifecycle-version = "2.9.0"
 ksp = "2.1.10-1.0.31"
 nav_version = "2.9.0"
 kotlin_serialization_json = "1.7.3"
-fastble = "2.4.0"
+fastble = "1.4.2"
 
 [libraries]
 androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -66,7 +66,7 @@ android-navigation-ui = { group = "androidx.navigation", name = "navigation-ui",
 android-navigation-dynamic-features-fragment = { group = "androidx.navigation", name = "navigation-dynamic-features-fragment", version.ref = "nav_version" }
 kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlin_serialization_json" }
 
-fastble = { group = "com.github.Jasonchenlijian", name = "FastBle", version.ref = "fastble" }
+fastble = { group = "com.github.kanshenmekan", name = "FastBle", version.ref = "fastble" }
 
 [plugins]
 android-application = { id = "com.android.application", version.ref = "agp" }

+ 83 - 25
ui-base/src/main/java/com/grkj/ui_base/business/BleBusinessManager.kt

@@ -1,8 +1,6 @@
 package com.grkj.ui_base.business
 
-import com.clj.fastble.BleManager
-import com.clj.fastble.data.BleDevice
-import com.clj.fastble.exception.BleException
+import android.bluetooth.BluetoothGattCharacteristic
 import com.google.gson.Gson
 import com.grkj.data.data.DictConstants
 import com.grkj.data.data.MainDomainData
@@ -27,6 +25,8 @@ import com.grkj.ui_base.utils.ble.BleBean
 import com.grkj.ui_base.utils.ble.BleCmdManager
 import com.grkj.ui_base.utils.ble.BleConnectionManager
 import com.grkj.ui_base.utils.ble.BleConst
+import com.grkj.ui_base.utils.ble.BleReturnDispatcher
+import com.grkj.ui_base.utils.ble.BleSendDispatcher
 import com.grkj.ui_base.utils.ble.CustomBleWriteCallback
 import com.grkj.ui_base.utils.event.CurrentModeEvent
 import com.grkj.ui_base.utils.event.DeviceExceptionEvent
@@ -37,6 +37,8 @@ import com.grkj.ui_base.utils.extension.serialNo
 import com.grkj.ui_base.utils.extension.tip
 import com.grkj.ui_base.utils.modbus.DeviceConst
 import com.grkj.ui_base.utils.modbus.ModBusController
+import com.huyuhui.fastble.data.BleDevice
+import com.huyuhui.fastble.exception.BleException
 import com.kongzue.dialogx.dialogs.PopTip
 import com.sik.sikcore.SIKCore
 import com.sik.sikcore.data.BeanUtils
@@ -123,8 +125,7 @@ object BleBusinessManager {
             1 -> {
                 if (res == 1) {
                     // 只能在这里断开,不能全部断开
-                    BleManager.getInstance().disconnect(bleBean.bleDevice)
-                    BleConnectionManager.deviceList.removeIf { it.bleDevice.mac == bleBean.bleDevice.mac }
+                    BleSendDispatcher.scheduleDisconnect(bleBean.bleDevice.mac)
                     logger.info("断开连接发钥匙:${bleBean.bleDevice.mac}")
                     // 打开钥匙卡扣
                     val keyBean = ModBusController.getKeyByMac(bleBean.bleDevice.mac)
@@ -182,11 +183,27 @@ object BleBusinessManager {
         logger.info("switchWorkMode - ${bleDevice.mac}")
         BleCmdManager.switchMode(
             BleConst.STATUS_WORK, bleDevice, object : CustomBleWriteCallback() {
-                override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
+                override fun onWriteSuccess(
+                    bleDevice: BleDevice,
+                    characteristic: BluetoothGattCharacteristic,
+                    current: Int,
+                    total: Int,
+                    justWrite: ByteArray,
+                    data: ByteArray
+                ) {
                     logger.info("switch mode work success : ${bleDevice.mac}")
                 }
 
-                override fun onWriteFailure(exception: BleException?) {
+                override fun onWriteFailure(
+                    bleDevice: BleDevice,
+                    characteristic: BluetoothGattCharacteristic?,
+                    exception: BleException,
+                    current: Int,
+                    total: Int,
+                    justWrite: ByteArray?,
+                    data: ByteArray?,
+                    isTotalFail: Boolean
+                ) {
                     logger.error("switch mode work fail : ${exception?.code} - ${exception?.description}")
                     Executor.delayOnMain(500) {
                         switchWorkMode(bleDevice, isNeedLoading)
@@ -201,11 +218,27 @@ object BleBusinessManager {
     private fun switchReadyMode(bleDevice: BleDevice) {
         BleCmdManager.switchMode(
             BleConst.STATUS_READY, bleDevice, object : CustomBleWriteCallback() {
-                override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
+                override fun onWriteSuccess(
+                    bleDevice: BleDevice,
+                    characteristic: BluetoothGattCharacteristic,
+                    current: Int,
+                    total: Int,
+                    justWrite: ByteArray,
+                    data: ByteArray
+                ) {
                     logger.info("switch mode ready success : ${bleDevice.mac}")
                 }
 
-                override fun onWriteFailure(exception: BleException?) {
+                override fun onWriteFailure(
+                    bleDevice: BleDevice,
+                    characteristic: BluetoothGattCharacteristic?,
+                    exception: BleException,
+                    current: Int,
+                    total: Int,
+                    justWrite: ByteArray?,
+                    data: ByteArray?,
+                    isTotalFail: Boolean
+                ) {
                     logger.error("switch mode ready fail : ${bleDevice.mac}")
                     Executor.delayOnMain(300) {
                         switchReadyMode(bleDevice)
@@ -293,14 +326,30 @@ object BleBusinessManager {
         )
         BleCmdManager.sendWorkTicket(
             jsonStr, bleDevice = bleDevice, callback = object : CustomBleWriteCallback() {
-                override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
+                override fun onWriteSuccess(
+                    bleDevice: BleDevice,
+                    characteristic: BluetoothGattCharacteristic,
+                    current: Int,
+                    total: Int,
+                    justWrite: ByteArray,
+                    data: ByteArray
+                ) {
                     logger.info("sendTicket success")
                     if (isNeedLoading) LoadingEvent.sendLoadingEvent(
                         CommonUtils.getStr(R.string.sending_ticket), true
                     )
                 }
 
-                override fun onWriteFailure(exception: BleException?) {
+                override fun onWriteFailure(
+                    bleDevice: BleDevice,
+                    characteristic: BluetoothGattCharacteristic?,
+                    exception: BleException,
+                    current: Int,
+                    total: Int,
+                    justWrite: ByteArray?,
+                    data: ByteArray?,
+                    isTotalFail: Boolean
+                ) {
                     logger.error("sendTicket fail : ${bleDevice.mac}")
                     if (isNeedLoading) LoadingEvent.sendLoadingEvent(
                         CommonUtils.getStr(R.string.send_ticket_fail), true
@@ -403,8 +452,10 @@ object BleBusinessManager {
                             false, currentModeEvent.bleBean.bleDevice.mac
                         )
                         LoadingEvent.sendLoadingEvent()
-                        //连上之后没有工作票要下发就断开 看是否还有设备等待连接并且连接数是否大于等于预期,没有就不断开,有就让路,一般是初始化的时候
                         ThreadUtils.runOnIO {
+                            if (BleReturnDispatcher.isConnected(currentModeEvent.bleBean.bleDevice.mac)) {
+                                BleReturnDispatcher.scheduleDisconnect(currentModeEvent.bleBean.bleDevice.mac)
+                            }
                             DataBusiness.checkMyTodoForHandleKey(currentModeEvent.bleBean.bleDevice.mac)
                         }
                     }
@@ -426,7 +477,7 @@ object BleBusinessManager {
     private fun getTicketStatusBusiness(
         mac: String, isNeedLoading: Boolean = false
     ) {
-        BleConnectionManager.registerConnectListener(mac) { isDone, bleBean ->
+        BleConnectionManager.registerConnectListener(mac, isSend = false) { isDone, bleBean ->
             if (isDone) {
                 Executor.delayOnMain(500) {
                     getTicketStatusWithRetry(bleBean!!.bleDevice, isNeedLoading)
@@ -471,12 +522,28 @@ object BleBusinessManager {
     ) {
         if (isNeedLoading) LoadingEvent.sendLoadingEvent("开始获取工作票", true)
         BleCmdManager.getTicketStatus(bleDevice, object : CustomBleWriteCallback() {
-            override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
+            override fun onWriteSuccess(
+                bleDevice: BleDevice,
+                characteristic: BluetoothGattCharacteristic,
+                current: Int,
+                total: Int,
+                justWrite: ByteArray,
+                data: ByteArray
+            ) {
                 if (isNeedLoading) LoadingEvent.sendLoadingEvent("工作票获取成功", true)
                 logger.info("getTicketStatus success")
             }
 
-            override fun onWriteFailure(exception: BleException?) {
+            override fun onWriteFailure(
+                bleDevice: BleDevice,
+                characteristic: BluetoothGattCharacteristic?,
+                exception: BleException,
+                current: Int,
+                total: Int,
+                justWrite: ByteArray?,
+                data: ByteArray?,
+                isTotalFail: Boolean
+            ) {
                 if (isNeedLoading) LoadingEvent.sendLoadingEvent("工作票获取失败", true)
                 processCallback?.invoke(false)
                 logger.error("getTicketStatus fail")
@@ -537,8 +604,7 @@ object BleBusinessManager {
                         },
                         onCancelClick = {
                             PopTip.build().tip(CommonUtils.getStr(R.string.continue_the_ticket))
-                            BleManager.getInstance().disconnect(bleDevice)
-                            BleConnectionManager.deviceList.removeIf { it.bleDevice.mac == bleDevice.mac }
+                            BleReturnDispatcher.scheduleDisconnect(bleDevice.mac)
                             logger.info("断开连接归还取消:${bleDevice.mac}")
                             // 打开卡扣,防止初始化的时候选择不处理钥匙导致无法使用
                             val dock = ModBusController.getDockByKeyMac(bleDevice.mac)
@@ -911,14 +977,6 @@ object BleBusinessManager {
      */
     fun connectExistsKey(exceptKeyMac: List<String> = listOf()) {
         ThreadUtils.runOnIO {
-            BleConnectionManager.cancelDisconnectAllJob()
-            val connectedDevice = BleManager.getInstance().allConnectedDevice
-            if (connectedDevice.isNotEmpty() && connectedDevice.subtract(exceptKeyMac.toSet())
-                    .isNotEmpty()
-            ) {
-                logger.info("已有钥匙连接")
-                return@runOnIO
-            }
             // —— 串行请求1 & 2 ——
             val slotsPage = DataBusiness.getSlotsPage()
             val keyPage = DataBusiness.getKeyPage()

+ 7 - 11
ui-base/src/main/java/com/grkj/ui_base/business/DataBusiness.kt

@@ -1,6 +1,5 @@
 package com.grkj.ui_base.business
 
-import com.clj.fastble.BleManager
 import com.grkj.data.data.MainDomainData
 import com.grkj.data.di.LogicManager
 import com.grkj.data.model.extension.splitTodoSteps
@@ -10,8 +9,7 @@ import com.grkj.data.model.res.CabinetSlotsRes
 import com.grkj.data.model.res.KeyPageRes
 import com.grkj.data.model.res.LockPageRes
 import com.grkj.ui_base.business.BleBusinessManager.connectExistsKey
-import com.grkj.ui_base.service.CheckKeyInfoTask
-import com.grkj.ui_base.utils.ble.BleConnectionManager
+import com.grkj.ui_base.utils.ble.BleSendDispatcher
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
 
@@ -58,7 +56,7 @@ object DataBusiness {
     /**
      * 获取我的待办的作业
      */
-    fun checkMyTodoForHandleKey(mac: String? = null): Boolean {
+    fun checkMyTodoForHandleKey(mac: String? = null, delay: Long = 60_000L): Boolean {
         val userInfo = MainDomainData.userInfo
         if (userInfo == null) {
             return false
@@ -72,21 +70,19 @@ object DataBusiness {
             myTodoStepJoin.map { it.toTodoVo(todoItemData.filter { temp -> it.ticketId == temp.ticketId }) }
                 .toMutableList()
         val todoData = splitTodoSteps(todoItemVos, 1)
-        if (BleManager.getInstance().allConnectedDevice.isEmpty() && (todoData.first.any { it.enableLock || it.enableUnlock } || todoData.second.any { it.enableLock || it.enableUnlock })) {
-            CheckKeyInfoTask.isTicketConnect = true
-            val maxPowerMac = ModbusBusinessManager.getMaxPowerKey().mac
+        if (todoData.first.any { it.enableLock || it.enableUnlock } || todoData.second.any { it.enableLock || it.enableUnlock }) {
+            val maxPowerMac = ModbusBusinessManager.getMaxPowerKey(mac?:"").mac
             if (maxPowerMac != mac) {
                 connectExistsKey(listOf(mac ?: ""))
-                BleConnectionManager.launchDisconnectJob(mac ?: "")
+                BleSendDispatcher.scheduleDisconnect(mac ?: "", delay)
             } else {
                 connectExistsKey()
             }
         } else {
-            CheckKeyInfoTask.isTicketConnect = false
             mac?.let {
-                BleConnectionManager.launchDisconnectJob(it)
+                BleSendDispatcher.scheduleDisconnect(mac, delay)
             } ?: let {
-                BleConnectionManager.launchDisconnectAllJob()
+                BleSendDispatcher.disconnectAll(delay)
             }
         }
         return true

+ 2 - 3
ui-base/src/main/java/com/grkj/ui_base/business/ModbusBusinessManager.kt

@@ -218,7 +218,6 @@ object ModbusBusinessManager {
     ) {
         // 你可以改成接收 CoroutineScope 或者直接在全局 Scope 启动
         ThreadUtils.runOnIO {
-            BleConnectionManager.cancelDisconnectAllJob()
             LoadingEvent.sendLoadingEvent(CommonUtils.getStr(R.string.check_key_and_lock))
             try {
                 // —— 串行请求1 & 2 ——
@@ -627,7 +626,7 @@ object ModbusBusinessManager {
     /**
      * 获取电量最多的钥匙
      */
-    fun getMaxPowerKey(): DockBean.KeyBean {
-        return ModBusController.getMaxPowerKey()
+    fun getMaxPowerKey(mac: String): DockBean.KeyBean {
+        return ModBusController.getMaxPowerKey(mac)
     }
 }

+ 6 - 72
ui-base/src/main/java/com/grkj/ui_base/service/CheckKeyInfoTask.kt

@@ -1,16 +1,10 @@
 package com.grkj.ui_base.service
 
-import com.clj.fastble.BleManager
-import com.grkj.shared.utils.extension.startsWith
 import com.grkj.ui_base.business.DataBusiness
 import com.grkj.ui_base.business.ModbusBusinessManager
-import com.grkj.ui_base.utils.ble.BleBean
-import com.grkj.ui_base.utils.ble.BleConnectionManager
-import com.grkj.ui_base.utils.ble.BleConst
-import com.grkj.ui_base.utils.ble.BleIndicateListener
+import com.grkj.ui_base.utils.ble.BleSendDispatcher
 import com.sik.cronjob.annotations.CronJob
 import com.sik.sikcore.thread.ThreadUtils
-import kotlinx.coroutines.delay
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 
@@ -21,80 +15,20 @@ import org.slf4j.LoggerFactory
 class CheckKeyInfoTask {
     private val logger: Logger = LoggerFactory.getLogger(this::class.java)
 
-    /**
-     * 是检查信息
-     */
-    private var isCheckInfo: Boolean = false
-
-    /**
-     * 已经检查的钥匙
-     */
-    private var checkedKey: MutableList<String> = mutableListOf()
-
-    companion object {
-        /**
-         * 是否作业票连接
-         */
-        var isTicketConnect = false
-            set(value) {
-                if (value) {
-                    BleConnectionManager.removeBleIndicateListener(BLE_INDICATE_TAG)
-                }
-            }
-
-        /**
-         * 蓝牙监听标签
-         */
-        const val BLE_INDICATE_TAG = "BLE_INDICATE_TAG"
-    }
-
     /**
      * 检查钥匙信息
      */
     @CronJob(intervalMillis = 30 * 60_000L, initialDelay = 0, runOnMainThread = false)
     fun checkKeyInfo() {
-        if (isTicketConnect) {
-            return
-        }
         logger.info("开始检查钥匙信息")
         val existsKey = ModbusBusinessManager.getExistsKey()
-        BleConnectionManager.addBLeIndicateListener(BLE_INDICATE_TAG, object : BleIndicateListener {
-            override fun handleRsp(
-                bleBean: BleBean,
-                byteArray: ByteArray,
-                isNeedLoading: Boolean,
-                prepareDoneCallBack: ((Boolean, BleBean?) -> Unit)?
-            ) {
-                when {
-                    byteArray.startsWith(BleConst.RSP_POWER_STATUS) -> {
-                        if (isCheckInfo && !isTicketConnect) {
-                            BleManager.getInstance().disconnect(bleBean.bleDevice)
-                            BleConnectionManager.deviceList.removeIf { it.bleDevice.mac == bleBean.bleDevice.mac }
-                            logger.info("断开连接检查信息:${bleBean.bleDevice.mac}")
-                            checkedKey.add(bleBean.bleDevice.mac)
-                            if (checkedKey.joinToString(",") == existsKey.joinToString(",") {
-                                    it.mac ?: ""
-                                }) {
-                                BleConnectionManager.removeBleIndicateListener(this)
-                                isCheckInfo = false
-                            }
-                        }
-                    }
-                }
-            }
-        })
-        checkedKey.clear()
-        isCheckInfo = true
         ThreadUtils.runOnIO {
-            DataBusiness.checkMyTodoForHandleKey()
             for (bean in existsKey) {
-                if (isTicketConnect) {
-                    isCheckInfo = false
-                    break
-                }
-                bean.mac?.let {
-                    BleConnectionManager.tryConnectWithOptionalCharge(it)
-                    delay(1000)
+                bean.mac?.let { mac ->
+                    BleSendDispatcher.submit(mac) {
+                        logger.info("信息获取完成,检查是否立即断开")
+                        DataBusiness.checkMyTodoForHandleKey(mac, 0)
+                    }
                 }
             }
         }

+ 1 - 1
ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleBean.kt

@@ -1,6 +1,6 @@
 package com.grkj.ui_base.utils.ble
 
-import com.clj.fastble.data.BleDevice
+import com.huyuhui.fastble.data.BleDevice
 import java.io.File
 
 /**

+ 38 - 6
ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleCmdManager.kt

@@ -1,11 +1,12 @@
 package com.grkj.ui_base.utils.ble
 
-import com.clj.fastble.data.BleDevice
-import com.clj.fastble.exception.BleException
+import android.bluetooth.BluetoothGattCharacteristic
 import com.grkj.ui_base.utils.event.GetTicketStatusEvent
 import com.grkj.shared.utils.extension.crc16
 import com.grkj.shared.utils.extension.toByteArray
 import com.grkj.shared.utils.extension.toHexStrings
+import com.huyuhui.fastble.data.BleDevice
+import com.huyuhui.fastble.exception.BleException
 import com.sik.sikcore.thread.ThreadUtils
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
@@ -185,13 +186,28 @@ object BleCmdManager {
                     bleBean.bleDevice,
                     object : CustomBleWriteCallback() {
                         override fun onWriteSuccess(
+                            bleDevice: BleDevice,
+                            characteristic: BluetoothGattCharacteristic,
                             current: Int,
                             total: Int,
-                            justWrite: ByteArray?
+                            justWrite: ByteArray,
+                            data: ByteArray
                         ) {
+
                         }
 
-                        override fun onWriteFailure(exception: BleException?) {}
+                        override fun onWriteFailure(
+                            bleDevice: BleDevice,
+                            characteristic: BluetoothGattCharacteristic?,
+                            exception: BleException,
+                            current: Int,
+                            total: Int,
+                            justWrite: ByteArray?,
+                            data: ByteArray?,
+                            isTotalFail: Boolean
+                        ) {
+
+                        }
                     })
             } else {
                 callBack?.invoke(false, res)
@@ -284,11 +300,27 @@ object BleCmdManager {
                 byteArrayOf(0x00.toByte()),
                 bleDevice,
                 object : CustomBleWriteCallback() {
-                    override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
+                    override fun onWriteSuccess(
+                        bleDevice: BleDevice,
+                        characteristic: BluetoothGattCharacteristic,
+                        current: Int,
+                        total: Int,
+                        justWrite: ByteArray,
+                        data: ByteArray
+                    ) {
                         logger.info("getTicketStatusPart success")
                     }
 
-                    override fun onWriteFailure(exception: BleException?) {
+                    override fun onWriteFailure(
+                        bleDevice: BleDevice,
+                        characteristic: BluetoothGattCharacteristic?,
+                        exception: BleException,
+                        current: Int,
+                        total: Int,
+                        justWrite: ByteArray?,
+                        data: ByteArray?,
+                        isTotalFail: Boolean
+                    ) {
                         logger.error("getTicketStatusPart fail")
                         BleConnectionManager.getBleDeviceByMac(bleDevice.mac)?.let {
                             it.ticketStatus = byteArrayOf()

+ 150 - 113
ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleConnectionManager.kt

@@ -1,19 +1,21 @@
 package com.grkj.ui_base.utils.ble
 
+import android.Manifest
+import android.annotation.SuppressLint
 import android.bluetooth.BluetoothDevice
 import android.bluetooth.BluetoothGatt
-import com.clj.fastble.BleManager
-import com.clj.fastble.data.BleDevice
-import com.clj.fastble.exception.BleException
-import com.grkj.data.data.CommonConstants
+import android.bluetooth.BluetoothGattCharacteristic
+import androidx.annotation.RequiresPermission
 import com.grkj.shared.utils.extension.startsWith
 import com.grkj.shared.utils.extension.toHexStrings
 import com.grkj.ui_base.R
 import com.grkj.ui_base.config.ISCSConfig
-import com.grkj.ui_base.service.CheckKeyInfoTask
 import com.grkj.ui_base.utils.CommonUtils
 import com.grkj.ui_base.utils.event.LoadingEvent
 import com.grkj.ui_base.utils.modbus.ModBusController
+import com.huyuhui.fastble.BleManager
+import com.huyuhui.fastble.data.BleDevice
+import com.huyuhui.fastble.exception.BleException
 import com.sik.sikandroid.activity.ActivityTracker
 import com.sik.sikcore.bluetooth.BLEScanner
 import com.sik.sikcore.bluetooth.IBluetoothScanCallback
@@ -64,11 +66,6 @@ object BleConnectionManager {
      */
     var mExceptionKeyList = mutableListOf<String>()
 
-    /**
-     * 断开连接任务
-     */
-    private val disconnectJob: HashMap<String, String> = hashMapOf()
-
     /**
      * 蓝牙的通信返回监听
      */
@@ -95,7 +92,6 @@ object BleConnectionManager {
                     bleBean.bleDevice, byteArray
                 ) { isSuccess ->
                     if (isSuccess) {
-                        prepareDoneCallBack?.invoke(true, bleBean)
                         //尝试使用命令作为心跳 ,获取token完成之后就要建立心跳了
                         getBatteryPower(bleBean.bleDevice, true)
                     }
@@ -106,67 +102,12 @@ object BleConnectionManager {
                     val power = byteArray[4].toInt()
                     ModBusController.updateKeyPower(power, bleBean.bleDevice.mac)
                     logger.info("电量(${bleBean.bleDevice.mac}):${power}")
+                    prepareDoneCallBack?.invoke(true, bleBean)
                 }
             }
         }
     }
 
-    /**
-     * 启动断连任务
-     */
-    fun launchDisconnectJob(mac: String) {
-        val jobId = ThreadUtils.runOnIODelayed(CommonConstants.BLE_DISCONNECT_DELAY_TIME) {
-            val bleBean = getBleDeviceByMac(mac)
-            bleBean?.bleDevice?.let {
-                BleManager.getInstance().disconnect(it)
-                deviceList.removeIf { it.bleDevice.mac == bleBean.bleDevice.mac }
-                disconnectJob.remove(it.mac)
-                logger.info("断开连接计划单把断开:${it.mac}")
-            }
-        }
-        disconnectJob[mac] = jobId
-    }
-
-    /**
-     * 启动断连所有设备任务
-     */
-    fun launchDisconnectAllJob() {
-        deviceList.forEach { bleBean ->
-            val jobId = ThreadUtils.runOnIODelayed(CommonConstants.BLE_DISCONNECT_DELAY_TIME) {
-                val bleBean =
-                    getBleDeviceByMac(bleBean.bleDevice.mac)
-                bleBean?.bleDevice?.let {
-                    BleManager.getInstance().disconnect(it)
-                    deviceList.removeIf { it.bleDevice.mac == bleBean.bleDevice.mac }
-                    disconnectJob.remove(it.mac)
-                    logger.info("断开连接计划全部断开:${it.mac}")
-                }
-            }
-            disconnectJob[bleBean.bleDevice.mac] = jobId
-        }
-    }
-
-    /**
-     * 取消断连所有设备任务
-     */
-    fun cancelDisconnectAllJob() {
-        disconnectJob.forEach {
-            ThreadUtils.cancel(it.value)
-        }
-        disconnectJob.clear()
-    }
-
-    /**
-     * 取消断连任务
-     */
-    fun cancelDisconnectJob(mac: String) {
-        val jobId = disconnectJob[mac]
-        jobId?.let {
-            ThreadUtils.cancel(it)
-            disconnectJob.remove(jobId)
-        }
-    }
-
     /**
      * 注册连接监听:
      * - 如果设备已在 deviceList 且拥有 token,立即回调并返回
@@ -177,6 +118,7 @@ object BleConnectionManager {
     fun registerConnectListener(
         mac: String,
         connectNow: Boolean = false,
+        isSend: Boolean = true,
         callBack: ((Boolean, BleBean?) -> Unit)? = null
     ) {
         logger.info("蓝牙连接-开始连接 : $mac")
@@ -184,11 +126,15 @@ object BleConnectionManager {
         logger.info("蓝牙连接-记录的设备:${deviceList}")
         logger.info(
             "蓝牙连接-是否连接:${
-                BleManager.getInstance().isConnected(mac)
+                if (isSend) BleSendDispatcher.isConnected(mac) else BleReturnDispatcher.isConnected(
+                    mac
+                )
             },记录的设备是否存在:${deviceList.any { it.bleDevice.mac == mac }}"
         )
         deviceList.find {
-            it.bleDevice.mac == mac && BleManager.getInstance().isConnected(mac) && it.token != null
+            it.bleDevice.mac == mac && (if (isSend) BleSendDispatcher.isConnected(mac) else BleReturnDispatcher.isConnected(
+                mac
+            )) && it.token != null
         }?.let { bean ->
             logger.info("蓝牙连接-设备已连接")
             callBack?.invoke(true, bean)
@@ -196,8 +142,7 @@ object BleConnectionManager {
         }
         if (connectNow) {
             logger.warn("蓝牙连接-立即连接 mac: $mac")
-            BleManager.getInstance().disconnectAllDevice()
-            deviceList.clear()
+            if (isSend) BleSendDispatcher.disconnectAll() else BleReturnDispatcher.disconnectAll()
             unregisterConnectListener(mac)
             currentConnectingMac.removeIf { it == mac }
         }
@@ -210,14 +155,19 @@ object BleConnectionManager {
         // 加入队列并启动连接
         fun checkAndConnect(isDisconnectAll: Boolean = false) {
             logger.warn("蓝牙连接-开始检查连接 mac: $mac")
-            if (BleManager.getInstance().allConnectedDevice.size < maxConnectCount) {
+            val canConnect =
+                if (isSend) BleSendDispatcher.canConnect() else BleReturnDispatcher.canConnect()
+            if (canConnect) {
                 connectListeners.add(ConnectListener(mac, callBack))
-                connectKey()
+                connectKey(isSend)
             } else {
                 if (connectNow && !isDisconnectAll) {
                     logger.info("蓝牙连接-超过最大连接数,但是需要立即连接,断开所有连接进行连接")
-                    BleManager.getInstance().disconnectAllDevice()
-                    deviceList.clear()
+                    if (isSend) {
+                        BleSendDispatcher.disconnectAll()
+                    } else {
+                        BleReturnDispatcher.disconnectAll()
+                    }
                     checkAndConnect(true)
                 } else {
                     ThreadUtils.runOnIODelayed(500) {
@@ -271,10 +221,12 @@ object BleConnectionManager {
     /**
      * 连接钥匙,单个mac走完prepare再进行下一个
      */
-    private fun connectKey() {
+    private fun connectKey(isSend: Boolean = true) {
         if (connectListeners.isEmpty()) return
-        if (isPreparing || BleManager.getInstance().allConnectedDevice.size >= maxStandbyCount) {
-            logger.debug("暂时不能连接:${isPreparing},${BleManager.getInstance().allConnectedDevice.size >= maxStandbyCount}")
+        val canConnect =
+            if (isSend) BleSendDispatcher.canConnect() else BleReturnDispatcher.canConnect()
+        if (isPreparing || !canConnect) {
+            logger.debug("暂时不能连接:${isPreparing},${canConnect}")
             ThreadUtils.runOnMainDelayed(1000) { connectKey() }
             return
         }
@@ -292,7 +244,7 @@ object BleConnectionManager {
             return
         }
         prepareBle(
-            listener.mac, false
+            listener.mac, false, isSend
         ) { isDone, bleBean ->
             ThreadUtils.runOnMain {
                 isPreparing = false
@@ -320,17 +272,20 @@ object BleConnectionManager {
     private fun prepareBle(
         mac: String,
         isNeedLoading: Boolean = false,
+        isSend: Boolean,
         prepareDoneCallBack: ((Boolean, BleBean?) -> Unit)?
     ) {
         ThreadUtils.runOnMain {
-            doScanBle(mac, isNeedLoading, 3, prepareDoneCallBack)
+            doScanBle(mac, isNeedLoading, 3, isSend, prepareDoneCallBack)
         }
     }
 
+    @SuppressLint("MissingPermission")
     private fun doScanBle(
         mac: String,
         isNeedLoading: Boolean = false,
         retryTimes: Int = 3,
+        isSend: Boolean,
         prepareDoneCallBack: ((Boolean, BleBean?) -> Unit)?
     ) {
         logger.info("蓝牙连接-doScanBle:$mac")
@@ -339,42 +294,47 @@ object BleConnectionManager {
             override fun onPrompt(promptStr: String?) {
                 // 蓝牙未启动重试
                 logger.info("蓝牙连接-参数:${promptStr}")
-                BleManager.getInstance().enableBluetooth()
                 ThreadUtils.runOnIODelayed(5000) {
-                    doScanBle(mac, isNeedLoading, 3, prepareDoneCallBack)
+                    doScanBle(mac, isNeedLoading, 3, isSend, prepareDoneCallBack)
                 }
             }
 
+            @RequiresPermission(Manifest.permission.BLUETOOTH_SCAN)
             override fun onScanStarted(success: Boolean) {
                 logger.info("蓝牙连接-onScanStarted:${success}")
                 if (!success && retryTimes == 0) {
                     if (isNeedLoading) LoadingEvent.sendLoadingEvent(null, false)
                     prepareDoneCallBack?.invoke(false, null)
                 } else if (!success && retryTimes > 0) {
-                    BleManager.getInstance().cancelScan()
+                    BleManager.cancelScan()
                     ThreadUtils.runOnMainDelayed(3000) {
-                        doScanBle(mac, isNeedLoading, retryTimes - 1, prepareDoneCallBack)
+                        doScanBle(mac, isNeedLoading, retryTimes - 1, isSend, prepareDoneCallBack)
                     }
                 }
             }
 
-            override fun onScanning(bleDevice: BleDevice?) {
-                val scanMac = bleDevice?.mac ?: return
+            @RequiresPermission(Manifest.permission.BLUETOOTH_SCAN)
+            override fun onLeScan(
+                oldDevice: BleDevice,
+                newDevice: BleDevice,
+                scannedBefore: Boolean
+            ) {
+                val scanMac = newDevice.mac
                 logger.info("蓝牙连接-onScanning:${mac}")
                 if (scanMac.equals(mac, ignoreCase = true)) {
                     // 找到目标设备,马上停止扫描
                     logger.info("找到目标设备 $mac,停止扫描并尝试连接")
-                    BleManager.getInstance().cancelScan()
+                    BleManager.cancelScan()
                     // 立刻调用 doConnect,下一步进入连接流程
                     doConnect(
-                        bleDevice,
+                        newDevice,
                         isNeedLoading,
                         prepareDoneCallBack
                     )
                 }
             }
 
-            override fun onScanFinished(scanResultList: MutableList<BleDevice>?) {
+            override fun onScanFinished(scanResultList: List<BleDevice>) {
                 logger.info("蓝牙连接-onScanFinished: $mac - ${scanResultList?.none { it.mac == mac }}")
                 if (isNeedLoading) LoadingEvent.sendLoadingEvent(null, false)
                 // 没有扫描到
@@ -384,12 +344,17 @@ object BleConnectionManager {
                     prepareDoneCallBack?.invoke(false, null)
                 }
             }
+
+            override fun onFilter(bleDevice: BleDevice): Boolean {
+                return bleDevice.name == BleConst.BLE_LOCAL_NAME
+            }
         })
     }
 
     /**
      * 连接蓝牙设备
      */
+    @SuppressLint("MissingPermission")
     private fun doConnect(
         bleDevice: BleDevice,
         isNeedLoading: Boolean = false,
@@ -400,10 +365,6 @@ object BleConnectionManager {
             CommonUtils.getStr(R.string.ble_connecting), true
         )
         ThreadUtils.runOnIO {
-            BleManager.getInstance().disconnect(bleDevice)
-            deviceList.removeIf { it.bleDevice.mac == bleDevice.mac }
-            logger.info("断开连接连接中断开:${bleDevice.mac}")
-            delay(300)
             BleUtil.Companion.instance?.connectBySelect(
                 bleDevice, object : CustomBleGattCallback() {
                     override fun onPrompt(promptStr: String?) {
@@ -413,9 +374,11 @@ object BleConnectionManager {
                         )
                     }
 
-                    override fun onStartConnect() {}
+                    override fun onStartConnect(bleDevice: BleDevice) {
+
+                    }
 
-                    override fun onConnectFail(bleDevice: BleDevice?, exception: BleException?) {
+                    override fun onConnectFail(bleDevice: BleDevice?, exception: BleException) {
                         if (isNeedLoading) LoadingEvent.sendLoadingEvent(
                             CommonUtils.getStr(R.string.ble_connect_fail), false
                         )
@@ -424,7 +387,9 @@ object BleConnectionManager {
                     }
 
                     override fun onConnectSuccess(
-                        bleDevice: BleDevice?, gatt: BluetoothGatt?, status: Int
+                        bleDevice: BleDevice,
+                        gatt: BluetoothGatt?,
+                        status: Int
                     ) {
                         if (isNeedLoading) LoadingEvent.sendLoadingEvent(
                             null, false
@@ -450,7 +415,7 @@ object BleConnectionManager {
 
                     override fun onDisConnected(
                         isActiveDisConnected: Boolean,
-                        device: BleDevice?,
+                        device: BleDevice,
                         gatt: BluetoothGatt?,
                         status: Int
                     ) {
@@ -458,10 +423,6 @@ object BleConnectionManager {
                             null, false
                         )
                         logger.info("蓝牙连接-onDisConnected : ${device?.mac} - $isActiveDisConnected")
-                        getBleDeviceByMac(device?.mac)?.let {
-                            deviceList.remove(it)
-                            it.token = null
-                        }
                         bleDevice.mac?.let { itMac ->
                             unregisterConnectListener(itMac)
                         }
@@ -485,6 +446,10 @@ object BleConnectionManager {
                             ModBusController.updateKeyReadyStatus(bleDevice.mac, false, 3)
                         }
                     }
+
+                    override fun onConnectCancel(bleDevice: BleDevice, skip: Boolean) {
+                        logger.info("蓝牙连接-onConnectCancel : ${bleDevice?.mac}")
+                    }
                 })
         }
     }
@@ -517,13 +482,29 @@ object BleConnectionManager {
             }
         }
         BleCmdManager.getCurrentStatus(bleDevice, object : CustomBleWriteCallback() {
-            override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
+            override fun onWriteSuccess(
+                bleDevice: BleDevice,
+                characteristic: BluetoothGattCharacteristic,
+                current: Int,
+                total: Int,
+                justWrite: ByteArray,
+                data: ByteArray
+            ) {
                 logger.info("getCurrentStatus success : ${bleDevice.mac}")
                 isTimeout = false
                 timeoutCallBack?.invoke(false)
             }
 
-            override fun onWriteFailure(exception: BleException?) {
+            override fun onWriteFailure(
+                bleDevice: BleDevice,
+                characteristic: BluetoothGattCharacteristic?,
+                exception: BleException,
+                current: Int,
+                total: Int,
+                justWrite: ByteArray?,
+                data: ByteArray?,
+                isTotalFail: Boolean
+            ) {
                 logger.info("getCurrentStatus fail : ${bleDevice.mac}")
                 isTimeout = false
                 ThreadUtils.runOnMainDelayed(1000) {
@@ -539,10 +520,27 @@ object BleConnectionManager {
     fun getBatteryPower(bleDevice: BleDevice, isHeartBeat: Boolean = false) {
         logger.debug("获取电池电量:${bleDevice.mac},是否为心跳:${isHeartBeat}")
         BleCmdManager.getPower(bleDevice.mac, object : CustomBleWriteCallback() {
-            override fun onWriteSuccess(p0: Int, p1: Int, p2: ByteArray?) {
+            override fun onWriteSuccess(
+                bleDevice: BleDevice,
+                characteristic: BluetoothGattCharacteristic,
+                current: Int,
+                total: Int,
+                justWrite: ByteArray,
+                data: ByteArray
+            ) {
+
             }
 
-            override fun onWriteFailure(p0: BleException?) {
+            override fun onWriteFailure(
+                bleDevice: BleDevice,
+                characteristic: BluetoothGattCharacteristic?,
+                exception: BleException,
+                current: Int,
+                total: Int,
+                justWrite: ByteArray?,
+                data: ByteArray?,
+                isTotalFail: Boolean
+            ) {
                 ThreadUtils.runOnIODelayed(500) {
                     getBatteryPower(bleDevice)
                 }
@@ -576,6 +574,7 @@ object BleConnectionManager {
     /**
      * 监听蓝牙设备
      */
+    @SuppressLint("MissingPermission")
     private fun indicate(
         bleBean: BleBean?,
         isNeedLoading: Boolean = false,
@@ -598,13 +597,20 @@ object BleConnectionManager {
                         logger.info("蓝牙连接-indicate onDisConnectPrompt : $promptStr")
                     }
 
-                    override fun onIndicateSuccess() {
+                    override fun onIndicateSuccess(
+                        bleDevice: BleDevice,
+                        characteristic: BluetoothGattCharacteristic
+                    ) {
                         logger.info("蓝牙连接-onIndicateSuccess")
                         isIndicateSuccess = true
                         getToken(bleBean, isNeedLoading, prepareDoneCallBack)
                     }
 
-                    override fun onIndicateFailure(exception: BleException?) {
+                    override fun onIndicateFailure(
+                        bleDevice: BleDevice,
+                        characteristic: BluetoothGattCharacteristic?,
+                        exception: BleException
+                    ) {
                         if (isNeedLoading) LoadingEvent.sendLoadingEvent(null, false)
                         logger.error("蓝牙连接-onIndicateFailure : ${bleBean.bleDevice.mac} - ${exception?.description}")
                         ThreadUtils.runOnIODelayed(500) {
@@ -615,7 +621,11 @@ object BleConnectionManager {
                         }
                     }
 
-                    override fun onCharacteristicChanged(data: ByteArray?) {
+                    override fun onCharacteristicChanged(
+                        bleDevice: BleDevice,
+                        characteristic: BluetoothGattCharacteristic,
+                        data: ByteArray?
+                    ) {
                         logger.info("蓝牙连接-onCharacteristicChanged : ${data?.toHexStrings()}")
                         if (bleIndicateListeners.isEmpty()) {
                             bleIndicateListeners.put(this, baseIndicateListeners)
@@ -628,6 +638,13 @@ object BleConnectionManager {
                             }
                         }
                     }
+
+                    override fun onIndicateCancel(
+                        bleDevice: BleDevice,
+                        characteristic: BluetoothGattCharacteristic
+                    ) {
+                        logger.info("蓝牙连接-onIndicateCancel : ${bleDevice.mac}")
+                    }
                 })
         }
     }
@@ -643,12 +660,28 @@ object BleConnectionManager {
         if (isNeedLoading) LoadingEvent.sendLoadingEvent("开始获取token...", true)
         bleBean?.let {
             BleCmdManager.getToken(it.bleDevice.mac, object : CustomBleWriteCallback() {
-                override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
+                override fun onWriteSuccess(
+                    bleDevice: BleDevice,
+                    characteristic: BluetoothGattCharacteristic,
+                    current: Int,
+                    total: Int,
+                    justWrite: ByteArray,
+                    data: ByteArray
+                ) {
                     if (isNeedLoading) LoadingEvent.sendLoadingEvent("token获取成功", true)
                     logger.info("getToken success : ${bleBean.bleDevice.mac}")
                 }
 
-                override fun onWriteFailure(exception: BleException?) {
+                override fun onWriteFailure(
+                    bleDevice: BleDevice,
+                    characteristic: BluetoothGattCharacteristic?,
+                    exception: BleException,
+                    current: Int,
+                    total: Int,
+                    justWrite: ByteArray?,
+                    data: ByteArray?,
+                    isTotalFail: Boolean
+                ) {
                     if (isNeedLoading) LoadingEvent.sendLoadingEvent("token获取失败", true)
                     logger.error("getToken fail : ${bleBean.bleDevice.mac}")
                     prepareDoneCallBack?.invoke(false, null)
@@ -662,7 +695,11 @@ object BleConnectionManager {
      *   1. 先尝试不充电连接,若成功就返回 true;
      *   2. 否则开启“充电”,等 500ms,再尝试一次连接,连接成功后断电并返回 true;否则返回 false。
      */
-    suspend fun tryConnectWithOptionalCharge(mac: String, withOpenCharge: Boolean = true): Boolean =
+    suspend fun tryConnectWithOptionalCharge(
+        mac: String,
+        withOpenCharge: Boolean = true,
+        isSend: Boolean
+    ): Boolean =
         withContext(Dispatchers.IO) {
             // -------- 第二次尝试:先开电,再连 --------
             // 开电,并等待回调
@@ -683,13 +720,13 @@ object BleConnectionManager {
             // 再次注册连接监听
             val secondTry = suspendCancellableCoroutine<Boolean> { cont ->
                 var isCalled = false
-                registerConnectListener(mac, true) { isDone, _ ->
+                registerConnectListener(mac, true, isSend) { isDone, _ ->
                     if (isCalled) {
                         return@registerConnectListener
                     }
                     isCalled = true
                     // 无论成功或失败,都先把电关掉
-//                    ModBusController.controlKeyCharge(false, mac) { }
+                    ModBusController.controlKeyCharge(false, mac) { }
                     if (cont.isActive) {
                         cont.resume(isDone)
                     }

+ 127 - 0
ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleQueueDispatcher.kt

@@ -0,0 +1,127 @@
+package com.grkj.ui_base.utils.ble
+
+import android.Manifest
+import android.annotation.SuppressLint
+import androidx.annotation.RequiresPermission
+import com.grkj.ui_base.utils.ble.BleConnectionManager.deviceList
+import com.huyuhui.fastble.BleManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+abstract class BleQueueDispatcher {
+    private val logger: Logger = LoggerFactory.getLogger(BleQueueDispatcher::class.java)
+    private val taskQueue = ArrayDeque<Pair<String, (Boolean) -> Unit>>() // mac + callback
+    private val activeMacs = mutableMapOf<String, (Boolean) -> Unit>()
+    private val connectedMacs = mutableSetOf<String>()
+    private val pendingDisconnectJobs = mutableMapOf<String, Job>()
+
+    open val maxConnections: Int = 1
+    private val dispatcherScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
+
+    @Synchronized
+    fun submit(mac: String, onResult: (Boolean) -> Unit) {
+
+        if (activeMacs.containsKey(mac) || taskQueue.any { it.first == mac } || connectedMacs.contains(
+                mac
+            )) {
+            if (connectedMacs.contains(mac)) {
+                onResult(true)
+            }
+            // 如果已存在断连计划,取消它
+            pendingDisconnectJobs.remove(mac)?.cancel()
+            return
+        }
+        taskQueue.add(mac to onResult)
+        tryStartNext()
+    }
+
+    @Synchronized
+    private fun tryStartNext() {
+        if ((activeMacs.size + connectedMacs.size) >= maxConnections || taskQueue.isEmpty()) return
+        val (mac, callback) = taskQueue.removeFirst()
+        activeMacs[mac] = callback
+        doConnect(mac) { success ->
+            synchronized(this) {
+                activeMacs.remove(mac)
+                if (success) {
+                    connectedMacs.add(mac)
+                }
+                callback(success)
+                tryStartNext()
+            }
+        }
+    }
+
+    protected abstract fun doConnect(mac: String, callback: (Boolean) -> Unit)
+
+    @Synchronized
+    fun clear(mac: String) {
+        taskQueue.removeIf { it.first == mac }
+        activeMacs.remove(mac)
+        connectedMacs.remove(mac)
+        pendingDisconnectJobs.remove(mac)?.cancel()
+        if ((activeMacs.size + connectedMacs.size) < maxConnections && taskQueue.isNotEmpty()) {
+            tryStartNext()
+        }
+    }
+
+    @SuppressLint("MissingPermission")
+    @Synchronized
+    fun scheduleDisconnect(mac: String, delayMillis: Long = 0) {
+        if (!connectedMacs.contains(mac)) return
+        pendingDisconnectJobs[mac]?.cancel()
+        val job = dispatcherScope.launch {
+            delay(delayMillis)
+            synchronized(this@BleQueueDispatcher) {
+                if (connectedMacs.remove(mac)) {
+                    val deviceBean = deviceList.find { it.bleDevice.mac == mac }
+                    deviceBean?.let {
+                        disconnectDeviceByMac(mac)
+                        deviceList.removeIf { it.bleDevice.mac == deviceBean.bleDevice.mac }
+                    }
+                    logger.info("当前线程:${Thread.currentThread().name}")
+                    clear(mac)
+                    tryStartNext()
+                }
+                pendingDisconnectJobs.remove(mac)
+            }
+        }
+        pendingDisconnectJobs[mac] = job
+    }
+
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+    private fun disconnectDeviceByMac(mac: String) {
+        val device = BleManager.getAllConnectedDevice().find { it.mac == mac }
+        if (device != null) {
+            BleManager.disconnect(device)
+        }
+    }
+
+    @Synchronized
+    fun disconnectAll(delayMillis: Long = 0) {
+        val allMacs = connectedMacs.toList()
+        for (mac in allMacs) {
+            scheduleDisconnect(mac, delayMillis)
+        }
+    }
+
+
+    fun isConnecting(mac: String): Boolean = activeMacs.containsKey(mac)
+    fun isQueued(mac: String): Boolean = taskQueue.any { it.first == mac }
+    fun isConnected(mac: String): Boolean = connectedMacs.contains(mac)
+
+    fun getConnectedMacs(): List<String> = connectedMacs.toList()
+
+    fun getActiveMacs(): List<String> = activeMacs.keys.toList()
+
+    fun canConnect(): Boolean = (activeMacs.size + connectedMacs.size) <= maxConnections
+
+    fun shouldDisconnect(mac: String): Boolean =
+        isConnecting(mac) || isQueued(mac) || isConnected(mac)
+}

+ 14 - 0
ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleReturnDispatcher.kt

@@ -0,0 +1,14 @@
+package com.grkj.ui_base.utils.ble
+
+import com.sik.sikcore.thread.ThreadUtils
+
+object BleReturnDispatcher : BleQueueDispatcher() {
+    override val maxConnections = 1
+
+    override fun doConnect(mac: String, callback: (Boolean) -> Unit) {
+        ThreadUtils.runOnIO {
+            val isConnected = BleConnectionManager.tryConnectWithOptionalCharge(mac, isSend = false)
+            callback(isConnected)
+        }
+    }
+}

+ 14 - 0
ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleSendDispatcher.kt

@@ -0,0 +1,14 @@
+package com.grkj.ui_base.utils.ble
+
+import com.sik.sikcore.thread.ThreadUtils
+
+object BleSendDispatcher : BleQueueDispatcher() {
+    override val maxConnections = 1
+
+    override fun doConnect(mac: String, callback: (Boolean) -> Unit) {
+        ThreadUtils.runOnIO {
+            val isConnected = BleConnectionManager.tryConnectWithOptionalCharge(mac, isSend = true)
+            callback(isConnected)
+        }
+    }
+}

+ 104 - 72
ui-base/src/main/java/com/grkj/ui_base/utils/ble/BleUtil.kt

@@ -1,16 +1,20 @@
 package com.grkj.ui_base.utils.ble
 
+import android.Manifest
 import android.app.Application
 import android.bluetooth.BluetoothGatt
+import android.bluetooth.le.ScanSettings
 import android.os.Build
 import android.util.Log
-import com.clj.fastble.BleManager
-import com.clj.fastble.callback.BleGattCallback
-import com.clj.fastble.callback.BleMtuChangedCallback
-import com.clj.fastble.data.BleDevice
-import com.clj.fastble.exception.BleException
-import com.clj.fastble.scan.BleScanRuleConfig
+import androidx.annotation.RequiresPermission
 import com.grkj.shared.utils.extension.toHexStrings
+import com.huyuhui.fastble.BleManager
+import com.huyuhui.fastble.callback.BleGattCallback
+import com.huyuhui.fastble.callback.BleMtuChangedCallback
+import com.huyuhui.fastble.data.BleDevice
+import com.huyuhui.fastble.exception.BleException
+import com.huyuhui.fastble.scan.BleScanRuleConfig
+import com.sik.sikcore.SIKCore
 import com.sik.sikcore.thread.ThreadUtils
 import kotlinx.coroutines.delay
 import org.slf4j.Logger
@@ -31,46 +35,53 @@ class BleUtil private constructor() {
             }
             private set
 
-        const val OPERATE_TIMEOUT = 10 * 1000
+        const val OPERATE_TIMEOUT = 10 * 1000L
     }
 
-    fun initBle(application: Application?) {
+    fun initBle(application: Application) {
         try {
-            BleManager.getInstance().init(application)
-            BleManager.getInstance().enableLog(false)
-                .setConnectOverTime(5 * 1000L)
-                .setReConnectCount(0, 0)
-                .setSplitWriteNum(500)
-                .operateTimeout =
-                OPERATE_TIMEOUT // 设置操作readRssi、setMtu、write、read、notify、indicate的超时时间(毫秒)
+            BleManager.init(application)
+            BleManager.enableLog(false)
+                .apply {
+                    this.splitWriteNum = 500
+                    this.maxConnectCount = 0
+                    operateTimeout = OPERATE_TIMEOUT
+                }
             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
                 //Android 12及以上不允许添加过滤器
                 val bleScanRuleConfig = BleScanRuleConfig.Builder()
-                    .setAutoConnect(false)
                     .setScanTimeOut(3_000L)
-                    .setDeviceName(true, BleConst.BLE_LOCAL_NAME)
+                    .setDeviceName(BleConst.BLE_LOCAL_NAME)
+                    .apply {
+                        setScanSettings(ScanSettings.Builder().apply {
+                            this.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+                            this.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
+                            this.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
+                        }.build())
+                    }
                     .build()
-                BleManager.getInstance().initScanRule(bleScanRuleConfig)
+                BleManager.bleScanRuleConfig = bleScanRuleConfig
             }
         } catch (e: Exception) {
             Log.d("initBlueTooth", "蓝牙初始化:${e.message}")
         }
     }
 
+    @RequiresPermission(Manifest.permission.BLUETOOTH_SCAN)
     fun scan(bleScanCallback: CustomBleScanCallback) {
-        if (BleManager.getInstance().isSupportBle) {
-            if (BleManager.getInstance().isBlueEnable) {
+        if (BleManager.isSupportBle(SIKCore.getApplication())) {
+            if (BleManager.isBleEnable(SIKCore.getApplication())) {
                 if (inScan) {
-                    BleManager.getInstance().cancelScan()
+                    BleManager.cancelScan()
                 }
-                BleManager.getInstance().scan(object : CustomBleScanCallback() {
+                BleManager.scan(object : CustomBleScanCallback() {
                     override fun onPrompt(promptStr: String?) {
                         bleScanCallback.onPrompt(promptStr)
                     }
 
-                    override fun onScanFinished(p0: List<BleDevice?>?) {
+                    override fun onScanFinished(scanResultList: List<BleDevice>) {
                         inScan = false
-                        bleScanCallback.onScanFinished(p0)
+                        bleScanCallback.onScanFinished(scanResultList)
                     }
 
                     override fun onScanStarted(p0: Boolean) {
@@ -78,8 +89,16 @@ class BleUtil private constructor() {
                         bleScanCallback.onScanStarted(p0)
                     }
 
-                    override fun onScanning(p0: BleDevice?) {
-                        bleScanCallback.onScanning(p0)
+                    override fun onFilter(bleDevice: BleDevice): Boolean {
+                        return bleDevice.name == BleConst.BLE_LOCAL_NAME
+                    }
+
+                    override fun onLeScan(
+                        oldDevice: BleDevice,
+                        newDevice: BleDevice,
+                        scannedBefore: Boolean
+                    ) {
+                        bleScanCallback.onLeScan(oldDevice, newDevice, scannedBefore)
                     }
 
                 })
@@ -91,22 +110,11 @@ class BleUtil private constructor() {
         }
     }
 
-    fun connectBySelect(bleDevice: BleDevice?, bleGattCallback: CustomBleGattCallback) {
-        if (BleManager.getInstance().isSupportBle) {
-            if (BleManager.getInstance().isBlueEnable) {
-                BleManager.getInstance().connect(bleDevice, bleGattCallback)
-            } else {
-                bleGattCallback.onPrompt("请打开您的蓝牙后重试")
-            }
-        } else {
-            bleGattCallback.onPrompt("您的设备不支持蓝牙设备")
-        }
-    }
-
-    fun connectByMac(mac: String?, bleGattCallback: CustomBleGattCallback) {
-        if (BleManager.getInstance().isSupportBle) {
-            if (BleManager.getInstance().isBlueEnable) {
-                BleManager.getInstance().connect(mac, bleGattCallback)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+    fun connectBySelect(bleDevice: BleDevice, bleGattCallback: CustomBleGattCallback) {
+        if (BleManager.isSupportBle(SIKCore.getApplication())) {
+            if (BleManager.isBleEnable(SIKCore.getApplication())) {
+                BleManager.connect(bleDevice, bleGattCallback)
             } else {
                 bleGattCallback.onPrompt("请打开您的蓝牙后重试")
             }
@@ -115,18 +123,20 @@ class BleUtil private constructor() {
         }
     }
 
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
     fun setMtu(bleDevice: BleDevice) {
-        BleManager.getInstance().setMtu(bleDevice, BleConst.MTU, object : BleMtuChangedCallback() {
-            override fun onSetMTUFailure(exception: BleException?) {
-//                indicate()
+        BleManager.setMtu(bleDevice, BleConst.MTU, object : BleMtuChangedCallback() {
+            override fun onSetMTUFailure(bleDevice: BleDevice, exception: BleException) {
+                logger.error("设置MTU失败")
             }
 
-            override fun onMtuChanged(mtu: Int) {
-//                indicate()
+            override fun onMtuChanged(bleDevice: BleDevice, mtu: Int) {
+                logger.info("设置MTU成功")
             }
         })
     }
 
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
     fun indicate(
         bleDevice: BleDevice,
         serviceUUID: String = BleConst.SERVICE_UUID,
@@ -135,48 +145,57 @@ class BleUtil private constructor() {
         indicateCallback: CustomBleIndicateCallback?
     ) {
         // stopIndicate包含removeIndicateCallback
-//        BleManager.getInstance().removeIndicateCallback(bleDevice, indicateUUID)
-        BleManager.getInstance().stopIndicate(bleDevice, serviceUUID, indicateUUID)
+        BleManager.stopIndicate(bleDevice, serviceUUID, indicateUUID)
         if (!isStart) {
             return
         }
-        if (!BleManager.getInstance().isSupportBle) {
+        if (!BleManager.isSupportBle(SIKCore.getApplication())) {
             indicateCallback?.onPrompt("该设备不支持蓝牙BLE")
             return
         }
 
-        if (!BleManager.getInstance().isBlueEnable) {
+        if (!BleManager.isBleEnable(SIKCore.getApplication())) {
             indicateCallback?.onPrompt("蓝牙已关闭,请打开蓝牙后重试")
             return
         }
-        if (BleManager.getInstance().isConnected(bleDevice.mac)) {
-            BleManager.getInstance()
-                .indicate(bleDevice, serviceUUID, indicateUUID, indicateCallback)
+        if (BleManager.isConnected(bleDevice.mac)) {
+            BleManager.indicate(bleDevice, serviceUUID, indicateUUID, indicateCallback)
         } else {
-            BleManager.getInstance().connect(bleDevice.mac, object : BleGattCallback() {
-                override fun onStartConnect() {}
-                override fun onConnectFail(bleDevice: BleDevice, exception: BleException) {
-                    BleManager.getInstance().removeConnectGattCallback(bleDevice)
+            BleManager.connect(bleDevice.mac, object : BleGattCallback() {
+                override fun onStartConnect(bleDevice: BleDevice) {
+                    indicateCallback?.onConnectPrompt("正在连接设备...")
+                }
+
+                override fun onConnectCancel(
+                    bleDevice: BleDevice,
+                    skip: Boolean
+                ) {
+                    BleManager.removeConnectGattCallback(bleDevice)
+                    indicateCallback?.onConnectPrompt("连接取消!")
+                }
+
+                override fun onConnectFail(bleDevice: BleDevice?, exception: BleException) {
+                    BleManager.removeConnectGattCallback(bleDevice)
                     indicateCallback?.onConnectPrompt("连接失败!请检查设备是否打开,并尝试重新连接 : $exception")
                 }
 
+                @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
                 override fun onConnectSuccess(
                     bleDevice: BleDevice,
-                    gatt: BluetoothGatt,
+                    gatt: BluetoothGatt?,
                     status: Int
                 ) {
-                    BleManager.getInstance().removeConnectGattCallback(bleDevice)
-                    BleManager.getInstance()
-                        .indicate(bleDevice, serviceUUID, indicateUUID, indicateCallback)
+                    BleManager.removeConnectGattCallback(bleDevice)
+                    BleManager.indicate(bleDevice, serviceUUID, indicateUUID, indicateCallback)
                 }
 
                 override fun onDisConnected(
                     isActiveDisConnected: Boolean,
                     device: BleDevice,
-                    gatt: BluetoothGatt,
+                    gatt: BluetoothGatt?,
                     status: Int
                 ) {
-                    BleManager.getInstance().removeConnectGattCallback(device)
+                    BleManager.removeConnectGattCallback(device)
                     indicateCallback?.onDisConnectPrompt("连接断开!请检查硬件状态,并尝试重新连接!")
                 }
             })
@@ -184,6 +203,7 @@ class BleUtil private constructor() {
     }
 
 
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
     fun write(
         bleDevice: BleDevice,
         serviceUUID: String = BleConst.SERVICE_UUID,
@@ -193,27 +213,39 @@ class BleUtil private constructor() {
     ) {
         logger.info("ble_write : ${cmd?.toHexStrings()}")
         cmd ?: return
-        if (!BleManager.getInstance().isSupportBle) {
+        if (!BleManager.isSupportBle(SIKCore.getApplication())) {
             writeCallback?.onPrompt("该设备不支持蓝牙BLE")
             return
         }
 
-        if (!BleManager.getInstance().isBlueEnable) {
+        if (!BleManager.isBleEnable(SIKCore.getApplication())) {
             writeCallback?.onPrompt("蓝牙已关闭,请打开蓝牙后重试")
             return
         }
-        if (BleManager.getInstance().isConnected(bleDevice.mac)) {
-            BleManager.getInstance().write(bleDevice, serviceUUID, writeUUID, cmd, writeCallback)
+        if (BleManager.isConnected(bleDevice.mac)) {
+            BleManager.write(bleDevice, serviceUUID, writeUUID, cmd, callback = writeCallback)
         } else {
             fun connectAndWrite() {
                 ThreadUtils.runOnIO {
-                    val isConnect = BleConnectionManager.tryConnectWithOptionalCharge(bleDevice.mac)
+                    val isConnect =
+                        BleSendDispatcher.isConnecting(bleDevice.mac) || BleReturnDispatcher.isConnecting(
+                            bleDevice.mac
+                        )
                     if (isConnect) {
-                        BleManager.getInstance()
-                            .write(bleDevice, serviceUUID, writeUUID, cmd, writeCallback)
+                        BleManager.write(
+                            bleDevice,
+                            serviceUUID,
+                            writeUUID,
+                            cmd,
+                            callback = writeCallback
+                        )
                     } else {
                         delay(800)
-                        connectAndWrite()
+                        bleDevice.mac?.let {
+                            BleSendDispatcher.submit(bleDevice.mac) {
+                                connectAndWrite()
+                            }
+                        }
                     }
                 }
             }

+ 1 - 1
ui-base/src/main/java/com/grkj/ui_base/utils/ble/CustomBleGattCallback.kt

@@ -1,6 +1,6 @@
 package com.grkj.ui_base.utils.ble
 
-import com.clj.fastble.callback.BleGattCallback
+import com.huyuhui.fastble.callback.BleGattCallback
 
 abstract class CustomBleGattCallback : BleGattCallback() {
     abstract fun onPrompt(promptStr: String?)

+ 1 - 1
ui-base/src/main/java/com/grkj/ui_base/utils/ble/CustomBleIndicateCallback.kt

@@ -1,6 +1,6 @@
 package com.grkj.ui_base.utils.ble
 
-import com.clj.fastble.callback.BleIndicateCallback
+import com.huyuhui.fastble.callback.BleIndicateCallback
 
 abstract class CustomBleIndicateCallback : BleIndicateCallback() {
     abstract fun onPrompt(promptStr: String?)

+ 2 - 2
ui-base/src/main/java/com/grkj/ui_base/utils/ble/CustomBleScanCallback.kt

@@ -1,7 +1,7 @@
 package com.grkj.ui_base.utils.ble
 
-import com.clj.fastble.callback.BleScanCallback
+import com.huyuhui.fastble.callback.BleScanCallback
 
-abstract class CustomBleScanCallback : BleScanCallback() {
+abstract class CustomBleScanCallback : BleScanCallback {
     abstract fun onPrompt(promptStr: String?)
 }

+ 1 - 1
ui-base/src/main/java/com/grkj/ui_base/utils/ble/CustomBleWriteCallback.kt

@@ -1,6 +1,6 @@
 package com.grkj.ui_base.utils.ble
 
-import com.clj.fastble.callback.BleWriteCallback
+import com.huyuhui.fastble.callback.BleWriteCallback
 
 abstract class CustomBleWriteCallback : BleWriteCallback() {
     open fun onPrompt(promptStr: String?) {}

+ 1 - 1
ui-base/src/main/java/com/grkj/ui_base/utils/event/GetTicketStatusEvent.kt

@@ -1,8 +1,8 @@
 package com.grkj.ui_base.utils.event
 
-import com.clj.fastble.data.BleDevice
 import com.grkj.shared.model.EventBean
 import com.grkj.data.data.EventConstants
+import com.huyuhui.fastble.data.BleDevice
 
 /**
  * 获取作业票状态事件

+ 42 - 107
ui-base/src/main/java/com/grkj/ui_base/utils/modbus/ModBusController.kt

@@ -1,6 +1,5 @@
 package com.grkj.ui_base.utils.modbus
 
-import com.clj.fastble.BleManager
 import com.grkj.data.di.LogicManager
 import com.grkj.data.model.res.CabinetSlotsRecord
 import com.grkj.shared.utils.extension.isPureZero
@@ -10,13 +9,18 @@ import com.grkj.ui_base.R
 import com.grkj.ui_base.config.ISCSConfig
 import com.grkj.ui_base.utils.CommonUtils
 import com.grkj.ui_base.utils.ble.BleConnectionManager
+import com.grkj.ui_base.utils.ble.BleReturnDispatcher
+import com.grkj.ui_base.utils.ble.BleSendDispatcher
 import com.grkj.ui_base.utils.event.ModbusInitCompleteEvent
 import com.grkj.ui_base.utils.extension.tip
 import com.kongzue.dialogx.dialogs.PopTip
+import kotlinx.coroutines.isActive
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import java.util.concurrent.atomic.AtomicInteger
 import java.util.stream.Collectors
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
 
 
 /**
@@ -258,11 +262,12 @@ object ModBusController {
                                         }
                                 logger.info("钥匙是否准备完毕:${isKeyReady},${ISCSConfig.isInit}")
                                 if (isKeyReady && ISCSConfig.isInit) {
-                                    ISCSConfig.canInitDevice = false
+                                    ISCSConfig.canInitDevice = true
                                     logger.info("发送初始化完成事件")
                                     ModbusInitCompleteEvent.sendModbusInitCompleteEvent()
-                                }else{
-                                    ISCSConfig.canInitDevice = false
+                                } else if (isKeyReady) {
+                                    ISCSConfig.canInitDevice = true
+                                    ModbusInitCompleteEvent.sendModbusInitCompleteEvent()
                                 }
                             }
                         }
@@ -857,96 +862,6 @@ object ModBusController {
             .flatMap { it.getKeyList() }.find { it.mac == mac }
     }
 
-    /**
-     * 根据底座获取钥匙
-     */
-    fun getKeyByDock(dockAddr: Byte?, idx: Int): DockBean.KeyBean? {
-        dockAddr ?: return null
-        return dockList.find { it.addr == dockAddr }?.getKeyList()?.find { it.idx == idx }
-    }
-
-    /**
-     * 钥匙是否存在
-     */
-    fun isKeyExist(dockAddr: Byte?, idx: Int): Boolean {
-        dockAddr ?: return false
-        return dockList.find { it.addr == dockAddr }?.getKeyList()
-            ?.find { it.idx == idx && it.isExist } != null
-    }
-
-    /**
-     * 获取钥匙锁仓的锁定状态
-     */
-    fun getKeyBuckleLockEnabled(dockAddr: Byte?, idx: Int): Boolean {
-        dockAddr ?: return false
-        return dockList.find { it.addr == dockAddr }?.getKeyList()
-            ?.find { it.idx == idx }?.lockEnabled == true
-    }
-
-    /**
-     * 获取挂锁锁仓的锁定状态
-     */
-    fun getLockBuckleLockEnabled(dockAddr: Byte?, lockIdx: Int): Boolean {
-        dockAddr ?: return false
-        return dockList.find { it.addr == dockAddr }?.getLockList()
-            ?.find { it.idx == lockIdx }?.lockEnabled == true
-    }
-
-    /**
-     * 获取挂锁是否存在
-     */
-    fun isLockExist(dockAddr: Byte?, lockIdx: Int): Boolean {
-        dockAddr ?: return false
-        return dockList.find { it.addr == dockAddr }?.getLockList()
-            ?.find { it.idx == lockIdx && it.isExist } != null
-    }
-
-    /**
-     * 获取钥匙状态
-     *
-     * @return 0:不存在 1:存在 2:有RFID 3:有MAC 4:连接上 5:待机模式 6: 待机模式无异常
-     */
-    fun getKeyStatus(dockAddr: Byte?, idx: Int): Int {
-        dockAddr ?: return 0
-        val key = dockList.find { it.addr == dockAddr }?.getKeyList()?.find { it.idx == idx }
-        logger.info("getKeyStatus key : $key")
-        key ?: return 0
-        var status = 0
-        if (key.isExist) {
-            status = 1
-        } else {
-            return status
-        }
-        if (key.rfid != null) {
-            status = 2
-        } else {
-            return status
-        }
-        if (key.mac != null) {
-            status = 3
-        } else {
-            return status
-        }
-        if (BleManager.getInstance()
-                .isConnected(BleConnectionManager.getBleDeviceByMac(key.mac)?.bleDevice)
-        ) {
-            status = 4
-        } else {
-            return status
-        }
-        if (key.isReady) {
-            status = 5
-        } else {
-            return status
-        }
-        if (key.rfid != null && BleConnectionManager.mExceptionKeyList.none { it == key.rfid }) {
-            status = 6
-        } else {
-            return status
-        }
-        return status
-    }
-
     /**
      * 更新钥匙的准备状态
      */
@@ -1076,8 +991,11 @@ object ModBusController {
         var keyList = keyDockList.flatMap { it.deviceList }.apply {
             logger.info("keyStatus:${this}")
         }.filterIsInstance<DockBean.KeyBean>()
+            //RFID不为空,RFID不在异常列表,mac不在异常列表,mac不为空,存在,不在归还连接列表,不在归还连接中列表
             .filterIndexed { idx, _ -> (idx + 1) !in slotCols }.filter { kb ->
-                !kb.rfid.isNullOrEmpty() && kb.rfid !in exceptionKeysRfid && kb.mac !in exceptionKeysMac && !kb.mac.isNullOrEmpty() && kb.isExist
+                !kb.rfid.isNullOrEmpty() && kb.rfid !in exceptionKeysRfid && kb.mac !in exceptionKeysMac && !kb.mac.isNullOrEmpty() && kb.isExist && !BleReturnDispatcher.isConnected(
+                    kb.mac ?: ""
+                ) && !BleReturnDispatcher.isConnecting(kb.mac ?: "")
             }
 
         logger.info("蓝牙连接-获取到钥匙信息:${keyList}")
@@ -1086,7 +1004,9 @@ object ModBusController {
         }
         keyList = keyList.sortedWith(
             compareByDescending<DockBean.KeyBean> {
-                BleManager.getInstance().isConnected(it.mac)
+                BleSendDispatcher.isConnected(it.mac ?: "") || BleSendDispatcher.isConnecting(
+                    it.mac ?: ""
+                )
             }    // 主键:在线优先
                 .thenByDescending { BleConnectionManager.getBleDeviceByMac(it.mac)?.token != null } // 次键:有 token 优先
                 .thenByDescending { it.power }                                                // 三级:电量越高
@@ -1094,17 +1014,31 @@ object ModBusController {
 
         for (kb in keyList) {
             val mac = kb.mac ?: continue
-
-            val connected = BleConnectionManager.tryConnectWithOptionalCharge(mac)
-            if (connected) {
-                logger.info("蓝牙连接完成 :${mac}")
-                // 找到第一个能连的:从 keyDockList 里拿同 rfid 的 addr
-                val addr =
-                    keyDockList.firstOrNull { it.getKeyList().any { it.rfid == kb.rfid } }?.addr
-                if (addr != null) {
-                    return addr to kb
+            val result = suspendCoroutine<Pair<Byte, DockBean.KeyBean>?> { cont ->
+                BleSendDispatcher.submit(mac) { connected ->
+                    if (connected) {
+                        logger.info("蓝牙连接完成 :${mac}")
+                        // 找到第一个能连的:从 keyDockList 里拿同 rfid 的 addr
+                        val addr =
+                            keyDockList.firstOrNull {
+                                it.getKeyList().any { it.rfid == kb.rfid }
+                            }?.addr
+                        if (addr != null) {
+                            if (cont.context.isActive) {
+                                cont.resume(addr to kb)
+                            }
+                        } else {
+                            null
+                        }
+                    } else {
+                        null
+                    }
                 }
             }
+            if (result != null) {
+                return result
+            }
+
         }
 
         // 一个都没成功
@@ -1207,13 +1141,14 @@ object ModBusController {
     /**
      * 获取电量最多的钥匙
      */
-    fun getMaxPowerKey(): DockBean.KeyBean {
+    fun getMaxPowerKey(mac: String): DockBean.KeyBean {
         return dockList.filter {
             it.type in listOf(
                 DeviceConst.DOCK_TYPE_KEY,
                 DeviceConst.DOCK_TYPE_PORTABLE
             )
         }
-            .flatMap { it.deviceList }.filterIsInstance<DockBean.KeyBean>().maxBy { it.power }
+            .flatMap { it.deviceList }.filterIsInstance<DockBean.KeyBean>().filter { it.mac != mac }
+            .maxBy { it.power }
     }
 }