Переглянути джерело

refactor(CAN):
- `DeviceParseStatus`: 移除`ExistDebouncer`防抖逻辑,改为在首次上报时不标记`deviceChange`,简化状态变化检测,避免了因异步回调带来的复杂性。
- `CanReadyPlugin`: 新增在轮询时打印CAN节点二进制状态的调试日志,便于问题排查。
- `HardwareBusinessManager`: 调整CAN设备状态变化的处理逻辑,在设备插入(`isExist`为true)时,仅在非首次初始化时才设置充电。

fix(CAN):
- `canDeviceKeyHandler`: 修复钥匙取出时未正确处理`deviceChange`状态的问题。
- `CanHardwareHelper`: 修正开锁/开卡扣时的充电控制逻辑。开锁时,应停止充电;关锁时不做操作。此变更确保了操作的正确性,并统一了相关调用。

refactor(UI):
- `SlotsManageViewModel`: 优化`registerStatusListener`接口,统一RS485和CAN模式下的状态监听回调参数为布尔值,简化上层调用。
- `SlotsManageFragment`: 适配`SlotsManageViewModel`的接口变更。

chore:
- `skin`: 新增`proguard-rules.pro`混淆规则文件。

周文健 3 тижнів тому
батько
коміт
0188873c82

+ 18 - 11
data/src/main/java/com/grkj/data/hardware/can/CanHardwareHelper.kt

@@ -169,15 +169,13 @@ class CanHardwareHelper : IHardwareHelper {
         keyDevice?.let {
             val req = CanCommands.forDevice(keyDevice.nodeId)
                 .controlLatch(keyDevice.id, if (isOpen) 0 else 1)
-            CanHelper.writeTo(req) {
-                val leftOn =
-                    if (keyDevice.id == 0 && isOpen) true else if (keyDevice.id == 0) false else null
-                val rightOn =
-                    if (keyDevice.id == 1 && isOpen) true else if (keyDevice.id == 1) false else null
-                CanCommands.forDevice(keyDevice.nodeId).setCharge(leftOn, rightOn).let {
-                    CanHelper.writeTo(it) {
-                        done?.invoke(it.toCommMessage().payload)
+            CanHelper.writeTo(req) { res ->
+                if (isOpen) {
+                    controlKeyCharge(false, keyDevice.id, keyDevice.nodeId) {
+                        done?.invoke(res.toCommMessage().payload)
                     }
+                } else {
+                    done?.invoke(res.toCommMessage().payload)
                 }
             }
         }
@@ -243,6 +241,7 @@ class CanHardwareHelper : IHardwareHelper {
         keys.forEach {
             val req = CanCommands.forDevice(it.key).setLatch(false, false)
             CanHelper.writeTo(req) {
+                controlAllKeyChargeDown()
                 complete()
             }
         }
@@ -392,8 +391,12 @@ class CanHardwareHelper : IHardwareHelper {
     ) {
         slaveAddress?.let {
             val req = CanCommands.forDevice(slaveAddress).controlLatch(idx, if (isOpen) 0 else 1)
-            CanHelper.writeTo(req) {res->
-                controlKeyCharge(!isOpen, idx, slaveAddress) {
+            CanHelper.writeTo(req) { res ->
+                if (isOpen) {
+                    controlKeyCharge(!isOpen, idx, slaveAddress) {
+                        done?.invoke(res.toCommMessage().payload)
+                    }
+                } else {
                     done?.invoke(res.toCommMessage().payload)
                 }
             }
@@ -429,7 +432,11 @@ class CanHardwareHelper : IHardwareHelper {
         done: ((ByteArray) -> Unit)?
     ) {
         controlKeyBuckle(isOpen, idx, slaveAddress) {
-            controlKeyCharge(!isOpen, idx, slaveAddress, done)
+            if (isOpen) {
+                controlKeyCharge(false, idx, slaveAddress, done)
+            } else {
+                done?.invoke(it)
+            }
         }
     }
 

+ 0 - 6
data/src/main/java/com/grkj/data/hardware/can/CanHelper.kt

@@ -81,12 +81,6 @@ object CanHelper {
 
                     }
                 }
-                nodeMap[nodeId]?.let {
-                    logger.debug(
-                        "硬件状态:{},{},{}",
-                        index, statusData.toBinaryString(), getDeviceByDeviceType(it)
-                    )
-                }
             }
         }
 

+ 28 - 5
data/src/main/java/com/grkj/data/hardware/can/CanReadyPlugin.kt

@@ -3,21 +3,28 @@ package com.grkj.data.hardware.can
 import com.grkj.data.config.ISCSConfig
 import com.grkj.data.di.LogicManager
 import com.grkj.data.enums.HardwareMode
+import com.grkj.data.hardware.can.CanHelper.getDeviceByDeviceType
 import com.grkj.data.hardware.modbus.DeviceConst
 import com.grkj.data.hardware.modbus.DockBean
-import com.grkj.data.hardware.modbus.ModBusController.controlKeyBuckle
 import com.grkj.data.utils.event.ModbusInitCompleteEvent
 import com.grkj.data.utils.event.ToastEvent
-import com.grkj.shared.utils.extension.removeLeadingZeros
+import com.grkj.shared.utils.extension.toBinaryString
 import com.grkj.shared.utils.extension.toHexFromLe
-import com.grkj.shared.utils.extension.toHexStrings
 import com.grkj.shared.utils.i18n.I18nManager
 import com.sik.comm.core.model.ProtocolState
 import com.sik.comm.core.plugin.CommPlugin
 import com.sik.comm.core.plugin.PluginScope
 import com.sik.comm.impl_can.SdoRequest
 import com.sik.comm.impl_can.SdoResponse
-import kotlinx.coroutines.*
+import com.sik.sikcore.extension.toJson
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withTimeoutOrNull
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import kotlin.coroutines.resume
@@ -93,6 +100,12 @@ class CanReadyPlugin : CommPlugin {
                                     rd.payload
                                 )
                             }
+                            logger.debug(
+                                "硬件状态:{},{},{}",
+                                rd.index,
+                                rd.payload.toBinaryString(),
+                                getDeviceByDeviceType(CanHelper.getDeviceTypeByNodeId(nodeId))
+                            )
                         }
                     }
                     safeRead(cmds.readControlReg())?.let { rd ->
@@ -103,6 +116,12 @@ class CanReadyPlugin : CommPlugin {
                                 rd.payload
                             )
                         }
+                        logger.debug(
+                            "硬件状态:{},{},{}",
+                            rd.index,
+                            rd.payload.toBinaryString(),
+                            getDeviceByDeviceType(CanHelper.getDeviceTypeByNodeId(nodeId))
+                        )
                     }
 
                 } catch (t: Throwable) {
@@ -164,7 +183,11 @@ class CanReadyPlugin : CommPlugin {
      * 初始化锁具——打开所有无锁的卡扣、读取RFID
      */
     private fun initLock() {
-        logger.info("initLock : ${HardwareMode.getCurrentHardwareMode().getLockDockData()}")
+        logger.info(
+            "initLock : ${
+                HardwareMode.getCurrentHardwareMode().getLockDockData().toJson()
+            }"
+        )
         HardwareMode.getCurrentHardwareMode().getLockDockData()
             .forEach { dockBean ->
                 val hasLockIdxList =

+ 26 - 39
data/src/main/java/com/grkj/data/hardware/can/DeviceParseStatus.kt

@@ -13,13 +13,13 @@ object DeviceParseStatus {
      */
     fun parseKeyDockStatus(nodeId: Int, index: Int, statusData: ByteArray) {
         val deviceModel = CanHelper.getDeviceByNodeId(nodeId)
+        val isFirst = deviceModel.isEmpty()
         val leftKeyModel = (deviceModel.getOrNull(0) ?: DeviceModel.DeviceKey().apply {
             this.nodeId = nodeId; this.deviceType = CanDeviceConst.DEVICE_KEY_DOCK; this.id = 0
         }) as DeviceModel.DeviceKey
         val rightKeyModel = (deviceModel.getOrNull(1) ?: DeviceModel.DeviceKey().apply {
             this.nodeId = nodeId; this.deviceType = CanDeviceConst.DEVICE_KEY_DOCK; this.id = 1
         }) as DeviceModel.DeviceKey
-
         when (index) {
             CanCommands.Command.STATUS -> {
                 require(statusData.size == 2) { "Status payload size must is 2" }
@@ -33,32 +33,14 @@ object DeviceParseStatus {
                 leftKeyModel.isCharging = ((leftKeyData.toInt() shr 1) and 1) == 1
                 rightKeyModel.isCharging = ((rightKeyData.toInt() shr 1) and 1) == 1
 
-                var changed = false
-                ExistDebouncer.submit(key(nodeId, CanDeviceConst.DEVICE_KEY_DOCK, 0), leftExists) { stable ->
-                    if (leftKeyModel.isExist != stable) {
-                        leftKeyModel.isExist = stable
-                        leftKeyModel.deviceChange = true
-                        changed = true
-                    }
-                }
-                ExistDebouncer.submit(key(nodeId, CanDeviceConst.DEVICE_KEY_DOCK, 1), rightExists) { stable ->
-                    if (rightKeyModel.isExist != stable) {
-                        rightKeyModel.isExist = stable
-                        rightKeyModel.deviceChange = true
-                        changed = true
-                    }
-                }
-
-                // 注意:上面回调在协程里跑,这里不能立刻 update。
-                // 简单做法:把 update 挪到回调里各自执行;更优做法:聚合后再刷。
-                // 这里选“各自回调里刷”,以减少结构改动:
-                ExistDebouncer.submit(key(nodeId, CanDeviceConst.DEVICE_KEY_DOCK, 0), leftExists) { _ ->
-                    CanHelper.updateDeviceData(nodeId, listOf(leftKeyModel, rightKeyModel))
+                if (leftKeyModel.isExist != leftExists) {
+                    leftKeyModel.isExist = leftExists
+                    leftKeyModel.deviceChange = true && !isFirst
                 }
-                ExistDebouncer.submit(key(nodeId, CanDeviceConst.DEVICE_KEY_DOCK, 1), rightExists) { _ ->
-                    CanHelper.updateDeviceData(nodeId, listOf(leftKeyModel, rightKeyModel))
+                if (rightKeyModel.isExist != rightExists) {
+                    rightKeyModel.isExist = rightExists
+                    rightKeyModel.deviceChange = true && !isFirst
                 }
-                return // 避免后面再刷一次
             }
 
             CanCommands.Command.CONTROL_REG -> {
@@ -78,10 +60,12 @@ object DeviceParseStatus {
      */
     fun parseLockDockStatus(nodeId: Int, index: Int, statusData: ByteArray) {
         var deviceModel = CanHelper.getDeviceByNodeId(nodeId)
+        val isFirst = deviceModel.isEmpty()
         if (deviceModel.isEmpty()) {
             deviceModel = mutableListOf<DeviceModel.CommonDevice>().apply {
                 for (i in 0 until 5) add(DeviceModel.CommonDevice().apply {
-                    this.nodeId = nodeId; this.deviceType = CanDeviceConst.DEVICE_LOCK_DOCK; this.id = i + 1
+                    this.nodeId = nodeId; this.deviceType =
+                    CanDeviceConst.DEVICE_LOCK_DOCK; this.id = i + 1
                 })
             }
         }
@@ -89,16 +73,15 @@ object DeviceParseStatus {
             CanCommands.Command.STATUS -> {
                 deviceModel.forEach { dev ->
                     val exists = ((statusData[0].toInt() shr (dev.id - 1)) and 1) == 1
-                    ExistDebouncer.submit("$nodeId-${CanDeviceConst.DEVICE_LOCK_DOCK}-${dev.id}", exists) { stable ->
-                        if (dev.isExist != stable) {
-                            dev.isExist = stable
-                            dev.deviceChange = true
-                            CanHelper.updateDeviceData(nodeId, deviceModel)
-                        }
+                    if (dev.isExist != exists) {
+                        dev.isExist = exists
+                        dev.deviceChange = true && !isFirst
+                        CanHelper.updateDeviceData(nodeId, deviceModel)
                     }
                 }
                 return
             }
+
             CanCommands.Command.CONTROL_REG -> {
                 deviceModel.forEach {
                     it.locked = ((statusData[0].toInt() shr (it.id - 1)) and 1) == 1
@@ -111,10 +94,13 @@ object DeviceParseStatus {
 
     fun parseKeyCabinetControlBoardStatus(nodeId: Int, index: Int, statusData: ByteArray) {
         var deviceModel = CanHelper.getDeviceByNodeId(nodeId)
+        val isFirst = deviceModel.isEmpty()
         if (deviceModel.isEmpty()) {
             deviceModel = mutableListOf<DeviceModel.DeviceKey>().apply {
                 for (i in 0 until 5) add(DeviceModel.DeviceKey().apply {
-                    this.nodeId = nodeId; this.deviceType = CanDeviceConst.DEVICE_KEY_CABINET_CONTROL_BOARD; this.id = i + 1
+                    this.nodeId = nodeId; this.deviceType =
+                    CanDeviceConst.DEVICE_KEY_CABINET_CONTROL_BOARD; this.id =
+                    i + 1
                 })
             }
         }
@@ -122,16 +108,15 @@ object DeviceParseStatus {
             CanCommands.Command.STATUS -> {
                 deviceModel.forEach { dev ->
                     val exists = ((statusData[0].toInt() shr (dev.id - 1)) and 1) == 1
-                    ExistDebouncer.submit("$nodeId-${CanDeviceConst.DEVICE_KEY_CABINET_CONTROL_BOARD}-${dev.id}", exists) { stable ->
-                        if (dev.isExist != stable) {
-                            dev.isExist = stable
-                            dev.deviceChange = true
-                            CanHelper.updateDeviceData(nodeId, deviceModel)
-                        }
+                    if (dev.isExist != exists) {
+                        dev.isExist = exists
+                        dev.deviceChange = true && !isFirst
+                        CanHelper.updateDeviceData(nodeId, deviceModel)
                     }
                 }
                 return
             }
+
             CanCommands.Command.CONTROL_REG -> {
                 deviceModel.forEach {
                     it.locked = ((statusData[0].toInt() shr (it.id - 1)) and 1) == 1
@@ -146,6 +131,7 @@ object DeviceParseStatus {
      */
     fun parseMaterialCabinetControlBoardStatus(nodeId: Int, index: Int, statusData: ByteArray) {
         var deviceModel = CanHelper.getDeviceByNodeId(nodeId)
+        val isFirst = deviceModel.isEmpty()
         if (deviceModel.isEmpty()) {
             deviceModel = mutableListOf<DeviceModel.MaterialDevice>()
             deviceModel.add(DeviceModel.MaterialDevice().apply {
@@ -161,6 +147,7 @@ object DeviceParseStatus {
                     it.leftDoorLocked = leftDoorLocked
                     val rightDoorLocked = ((statusData[0].toInt() shr 4) and 1) == 0
                     it.rightDoorLocked = rightDoorLocked
+                    it.deviceChange = true && !isFirst
                 }
             }
         }

+ 0 - 37
data/src/main/java/com/grkj/data/hardware/can/ExistDebouncer.kt

@@ -1,37 +0,0 @@
-package com.grkj.data.hardware.can
-
-import kotlinx.coroutines.*
-import java.util.concurrent.ConcurrentHashMap
-
-internal object ExistDebouncer {
-    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
-
-    // 每个设备一个 job + 最新值
-    private val jobs = ConcurrentHashMap<String, Job>()
-    private val latest = ConcurrentHashMap<String, Boolean>()
-
-    // 默认防抖窗口,按你的 CAN 上报频率自己斟酌,100~300ms 都行
-    @Volatile var windowMs: Long = 300
-
-    /**
-     * @param key 唯一标识:nodeId-deviceType-id
-     * @param value 新的 exists 值
-     * @param onEmit 当 value 在窗口内稳定时触发
-     */
-    fun submit(key: String, value: Boolean, onEmit: (Boolean) -> Unit) {
-        val prev = latest.put(key, value)
-        // 如果值没变,直接忽略(你要的“只 exists 变化才更新”)
-        if (prev != null && prev == value) return
-
-        jobs[key]?.cancel()
-        jobs[key] = scope.launch {
-            delay(windowMs)
-            // 窗口结束,若期间没有被新值覆盖,则发射
-            if (latest[key] == value) onEmit(value)
-        }
-    }
-
-    fun cancel(key: String) {
-        jobs.remove(key)?.cancel()
-    }
-}

+ 1 - 1
iscs_lock/src/main/java/com/grkj/iscs/features/main/fragment/hardware_manage/SlotsManageFragment.kt

@@ -586,7 +586,7 @@ class SlotsManageFragment : BaseFragment<FragmentSlotsManageBinding>() {
         ISCSConfig.needDeviceName = true
         getExceptionData()
         viewModel.registerStatusListener {
-            if (it.deviceList.isNotEmpty()) {
+            if (it) {
                 getData()
             }
         }

+ 1 - 1
iscs_lock/src/main/java/com/grkj/iscs/features/main/viewmodel/MainViewModel.kt

@@ -172,9 +172,9 @@ class MainViewModel @Inject constructor(
                     if (deviceModel.deviceType == CanDeviceConst.DEVICE_KEY_DOCK) {
                         // 只在 exists 变化(解析层已做)并且当前存在时处理
                         if (deviceModel.isExist && deviceModel.deviceChange) {
-
                             val key = devKey(deviceModel.nodeId, deviceModel.deviceType, deviceModel.id)
                             if (!HandlerGate.tryEnter(key)) {
+                                logger.info("是否正在处理同一个设备:$key")
                                 // 正在处理同一个设备,直接忽略这次回调
                                 return@forEach
                             }

+ 9 - 3
iscs_lock/src/main/java/com/grkj/iscs/features/main/viewmodel/hardware_manage/SlotsManageViewModel.kt

@@ -57,9 +57,15 @@ class SlotsManageViewModel @Inject constructor(
     /**
      * 注册监听
      */
-    fun registerStatusListener(statusListener: (DockBean) -> Unit) {
-        HardwareBusinessManager.registerStatusListener(this) {
-            statusListener(it)
+    fun registerStatusListener(statusListener: (Boolean) -> Unit) {
+        if (MMKVConstants.KEY_HARDWARE_MODE.getMMKVData(HardwareMode.RS485.name) == HardwareMode.RS485.name) {
+            HardwareBusinessManager.registerStatusListener(this) {
+                statusListener(it.deviceList.isNotEmpty())
+            }
+        }else{
+            HardwareBusinessManager.registerCanStatusListener(this){
+                statusListener(it.isNotEmpty())
+            }
         }
     }
 

+ 21 - 0
skin/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 69 - 24
ui-base/src/main/java/com/grkj/ui_base/business/HardwareBusinessManager.kt

@@ -356,7 +356,7 @@ object HardwareBusinessManager {
      * 5、蓝牙数据通讯
      */
     private fun canDeviceStatusHandle(res: List<DeviceModel>) {
-        logger.debug("硬件状态:{}", res)
+//        logger.debug("硬件状态:{}", res)
         if (res.isEmpty()) {
             return@canDeviceStatusHandle
         }
@@ -588,7 +588,8 @@ object HardwareBusinessManager {
                 val rfid = rfidData.toHexFromLe()
                 lockBean.rfid = rfid
                 ThreadUtils.runOnIO {
-                    val lockStatusReq = async { DataBusiness.fetchDict(DictConstants.KEY_PAD_LOCK_STATUS) }
+                    val lockStatusReq =
+                        async { DataBusiness.fetchDict(DictConstants.KEY_PAD_LOCK_STATUS) }
                     val slotStatus = async { DataBusiness.fetchDict(DictConstants.KEY_SLOT_STATUS) }
                     val slotType = async { DataBusiness.fetchDict(DictConstants.KEY_SLOT_TYPE) }
                     val slotsPageReq = async { DataBusiness.getSlotsPage() }
@@ -600,12 +601,24 @@ object HardwareBusinessManager {
 
                     LogicManager.hardwareLogic.getIsLockPage { lockData ->
                         val isLockAbnormal = rfid in (lockData?.records?.filter {
-                            it.exStatus == lockStatus.find { d -> I18nManager.t(d.dictLabel) == I18nManager.t("abnormal") }?.dictValue
+                            it.exStatus == lockStatus.find { d ->
+                                I18nManager.t(d.dictLabel) == I18nManager.t(
+                                    "abnormal"
+                                )
+                            }?.dictValue
                         }?.map { it.lockNfc } ?: emptyList())
 
                         val isSlotAbnormal = slotsPage?.records?.any {
-                            it.slotType == slotTypeList.find { d -> I18nManager.t(d.dictLabel) == I18nManager.t("lock") }?.dictValue &&
-                                    it.status == slotStatusList.find { d -> I18nManager.t(d.dictLabel) == I18nManager.t("abnormal") }?.dictValue &&
+                            it.slotType == slotTypeList.find { d ->
+                                I18nManager.t(d.dictLabel) == I18nManager.t(
+                                    "lock"
+                                )
+                            }?.dictValue &&
+                                    it.status == slotStatusList.find { d ->
+                                I18nManager.t(d.dictLabel) == I18nManager.t(
+                                    "abnormal"
+                                )
+                            }?.dictValue &&
                                     it.row?.toInt() == lockBean.nodeId && lockBean.id == it.col?.toInt()
                         } == true
 
@@ -624,7 +637,8 @@ object HardwareBusinessManager {
                         LogicManager.hardwareLogic.getLockInfo(rfid) {
                             logger.info("挂锁信息:${it}")
                             if (it != null && it.lockNfc?.isNotEmpty() == true) {
-                                val ctrl = CanCommands.forDevice(lockBean.nodeId).controlOne_1to5(lockBean.id, true)
+                                val ctrl = CanCommands.forDevice(lockBean.nodeId)
+                                    .controlOne_1to5(lockBean.id, true)
                                 CanHelper.writeTo(ctrl) {
                                     LogicManager.jobTicketLogic.updateLockReturn(
                                         rfid, SIKCore.getApplication().serialNo()
@@ -632,8 +646,11 @@ object HardwareBusinessManager {
                                 }
                             }
                             Executor.delayOnMain(200) {
-                                try { canListeners.forEach { it.callBack(listOf(lockBean)) } }
-                                finally { lockBean.deviceChange = false } // ✅ 最后归零
+                                try {
+                                    canListeners.forEach { it.callBack(listOf(lockBean)) }
+                                } finally {
+                                    lockBean.deviceChange = false
+                                } // ✅ 最后归零
                             }
                         }
                     }
@@ -641,7 +658,10 @@ object HardwareBusinessManager {
             }
         } else {
             logger.info("挂锁取出-:${lockBean.rfid}")
-            handleDeviceTake(DeviceTakeUpdateEvent(DeviceConst.DEVICE_TYPE_LOCK, lockBean.rfid), lockBean.rfid)
+            handleDeviceTake(
+                DeviceTakeUpdateEvent(DeviceConst.DEVICE_TYPE_LOCK, lockBean.rfid),
+                lockBean.rfid
+            )
             lockBean.deviceChange = false // 取出分支可在同步处理后立即归零
         }
     }
@@ -656,11 +676,15 @@ object HardwareBusinessManager {
         logger.info("钥匙状态变化canDeviceKeyHandler:$keyBean")
 
         if (keyBean.isExist) {
-            val req = CanCommands.forDevice(keyBean.nodeId).let { if (keyBean.id == 0) it.getLeftRfid() else it.getRightRfid() }
+            val req = CanCommands.forDevice(keyBean.nodeId)
+                .let { if (keyBean.id == 0) it.getLeftRfid() else it.getRightRfid() }
             CanHelper.readFrom(req) { res ->
                 val rfidData = res?.payload ?: byteArrayOf()
                 if (ISCSConfig.isInit) {
-                    CanHelper.writeTo(CanCommands.forDevice(keyBean.nodeId).setCharge(keyBean.id == 0, keyBean.id == 1)){}
+                    CanHelper.writeTo(
+                        CanCommands.forDevice(keyBean.nodeId)
+                            .setCharge(keyBean.id == 0, keyBean.id == 1)
+                    ) {}
                 }
                 if (rfidData.size < 4) {
                     logger.error("Key rfid error")
@@ -676,7 +700,8 @@ object HardwareBusinessManager {
                     val slotStatus = async { DataBusiness.fetchDict(DictConstants.KEY_SLOT_STATUS) }
                     val slotType = async { DataBusiness.fetchDict(DictConstants.KEY_SLOT_TYPE) }
                     val slotsPageReq = async { DataBusiness.getSlotsPage() }
-                    val keyStatusReq = async { DataBusiness.fetchDict(DictConstants.KEY_KEY_STATUS) }
+                    val keyStatusReq =
+                        async { DataBusiness.fetchDict(DictConstants.KEY_KEY_STATUS) }
                     val keyPageReq = async { DataBusiness.getKeyPage() }
 
                     val keyStatus = keyStatusReq.await()
@@ -686,12 +711,24 @@ object HardwareBusinessManager {
                     val slotTypeList = slotType.await()
 
                     val isKeyAbnormal = rfid in (keyData?.records?.filter {
-                        it.exStatus == keyStatus.find { d -> I18nManager.t(d.dictLabel) == I18nManager.t("abnormal") }?.dictValue
+                        it.exStatus == keyStatus.find { d ->
+                            I18nManager.t(d.dictLabel) == I18nManager.t(
+                                "abnormal"
+                            )
+                        }?.dictValue
                     }?.map { it.keyNfc } ?: emptyList())
 
                     val isSlotAbnormal = slotsPage?.records?.any {
-                        it.slotType == slotTypeList.find { d -> I18nManager.t(d.dictLabel) == I18nManager.t("key") }?.dictValue &&
-                                it.status == slotStatusList.find { d -> I18nManager.t(d.dictLabel) == I18nManager.t("abnormal") }?.dictValue &&
+                        it.slotType == slotTypeList.find { d ->
+                            I18nManager.t(d.dictLabel) == I18nManager.t(
+                                "key"
+                            )
+                        }?.dictValue &&
+                                it.status == slotStatusList.find { d ->
+                            I18nManager.t(d.dictLabel) == I18nManager.t(
+                                "abnormal"
+                            )
+                        }?.dictValue &&
                                 it.row?.toInt() == keyBean.nodeId &&
                                 it.col?.toInt() == (keyBean.nodeId + (keyBean.id) * 2 + 1)
                     } == true
@@ -718,25 +755,33 @@ object HardwareBusinessManager {
                                 if (ISCSConfig.isInit) {
                                     PopTip.build().tip(CommonUtils.getStr("get_key_info_fail"))
                                 }
-                                val unlock = CanCommands.forDevice(keyBean.nodeId).controlLatch(keyBean.id, 0)
-                                CanHelper.writeTo(unlock){}
+                                val unlock = CanCommands.forDevice(keyBean.nodeId)
+                                    .controlLatch(keyBean.id, 0)
+                                CanHelper.writeTo(unlock) {}
                             }
                             Executor.delayOnMain(200) {
-                                try { canListeners.forEach { l -> l.callBack(listOf(keyBean)) } }
-                                finally { keyBean.deviceChange = false } // ✅ 放最后
+                                try {
+                                    canListeners.forEach { l -> l.callBack(listOf(keyBean)) }
+                                } finally {
+                                    keyBean.deviceChange = false
+                                } // ✅ 放最后
                             }
                         }
                     }
                 }
             }
         } else if (!keyBean.isCharging) {
-            handleDeviceTake(DeviceTakeUpdateEvent(DeviceConst.DEVICE_TYPE_KEY, keyBean.rfid), keyBean.rfid)
+            handleDeviceTake(
+                DeviceTakeUpdateEvent(DeviceConst.DEVICE_TYPE_KEY, keyBean.rfid),
+                keyBean.rfid
+            )
             Executor.delayOnMain(200) {
-                try { canListeners.forEach { it.callBack(listOf(keyBean)) } }
-                finally { keyBean.deviceChange = false } // ✅ 已有
+                try {
+                    canListeners.forEach { it.callBack(listOf(keyBean)) }
+                } finally {
+                    keyBean.deviceChange = false
+                } // ✅ 已有
             }
-        } else {
-            keyBean.deviceChange = false
         }
     }