Forráskód Böngészése

refactor(更新) :
- 钥匙的检测匹配完成

周文健 5 hónapja
szülő
commit
c4da436fbf

+ 25 - 0
app/src/main/java/com/grkj/iscs/BusinessManager.kt

@@ -34,6 +34,7 @@ import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_KEY
 import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_LOCK
 import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_PORTABLE
 import com.grkj.iscs.model.DictAndSystemConstants
+import com.grkj.iscs.model.ISCSDomainData
 import com.grkj.iscs.model.bo.DeviceTakeUpdateBO
 import com.grkj.iscs.model.bo.UpdateKeyReturnBO
 import com.grkj.iscs.model.bo.WorkTicketGetBO
@@ -99,6 +100,8 @@ object BusinessManager {
 
     private val listeners = ArrayList<DeviceListener>()
 
+    private var initListener: (() -> Unit)? = null
+
     // 归还设备是否需要登录
     var NEED_AUTH = true
 
@@ -284,6 +287,20 @@ object BusinessManager {
         listeners.add(DeviceListener(key, listener))
     }
 
+    /**
+     * 注册初始化监听
+     */
+    fun registerInitListener(listener: () -> Unit) {
+        this.initListener = listener
+    }
+
+    /**
+     * 取消注册初始化监听
+     */
+    fun unRegisterInitListener() {
+        this.initListener = null
+    }
+
     /**
      * 取消注册状态监听
      */
@@ -392,6 +409,9 @@ object BusinessManager {
                                             false, keyBean.isLeft, dockBean.addr
                                         ) {
                                             NetApi.getKeyInfo(rfid) {
+                                                ModBusController.updateKeyNewHardware(
+                                                    dockBean.addr, true, it == null
+                                                )
                                                 if (it != null && !it.macAddress.isNullOrEmpty()) {
                                                     ModBusController.updateKeyMac(
                                                         dockBean.addr, keyBean.isLeft, it.macAddress
@@ -620,6 +640,11 @@ object BusinessManager {
                 listeners.forEach { it.callBack(dockBean) }
             }
         }
+        Executor.delayOnMain(200) {
+            if (ISCSDomainData.isDeviceEnter) {
+                initListener?.invoke()
+            }
+        }
     }
 
     /**

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

@@ -21,6 +21,7 @@ import com.grkj.iscs.model.Constants.PERMISSION_REQUEST_CODE
 import com.grkj.iscs.model.eventmsg.LoadingMsg
 import com.grkj.iscs.model.eventmsg.MsgEvent
 import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_LOADING
+import com.grkj.iscs.model.eventmsg.SwitchModeMsg
 import com.grkj.iscs.util.ActivityUtils
 import com.grkj.iscs.util.CommonUtils
 import com.grkj.iscs.util.Executor
@@ -514,7 +515,7 @@ object BleConnectionManager {
      *   1. 先尝试不充电连接,若成功就返回 true;
      *   2. 否则开启“充电”,等 500ms,再尝试一次连接,连接成功后断电并返回 true;否则返回 false。
      */
-    suspend fun tryConnectWithOptionalCharge(mac: String): Boolean =
+    suspend fun tryConnectWithOptionalCharge(mac: String, withOpenCharge: Boolean = true): Boolean =
         withContext(Dispatchers.IO) {
             // -------- 第一次尝试 --------
             LogUtil.i("蓝牙连接-第一次尝试")
@@ -536,10 +537,13 @@ object BleConnectionManager {
                     cont.cancel()
                 }
             }
-            LogUtil.i("蓝牙连接-第一次连接:${firstTry}")
+            LogUtil.i("蓝牙连接-第一次连接:${firstTry},是否继续尝试上电连接${withOpenCharge}")
             if (firstTry) {
                 return@withContext true
             }
+            if (!withOpenCharge) {
+                return@withContext false
+            }
             // -------- 第二次尝试:先开电,再连 --------
             // 开电,并等待回调
             suspendCoroutine<Unit> { unitCont ->
@@ -573,6 +577,113 @@ object BleConnectionManager {
             return@withContext secondTry
         }
 
+    /**
+     * 扫描在线的蓝牙钥匙并发送指令关机
+     */
+    suspend fun scanOnlineKeyLockMacAndSwitchModeToClose(): Boolean {
+        return suspendCancellableCoroutine { parentCont ->
+            val keyMacs: MutableList<BleDevice> = mutableListOf()
+            BleUtil.instance?.scan(object : CustomBleScanCallback() {
+                override fun onPrompt(promptStr: String?) {
+                    // 蓝牙未启动重试
+                    LogUtil.i("设备录入-参数:${promptStr}")
+                    BleManager.getInstance().enableBluetooth()
+                    ThreadUtils.runOnMainDelayed(300) {
+                        scanOnlineKeyLockMacAndSwitchModeToClose()
+                    }
+                }
+
+                override fun onScanStarted(success: Boolean) {
+                    LogUtil.i("设备录入-onScanStarted:${success}")
+                    if (!success) {
+                        ThreadUtils.runOnMainDelayed(300) {
+                            scanOnlineKeyLockMacAndSwitchModeToClose()
+                        }
+                    }
+                }
+
+                override fun onScanning(bleDevice: BleDevice?) {
+                    val mac = bleDevice?.mac ?: return
+                    LogUtil.i("设备录入-onScanning:$mac")
+                    keyMacs.add(bleDevice)
+                }
+
+                override fun onScanFinished(scanResultList: MutableList<BleDevice>?) {
+                    ThreadUtils.runOnIO {
+                        keyMacs.forEach {
+                            val connected = tryConnectWithOptionalCharge(it.mac, false)
+                            if (connected) {
+                                suspend fun switchReadyMode(bleDevice: BleDevice): Boolean {
+                                    return suspendCancellableCoroutine<Boolean> { cont ->
+                                        BleCmdManager.switchMode(
+                                            BleConst.STATUS_READY,
+                                            bleDevice,
+                                            object : CustomBleWriteCallback() {
+                                                override fun onWriteSuccess(
+                                                    current: Int,
+                                                    total: Int,
+                                                    justWrite: ByteArray?
+                                                ) {
+                                                    BleManager.getInstance().disconnect(bleDevice)
+                                                    cont.resume(true)
+                                                    LogUtil.i("设备录入-切换模式发送成功 : ${bleDevice.mac}")
+                                                }
+
+                                                override fun onWriteFailure(exception: BleException?) {
+                                                    LogUtil.e("设备录入-切换模式发送失败 : ${bleDevice.mac}")
+                                                    ThreadUtils.runOnMainDelayed(300) {
+                                                        cont.resume(switchReadyMode(bleDevice))
+                                                    }
+                                                }
+                                            })
+                                    }
+                                }
+
+                                val sendSuccess = switchReadyMode(it)
+                                LogUtil.i("设备录入-发送切换待机模式:${it.mac},${sendSuccess}")
+                            }
+                        }
+                        parentCont.resume(true)
+                    }
+                }
+            })
+        }
+    }
+
+    /**
+     * 扫描在线的蓝牙
+     */
+    fun scanOnlineKeyLockMac(callback: (MutableList<BleDevice>?) -> Unit) {
+        BleUtil.instance?.scan(object : CustomBleScanCallback() {
+            override fun onPrompt(promptStr: String?) {
+                // 蓝牙未启动重试
+                LogUtil.d("设备录入-参数:${promptStr}")
+                BleManager.getInstance().enableBluetooth()
+                ThreadUtils.runOnMainDelayed(300) {
+                    scanOnlineKeyLockMac(callback)
+                }
+            }
+
+            override fun onScanStarted(success: Boolean) {
+                LogUtil.d("设备录入-onScanStarted:${success}")
+                if (!success) {
+                    ThreadUtils.runOnMainDelayed(300) {
+                        scanOnlineKeyLockMac(callback)
+                    }
+                }
+            }
+
+            override fun onScanning(bleDevice: BleDevice?) {
+                val mac = bleDevice?.mac ?: return
+                LogUtil.d("设备录入-onScanning:$mac")
+            }
+
+            override fun onScanFinished(scanResultList: MutableList<BleDevice>?) {
+                LogUtil.d("设备录入-扫描完成:$scanResultList")
+                callback(scanResultList)
+            }
+        })
+    }
 
     // 蓝牙连接准备监听
     data class ConnectListener(

+ 14 - 0
app/src/main/java/com/grkj/iscs/modbus/ModBusController.kt

@@ -719,6 +719,20 @@ object ModBusController {
             }
     }
 
+    /**
+     * 关锁并充电
+     */
+    fun controlKeyLockAndCharge(
+        isOpen: Boolean,
+        isLeft: Boolean,
+        slaveAddress: Byte?,
+        done: ((res: ByteArray) -> Unit)? = null
+    ) {
+        controlKeyBuckle(!isOpen, isLeft, slaveAddress) {
+            controlKeyCharge(isOpen, isLeft, slaveAddress, done)
+        }
+    }
+
     /**
      * 控制钥匙充电
      */

+ 4 - 1
app/src/main/java/com/grkj/iscs/model/ISCSDomainData.kt

@@ -11,5 +11,8 @@ object ISCSDomainData {
      */
     var deviceInputType: DeviceInputTypeEnum = DeviceInputTypeEnum.NONE
 
-
+    /**
+     * 是否是硬件录入
+     */
+    var isDeviceEnter: Boolean = false
 }

+ 60 - 8
app/src/main/java/com/grkj/iscs/view/fragment/DeviceInputKeyAndLockFragment.kt

@@ -12,15 +12,18 @@ import com.grkj.iscs.R
 import com.grkj.iscs.databinding.FragmentDeviceInputKeyAndLockBinding
 import com.grkj.iscs.extentions.setSelected
 import com.grkj.iscs.extentions.setVisibleWithHolder
+import com.grkj.iscs.modbus.DockBean
 import com.grkj.iscs.modbus.ModBusController
 import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_KEY
 import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_LOCK
+import com.grkj.iscs.model.ISCSDomainData
 import com.grkj.iscs.model.bo.DockStatusBO
 import com.grkj.iscs.util.CommonUtils
 import com.grkj.iscs.util.SPUtils
+import com.grkj.iscs.util.log.LogUtil
 import com.grkj.iscs.view.base.BaseNavFragment
 import com.grkj.iscs.view.dialog.TipDialog
-import com.grkj.iscs.view.presenter.DeviceInputKeyAndLockViewModel
+import com.grkj.iscs.view.viewmodel.DeviceInputKeyAndLockViewModel
 import com.sik.sikcore.extension.setDebouncedClickListener
 import com.sik.sikcore.thread.ThreadUtils
 import com.zhy.adapter.recyclerview.CommonAdapter
@@ -45,18 +48,29 @@ class DeviceInputKeyAndLockFragment :
     }
 
     override fun initView() {
+        ISCSDomainData.isDeviceEnter = true
         viewModel.loginUser = SPUtils.getLoginUser(requireContext())
+        //打开所有钥匙仓并关闭充电
+        LogUtil.d("设备录入-打开所有钥匙仓并关闭充电")
+        ModBusController.controlAllKeyBuckleOpen()
+        viewModel.registerInitListener().observe(this) {
+            LogUtil.d("设备录入-初始化检测完成")
+        }
         viewModel.initData(mRowList)
         binding.cbBack.setDebouncedClickListener {
             navController.popBackStack()
         }
+        viewModel.isLoadComplete.observe(this) {
+            binding.scanTip.isVisible = !it
+            binding.rvDock.isVisible = it
+            binding.cbRescanOrInput.isVisible = it
+        }
         binding.cbRescanOrInput.setDebouncedClickListener {
             if (isAlreadyInput) {
                 mRowList.clear()
+                viewModel.isStartCheckKey = false
+                viewModel.isLoadComplete.postValue(false)
                 viewModel.initData(mRowList)
-                binding.scanTip.isVisible = mRowList.isEmpty()
-                binding.rvDock.isVisible = mRowList.isNotEmpty()
-                binding.cbRescanOrInput.isVisible = binding.rvDock.isVisible
                 isAlreadyInput = false
                 binding.cbRescanOrInput.setText(
                     if (isAlreadyInput) CommonUtils.getStr(R.string.rescan)
@@ -107,9 +121,6 @@ class DeviceInputKeyAndLockFragment :
     override fun onResume() {
         super.onResume()
         fun refreshAdapter() {
-            binding.scanTip.isVisible = mRowList.isEmpty()
-            binding.rvDock.isVisible = mRowList.isNotEmpty()
-            binding.cbRescanOrInput.isVisible = binding.rvDock.isVisible
             binding.cbRescanOrInput.setText(
                 if (isAlreadyInput) CommonUtils.getStr(R.string.rescan)
                     .toString() else CommonUtils.getStr(R.string.input_to_system).toString()
@@ -120,7 +131,9 @@ class DeviceInputKeyAndLockFragment :
                         binding.rvDock?.adapter?.notifyDataSetChanged()
                     }
                     delay(1000)
-                    refreshAdapter()
+                    withContext(Dispatchers.Main) {
+                        refreshAdapter()
+                    }
                 }
             }
 
@@ -128,6 +141,13 @@ class DeviceInputKeyAndLockFragment :
         refreshAdapter()
     }
 
+    override fun onDestroyView() {
+        ISCSDomainData.isDeviceEnter = false
+        viewModel.unregisterInitListener()
+        viewModel.isDestroy = true
+        super.onDestroyView()
+    }
+
     class KeyDockItemDelegate(
         var context: Context,
         var presenter: DeviceInputKeyAndLockViewModel?
@@ -161,6 +181,38 @@ class DeviceInputKeyAndLockFragment :
                 R.id.iv_key_4,
                 ModBusController.isKeyExist(row.dockList.find { it.column == "2" }?.address, false)
             )
+            holder?.getView<TextView>(R.id.tv_new_device_mac_1)?.apply {
+                val keyBean = ModBusController.getKeyByDock(
+                    row.dockList.find { it.column == "1" }?.address,
+                    true
+                )?.mac
+                isVisible = !keyBean.isNullOrEmpty()
+                text = keyBean
+            }
+            holder?.getView<TextView>(R.id.tv_new_device_mac_2)?.apply {
+                val keyBean = ModBusController.getKeyByDock(
+                    row.dockList.find { it.column == "1" }?.address,
+                    false
+                )?.mac
+                isVisible = !keyBean.isNullOrEmpty()
+                text = keyBean
+            }
+            holder?.getView<TextView>(R.id.tv_new_device_mac_3)?.apply {
+                val keyBean = ModBusController.getKeyByDock(
+                    row.dockList.find { it.column == "2" }?.address,
+                    true
+                )?.mac
+                isVisible = !keyBean.isNullOrEmpty()
+                text = keyBean
+            }
+            holder?.getView<TextView>(R.id.tv_new_device_mac_4)?.apply {
+                val keyBean = ModBusController.getKeyByDock(
+                    row.dockList.find { it.column == "2" }?.address,
+                    false
+                )?.mac
+                isVisible = !keyBean.isNullOrEmpty()
+                text = keyBean
+            }
 //            holder?.getView<View>(R.id.v_buckle_status_1)?.backgroundTintList =
 //                if (presenter?.getKeyBuckleLockEnabled(
 //                        row.dockList.find { it.column == "1" }?.address,

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

@@ -32,7 +32,7 @@ class DeviceInputScanFragment : BaseNavFragment<FragmentDeviceInputScanBinding>(
         }
 
         (activity as HomeActivity).cardNoLiveData.observeForever {deviceNfc->
-            if (isVisible && deviceNfc.isNotEmpty()) {
+            if (isVisible && !deviceNfc.isNullOrEmpty()) {
                 val tipDialog = TipDialog(requireContext())
                 tipDialog.setTip(
                     requireContext().getString(
@@ -42,7 +42,6 @@ class DeviceInputScanFragment : BaseNavFragment<FragmentDeviceInputScanBinding>(
                 )
                 tipDialog.setConfirmListener {
                     viewModel.deviceInputScan(deviceNfc).observe(this) {
-                        (activity as HomeActivity).cardNoLiveData.postValue("")
                         if (it) {
                             tipDialog.setTip(
                                 requireContext().getString(

+ 3 - 0
app/src/main/java/com/grkj/iscs/view/fragment/DeviceInputTypeSelectFragment.kt

@@ -4,6 +4,7 @@ import com.grkj.iscs.R
 import com.grkj.iscs.databinding.FragmentDeviceInputTypeSelectBinding
 import com.grkj.iscs.enums.DeviceInputTypeEnum
 import com.grkj.iscs.model.ISCSDomainData
+import com.grkj.iscs.view.activity.HomeActivity
 import com.grkj.iscs.view.base.BaseNavFragment
 import com.sik.sikcore.extension.setDebouncedClickListener
 
@@ -20,10 +21,12 @@ class DeviceInputTypeSelectFragment : BaseNavFragment<FragmentDeviceInputTypeSel
             navController.navigate(R.id.action_deviceInputTypeSelectFragment_to_deviceInputKeyAndLockFragment)
         }
         binding.cardInputLayout.setDebouncedClickListener {
+            (activity as HomeActivity).cardNoLiveData.value = ""
             ISCSDomainData.deviceInputType = DeviceInputTypeEnum.CARD
             navController.navigate(R.id.action_deviceInputTypeSelectFragment_to_deviceInputScanFragment)
         }
         binding.rfidInputLayout.setDebouncedClickListener {
+            (activity as HomeActivity).cardNoLiveData.value = ""
             ISCSDomainData.deviceInputType = DeviceInputTypeEnum.RFID
             navController.navigate(R.id.action_deviceInputTypeSelectFragment_to_deviceInputScanFragment)
         }

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

@@ -43,7 +43,6 @@ class JobExecutionFragment(val changePageCallback: (PageChangeBO) -> Unit) :
         (activity as HomeActivity).cardNoLiveData.observeForever {
             if (mBinding?.vp?.currentItem == 2) {
                 (mMenuList[2].fragment as JobProgressFragment).getCardNo(it)
-                (activity as HomeActivity).cardNoLiveData.postValue("")
             }
         }
     }

+ 0 - 141
app/src/main/java/com/grkj/iscs/view/presenter/DeviceInputKeyAndLockViewModel.kt

@@ -1,141 +0,0 @@
-package com.grkj.iscs.view.presenter
-
-import android.graphics.Bitmap
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.liveData
-import com.clj.fastble.BleManager
-import com.google.gson.Gson
-import com.google.gson.reflect.TypeToken
-import com.grkj.iscs.BusinessManager
-import com.grkj.iscs.BusinessManager.getCurrentStatus
-import com.grkj.iscs.MyApplication
-import com.grkj.iscs.R
-import com.grkj.iscs.extentions.removeLeadingZeros
-import com.grkj.iscs.extentions.toHexStrings
-import com.grkj.iscs.modbus.DockBean
-import com.grkj.iscs.modbus.ModBusController
-import com.grkj.iscs.model.DeviceConst
-import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_LOCK
-import com.grkj.iscs.model.DictAndSystemConstants
-import com.grkj.iscs.model.bo.DockStatusBO
-import com.grkj.iscs.model.bo.LoginUserBO
-import com.grkj.iscs.model.vo.dict.CommonDictRespVO
-import com.grkj.iscs.model.vo.hardware.CabinetSlotsRecord
-import com.grkj.iscs.model.vo.hardware.SlotExDTO
-import com.grkj.iscs.util.CommonUtils
-import com.grkj.iscs.util.Executor
-import com.grkj.iscs.util.NetApi
-import com.grkj.iscs.util.SPUtils
-import com.grkj.iscs.util.ToastUtils
-import com.grkj.iscs.util.log.LogUtil
-import com.grkj.iscs.view.base.BaseViewModel
-import com.grkj.iscs.view.fragment.DockTestFragment.DockTestBean
-import com.sik.sikcore.thread.ThreadUtils
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.suspendCancellableCoroutine
-import kotlin.coroutines.resume
-
-class DeviceInputKeyAndLockViewModel : BaseViewModel() {
-    var loginUser: LoginUserBO? = null
-
-    fun initData(rowList: MutableList<DockStatusBO>) {
-        val dockConfigJson = SPUtils.getDockConfig(MyApplication.instance?.applicationContext!!)
-        if (!dockConfigJson.isNullOrEmpty()) {
-            val tempList: MutableList<DockTestBean> = Gson().fromJson(
-                dockConfigJson, object : TypeToken<MutableList<DockTestBean>>() {}.type
-            )
-            if (tempList.isNotEmpty()) {
-                tempList.forEach { dock ->
-                    try {
-                        if (dock.type == DOCK_TYPE_LOCK) {
-                            dock.deviceList = mutableListOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
-                        }
-                        rowList.find { it.row == dock.row.toInt() }?.let {
-                            it.dockList.add(dock)
-                        } ?: let {
-                            rowList.add(DockStatusBO(dock.row.toInt(), mutableListOf(dock)))
-                        }
-                    } catch (e: Exception) {
-                        LogUtil.e("Device status data error : ${e.message}")
-                    }
-                }
-                // 添加空行
-                val maxRow = rowList.maxBy { it.row }.row
-                for (i in 1..maxRow) {
-                    if (rowList.find { it.row == i } == null) {
-                        rowList.add(DockStatusBO(i, mutableListOf()))
-                    }
-                }
-                rowList.sortBy { it.row }
-            }
-        }
-    }
-
-    /**
-     * 设备录入挂锁
-     */
-    fun deviceInputLock(lockNfc: String): LiveData<Boolean> {
-        return liveData(Dispatchers.IO) {
-            emit(deviceInputLockSuspend(lockNfc))
-        }
-    }
-
-    /**
-     * 设备录入钥匙
-     */
-    fun deviceInputKey(keyNfc: String, keyMacAddress: String): LiveData<Boolean> {
-        return liveData(Dispatchers.IO) {
-            emit(deviceInputKeySuspend(keyNfc, keyMacAddress))
-        }
-    }
-
-    /**
-     * 设备录入挂起任务
-     */
-    private suspend fun deviceInputKeySuspend(keyNfc: String, keyMacAddress: String): Boolean {
-        return suspendCancellableCoroutine<Boolean> { cont ->
-            NetApi.deviceInputKey(keyNfc, keyMacAddress) {
-                cont.resume(it)
-                cont.cancel()
-            }
-        }
-    }
-
-    /**
-     * 设备录入挂起任务
-     */
-    private suspend fun deviceInputLockSuspend(cardNo: String): Boolean {
-        return suspendCancellableCoroutine<Boolean> { cont ->
-            NetApi.deviceInputLock(cardNo) {
-                cont.resume(it)
-                cont.cancel()
-            }
-        }
-    }
-
-    /**
-     * 设备录入(钥匙和挂锁)
-     * //todo 录入待完成
-     */
-    fun deviceInputData(dockData: MutableList<DockStatusBO>): LiveData<Triple<Boolean, Int, Int>> {
-        return liveData(Dispatchers.IO) {
-            val dockList = dockData.map { it.dockList }.flatten()
-            val deviceList =
-                ModBusController.dockList.filter { it.addr in dockList.map { it.address } }
-                    .map { it.deviceList }.flatten()
-            val lockDevice = deviceList.filter { it.type == DeviceConst.DEVICE_TYPE_LOCK }
-                .filterIsInstance<DockBean.LockBean>()
-            val keyDevice = deviceList.filter { it.type == DeviceConst.DEVICE_TYPE_KEY }
-                .filterIsInstance<DockBean.KeyBean>()
-            lockDevice.filter { it.rfid?.isNotEmpty() == true }.map { it.rfid }.forEach { rfid ->
-                deviceInputLockSuspend(rfid.toString())
-            }
-            emit(
-                Triple(
-                    true,
-                    lockDevice.count { it.rfid?.isNotEmpty() == true },
-                    keyDevice.count { it.rfid?.isNotEmpty() == true })
-            )
-        }
-    }
-}

+ 2 - 1
app/src/main/java/com/grkj/iscs/view/presenter/HomePresenter.kt

@@ -9,6 +9,7 @@ import com.grkj.iscs.modbus.DockBean
 import com.grkj.iscs.modbus.ModBusController
 import com.grkj.iscs.model.DeviceConst
 import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_KEY
+import com.grkj.iscs.model.ISCSDomainData
 import com.grkj.iscs.util.Executor
 import com.grkj.iscs.util.NetApi
 import com.grkj.iscs.util.SPUtils
@@ -23,7 +24,7 @@ class HomePresenter : BasePresenter<IHomeView>() {
 
     fun registerStatusListener() {
         BusinessManager.registerStatusListener(this) { dockBean ->
-            if (!BusinessManager.CAN_RETURN) {
+            if (!BusinessManager.CAN_RETURN || ISCSDomainData.isDeviceEnter) {
                 return@registerStatusListener
             }
             when (dockBean.type) {

+ 220 - 0
app/src/main/java/com/grkj/iscs/view/viewmodel/DeviceInputKeyAndLockViewModel.kt

@@ -0,0 +1,220 @@
+package com.grkj.iscs.view.viewmodel
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.liveData
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.grkj.iscs.BusinessManager
+import com.grkj.iscs.MyApplication
+import com.grkj.iscs.ble.BleConnectionManager
+import com.grkj.iscs.modbus.DockBean
+import com.grkj.iscs.modbus.ModBusController
+import com.grkj.iscs.model.DeviceConst
+import com.grkj.iscs.model.bo.DockStatusBO
+import com.grkj.iscs.model.bo.LoginUserBO
+import com.grkj.iscs.util.NetApi
+import com.grkj.iscs.util.SPUtils
+import com.grkj.iscs.util.log.LogUtil
+import com.grkj.iscs.view.base.BaseViewModel
+import com.grkj.iscs.view.fragment.DockTestFragment
+import com.sik.sikcore.thread.ThreadUtils
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlin.collections.iterator
+import kotlin.coroutines.resume
+
+class DeviceInputKeyAndLockViewModel : BaseViewModel() {
+    var loginUser: LoginUserBO? = null
+    val isLoadComplete: MutableLiveData<Boolean> = MutableLiveData(false)
+    var isStartCheckKey: Boolean = false
+    var isDestroy: Boolean = false
+    private val newHardwareKeyBean: MutableMap<Byte, MutableList<DockBean.KeyBean>> = mutableMapOf()
+
+    fun initData(rowList: MutableList<DockStatusBO>) {
+        val dockConfigJson = SPUtils.getDockConfig(MyApplication.Companion.instance?.applicationContext!!)
+        if (!dockConfigJson.isNullOrEmpty()) {
+            val tempList: MutableList<DockTestFragment.DockTestBean> = Gson().fromJson(
+                dockConfigJson, object : TypeToken<MutableList<DockTestFragment.DockTestBean>>() {}.type
+            )
+            if (tempList.isNotEmpty()) {
+                tempList.forEach { dock ->
+                    try {
+                        if (dock.type == DeviceConst.DOCK_TYPE_LOCK) {
+                            dock.deviceList = mutableListOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
+                        }
+                        rowList.find { it.row == dock.row.toInt() }?.let {
+                            it.dockList.add(dock)
+                        } ?: let {
+                            rowList.add(DockStatusBO(dock.row.toInt(), mutableListOf(dock)))
+                        }
+                    } catch (e: Exception) {
+                        LogUtil.e("Device status data error : ${e.message}")
+                    }
+                }
+                // 添加空行
+                val maxRow = rowList.maxBy { it.row }.row
+                for (i in 1..maxRow) {
+                    if (rowList.find { it.row == i } == null) {
+                        rowList.add(DockStatusBO(i, mutableListOf()))
+                    }
+                }
+                rowList.sortBy { it.row }
+            }
+        }
+    }
+
+    fun registerInitListener(): LiveData<Boolean> {
+        return liveData(Dispatchers.IO) {
+            isStartCheckKey = false
+            newHardwareKeyBean.clear()
+            isLoadComplete.postValue(false)
+            val allDeviceCloseCmdSend =
+                BleConnectionManager.scanOnlineKeyLockMacAndSwitchModeToClose()
+            LogUtil.i("设备录入-是否所有关闭命令发送成功:${allDeviceCloseCmdSend}")
+            BusinessManager.registerInitListener {
+                if (isStartCheckKey) {
+                    return@registerInitListener
+                }
+                isStartCheckKey = true
+                val dockList = ModBusController.dockList
+
+                newHardwareKeyBean.putAll(dockList.filter { it.type == DeviceConst.DOCK_TYPE_KEY || it.type == DeviceConst.DOCK_TYPE_PORTABLE }
+                    .associate {
+                        it.addr to it.deviceList.filterIsInstance<DockBean.KeyBean>()
+                            .filter { it.newHardware && it.isExist }
+                            .toMutableList()
+                    })
+                LogUtil.d("设备录入-新设备:${newHardwareKeyBean}")
+                ThreadUtils.runOnIO {
+                    for ((addr, keyBeans) in newHardwareKeyBean) {
+                        for (keyBean in keyBeans) {
+                            if (isDestroy) {
+                                return@runOnIO
+                            }
+                            openChargeAndScanMac(addr, keyBean)
+                        }
+                    }
+                    isLoadComplete.postValue(true)
+                    emit(true)
+                }
+            }
+        }
+    }
+
+    /**
+     * 打开充电并扫描蓝牙
+     */
+    suspend fun openChargeAndScanMac(addr: Byte, keyBean: DockBean.KeyBean): Boolean {
+        return suspendCancellableCoroutine<Boolean> { cont ->
+            LogUtil.i("设备录入-关闭充电:${addr},${keyBean.idx}")
+            ModBusController.controlKeyCharge(false, keyBean.isLeft, addr) {
+                ThreadUtils.runOnIO {
+                    delay(3000)
+                    LogUtil.i("设备录入-打开充电:${addr},${keyBean.idx}")
+                    ModBusController.controlKeyLockAndCharge(true, keyBean.isLeft, addr) {
+                        ThreadUtils.runOnIO {
+                            delay(3000)
+                            LogUtil.i("设备录入-开始扫描在线蓝牙Mac")
+                            BleConnectionManager.scanOnlineKeyLockMac { bleDevices ->
+                                LogUtil.i(
+                                    "设备录入-在线的蓝牙设备:${keyBean.rfid},${
+                                        bleDevices?.joinToString(
+                                            ","
+                                        ) { it.mac }
+                                    }"
+                                )
+                                if (bleDevices?.isEmpty() == true) {
+                                    ThreadUtils.runOnIO {
+                                        if (isDestroy) {
+                                            return@runOnIO
+                                        }
+                                        openChargeAndScanMac(addr, keyBean)
+                                    }
+                                } else {
+                                    LogUtil.i(
+                                        "设备录入-没有使用过的mac:${keyBean.rfid},${
+                                            bleDevices?.find {
+                                                it.mac !in newHardwareKeyBean.map { it.value }
+                                                    .flatten()
+                                                    .map { it.mac }
+                                            }?.mac
+                                        }"
+                                    )
+                                    keyBean.mac = bleDevices?.find {
+                                        it.mac !in newHardwareKeyBean.map { it.value }.flatten()
+                                            .map { it.mac }
+                                    }?.mac
+                                    cont.resume(true)
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fun unregisterInitListener() {
+        BusinessManager.unRegisterInitListener()
+    }
+
+    /**
+     * 设备录入挂起任务
+     */
+    private suspend fun deviceInputKeySuspend(keyNfc: String, keyMacAddress: String): Boolean {
+        return suspendCancellableCoroutine<Boolean> { cont ->
+            NetApi.deviceInputKey(keyNfc, keyMacAddress) {
+                cont.resume(it)
+                cont.cancel()
+            }
+        }
+    }
+
+    /**
+     * 设备录入挂起任务
+     */
+    private suspend fun deviceInputLockSuspend(cardNo: String): Boolean {
+        return suspendCancellableCoroutine<Boolean> { cont ->
+            NetApi.deviceInputLock(cardNo) {
+                cont.resume(it)
+                cont.cancel()
+            }
+        }
+    }
+
+    /**
+     * 设备录入(钥匙和挂锁)
+     * //todo 录入待完成
+     */
+    fun deviceInputData(dockData: MutableList<DockStatusBO>): LiveData<Triple<Boolean, Int, Int>> {
+        return liveData(Dispatchers.IO) {
+            val dockList = dockData.map { it.dockList }.flatten()
+            val deviceList =
+                ModBusController.dockList.filter { it.addr in dockList.map { it.address } }
+                    .map { it.deviceList }.flatten()
+            val lockDevice = deviceList.filter { it.type == DeviceConst.DEVICE_TYPE_LOCK }
+                .filterIsInstance<DockBean.LockBean>().filter { it.newHardware == true }
+            val keyDevice = deviceList.filter { it.type == DeviceConst.DEVICE_TYPE_KEY }
+                .filterIsInstance<DockBean.KeyBean>().filter { it.newHardware == true }
+            lockDevice.filter { it.rfid?.isNotEmpty() == true }.map { it.rfid }.forEach { rfid ->
+                deviceInputLockSuspend(rfid.toString())
+            }
+            keyDevice.filter { it.rfid?.isNotEmpty() == true && it.mac?.isNotEmpty() == true }
+                .forEach { keyDevice ->
+                    val isBind =
+                        deviceInputKeySuspend(keyDevice.rfid.toString(), keyDevice.mac.toString())
+                    if (isBind) {
+                        keyDevice.newHardware = false
+                    }
+                }
+            emit(
+                Triple(
+                    true,
+                    keyDevice.count { it.rfid?.isNotEmpty() == true && it.mac?.isNotEmpty() == true },
+                    lockDevice.count { it.rfid?.isNotEmpty() == true })
+            )
+        }
+    }
+}

+ 40 - 0
app/src/main/res/layout/item_rv_key_dock_device_input.xml

@@ -37,6 +37,16 @@
                 android:text="@string/new_device"
                 android:visibility="invisible" />
 
+            <TextView
+                android:id="@+id/tv_new_device_mac_1"
+                style="@style/CommonTextView"
+                android:layout_below="@+id/tv_new_device_1"
+                android:layout_alignLeft="@+id/iv_key_1"
+                android:layout_alignRight="@+id/iv_key_1"
+                android:layout_marginTop="5dp"
+                android:background="@drawable/common_btn_green_bg"
+                android:visibility="invisible" />
+
             <View
                 android:id="@+id/v_buckle_status_1"
                 android:layout_width="@dimen/common_status_circle_small"
@@ -69,6 +79,16 @@
                 android:text="@string/new_device"
                 android:visibility="invisible" />
 
+            <TextView
+                android:id="@+id/tv_new_device_mac_2"
+                style="@style/CommonTextView"
+                android:layout_below="@+id/tv_new_device_2"
+                android:layout_alignLeft="@+id/iv_key_2"
+                android:layout_alignRight="@+id/iv_key_2"
+                android:layout_marginTop="5dp"
+                android:background="@drawable/common_btn_green_bg"
+                android:visibility="invisible" />
+
             <View
                 android:id="@+id/v_buckle_status_2"
                 android:layout_width="@dimen/common_status_circle_small"
@@ -109,6 +129,16 @@
                 android:text="@string/new_device"
                 android:visibility="invisible" />
 
+            <TextView
+                android:id="@+id/tv_new_device_mac_3"
+                style="@style/CommonTextView"
+                android:layout_below="@+id/tv_new_device_3"
+                android:layout_alignLeft="@+id/iv_key_3"
+                android:layout_alignRight="@+id/iv_key_3"
+                android:layout_marginTop="5dp"
+                android:background="@drawable/common_btn_green_bg"
+                android:visibility="invisible" />
+
             <View
                 android:id="@+id/v_buckle_status_3"
                 android:layout_width="@dimen/common_status_circle_small"
@@ -141,6 +171,16 @@
                 android:text="@string/new_device"
                 android:visibility="invisible" />
 
+            <TextView
+                android:id="@+id/tv_new_device_mac_4"
+                style="@style/CommonTextView"
+                android:layout_below="@+id/tv_new_device_4"
+                android:layout_alignLeft="@+id/iv_key_4"
+                android:layout_alignRight="@+id/iv_key_4"
+                android:layout_marginTop="5dp"
+                android:background="@drawable/common_btn_green_bg"
+                android:visibility="invisible" />
+
             <View
                 android:id="@+id/v_buckle_status_4"
                 android:layout_width="@dimen/common_status_circle_small"