浏览代码

refactor(模式校验):
- 新增共锁人最低数量的模式校验
- handler线程处理改为协程

周文健 6 月之前
父节点
当前提交
11d5b3f7f0

+ 5 - 2
app/src/main/java/com/grkj/iscs/BusinessManager.kt

@@ -249,7 +249,7 @@ object BusinessManager {
      */
     fun registerMainListener() {
         ModBusController.registerStatusListener(this) { res ->
-            LogUtil.i("设备状态:${(res as List<ByteArray>).map { it.toHexStrings() }}")
+            LogUtil.i("硬件状态:${(res as List<ByteArray>).map { it.toHexStrings() }}")
             if (res.isEmpty() || res.any { it.isEmpty() }) {
                 var tipStr = CommonUtils.getStr(R.string.no_response_board_exists) + " : "
                 val addressList = mutableListOf<String>()
@@ -1527,6 +1527,9 @@ object BusinessManager {
         workTicketGetBO?.data?.forEach { data ->
             val updateList = mutableListOf<LockPointUpdateReqVO>()
             data.dataList?.forEach { dataListDTO ->
+                data.taskCode?.toLong()?.let {
+                    SPUtils.returnKey(it)
+                }
                 val updateVO = LockPointUpdateReqVO(
                     data.taskCode?.toLong(),
                     dataListDTO.infoRfidNo,
@@ -1694,6 +1697,7 @@ object BusinessManager {
                 mDeviceTakeList.find { it.deviceType == DEVICE_TYPE_KEY && it.nfc == deviceTakeUpdateBO.nfc }
                     ?.let { info ->
                         sendLoadingEventMsg(null, false)
+                        SPUtils.takeKey(info.ticketId)
                         NetApi.updateKeyTake(
                             info.ticketId,
                             info.nfc,
@@ -1903,7 +1907,6 @@ object BusinessManager {
         val itemsToRemove = returnList.toList()
         var count = 0
         itemsToRemove.forEach { itData ->
-            SPUtils.returnKey(itData.ticketId)
             NetApi.updateKeyReturn(
                 itData.ticketId, itData.keyNfc, context.serialNo()
             ) { isSuccess ->

+ 2 - 2
app/src/main/java/com/grkj/iscs/ble/BleUtil.kt

@@ -139,7 +139,7 @@ class BleUtil private constructor() {
 
                 override fun onDisConnected(isActiveDisConnected: Boolean, device: BleDevice, gatt: BluetoothGatt, status: Int) {
                     BleManager.getInstance().removeConnectGattCallback(device)
-                    indicateCallback?.onDisConnectPrompt("连接断开!请检查设备状态,并尝试重新连接!")
+                    indicateCallback?.onDisConnectPrompt("连接断开!请检查硬件状态,并尝试重新连接!")
                 }
             })
         }
@@ -180,7 +180,7 @@ class BleUtil private constructor() {
 
                 override fun onDisConnected(isActiveDisConnected: Boolean, device: BleDevice, gatt: BluetoothGatt, status: Int) {
                     BleManager.getInstance().removeConnectGattCallback(device)
-                    writeCallback?.onDisConnectPrompt("连接断开!请检查设备状态,并尝试重新连接!")
+                    writeCallback?.onDisConnectPrompt("连接断开!请检查硬件状态,并尝试重新连接!")
                 }
             })
         }

+ 2 - 0
app/src/main/java/com/grkj/iscs/extentions/Context.kt

@@ -5,6 +5,7 @@ import android.annotation.SuppressLint
 import android.content.Context
 import android.content.pm.PackageManager
 import android.os.Build
+import androidx.annotation.RequiresPermission
 import androidx.core.app.ActivityCompat
 import androidx.core.content.ContextCompat
 import androidx.lifecycle.Observer
@@ -24,6 +25,7 @@ fun Context.removeNetObserver(observer: Observer<Boolean>) {
     netManager().liveData.removeObserver(observer)
 }
 
+@SuppressLint("MissingPermission")
 fun Context.serialNo() : String {
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
         && ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)

+ 13 - 5
app/src/main/java/com/grkj/iscs/modbus/DockBean.kt

@@ -78,6 +78,7 @@ class DockBean(
 
                     return DockBean(addr, it, changeList)
                 }
+
                 DOCK_TYPE_LOCK -> {
                     val tempList = mutableListOf<Boolean>()
                     for (i in 0..7) {
@@ -109,10 +110,12 @@ class DockBean(
                     LogUtil.i("锁具刷新状态 : $changeList")
                     return DockBean(addr, it, changeList)
                 }
+
                 DOCK_TYPE_ELEC_LOCK_BOARD -> {
                     // TODO 临时占位
                     return null
                 }
+
                 DOCK_TYPE_PORTABLE -> {
                     // TODO 便携式底座更新
                     val tempList = mutableListOf<Boolean>()
@@ -182,25 +185,30 @@ class DockBean(
                     LogUtil.i("便携式刷新状态 : $changeList")
                     return DockBean(addr, it, changeList)
                 }
+
                 else -> return null
             }
         } ?: return null
     }
 
     fun getKeyList(): MutableList<KeyBean> {
-        return deviceList.filter { it.type == DEVICE_TYPE_KEY } as MutableList<KeyBean>
+        return deviceList.filterIsInstance<KeyBean>().filter { it.type == DEVICE_TYPE_KEY }
+            .toMutableList()
     }
 
-    fun getLockList() : MutableList<LockBean> {
-        return deviceList.filter { it.type == DEVICE_TYPE_LOCK } as MutableList<LockBean>
+    fun getLockList(): MutableList<LockBean> {
+        return deviceList.filterIsInstance<LockBean>().filter { it.type == DEVICE_TYPE_LOCK }
+            .toMutableList()
     }
 
     fun getCardList(): MutableList<CardBean> {
-        return deviceList.filter { it.type == DEVICE_TYPE_CARD } as MutableList<CardBean>
+        return deviceList.filterIsInstance<CardBean>().filter { it.type == DEVICE_TYPE_CARD }
+            .toMutableList()
     }
 
     fun getFingerPrintList(): MutableList<FingerPrintBean> {
-        return deviceList.filter { it.type == DEVICE_TYPE_FINGERPRINT } as MutableList<FingerPrintBean>
+        return deviceList.filterIsInstance<FingerPrintBean>()
+            .filter { it.type == DEVICE_TYPE_FINGERPRINT }.toMutableList()
     }
 
     fun getBit(by: Byte): String {

+ 149 - 122
app/src/main/java/com/grkj/iscs/modbus/ModBusController.kt

@@ -38,23 +38,14 @@ object ModBusController {
     var modBusManager: ModBusManager? = null
     private var slaveCount: Int = 0
 
-    private val threadPool = Executors.newScheduledThreadPool(4)
-
     private val listeners = ArrayList<StatusListener>()
 
     // 是否中断读取状态
     private var interruptReadStatus: ArrayList<Boolean> = ArrayList()
 
-    var shouldStopUpgrade = false
-
     // TODO 临时改成5s
     const val REPEAT_FREQUENCY = 800L
 
-    fun setSlaveCount(count: Int) {
-        modBusManager?.slaveCount = count
-        slaveCount = count
-    }
-
     class StatusListener(
         val key: Any,
         val listener: (Any) -> Unit,
@@ -81,8 +72,6 @@ object ModBusController {
                 LogUtil.i("****************************************************************************")
                 // 过滤非空的数据,重置slaveCount
                 // 不再使用slaveCount,改用地址池
-//                val onlineCount = res.filter { it.isNotEmpty() }.size
-//                setSlaveCount(onlineCount)
                 for (l in listeners) {
                     if (l.type == LISTENER_TYPE_STATUS) {
                         l.listener(res)
@@ -91,9 +80,6 @@ object ModBusController {
             }, REPEAT_FREQUENCY)
             ?.also {
                 modBusManager = it
-                Executor.runOnIO {
-//                    refreshAllowOpenDoorUnidentified(ctx, it)
-                }
             }
             ?.start()
     }
@@ -157,24 +143,27 @@ object ModBusController {
      */
     private fun initLock() {
         LogUtil.i("initLock : $dockList")
-        dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean ->
-            val hasLockIdxList = dockBean.getLockList().filter { it.isExist }.map { it.idx } as MutableList<Int>
-            val noLockIdxList = dockBean.getLockList().filter { !it.isExist }.map { it.idx } as MutableList<Int>
-
-            hasLockIdxList.forEach { idx ->
-                readLockRfid(dockBean.addr, idx) { res ->
-                    if (res.size < 11) {
-                        LogUtil.e("Lock rfid error")
-                        return@readLockRfid
+        dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }
+            .forEach { dockBean ->
+                val hasLockIdxList =
+                    dockBean.getLockList().filter { it.isExist }.map { it.idx } as MutableList<Int>
+                val noLockIdxList =
+                    dockBean.getLockList().filter { !it.isExist }.map { it.idx } as MutableList<Int>
+
+                hasLockIdxList.forEach { idx ->
+                    readLockRfid(dockBean.addr, idx) { res ->
+                        if (res.size < 11) {
+                            LogUtil.e("Lock rfid error")
+                            return@readLockRfid
+                        }
+                        val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
+                        LogUtil.i("初始化锁具 RFID : $rfid")
+                        updateLockRfid(dockBean.addr, idx, rfid)
                     }
-                    val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
-                    LogUtil.i("初始化锁具 RFID : $rfid")
-                    updateLockRfid(dockBean.addr, idx, rfid)
                 }
+                controlLockBuckle(false, dockBean.addr, hasLockIdxList)
+                controlLockBuckle(true, dockBean.addr, noLockIdxList)
             }
-            controlLockBuckle(false, dockBean.addr, hasLockIdxList)
-            controlLockBuckle(true, dockBean.addr, noLockIdxList)
-        }
     }
 
     /**
@@ -182,43 +171,48 @@ object ModBusController {
      */
     private fun initKey() {
         LogUtil.i("initKey : $dockList")
-        dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean ->
-            dockBean.getKeyList().forEach { key ->
-                if (key.isExist) {
-                    LogUtil.i("initKey : ${dockBean.addr} : ${key.isLeft}")
-                    readKeyRfid(dockBean.addr, if (key.isLeft) 0 else 1) { isLeft, res ->
-                        if (res.size < 11) {
-                            LogUtil.e("Key rfid error")
-                            return@readKeyRfid
-                        }
-                        val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
-                        LogUtil.i("初始化钥匙 RFID : $rfid")
-                        // 更新rfid
-                        updateKeyRfid(dockBean.addr, isLeft, rfid)
-                        // 蓝牙准备操作
-                        NetApi.getKeyInfo(rfid) {
-                            LogUtil.i("getKeyInfo : $rfid - ${it?.macAddress}")
-                            if (it != null && !it.macAddress.isNullOrEmpty()) {
-                                // 更新mac
-                                updateKeyMac(dockBean.addr, key.isLeft, it.macAddress)
-                                BusinessManager.registerConnectListener(it.macAddress) { isDone, bleBean ->
-                                    if (isDone && bleBean?.bleDevice != null) {
-                                        Executor.delayOnMain(500) {
-                                            BusinessManager.getCurrentStatus(3, bleBean.bleDevice)
+        dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }
+            .forEach { dockBean ->
+                dockBean.getKeyList().forEach { key ->
+                    if (key.isExist) {
+                        LogUtil.i("initKey : ${dockBean.addr} : ${key.isLeft}")
+                        readKeyRfid(dockBean.addr, if (key.isLeft) 0 else 1) { isLeft, res ->
+                            if (res.size < 11) {
+                                LogUtil.e("Key rfid error")
+                                return@readKeyRfid
+                            }
+                            val rfid =
+                                res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
+                            LogUtil.i("初始化钥匙 RFID : $rfid")
+                            // 更新rfid
+                            updateKeyRfid(dockBean.addr, isLeft, rfid)
+                            // 蓝牙准备操作
+                            NetApi.getKeyInfo(rfid) {
+                                LogUtil.i("getKeyInfo : $rfid - ${it?.macAddress}")
+                                if (it != null && !it.macAddress.isNullOrEmpty()) {
+                                    // 更新mac
+                                    updateKeyMac(dockBean.addr, key.isLeft, it.macAddress)
+                                    BusinessManager.registerConnectListener(it.macAddress) { isDone, bleBean ->
+                                        if (isDone && bleBean?.bleDevice != null) {
+                                            Executor.delayOnMain(500) {
+                                                BusinessManager.getCurrentStatus(
+                                                    3,
+                                                    bleBean.bleDevice
+                                                )
+                                            }
                                         }
                                     }
+                                } else {
+                                    ToastUtils.tip(R.string.get_key_info_fail)
                                 }
-                            } else {
-                                ToastUtils.tip(R.string.get_key_info_fail)
                             }
                         }
+                        controlKeyBuckle(false, key.isLeft, dockBean.addr)
+                    } else {
+                        controlKeyBuckle(true, key.isLeft, dockBean.addr)
                     }
-                    controlKeyBuckle(false, key.isLeft, dockBean.addr)
-                } else {
-                    controlKeyBuckle(true, key.isLeft, dockBean.addr)
                 }
             }
-        }
     }
 
     /**
@@ -259,7 +253,11 @@ object ModBusController {
      * @param isLock true:读锁具底座 false:读钥匙底座
      * @param type 0:钥匙底座 1:锁具底座1-8 2:锁具底座9、10
      */
-    fun readBuckleStatus(isLock: Boolean, slaveAddress: Byte?, doneSingle: ((type: Int, res: ByteArray) -> Unit)? = null) {
+    fun readBuckleStatus(
+        isLock: Boolean,
+        slaveAddress: Byte?,
+        doneSingle: ((type: Int, res: ByteArray) -> Unit)? = null
+    ) {
         // TODO 电磁锁控制板可能不是,并且锁和钥匙的读取不一样
         slaveAddress?.let {
             modBusManager?.sendTo(it, MBFrame.READ_BUCKLE_STATUS) { res ->
@@ -276,7 +274,12 @@ object ModBusController {
     /**
      * 开/关锁具卡扣
      */
-    fun controlLockBuckle(isOpen: Boolean, slaveAddress: Byte?, lockIdx: Int, done: ((res: ByteArray) -> Unit)? = null)  {
+    fun controlLockBuckle(
+        isOpen: Boolean,
+        slaveAddress: Byte?,
+        lockIdx: Int,
+        done: ((res: ByteArray) -> Unit)? = null
+    ) {
         slaveAddress?.let {
             modBusManager?.generateLockBuckleCmd(isOpen, lockIdx)?.let { cmd ->
                 modBusManager?.sendTo(it, cmd) { res ->
@@ -286,7 +289,12 @@ object ModBusController {
         }
     }
 
-    fun controlLockBuckle(isOpen: Boolean, slaveAddress: Byte?, lockIdxList: MutableList<Int>, done: ((res: ByteArray) -> Unit)? = null)  {
+    fun controlLockBuckle(
+        isOpen: Boolean,
+        slaveAddress: Byte?,
+        lockIdxList: MutableList<Int>,
+        done: ((res: ByteArray) -> Unit)? = null
+    ) {
         slaveAddress?.let {
             modBusManager?.generateLockBuckleCmd(isOpen, lockIdxList)?.let { cmdList ->
                 cmdList.forEach { cmd ->
@@ -301,7 +309,11 @@ object ModBusController {
     /**
      * 读取钥匙RFID
      */
-    fun readKeyRfid(slaveAddress: Byte?, idx: Int, done: ((isLeft: Boolean, res: ByteArray) -> Unit)? = null) {
+    fun readKeyRfid(
+        slaveAddress: Byte?,
+        idx: Int,
+        done: ((isLeft: Boolean, res: ByteArray) -> Unit)? = null
+    ) {
         slaveAddress?.let {
             modBusManager?.generateRfidCmd(idx)?.let { cmd ->
                 modBusManager?.sendTo(it, cmd) {
@@ -341,28 +353,32 @@ object ModBusController {
      * 更新钥匙RFID
      */
     fun updateKeyRfid(slaveAddress: Byte, isLeft: Boolean, rfid: String) {
-        dockList.find { it.addr == slaveAddress }?.getKeyList()?.find { it.isLeft == isLeft }?.rfid = rfid
+        dockList.find { it.addr == slaveAddress }?.getKeyList()
+            ?.find { it.isLeft == isLeft }?.rfid = rfid
     }
 
     /**
      * 更新钥匙MAC
      */
     fun updateKeyMac(slaveAddress: Byte, isLeft: Boolean, mac: String) {
-        dockList.find { it.addr == slaveAddress }?.getKeyList()?.find { it.isLeft == isLeft }?.mac = mac
+        dockList.find { it.addr == slaveAddress }?.getKeyList()?.find { it.isLeft == isLeft }?.mac =
+            mac
     }
 
     /**
      * 通过RFID更新对应的Mac
      */
     fun updateKeyMacByRfid(rfid: String, mac: String) {
-        dockList.find { it.type == DOCK_TYPE_KEY }?.getKeyList()?.find { it.rfid == rfid }?.mac = mac
+        dockList.find { it.type == DOCK_TYPE_KEY }?.getKeyList()?.find { it.rfid == rfid }?.mac =
+            mac
     }
 
     /**
      * 更新锁具RFID
      */
     fun updateLockRfid(slaveAddress: Byte, lockIdx: Int, rfid: String) {
-        dockList.find { it.addr == slaveAddress }?.getLockList()?.find { it.idx == lockIdx }?.rfid = rfid
+        dockList.find { it.addr == slaveAddress }?.getLockList()?.find { it.idx == lockIdx }?.rfid =
+            rfid
     }
 
     /**
@@ -370,14 +386,18 @@ object ModBusController {
      *
      * @param deviceType {@link [com.grkj.iscs.model.bo.DeviceTakeUpdateBO]<class>#[deviceType]}
      */
-    fun isDeviceExist(rfid: String, deviceType: Int) : Boolean {
+    fun isDeviceExist(rfid: String, deviceType: Int): Boolean {
         return when (deviceType) {
             DEVICE_TYPE_KEY -> {
-                dockList.find { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }?.getKeyList()?.find { it.rfid == rfid } != null
+                dockList.find { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }
+                    ?.getKeyList()?.find { it.rfid == rfid } != null
             }
+
             DEVICE_TYPE_LOCK -> {
-                dockList.find { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE  }?.getLockList()?.find { it.rfid == rfid } != null
+                dockList.find { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }
+                    ?.getLockList()?.find { it.rfid == rfid } != null
             }
+
             else -> {
                 false
             }
@@ -389,7 +409,12 @@ object ModBusController {
      *
      * @param leftAction、rightAction 0:保持当前状态 1:点亮 2:熄灭 默认0
      */
-    fun controlKeyLight(slaveAddress: Byte?, leftAction: Int = 0, rightAction: Int = 0, done: ((res: ByteArray) -> Unit)? = null) {
+    fun controlKeyLight(
+        slaveAddress: Byte?,
+        leftAction: Int = 0,
+        rightAction: Int = 0,
+        done: ((res: ByteArray) -> Unit)? = null
+    ) {
         slaveAddress?.let {
             modBusManager?.generateKeyLightCmd(leftAction, rightAction)?.let { cmd ->
                 modBusManager?.sendTo(it, cmd) {
@@ -416,7 +441,12 @@ object ModBusController {
      * @param isOpen true:开操作 false:关操作
      * @param isLeft true:左卡扣 false:右卡扣
      */
-    fun controlKeyBuckle(isOpen: Boolean, isLeft: Boolean, slaveAddress: Byte?, done: ((res: ByteArray) -> Unit)? = null)  {
+    fun controlKeyBuckle(
+        isOpen: Boolean,
+        isLeft: Boolean,
+        slaveAddress: Byte?,
+        done: ((res: ByteArray) -> Unit)? = null
+    ) {
         slaveAddress?.let {
             modBusManager?.generateKeyBuckleCmd(isOpen, if (isLeft) 0 else 1)?.let { cmd ->
                 modBusManager?.sendTo(it, cmd) { res ->
@@ -430,14 +460,16 @@ object ModBusController {
      * 根据RFID找钥匙
      */
     fun getKeyByRfid(rfid: String): DockBean.KeyBean? {
-        return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getKeyList() }.find { it.rfid == rfid }
+        return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }
+            .flatMap { it.getKeyList() }.find { it.rfid == rfid }
     }
 
     /**
      * 根据Mac找钥匙
      */
     fun getKeyByMac(mac: String): DockBean.KeyBean? {
-        return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getKeyList() }.find { it.mac == mac }
+        return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }
+            .flatMap { it.getKeyList() }.find { it.mac == mac }
     }
 
     fun getKeyByDock(dockAddr: Byte?, isLeft: Boolean): DockBean.KeyBean? {
@@ -449,7 +481,8 @@ object ModBusController {
      * 根据RFID找锁具
      */
     fun getLockByRfid(rfid: String): DockBean.LockBean? {
-        return dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getLockList() }.find { it.rfid == rfid }
+        return dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }
+            .flatMap { it.getLockList() }.find { it.rfid == rfid }
     }
 
     fun isKeyExist(dockAddr: Byte?, isLeft: Boolean): Boolean {
@@ -490,7 +523,9 @@ object ModBusController {
         } else {
             return status
         }
-        if (BleManager.getInstance().isConnected(BusinessManager.getBleDeviceByMac(key.mac)?.bleDevice)) {
+        if (BleManager.getInstance()
+                .isConnected(BusinessManager.getBleDeviceByMac(key.mac)?.bleDevice)
+        ) {
             status = 4
         } else {
             return status
@@ -548,17 +583,19 @@ object ModBusController {
 
     fun getKeyByDockType(type: Byte): MutableList<DockBean.KeyBean>? {
         return dockList.find { it.type == type }?.let {
-             it.getKeyList()
+            it.getKeyList()
         }
     }
 
     fun controlAllLockBuckles(isOpen: Boolean) {
-        dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean ->
-            val list = dockBean.getLockList().stream().map { it.idx }.collect(Collectors.toList())
-            controlLockBuckle(isOpen, dockBean.addr, list) {
-                LogUtil.i("${if (isOpen) "开启" else "关闭"}所有锁卡扣 : ${it.toHexStrings()}")
+        dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }
+            .forEach { dockBean ->
+                val list =
+                    dockBean.getLockList().stream().map { it.idx }.collect(Collectors.toList())
+                controlLockBuckle(isOpen, dockBean.addr, list) {
+                    LogUtil.i("${if (isOpen) "开启" else "关闭"}所有锁卡扣 : ${it.toHexStrings()}")
+                }
             }
-        }
     }
 
     fun printDockInfo() {
@@ -570,11 +607,13 @@ object ModBusController {
                         LogUtil.i("${dockBean.addr}锁${lockBean.idx} : ${lockBean.rfid}")
                     }
                 }
+
                 DOCK_TYPE_KEY -> {
                     dockBean.getKeyList().forEach { keyBean ->
                         LogUtil.i("${dockBean.addr}钥${keyBean.idx} : ${keyBean.rfid}")
                     }
                 }
+
                 DOCK_TYPE_PORTABLE -> {
                     dockBean.getLockList().forEach { lockBean ->
                         LogUtil.i("${dockBean.addr}柜锁${lockBean.idx} : ${lockBean.rfid}")
@@ -591,7 +630,7 @@ object ModBusController {
         LogUtil.i("____________________________________")
         readDeviceType { res ->
             LogUtil.i("设备类型数量 : ${res.size}")
-            LogUtil.i("设备类型 : ${res.map { it.toHexStrings()}}")
+            LogUtil.i("设备类型 : ${res.map { it.toHexStrings() }}")
             res.forEach { bytes ->
                 if (bytes.size < 5) return@forEach
                 // 设备具体数据由0x0011寄存器提供
@@ -615,7 +654,8 @@ object ModBusController {
      * @return 底座地址,钥匙
      */
     fun getOneKey(): Pair<Byte, DockBean.KeyBean?>? {
-        val keyDockList = dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }
+        val keyDockList =
+            dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }
         val keyList = keyDockList.flatMap { it.getKeyList() }.filter { it.isExist }
         LogUtil.i("keyList : $keyList")
         if (keyList.isEmpty()) {
@@ -624,14 +664,22 @@ object ModBusController {
         }
 
         keyList.forEach {
-            LogUtil.i("keyStatus : ${it.isExist} - ${it.rfid} - ${it.mac} - ${it.isReady} - " +
-                    "${BusinessManager.getBleDeviceByMac(it.mac)?.bleDevice != null} - " +
-                    "${BleManager.getInstance().isConnected(BusinessManager.getBleDeviceByMac(it.mac)?.bleDevice)} - " +
-                    "${!BusinessManager.mExceptionKeyList.contains(it.rfid)}")
-        }
-        val key = keyList.filter { it.isExist && it.rfid != null && it.mac != null && it.isReady
-                && BleManager.getInstance().isConnected(BusinessManager.getBleDeviceByMac(it.mac)?.bleDevice)
-                && !BusinessManager.mExceptionKeyList.contains(it.rfid) }
+            LogUtil.i(
+                "keyStatus : ${it.isExist} - ${it.rfid} - ${it.mac} - ${it.isReady} - " +
+                        "${BusinessManager.getBleDeviceByMac(it.mac)?.bleDevice != null} - " +
+                        "${
+                            BleManager.getInstance()
+                                .isConnected(BusinessManager.getBleDeviceByMac(it.mac)?.bleDevice)
+                        } - " +
+                        "${!BusinessManager.mExceptionKeyList.contains(it.rfid)}"
+            )
+        }
+        val key = keyList.filter {
+            it.isExist && it.rfid != null && it.mac != null && it.isReady
+                    && BleManager.getInstance()
+                .isConnected(BusinessManager.getBleDeviceByMac(it.mac)?.bleDevice)
+                    && !BusinessManager.mExceptionKeyList.contains(it.rfid)
+        }
             .shuffled().firstOrNull()
         if (key == null) {
             LogUtil.e("getOneKey : no key match")
@@ -658,41 +706,20 @@ object ModBusController {
         if (needLockCount == 0) {
             return map
         }
-        val lockDockList = dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }
+        val lockDockList =
+            dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }
 
         var provideCount = 0
-        lockDockList.forEach loop@ { lockDock ->
-            val lockList = lockDock.getLockList().filter { it.isExist }.toMutableList()
-            if (lockList.size < (needLockCount - provideCount)) {
-                provideCount += lockList.size
-                map[lockDock.addr] = lockList
-            } else {
-                val rfidList = lockList.subList(0, needLockCount - provideCount)
-                map[lockDock.addr] = rfidList
-                return@loop
+        for (lockDock in lockDockList) {
+            if (provideCount >= needLockCount) break
+
+            val validLocks = lockDock.getLockList().filter { it.isExist }
+            val toTake = (needLockCount - provideCount).coerceAtMost(validLocks.size)
+            if (toTake > 0) {
+                map[lockDock.addr] = validLocks.take(toTake).toMutableList()
+                provideCount += toTake
             }
         }
         return map
     }
-
-//    fun getLocks(needLockCount: Int): MutableMap<Byte, MutableList<DockBean.LockBean>> {
-//        val map = mutableMapOf<Byte, MutableList<DockBean.LockBean>>()
-//        if (needLockCount == 0) {
-//            return map
-//        }
-//
-//        val lockList =
-//            dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }
-//                .flatMap { it.getLockList() }.filter { it.isExist }.shuffled().take(needLockCount)
-//        lockList.forEach loop@{ lock ->
-//            lock.rfid ?: return@loop
-//            val dock = getDockByLockNfc(lock.rfid!!)
-//            dock ?: return@loop
-//            if (map[dock.addr] == null) {
-//                map[dock.addr] = mutableListOf()
-//            }
-//            map[dock.addr]!!.add(lock)
-//        }
-//        return map
-//    }
 }

+ 221 - 66
app/src/main/java/com/grkj/iscs/util/Executor.kt

@@ -2,117 +2,132 @@ package com.grkj.iscs.util
 
 import android.os.Handler
 import android.os.Looper
+import kotlinx.coroutines.*
 import java.io.PrintWriter
 import java.io.StringWriter
-import java.util.concurrent.CountDownLatch
 
 object Executor {
 
-    private var io: Handler? = null
-    private val main: Handler = Handler(Looper.getMainLooper())
+    private val mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
+    private val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
 
-    private val latch = CountDownLatch(1)
-
-    init {
-        val thread = Thread {
-            Looper.prepare()
-            io = Handler()
-            latch.countDown()
-            Looper.loop()
-        }
-        thread.isDaemon = true
-        thread.start()
-    }
-
-    val mainHandler: Handler
-        get() = main
+    val mainHandler: Handler = Handler(Looper.getMainLooper())
 
+    @Deprecated("不再使用,仅为兼容保留,避免误用 Handler")
     val ioHandler: Handler
-        get() {
-            if (io == null) {
-                latch.await()
-            }
-            return io!!
-        }
+        get() = throw UnsupportedOperationException("ioHandler 已被协程替代")
 
     fun runOnMain(run: Runnable) {
-        main.post(run)
+        mainScope.launch {
+            run.run()
+        }
     }
 
     fun delayOnMain(delayMills: Long, run: Runnable) {
-        main.postDelayed(run, delayMills)
+        mainScope.launch {
+            delay(delayMills)
+            run.run()
+        }
     }
 
-    fun repeatOnMain(run: () -> Boolean, intervalMills: Long, immediately: Boolean = true) {
-        repeat(main, run, intervalMills, immediately)
+    fun repeatOnMain(
+        run: () -> Boolean,
+        intervalMills: Long,
+        immediately: Boolean = true
+    ) {
+        mainScope.launch {
+            if (immediately && !run()) return@launch
+            while (isActive) {
+                delay(intervalMills)
+                if (!run()) break
+            }
+        }
     }
 
     fun runOnIO(run: Runnable) {
         val traces = Thread.currentThread().stackTrace
-        ioHandler.post {
+        ioScope.launch {
             runWithTraces(traces, run)
         }
     }
 
-    fun runOnIO(run: Runnable, runnableTimeoutMillis: Long = 5000) {
+    fun runOnIO(run: Runnable, runnableTimeoutMillis: Long) {
         val traces = Thread.currentThread().stackTrace
-        ioHandler.post {
-            runWithTraces(traces, run, runnableTimeoutMillis)
+        ioScope.launch {
+            withTimeoutOrNull(runnableTimeoutMillis.toLong()) {
+                runWithTraces(traces, run)
+            } ?: printTimeoutTraces(traces)
         }
     }
 
-    fun delayOnIO(run: Runnable, delayMills: Long, runnableTimeoutMillis: Long = 10_000) {
+    fun delayOnIO(
+        run: Runnable,
+        delayMills: Long,
+        runnableTimeoutMillis: Long = 10_000
+    ) {
         val traces = Thread.currentThread().stackTrace
-        ioHandler.postDelayed({
-            runWithTraces(traces, run, runnableTimeoutMillis)
-        }, delayMills)
+        ioScope.launch {
+            delay(delayMills)
+            withTimeoutOrNull(runnableTimeoutMillis.toLong()) {
+                runWithTraces(traces, run)
+            } ?: printTimeoutTraces(traces)
+        }
     }
 
-    fun delayOnIO(delayMills: Long, runnableTimeoutMillis: Long = 10_000, run: Runnable) {
-        val traces = Thread.currentThread().stackTrace
-        ioHandler.postDelayed({
-            runWithTraces(traces, run, runnableTimeoutMillis)
-        }, delayMills)
+    fun delayOnIO(
+        delayMills: Long,
+        runnableTimeoutMillis: Long = 10_000,
+        run: Runnable
+    ) {
+        delayOnIO(run, delayMills, runnableTimeoutMillis)
     }
 
-    fun repeatOnIO(run: () -> Boolean, intervalMills: Long, immediately: Boolean = true, runnableTimeoutMillis: Long = 5_000) {
+    fun repeatOnIO(
+        run: () -> Boolean,
+        intervalMills: Long,
+        immediately: Boolean = true,
+        runnableTimeoutMillis: Long = 5000
+    ) {
         val traces = Thread.currentThread().stackTrace
-        repeat(ioHandler, {
-            var blocked = true
-            val thread = Thread.currentThread()
-            delayOnMain(runnableTimeoutMillis) {
-                // 如果线程被阻塞达到 1 秒,则打断唤醒
-                if (blocked) {
+        ioScope.launch {
+            suspend fun safeRun(): Boolean {
+                return withTimeoutOrNull(runnableTimeoutMillis) {
+                    run()
+                } ?: run {
                     printTimeoutTraces(traces)
-                    thread.interrupt()
+                    false
                 }
             }
-            val res = run()
-            blocked = false
-            return@repeat res
-        }, intervalMills, immediately)
-    }
 
-    private fun runWithTraces(traces: Array<StackTraceElement>, run: Runnable, runnableTimeoutMillis: Long = 10_000) {
-        var blocked = true
-        val thread = Thread.currentThread()
-        delayOnMain(runnableTimeoutMillis) {
-            // 如果线程被阻塞达到 1 秒,则打断唤醒
-            if (blocked) {
-                printTimeoutTraces(traces)
-                thread.interrupt()
+            if (immediately && !safeRun()) return@launch
+            while (isActive) {
+                delay(intervalMills)
+                if (!safeRun()) break
             }
         }
-        run.run()
-        blocked = false
     }
 
-    private fun printTimeoutTraces(traces: Array<StackTraceElement>) {
+    private suspend fun runWithTraces(
+        traces: Array<StackTraceElement>,
+        run: Runnable
+    ) {
+        try {
+            run.run()
+        } catch (e: Exception) {
+            printTimeoutTraces(traces, e)
+        }
+    }
+
+    private fun printTimeoutTraces(
+        traces: Array<StackTraceElement>,
+        e: Throwable? = null
+    ) {
         val writer = StringWriter()
         val print = PrintWriter(writer)
         for (traceElement in traces) {
             print.println("\tat $traceElement")
         }
+        e?.printStackTrace(print)
 //        Logger.w("Executor", "执行器超时(10秒):\n${writer}")
     }
 
@@ -136,5 +151,145 @@ object Executor {
             }, intervalMills)
         }
     }
-
 }
+
+//package com.grkj.iscs.util
+//
+//import android.os.Handler
+//import android.os.Looper
+//import java.io.PrintWriter
+//import java.io.StringWriter
+//import java.util.concurrent.CountDownLatch
+//
+//object Executor {
+//
+//    private var io: Handler? = null
+//    private val main: Handler = Handler(Looper.getMainLooper())
+//
+//    private val latch = CountDownLatch(1)
+//
+//    init {
+//        val thread = Thread {
+//            Looper.prepare()
+//            io = Handler()
+//            latch.countDown()
+//            Looper.loop()
+//        }
+//        thread.isDaemon = true
+//        thread.start()
+//    }
+//
+//    val mainHandler: Handler
+//        get() = main
+//
+//    val ioHandler: Handler
+//        get() {
+//            if (io == null) {
+//                latch.await()
+//            }
+//            return io!!
+//        }
+//
+//    fun runOnMain(run: Runnable) {
+//        main.post(run)
+//    }
+//
+//    fun delayOnMain(delayMills: Long, run: Runnable) {
+//        main.postDelayed(run, delayMills)
+//    }
+//
+//    fun repeatOnMain(run: () -> Boolean, intervalMills: Long, immediately: Boolean = true) {
+//        repeat(main, run, intervalMills, immediately)
+//    }
+//
+//    fun runOnIO(run: Runnable) {
+//        val traces = Thread.currentThread().stackTrace
+//        ioHandler.post {
+//            runWithTraces(traces, run)
+//        }
+//    }
+//
+//    fun runOnIO(run: Runnable, runnableTimeoutMillis: Long = 5000) {
+//        val traces = Thread.currentThread().stackTrace
+//        ioHandler.post {
+//            runWithTraces(traces, run, runnableTimeoutMillis)
+//        }
+//    }
+//
+//    fun delayOnIO(run: Runnable, delayMills: Long, runnableTimeoutMillis: Long = 10_000) {
+//        val traces = Thread.currentThread().stackTrace
+//        ioHandler.postDelayed({
+//            runWithTraces(traces, run, runnableTimeoutMillis)
+//        }, delayMills)
+//    }
+//
+//    fun delayOnIO(delayMills: Long, runnableTimeoutMillis: Long = 10_000, run: Runnable) {
+//        val traces = Thread.currentThread().stackTrace
+//        ioHandler.postDelayed({
+//            runWithTraces(traces, run, runnableTimeoutMillis)
+//        }, delayMills)
+//    }
+//
+//    fun repeatOnIO(run: () -> Boolean, intervalMills: Long, immediately: Boolean = true, runnableTimeoutMillis: Long = 5_000) {
+//        val traces = Thread.currentThread().stackTrace
+//        repeat(ioHandler, {
+//            var blocked = true
+//            val thread = Thread.currentThread()
+//            delayOnMain(runnableTimeoutMillis) {
+//                // 如果线程被阻塞达到 1 秒,则打断唤醒
+//                if (blocked) {
+//                    printTimeoutTraces(traces)
+//                    thread.interrupt()
+//                }
+//            }
+//            val res = run()
+//            blocked = false
+//            return@repeat res
+//        }, intervalMills, immediately)
+//    }
+//
+//    private fun runWithTraces(traces: Array<StackTraceElement>, run: Runnable, runnableTimeoutMillis: Long = 10_000) {
+//        var blocked = true
+//        val thread = Thread.currentThread()
+//        delayOnMain(runnableTimeoutMillis) {
+//            // 如果线程被阻塞达到 1 秒,则打断唤醒
+//            if (blocked) {
+//                printTimeoutTraces(traces)
+//                thread.interrupt()
+//            }
+//        }
+//        run.run()
+//        blocked = false
+//    }
+//
+//    private fun printTimeoutTraces(traces: Array<StackTraceElement>) {
+//        val writer = StringWriter()
+//        val print = PrintWriter(writer)
+//        for (traceElement in traces) {
+//            print.println("\tat $traceElement")
+//        }
+////        Logger.w("Executor", "执行器超时(10秒):\n${writer}")
+//    }
+//
+//    private fun repeat(
+//        handler: Handler,
+//        run: () -> Boolean,
+//        intervalMills: Long,
+//        immediately: Boolean = true
+//    ) {
+//        if (immediately) {
+//            if (run()) {
+//                handler.postDelayed({
+//                    repeat(handler, run, intervalMills, immediately)
+//                }, intervalMills)
+//            }
+//        } else {
+//            handler.postDelayed({
+//                if (run()) {
+//                    repeat(handler, run, intervalMills, immediately)
+//                }
+//            }, intervalMills)
+//        }
+//    }
+//
+//}

+ 1 - 1
app/src/main/java/com/grkj/iscs/view/dialog/LoginDialog.kt

@@ -101,7 +101,7 @@ class LoginDialog(
                 !device.isVirtual
         LogUtil.d("is HID Input: ${isHidInput}")
         // 如果是 HID 设备输入(扫码器等),自定义处理逻辑
-        if (isHidInput && mLoginType != 2) {
+        if (isHidInput && mLoginType == 2) {
             if (event.action == KeyEvent.ACTION_UP) {
                 if (event.keyCode == KeyEvent.KEYCODE_ENTER) {
                     LogUtil.i("Swipe card login Origin: $cardNo")

+ 1 - 1
app/src/main/java/com/grkj/iscs/view/fragment/DeviceStatusFragment.kt

@@ -19,7 +19,7 @@ import com.zhy.adapter.recyclerview.base.ItemViewDelegate
 import com.zhy.adapter.recyclerview.base.ViewHolder
 
 /**
- * 设备状态页
+ * 硬件状态页
  */
 class DeviceStatusFragment : BaseMvpFragment<IDeviceStatusView, DeviceStatusPresenter, FragmentDeviceStatusBinding>() {
     private var mRowList = mutableListOf<DockStatusBO>()

+ 0 - 1
app/src/main/java/com/grkj/iscs/view/fragment/JobProgressFragment.kt

@@ -116,7 +116,6 @@ class JobProgressFragment(val goBack: () -> Unit) :
                 } else {
                     BusinessManager.sendLoadingEventMsg(getString(R.string.system_is_processing))
                     presenter?.handleLockProcess(mPageChangeBO?.ticketId!!)
-                    SPUtils.takeKey(mPageChangeBO?.ticketId!!)
                 }
             } else if (mStep == 7) {
                 val checkResult = presenter?.checkUnlock(requireContext(), mUserList)

+ 16 - 6
app/src/main/java/com/grkj/iscs/view/fragment/WorkerFragment.kt

@@ -72,8 +72,9 @@ class WorkerFragment(val goBack: () -> Unit, val changePage: (PageChangeBO) -> U
                     holder.setText(R.id.tv_name, data.userName)
                     holder.setOnClickListener(R.id.root) {
                         if (presenter?.colockerCanRemove(requireContext(), data, mStep) == true) {
-                            if (mColockerSelectedShowList.size == 1) {
-                                ToastUtils.tip(getString(R.string.keep_at_least_one_colocker))
+                            val minColockerSize = presenter?.getMinColockerSize(requireContext())?:1
+                            if (mColockerSelectedShowList.size == minColockerSize) {
+                                ToastUtils.tip(getString(R.string.keep_at_least_colocker,minColockerSize))
                                 return@setOnClickListener
                             }
                             // 共锁人取消
@@ -168,8 +169,9 @@ class WorkerFragment(val goBack: () -> Unit, val changePage: (PageChangeBO) -> U
                                     mStep
                                 ) == true
                             ) {
-                                if (mColockerSelectedShowList.size == 1) {
-                                    ToastUtils.tip(getString(R.string.keep_at_least_one_colocker))
+                                val minColockerSize = presenter?.getMinColockerSize(requireContext())?:1
+                                if (mColockerSelectedShowList.size == minColockerSize) {
+                                    ToastUtils.tip(getString(R.string.keep_at_least_colocker,minColockerSize))
                                     return@setOnClickListener
                                 }
                                 mSelectedList.removeIf { it.userId == user.userId }
@@ -220,6 +222,11 @@ class WorkerFragment(val goBack: () -> Unit, val changePage: (PageChangeBO) -> U
                                     mStep
                                 ) == true
                             ) {
+                                val minColockerSize = presenter?.getMinColockerSize(requireContext())?:1
+                                if (mColockerSelectedShowList.size == minColockerSize) {
+                                    ToastUtils.tip(getString(R.string.keep_at_least_colocker,minColockerSize))
+                                    return@setOnClickListener
+                                }
                                 mSelectedList.removeIf { it.userId == user.userId }
                                 mColockerSelectedShowList.removeIf { it.userId == user.userId }
                             } else {
@@ -376,9 +383,12 @@ class WorkerFragment(val goBack: () -> Unit, val changePage: (PageChangeBO) -> U
         }
         //选择人员的时候一定要添加共锁人
         if (mStep == 2) {
-            val hasColocker = mSelectedList.any { it.userRole == USER_ROLE_COLOCKER }
+            val minColockerSize =
+                presenter?.getMinColockerSize(requireContext()) ?: 1
+            val hasColocker =
+                mSelectedList.count { it.userRole == USER_ROLE_COLOCKER } > minColockerSize
             if (!hasColocker) {
-                ToastUtils.tip(getString(R.string.please_add_colocker))
+                ToastUtils.tip(getString(R.string.please_add_at_least_colockers, minColockerSize))
                 return false
             }
         }

+ 9 - 2
app/src/main/java/com/grkj/iscs/view/presenter/WorkerPresenter.kt

@@ -103,9 +103,9 @@ class WorkerPresenter : BasePresenter<IWorkerView>() {
         context: Context,
         stepDetailList: MutableList<StepDetailRespVO>,
         mStep: Int,
-        jumpSuccess:()->Unit
+        jumpSuccess: () -> Unit
     ) {
-        IStepMode.getStepMode(context)?.checkStepJump(mStep, stepDetailList,jumpSuccess)
+        IStepMode.getStepMode(context)?.checkStepJump(mStep, stepDetailList, jumpSuccess)
     }
 
     /**
@@ -114,4 +114,11 @@ class WorkerPresenter : BasePresenter<IWorkerView>() {
     fun canChangeColocker(context: Context, mStep: Int): Boolean {
         return IStepMode.getStepMode(context)?.canChangeColocker(mStep) == true
     }
+
+    /**
+     * 最少共锁人
+     */
+    fun getMinColockerSize(context: Context): Int {
+        return IStepMode.getStepMode(context)?.getMinColockerSize() ?: 1
+    }
 }

+ 5 - 0
app/src/main/java/com/grkj/iscs/view/step_mode/IStepMode.kt

@@ -99,6 +99,11 @@ interface IStepMode {
      */
     fun tipToJobProgressPageCheck(mStep: Int, checked: () -> Unit)
 
+    /**
+     * 最少共锁人要求
+     */
+    fun getMinColockerSize(): Int
+
     companion object {
         /**
          * 根据存储的模式获取模式

+ 4 - 0
app/src/main/java/com/grkj/iscs/view/step_mode/StepMode1.kt

@@ -81,4 +81,8 @@ class StepMode1 : IStepMode {
     override fun tipToJobProgressPageCheck(mStep: Int, checked: () -> Unit) {
         LogUtil.i("模式1不进入")
     }
+
+    override fun getMinColockerSize(): Int {
+        return  1
+    }
 }

+ 4 - 0
app/src/main/java/com/grkj/iscs/view/step_mode/StepMode2.kt

@@ -160,4 +160,8 @@ class StepMode2 : IStepMode {
             checked()
         }
     }
+
+    override fun getMinColockerSize(): Int {
+        return 2
+    }
 }

+ 1 - 3
app/src/main/java/com/grkj/iscs/view/widget/CustomMarkLayer.kt

@@ -144,8 +144,6 @@ class CustomMarkLayer @JvmOverloads constructor(
         this.currentZoom = currentZoom
         currentDegree = 360 - currentRotateDegrees
         if (isVisible) {
-            LogUtil.i("Map Size: ${mapView.width},${mapView.height}")
-            LogUtil.i("Canvas Size: ${canvas.width},${canvas.height}")
             canvas.save()
             if (pointList.isNotEmpty()) {
                 pointList.forEach { point ->
@@ -154,6 +152,7 @@ class CustomMarkLayer @JvmOverloads constructor(
                     currentMatrix.mapPoints(goal)
 
                     // 文字背景
+                    paint.textSize = radiusMark
                     val width = paint.measureText(point.name)
                     paint.color = Color.parseColor("#70b26f")
                     paint.style = Paint.Style.FILL
@@ -170,7 +169,6 @@ class CustomMarkLayer @JvmOverloads constructor(
                     )
 
                     paint.color = Color.parseColor("#FFFFFF")
-                    paint.textSize = radiusMark
                     // 一直显示文字
                     canvas.drawText(
                         point.name,

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

@@ -273,7 +273,7 @@
     <string name="current_step_can_not_be_process">Current step cannot be processed temporarily</string>
     <string name="no_permission_to_handle">You do not have permission for this permit</string>
     <string name="can_not_change_locker">Cannot change locker currently</string>
-    <string name="please_add_colocker">Please Add Colocker</string>
+    <string name="please_add_at_least_colockers">Please add at least %1$d colockers</string>
     <string name="can_not_change_colocker">Cannot change co-locker currently</string>
     <string name="is_processing_please_wait">Processing, please wait...</string>
     <string name="doing_login">Logging in...</string>
@@ -327,7 +327,7 @@
     <string name="current_colocker_already_unlocked">current colocker already unlocked</string>
     <string name="can_not_remove_current_colocker">can not remove current colocker</string>
     <string name="current_step_not_allowed_to_add_colocker">current step not allowed to add colocker</string>
-    <string name="keep_at_least_one_colocker">keep at least one colocker</string>
+    <string name="keep_at_least_colocker">keep at least %1$d colocker</string>
     <string name="action_hint">action hint</string>
     <string name="take_one_more_key_hint">I have already taken out the key, would you like to take out another one</string>
 </resources>

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

@@ -274,7 +274,7 @@
     <string name="no_permission_to_handle">您暂无权限操作当前作业票</string>
     <string name="can_not_change_locker">当前无法更换上锁人</string>
     <string name="can_not_change_colocker">当前无法更换共锁人</string>
-    <string name="please_add_colocker">请添加共锁人</string>
+    <string name="please_add_at_least_colockers">请添加至少%1$d共锁人</string>
     <string name="is_processing_please_wait">正在处理中,请稍后······</string>
     <string name="doing_login">正在登录······</string>
     <string name="start_to_send_ticket">开始下发工作票······</string>
@@ -294,7 +294,7 @@
     <string name="row_and_column_conflict">存在相同的行列号的同类型主板,请重新输入</string>
     <string name="column_must_be_1_or_2">钥匙列号只能输入1或者2</string>
     <string name="row_conflict">行号冲突</string>
-    <string name="device_status">设备状态</string>
+    <string name="device_status">硬件状态</string>
     <string name="key_does_not_exists">钥匙不存在</string>
     <string name="key_no_rfid">钥匙RFID缺失</string>
     <string name="key_no_mac">钥匙Mac缺失</string>
@@ -305,7 +305,7 @@
     <string name="key_unknown">未知</string>
     <string name="repair_key">修复</string>
     <string name="no_key_to_repair">钥匙不存在,无法修复</string>
-    <string name="key_take_error_tip">钥匙分配失败,请检查设备状态</string>
+    <string name="key_take_error_tip">钥匙分配失败,请检查硬件状态</string>
 
     <string name="index_number">编号</string>
     <string name="icon">图标</string>
@@ -327,7 +327,7 @@
     <string name="current_colocker_already_unlocked">当前共锁人已解锁</string>
     <string name="can_not_remove_current_colocker">无法移除当前共锁人</string>
     <string name="current_step_not_allowed_to_add_colocker">当前步骤不允许添加共锁人</string>
-    <string name="keep_at_least_one_colocker">至少保留一位共锁人</string>
+    <string name="keep_at_least_colocker">至少保留%1$d位共锁人</string>
     <string name="action_hint">操作提醒</string>
     <string name="take_one_more_key_hint">已有钥匙取出,是否再取一个</string>
 </resources>

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

@@ -274,7 +274,7 @@
     <string name="no_permission_to_handle">您暂无权限操作当前作业票</string>
     <string name="can_not_change_locker">当前无法更换上锁人</string>
     <string name="can_not_change_colocker">当前无法更换共锁人</string>
-    <string name="please_add_colocker">请添加共锁人</string>
+    <string name="please_add_at_least_colockers">请添加至少%1$d共锁人</string>
     <string name="is_processing_please_wait">正在处理中,请稍后······</string>
     <string name="doing_login">正在登录······</string>
     <string name="start_to_send_ticket">开始下发工作票······</string>
@@ -294,7 +294,7 @@
     <string name="row_and_column_conflict">存在相同的行列号的同类型主板,请重新输入</string>
     <string name="column_must_be_1_or_2">钥匙列号只能输入1或者2</string>
     <string name="row_conflict">行号冲突</string>
-    <string name="device_status">设备状态</string>
+    <string name="device_status">硬件状态</string>
     <string name="key_does_not_exists">钥匙不存在</string>
     <string name="key_no_rfid">钥匙RFID缺失</string>
     <string name="key_no_mac">钥匙Mac缺失</string>
@@ -305,7 +305,7 @@
     <string name="key_unknown">未知</string>
     <string name="repair_key">修复</string>
     <string name="no_key_to_repair">钥匙不存在,无法修复</string>
-    <string name="key_take_error_tip">钥匙分配失败,请检查设备状态</string>
+    <string name="key_take_error_tip">钥匙分配失败,请检查硬件状态</string>
     
     <string name="index_number">编号</string>
     <string name="icon">图标</string>
@@ -327,7 +327,7 @@
     <string name="current_colocker_already_unlocked">当前共锁人已解锁</string>
     <string name="can_not_remove_current_colocker">无法移除当前共锁人</string>
     <string name="current_step_not_allowed_to_add_colocker">当前步骤不允许添加共锁人</string>
-    <string name="keep_at_least_one_colocker">至少保留一位共锁人</string>
+    <string name="keep_at_least_colocker">至少保留%1$d位共锁人</string>
     <string name="action_hint">操作提醒</string>
     <string name="take_one_more_key_hint">已有钥匙取出,是否再取一个</string>
 </resources>