Przeglądaj źródła

refactor(更新) :
- 重构检查钥匙信息任务,增加登录状态判断和防重入机制
- 优化蓝牙连接管理,增加设备工作状态标记,及时释放非工作状态的连接
- 修复HomeActivity中卡号转换异常未处理的问题
- 调整部分提示文案

周文健 2 miesięcy temu
rodzic
commit
ba82a75bcb

+ 16 - 1
app/src/main/java/com/grkj/iscs_mars/BusinessManager.kt

@@ -129,6 +129,11 @@ object BusinessManager {
     // 有问题的钥匙的列表 - rfid
     var mExceptionKeyList = mutableListOf<String>()
 
+    /**
+     * 检查钥匙任务
+     */
+    var checkKeyInfoTask: CheckKeyInfoTask = CheckKeyInfoTask()
+
     /**
      * 初始化消息总线
      */
@@ -151,7 +156,7 @@ object BusinessManager {
                 }
 
                 MsgEventConstants.MSG_EVENT_INIT_KEY_COMPLETE -> {
-                    val job = CronJobScanner.scanJobs(CheckKeyInfoTask())
+                    val job = CronJobScanner.scanJobs(checkKeyInfoTask)
                     MyApplication.cronJobManager.registerJobs(job)
                 }
                 // 钥匙当前模式
@@ -959,6 +964,7 @@ object BusinessManager {
         if (BleReturnDispatcher.isConnected(mac)) {
             getBleDeviceByMac(mac)?.bleDevice?.let {
                 LogUtil.i("开始读取作业票")
+                BleReturnDispatcher.busy(mac)
                 getTicketStatusWithRetry(
                     it,
                     isNeedLoading
@@ -1321,6 +1327,14 @@ object BusinessManager {
                                 from, bleDevice, retryCount - 1, timeoutCallBack
                             )
                         }
+                    } else {
+                        BleReturnDispatcher.clearNoBusyConnectedDevice()
+                        BleReturnDispatcher.scheduleDisconnect(bleDevice.mac)
+                        BleReturnDispatcher.submit(bleDevice.mac) {
+                            getCurrentStatus(
+                                from, bleDevice, retryCount - 1, timeoutCallBack
+                            )
+                        }
                     }
                 } else {
                     BleSendDispatcher.scheduleDisconnect(bleDevice.mac)
@@ -1454,6 +1468,7 @@ object BusinessManager {
                 )
             )
             LogUtil.i("Get ticket status complete : ${bleDevice.mac}")
+            BleReturnDispatcher.removeBusy(bleDevice.mac)
             // TD:Ticket Done
             if (isNeedLoading) sendEventMsg(
                 MsgEvent(

+ 28 - 0
app/src/main/java/com/grkj/iscs_mars/ble/BleQueueDispatcher.kt

@@ -27,6 +27,11 @@ abstract class BleQueueDispatcher {
      */
     private val activeMacs = mutableMapOf<String, MutableList<(Boolean) -> Unit>>()
 
+    /**
+     * 工作中的设备
+     */
+    private val busyMacs = mutableSetOf<String>()
+
     /**
      * 已连接的设备
      */
@@ -191,4 +196,27 @@ abstract class BleQueueDispatcher {
      */
     fun shouldDisconnect(mac: String): Boolean =
         isConnecting(mac) || isQueued(mac) || isConnected(mac)
+
+    /**
+     * mac加入工作状态
+     */
+    fun busy(mac: String) {
+        busyMacs.add(mac)
+    }
+
+    /**
+     * mac移除工作状态
+     */
+    fun removeBusy(mac: String) {
+        busyMacs.remove(mac)
+    }
+
+    /**
+     * 清除不繁忙的连接设备
+     */
+    fun clearNoBusyConnectedDevice() {
+        connectedMacs.filter { it in busyMacs }.forEach {
+            scheduleDisconnect(it)
+        }
+    }
 }

+ 123 - 20
app/src/main/java/com/grkj/iscs_mars/service/CheckKeyInfoTask.kt

@@ -1,38 +1,141 @@
 package com.grkj.iscs_mars.service
 
-import com.grkj.iscs_mars.BusinessManager
+import android.annotation.SuppressLint
 import com.grkj.iscs_mars.ble.BleSendDispatcher
 import com.grkj.iscs_mars.modbus.ModBusController
-import com.grkj.iscs_mars.util.log.LogUtil
 import com.sik.cronjob.annotations.CronJob
-import com.sik.sikcore.thread.ThreadUtils
+import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 
 /**
  * 检查钥匙信息任务
+ *
+ * - 支持在检查过程中“挂起等待 isInLogin=true 后继续”
+ * - 防重入:同一时刻只会有一个检查任务在跑
+ * - 仍保留 CronJob 定时入口;也支持在进入登录页时延迟启动一次
  */
-
 class CheckKeyInfoTask {
 
+    private val logger: Logger = LoggerFactory.getLogger(this::class.java)
+
+    /** 私有协程域(IO) */
+    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
+
+    /** 防重入锁:避免并发跑多个 check */
+    private val runMutex = Mutex()
+
+    /** 登录闸门:true=放行;false=挂起等待 */
+    private val loginGate = MutableStateFlow(false)
+
+    /** 延迟启动任务的句柄(可取消) */
+    private var startCheckKeyInfoJob: Job? = null
+
     /**
-     * 检查钥匙信息
+     * 是否在登录界面
+     * 设为 true:打开闸门,并延迟 60s 启动一次检查
+     * 设为 false:关闭闸门,正在执行的检查会在下一个 await 处挂起
      */
+    var isInLogin: Boolean = false
+        set(value) {
+            field = value
+            loginGate.value = value
+            if (value) {
+                // 如果已经有一个延迟任务在排队,先取消再重新计时
+                startCheckKeyInfoJob?.cancel()
+                startCheckKeyInfoJob = scope.launch {
+                    delay(60_000L)
+                    safeCheckKeyInfo()
+                }
+            } else {
+                // 退出登录页时,不强制取消正在跑的任务——让它在 await 处挂起即可
+                startCheckKeyInfoJob?.cancel()
+                startCheckKeyInfoJob = null
+            }
+        }
+
+    /** 对外:定时器触发的入口(保持不变) */
+    @SuppressLint("MissingPermission")
     @CronJob(intervalMillis = 30 * 60_000L, initialDelay = 0, runOnMainThread = false)
     fun checkKeyInfo() {
-//        LogUtil.i("开始检查钥匙信息")
-//        val existsKey = ModBusController.getExistsKey()
-//        ThreadUtils.runOnIO {
-//            for (bean in existsKey) {
-//                bean.mac?.let { mac ->
-//                    BleSendDispatcher.submit(mac) {
-//                        LogUtil.i("信息获取完成,检查是否立即断开")
-//                        ThreadUtils.runOnIO {
-//                            BusinessManager.checkMyTodoForHandleKey(mac, 0)
-//                        }
-//                    }
-//                }
-//            }
-//        }
+        // 用协程跑,避免阻塞 CronJob 线程
+        scope.launch { safeCheckKeyInfo() }
+    }
+
+    /**
+     * 受互斥保护的检查逻辑
+     */
+    @SuppressLint("MissingPermission")
+    private suspend fun safeCheckKeyInfo() = runMutex.withLock {
+        logger.info("开始检查钥匙信息")
+        awaitLogin()
+
+        for (bean in ModBusController.getExistsKey()) {
+            val mac = bean.mac ?: continue
+            handleSingleMac(mac) // 串行执行
+        }
+
+        logger.info("检查钥匙信息结束")
+    }
+
+    /**
+     * 串行处理单个 MAC
+     */
+    private suspend fun handleSingleMac(mac: String) {
+        // 登录状态检查
+        awaitLogin()
+        waitUntilCanConnect()
+
+        if (!isInLogin) {
+            logger.info("检测到不在登录页,暂停检查;mac=$mac")
+            awaitLogin()
+        }
+
+        // 提交连接并等待回调完成
+        awaitBleCheck(mac)
+    }
+
+    /**
+     * 提交到 BleSendDispatcher 并等待回调完成
+     */
+    @SuppressLint("MissingPermission")
+    private suspend fun awaitBleCheck(mac: String) {
+        return suspendCancellableCoroutine { cont ->
+            BleSendDispatcher.submit(mac) { ok ->
+                if (isInLogin) {
+                    BleSendDispatcher.scheduleDisconnect(mac)
+                }
+                cont.resume(Unit) {}
+            }
+        }
+    }
+
+
+
+    /**
+     * 挂起直到登录页(isInLogin=true)
+     * - 不 busy-wait;真正挂起,省电省 CPU
+     */
+    private suspend fun awaitLogin() {
+        if (loginGate.value) return
+        logger.info("不在登录页,挂起等待 …")
+        loginGate.first { it } // 挂起直到变为 true
+        logger.info("登录页已就绪,恢复检查")
+    }
+
+    /**
+     * 如果连接达到上限,则小睡等待;防止 submit 直接把队列堆爆
+     * (这里选了一个很轻的策略:短暂重试,避免卡死。你也可以结合信号量/通道做更细控制)
+     */
+    private suspend fun waitUntilCanConnect(maxWaitMillis: Long = 5_000L) {
+        val start = System.currentTimeMillis()
+        while (!BleSendDispatcher.canConnect()) {
+            if (System.currentTimeMillis() - start > maxWaitMillis) break
+            delay(100) // 轻量轮询,避免忙等
+        }
     }
-}
+}

+ 1 - 0
app/src/main/java/com/grkj/iscs_mars/view/activity/HomeActivity.kt

@@ -195,6 +195,7 @@ class HomeActivity : BaseMvpActivity<IHomeView, HomePresenter, ActivityHomeBindi
                 cardNo = try {
                     cardNo?.toLong()?.toByteArrays()?.toHexStrings(false)
                 } catch (e: Exception) {
+                    e.printStackTrace()
                     null
                 }
                 if (cardNo == null) {

+ 2 - 0
app/src/main/java/com/grkj/iscs_mars/view/activity/LoginActivity.kt

@@ -92,6 +92,7 @@ class LoginActivity : BaseMvpActivity<ILoginView, LoginPresenter, ActivityLoginB
 
     override fun onResume() {
         super.onResume()
+        BusinessManager.checkKeyInfoTask.isInLogin = true
         if (ModBusController.isRunning() != true) {
             BusinessManager.connectDock(true)
         }
@@ -141,6 +142,7 @@ class LoginActivity : BaseMvpActivity<ILoginView, LoginPresenter, ActivityLoginB
 
     override fun onStop() {
         super.onStop()
+        BusinessManager.checkKeyInfoTask.isInLogin = false
         cardLoginDialog?.dismiss()
         presenter?.unregisterListener()
         cardNo = ""

+ 2 - 2
app/src/main/res/values-zh/strings.xml

@@ -400,8 +400,8 @@
     <string name="operation">操作</string>
     <string name="ticket_get_failed">作业票获取失败,请重新放置钥匙</string>
     <string name="ticket_not_finish_can_not_return_lock">作业票未完成,禁止归还挂锁</string>
-    <string name="lock_is_not_enough_check_issue_ticket">当前机柜可派发%1$d把挂锁,剩余挂锁请从其他机柜获取或等待其他作业归还挂锁\n确定要执⾏操作吗?</string>
+    <string name="lock_is_not_enough_check_issue_ticket">当前机柜可派发%1$d把挂锁,剩余挂锁请从其他机柜获取或等待其他作业归还挂锁\n确定要执⾏操作吗?</string>
     <string name="lock_is_empty_stop_issue_ticket">当前机柜无可派发挂锁,请从其他机柜获取挂锁或等待其他作业归还挂锁。</string>
-    <string name="lock_enough_but_key_not_enough">当前机柜可派发%1$d把挂锁,请从其他机柜获取钥匙或等待其他作业归还钥匙\n确定要执行操作吗?</string>
+    <string name="lock_enough_but_key_not_enough">当前机柜可派发%1$d把挂锁,请从其他机柜获取钥匙或等待其他作业归还钥匙\n确定要执行操作吗?</string>
     <string name="key_not_enough_wait_or_take_on_another">当前机柜无可派发钥匙,请从其他机柜获取或等待其他作业归还钥匙。</string>
 </resources>

+ 2 - 2
app/src/main/res/values/strings.xml

@@ -400,8 +400,8 @@
     <string name="operation">操作</string>
     <string name="ticket_get_failed">作业票获取失败,请重新放置钥匙</string>
     <string name="ticket_not_finish_can_not_return_lock">作业票未完成,禁止归还挂锁</string>
-    <string name="lock_is_not_enough_check_issue_ticket">当前机柜可派发%1$d把挂锁,剩余挂锁请从其他机柜获取或等待其他作业归还挂锁\n确定要执⾏操作吗?</string>
+    <string name="lock_is_not_enough_check_issue_ticket">当前机柜可派发%1$d把挂锁,剩余挂锁请从其他机柜获取或等待其他作业归还挂锁\n确定要执⾏操作吗?</string>
     <string name="lock_is_empty_stop_issue_ticket">当前机柜无可派发挂锁,请从其他机柜获取挂锁或等待其他作业归还挂锁。</string>
-    <string name="lock_enough_but_key_not_enough">当前机柜可派发%1$d把挂锁,请从其他机柜获取钥匙或等待其他作业归还钥匙\n确定要执行操作吗?</string>
+    <string name="lock_enough_but_key_not_enough">当前机柜可派发%1$d把挂锁,请从其他机柜获取钥匙或等待其他作业归还钥匙\n确定要执行操作吗?</string>
     <string name="key_not_enough_wait_or_take_on_another">当前机柜无可派发钥匙,请从其他机柜获取或等待其他作业归还钥匙。</string>
 </resources>