|
@@ -1,38 +1,141 @@
|
|
|
package com.grkj.iscs_mars.service
|
|
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.ble.BleSendDispatcher
|
|
|
import com.grkj.iscs_mars.modbus.ModBusController
|
|
import com.grkj.iscs_mars.modbus.ModBusController
|
|
|
-import com.grkj.iscs_mars.util.log.LogUtil
|
|
|
|
|
import com.sik.cronjob.annotations.CronJob
|
|
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.Logger
|
|
|
import org.slf4j.LoggerFactory
|
|
import org.slf4j.LoggerFactory
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 检查钥匙信息任务
|
|
* 检查钥匙信息任务
|
|
|
|
|
+ *
|
|
|
|
|
+ * - 支持在检查过程中“挂起等待 isInLogin=true 后继续”
|
|
|
|
|
+ * - 防重入:同一时刻只会有一个检查任务在跑
|
|
|
|
|
+ * - 仍保留 CronJob 定时入口;也支持在进入登录页时延迟启动一次
|
|
|
*/
|
|
*/
|
|
|
-
|
|
|
|
|
class CheckKeyInfoTask {
|
|
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)
|
|
@CronJob(intervalMillis = 30 * 60_000L, initialDelay = 0, runOnMainThread = false)
|
|
|
fun checkKeyInfo() {
|
|
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) // 轻量轮询,避免忙等
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
+}
|