package com.grkj.iscs //todo 所有蓝牙包替换com.clj. -> com.clj. import android.content.Context import android.content.Intent import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.MutableLiveData import com.clj.fastble.BleManager import com.clj.fastble.data.BleDevice import com.clj.fastble.exception.BleException import com.google.gson.Gson import com.grkj.iscs.ble.BleBean import com.grkj.iscs.ble.BleCmdManager import com.grkj.iscs.ble.BleConnectionManager import com.grkj.iscs.ble.BleConst import com.grkj.iscs.ble.BleConst.STATUS_READY import com.grkj.iscs.ble.BleConst.STATUS_WORK import com.grkj.iscs.ble.BleUtil import com.grkj.iscs.ble.CustomBleWriteCallback import com.grkj.iscs.extentions.removeLeadingZeros import com.grkj.iscs.extentions.serialNo import com.grkj.iscs.extentions.startsWith import com.grkj.iscs.extentions.toHexStrings import com.grkj.iscs.modbus.DockBean import com.grkj.iscs.modbus.ModBusController import com.grkj.iscs.modbus.ModBusController.dockList import com.grkj.iscs.modbus.ModBusController.getOneKey import com.grkj.iscs.model.Constants.USER_TYPE_LOCKER import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_CARD import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_FINGERPRINT import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_KEY import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_LOCK import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_ELEC_LOCK_BOARD 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 import com.grkj.iscs.model.bo.WorkTicketSendBO import com.grkj.iscs.model.bo.WorkTicketSendBO.LockListBO import com.grkj.iscs.model.eventmsg.CurrentModeMsg import com.grkj.iscs.model.eventmsg.DeviceExceptionMsg import com.grkj.iscs.model.eventmsg.DeviceTakeUpdateMsg import com.grkj.iscs.model.eventmsg.LoadingMsg import com.grkj.iscs.model.eventmsg.MsgEvent import com.grkj.iscs.model.eventmsg.MsgEventConstants import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_CURRENT_MODE import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_DEVICE_EXCEPTION import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_DEVICE_TAKE_UPDATE import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_LOADING import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_SWITCH_COLLECTION_UPDATE import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_SWITCH_MODE import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_UPDATE_TICKET_PROGRESS import com.grkj.iscs.model.eventmsg.SwitchModeMsg import com.grkj.iscs.model.eventmsg.UpdateTicketProgressMsg import com.grkj.iscs.model.vo.dict.CommonDictRespVO import com.grkj.iscs.model.vo.hardware.CabinetSlotsRespVo import com.grkj.iscs.model.vo.hardware.SwitchListReqVO import com.grkj.iscs.model.vo.key.KeyPageRespVO import com.grkj.iscs.model.vo.lock.LockPageRespVO import com.grkj.iscs.model.vo.lock.LockTakeUpdateReqVO import com.grkj.iscs.model.vo.ticket.LockPointUpdateReqVO import com.grkj.iscs.model.vo.ticket.TicketDetailRespVO import com.grkj.iscs.util.ActivityUtils 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.activity.LoginActivity import com.grkj.iscs.view.base.BaseActivity import com.grkj.iscs.view.dialog.TipDialog import com.sik.sikcore.SIKCore import com.sik.sikcore.date.TimeUtils import com.sik.sikcore.thread.ThreadUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext import kotlin.coroutines.resume /** * 业务层管理 */ object BusinessManager { // 消息总线 val mEventBus = MutableLiveData() @JvmStatic @Volatile // 已连接的蓝牙钥匙集合 var deviceList: MutableList = mutableListOf() // Modbus数据页面监听 class DeviceListener( val key: Any, val callBack: (DockBean) -> Unit ) private val listeners = ArrayList() private var initListener: (() -> Unit)? = null // 归还设备是否需要登录 var NEED_AUTH = true // 归还设备是否需要登录及角色验证 var CAN_RETURN = true get() { val loginUser = SPUtils.getLoginUser(MyApplication.instance!!.applicationContext!!) return (NEED_AUTH && loginUser != null) || !NEED_AUTH } // 设备待取列表(需要报给后台的列表,等实际取完再上报) @JvmStatic val mDeviceTakeList = mutableListOf() // 是否是测试人员登录的 var isTestMode = false // 有问题的钥匙的列表 - rfid var mExceptionKeyList = mutableListOf() /** * 初始化消息总线 */ fun initMsgEventBus() { mEventBus.observeForever { LogUtil.i("msgEvent : $it") when (it.code) { // loading消息 MSG_EVENT_LOADING -> { Executor.runOnMain { val loadingMsg = it.data as LoadingMsg (ActivityUtils.currentActivity() as BaseActivity<*>).handleLoading( loadingMsg.isShow, loadingMsg.loadingText ) } } // 设备取出 MSG_EVENT_DEVICE_TAKE_UPDATE -> { handleDeviceTake(it.data as DeviceTakeUpdateMsg) } // 钥匙当前模式 MSG_EVENT_CURRENT_MODE -> { handleCurrentMode(it.data as CurrentModeMsg) } // 钥匙切换模式结果 MSG_EVENT_SWITCH_MODE -> { when ((it.data as SwitchModeMsg).job) { // 工作模式 1 -> { if (it.data.res == 1) { // 只能在这里断开,不能全部断开 BleManager.getInstance().disconnect(it.data.bleBean.bleDevice) // 打开钥匙卡扣 val keyBean = ModBusController.getKeyByMac(it.data.bleBean.bleDevice.mac) if (keyBean == null) { sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, "未找到钥匙信息", false) ) ) ToastUtils.tip(R.string.key_not_exists) } else { sendLoadingEventMsg(CommonUtils.getStr(R.string.take_out_key_tip)) val dock = ModBusController.getDockByKeyMac(it.data.bleBean.bleDevice.mac) keyBean.isReady = false ModBusController.controlKeyBuckle( true, keyBean.isLeft, dock?.addr ) ModBusController.updateKeyReadyStatus( it.data.bleBean.bleDevice.mac, false, 1 ) ToastUtils.tip(R.string.take_out_key) } } else { LogUtil.e("切换工作模式失败 : ${it.data.bleBean.bleDevice.mac}") Executor.delayOnMain(500) { switchWorkMode(it.data.bleBean.bleDevice, false) } } } // 待机模式 2 -> { if (it.data.res == 1) { ModBusController.updateKeyReadyStatus( it.data.bleBean.bleDevice.mac, true, 2 ) // 延时再次获取当前状态,触发handleCurrentMode里工作票下发状态检查 Executor.delayOnMain(500) { getCurrentStatus(1, it.data.bleBean.bleDevice) } } else { LogUtil.e("切换待机模式失败 : ${it.data.bleBean.bleDevice.mac}") Executor.delayOnMain(500) { switchReadyMode(it.data.bleBean.bleDevice) } } } } } MSG_EVENT_SWITCH_COLLECTION_UPDATE -> { ThreadUtils.runOnIO { val switchStatus = NetApi.getDictData(DictAndSystemConstants.KEY_SWITCH_STATUS) val switchListReqVOS = ModBusController.getSwitchData().map { SwitchListReqVO( it.idx.toString(), if (it.enabled) switchStatus?.find { it.dictLabel == "打开" }?.dictValue else switchStatus?.find { it.dictLabel == "关闭" }?.dictValue, TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT) ) } NetApi.updateSwitchList(switchListReqVOS) { LogUtil.i("开关更新完成") } } } } } } /** * 连接一把存在的可连接的钥匙 */ private fun connectExistsKey(exceptKeyMac: String) { ThreadUtils.runOnIO { // —— 串行请求1 & 2 —— val slotsPage = getSlotsPage() // —— 并行加载字典(或按需串行也行) —— val slotStatus = async { fetchDict(DictAndSystemConstants.KEY_SLOT_STATUS) } val keyStatus = async { fetchDict(DictAndSystemConstants.KEY_KEY_STATUS) } val slotType = async { fetchDict(DictAndSystemConstants.KEY_SLOT_TYPE) } // 等待字典加载完成 val slotStatusList = slotStatus.await() val keyStatusList = keyStatus.await() val slotTypeList = slotType.await() withContext(Dispatchers.Default) { val keyPage = withContext(Dispatchers.IO) { getKeyPage() } getOneKey( slotsPage?.records?.filter { it.slotType == slotTypeList.find { d -> d.dictLabel == "钥匙" }?.dictValue && it.status == slotStatusList.find { d -> d.dictLabel == "异常" }?.dictValue }?.toMutableList() ?: mutableListOf(), (keyPage?.records?.filter { it.exStatus == keyStatusList.find { d -> d.dictLabel == "异常" }?.dictValue } ?.map { it.keyNfc ?: "" }?.toMutableList() ?: mutableListOf()), mutableListOf(exceptKeyMac) ) } } } /****************************************** ModBus ******************************************/ /** * 链接底座 */ fun connectDock(isNeedInit: Boolean = false) { ModBusController.interruptReadTrashBinStatus(false) ModBusController.start(MyApplication.instance!!.applicationContext) ModBusController.unregisterListener(MyApplication.instance!!.applicationContext) if (isNeedInit) { ModBusController.initDevicesStatus() } } /** * 断开底座链接 */ fun disconnectDock() { ModBusController.stop() } /** * 注册状态监听 */ fun registerStatusListener(key: Any, listener: (DockBean) -> Unit) { listeners.add(DeviceListener(key, listener)) } /** * 注册初始化监听 */ fun registerInitListener(listener: () -> Unit) { this.initListener = listener } /** * 取消注册初始化监听 */ fun unRegisterInitListener() { this.initListener = null } /** * 取消注册状态监听 */ fun unregisterListener(key: Any) { val it = listeners.iterator() while (it.hasNext()) { if (it.next().key == key) { it.remove() } } } /** * 总的监听,做预处理,其余的所有监听均使用本监听处理后的数据,只允许调用一次 */ fun registerMainListener() { ModBusController.registerStatusListener(this) { res -> deviceStatusHandle(res) } } /** * 硬件状态 * 1、检测到有钥匙 * 2、上锁 * 3、开启充电 * 4、蓝牙连接 * 5、蓝牙数据通讯 */ private fun deviceStatusHandle(res: Any) { LogUtil.i("硬件状态:${(res as List).map { it.toHexStrings() }}") if (res.isEmpty() || res.any { it.isEmpty() }) { var tipStr = CommonUtils.getStr(R.string.no_response_board_exists) + " : " val addressList = mutableListOf() ModBusController.modBusManager?.mSlaveAddressList?.forEach { itDock -> if (res.none { it.isNotEmpty() && it[0] == itDock }) { addressList.add("0x${String.format("%02X", itDock)}") } } tipStr += addressList ToastUtils.tip(tipStr) } res.forEachIndexed { index, bytes -> val dockBean = ModBusController.updateStatus(bytes) ?: return@forEachIndexed ModBusController.isInitReady = true if (!CAN_RETURN) { return@forEachIndexed } when (dockBean.type) { DOCK_TYPE_KEY -> { dockBean.getKeyList().forEach { keyBean -> deviceKeyHandler(dockBean, keyBean) } } DOCK_TYPE_LOCK -> { dockBean.getLockList().forEach { lockBean -> deviceLockHandler(dockBean, lockBean) } } DOCK_TYPE_ELEC_LOCK_BOARD -> { // TODO 占位 } DOCK_TYPE_PORTABLE -> { // TODO 便携式待完善 dockBean.deviceList.forEach { deviceBean -> if (deviceBean.isExist) { when (deviceBean.type) { DEVICE_TYPE_KEY -> { deviceKeyHandler(dockBean, deviceBean as DockBean.KeyBean) } DEVICE_TYPE_LOCK -> { deviceLockHandler(dockBean, deviceBean as DockBean.LockBean) } DEVICE_TYPE_CARD -> { ModBusController.readPortalCaseCardRfid(dockBean.addr) { res -> if (res.size < 11) { LogUtil.e("Portal Case card rfid error") return@readPortalCaseCardRfid } val rfid = res.copyOfRange(3, 11).toHexStrings(false) .removeLeadingZeros() LogUtil.i("卡片RFID : $rfid") } } DEVICE_TYPE_FINGERPRINT -> { } } } } } } Executor.delayOnMain(200) { if (!ISCSDomainData.isDeviceRegistration) { listeners.forEach { it.callBack(dockBean) } } } } Executor.delayOnMain(200) { if (ISCSDomainData.isDeviceRegistration) { initListener?.invoke() } } } /** * 挂锁处理 */ private fun deviceLockHandler( dockBean: DockBean, lockBean: DockBean.LockBean ) { if (lockBean.isExist) { ModBusController.readLockRfid(dockBean.addr, lockBean.idx) { res -> if (res.size < 11) { LogUtil.e("Lock rfid error") return@readLockRfid } val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros() ModBusController.updateLockRfid( dockBean.addr, lockBean.idx, rfid ) ThreadUtils.runOnIO { val lockStatusReq = async { fetchDict(DictAndSystemConstants.KEY_PAD_LOCK_STATUS) } val slotStatus = async { fetchDict(DictAndSystemConstants.KEY_SLOT_STATUS) } val slotType = async { fetchDict(DictAndSystemConstants.KEY_SLOT_TYPE) } val slotsPageReq = async { getSlotsPage() } var lockStatus = lockStatusReq.await() val slotsPage = slotsPageReq.await() val slotStatusList = slotStatus.await() val slotTypeList = slotType.await() NetApi.getIsLockPage { lockData -> //锁rfid未异常正常请求锁数据,关锁 if (rfid in (lockData?.records?.filter { it.exStatus == lockStatus.find { it.dictLabel == "异常" }?.dictValue } ?.map { it.lockNfc }?.toMutableList() ?: mutableListOf()) ) { ToastUtils.tip( MyApplication.instance?.applicationContext!!.getString( R.string.lock_exception_tag ) ) } else if (slotsPage?.records?.filter { it.slotType == slotTypeList.find { d -> d.dictLabel == "锁" }?.dictValue && it.status == slotStatusList.find { d -> d.dictLabel == "异常" }?.dictValue } ?.find { it.row?.toInt() == dockBean.row && (lockBean.idx + 1) == it.col?.toInt() } != null) { ToastUtils.tip( MyApplication.instance?.applicationContext!!.getString( R.string.slot_exception_tag ) ) } else { NetApi.getLockInfo(rfid) { if (it != null) { // TODO 考虑快速拿取 ModBusController.controlLockBuckle( false, dockBean.addr, lockBean.idx ) { itRst -> if (itRst.isNotEmpty()) { // 上报锁具信息 NetApi.updateLockReturn( rfid, MyApplication.instance!!.serialNo() ) {} } } } } } } } } } else { LogUtil.i("挂锁取出-:${lockBean.rfid}") sendEventMsg( MsgEvent( MSG_EVENT_DEVICE_TAKE_UPDATE, DeviceTakeUpdateMsg(DEVICE_TYPE_LOCK, lockBean.rfid) ) ) } } private fun deviceKeyHandler(dockBean: DockBean, keyBean: DockBean.KeyBean) { if (keyBean.isExist) { // 放回钥匙,读取rfid ModBusController.readKeyRfid( dockBean.addr, if (keyBean.isLeft) 0 else 1 ) { isLeft, res -> if (!ISCSDomainData.isDeviceRegistration) { ModBusController.controlKeyCharge( true, keyBean.isLeft, dockBean.addr ) } if (res.size < 11) { LogUtil.e("Key rfid error") return@readKeyRfid } val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros() ThreadUtils.runOnIO { val slotStatus = async { fetchDict(DictAndSystemConstants.KEY_SLOT_STATUS) } val slotType = async { fetchDict(DictAndSystemConstants.KEY_SLOT_TYPE) } val slotsPageReq = async { getSlotsPage() } val keyStatusReq = async { fetchDict(DictAndSystemConstants.KEY_KEY_STATUS) } val keyPageReq = async { getKeyPage() } var keyStatus = keyStatusReq.await() var keyData = keyPageReq.await() val slotsPage = slotsPageReq.await() val slotStatusList = slotStatus.await() val slotTypeList = slotType.await() //锁钥匙未异常正常请求锁数据,关锁 if (rfid in (keyData?.records?.filter { it.exStatus == keyStatus.find { it.dictLabel == "异常" }?.dictValue } ?.map { it.keyNfc }?.toMutableList() ?: mutableListOf()) ) { ToastUtils.tip( MyApplication.instance?.applicationContext!!.getString( R.string.key_exception_tag ) ) } else if (slotsPage?.records?.filter { it.slotType == slotTypeList.find { d -> d.dictLabel == "钥匙" }?.dictValue && it.status == slotStatusList.find { d -> d.dictLabel == "异常" }?.dictValue } ?.find { it.row?.toInt() == dockBean.row && it.col?.toInt() == (dockBean.col + (if (keyBean.isLeft) 0 else 1) * 2) } != null) { ToastUtils.tip( MyApplication.instance?.applicationContext!!.getString( R.string.slot_exception_tag ) ) } else { ModBusController.updateKeyRfid( dockBean.addr, keyBean.isLeft, rfid ) // 放回钥匙,上锁 ModBusController.controlKeyBuckle( 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 ) ModBusController.updateKeyReadyStatus( it.macAddress, false, 5 ) } else { LogUtil.e("Get key info fail : $rfid") if (!ISCSDomainData.isDeviceRegistration) { ToastUtils.tip(R.string.get_key_info_fail) } ModBusController.controlKeyBuckle( true, keyBean.isLeft, dockBean.addr ) } } } } } } } else if (!keyBean.isCharging) {//增加充电判断,防止无线充电干扰锁仓状态导致判断为取出 // 移出待连监听集合,防止connectKey循环失败 keyBean.mac?.let { unregisterConnectListener(it) } sendEventMsg( MsgEvent( MSG_EVENT_DEVICE_TAKE_UPDATE, DeviceTakeUpdateMsg(DEVICE_TYPE_KEY, keyBean.rfid) ) ) } } /** * 更新所有锁仓状态 */ fun updateAllBuckleStatus(done: () -> Unit) { ModBusController.updateAllBuckleStatus(done) } /** * 更新开关状态 */ fun updateSwitchStatus(done: () -> Unit) { ModBusController.updateSwitchStatus(done) } /** * 钥匙归还提示确认弹框,当前策略:作业票未完成禁止归还钥匙 */ private fun showKeyReturnDialog(onConfirm: () -> Unit) { val ctx = ActivityUtils.currentActivity() as BaseActivity<*> val dlg = TipDialog(ctx) dlg.setTip(ctx.getString(R.string.key_return_tip)) dlg.setType(TipDialog.TYPE_CONFIRM) // 加个选择判断,如果是直接取消弹框而不是点击“确定”,当成确定 var state = 0 dlg.setConfirmListener { state = 1 onConfirm.invoke() } dlg.setOnDismissListener { if (state == 0) { onConfirm.invoke() } } dlg.show() } fun readLockBuckleStatus() { // TODO slaveIdx暂时写死,调试用 ModBusController.readBuckleStatus(true, 0) { type, res -> LogUtil.i("单slave卡扣状态 : $type - ${res.toHexStrings()}") when (type) { 0 -> { val isLeftLock = (res[4].toInt() shr 0) and 0x1 == 1 val isRightLock = (res[4].toInt() shr 4) and 0x1 == 1 LogUtil.i("锁具底座卡扣状态 : $isLeftLock - $isRightLock") } 1 -> { val tempList = mutableListOf() for (i in 0..7) { tempList.add((res[4].toInt() shr i) and 0x1 == 1) } LogUtil.i("锁具底座卡扣1-8状态 : $tempList") } 2 -> { val lock9Status = (res[4].toInt() shr 0) and 0x1 == 1 val lock10Status = (res[4].toInt() shr 1) and 0x1 == 1 LogUtil.i("锁具底座卡扣9、10状态 : $lock9Status - $lock10Status") } } } } fun readKeyBuckleStatus() { // TODO slaveIdx暂时写死,调试用 ModBusController.readBuckleStatus(false, 1) { type, res -> LogUtil.i("单slave卡扣状态 : $type - ${res.toHexStrings()}") // TODO 待验证 when (type) { 0 -> { val isLeftLock = (res[4].toInt() shr 0) and 0x1 == 1 val isRightLock = (res[4].toInt() shr 4) and 0x1 == 1 LogUtil.i("钥匙底座卡扣状态 : $isLeftLock - $isRightLock") } 1 -> { val tempList = mutableListOf() for (i in 0..7) { tempList.add((res[4].toInt() shr i) and 0x1 == 1) } LogUtil.i("锁具底座卡扣1-8状态 : $tempList") } 2 -> { val lock9Status = (res[4].toInt() shr 0) and 0x1 == 1 val lock10Status = (res[4].toInt() shr 1) and 0x1 == 1 LogUtil.i("锁具底座卡扣9、10状态 : $lock9Status - $lock10Status") } } } } // 1. 把 NetApi.get…Page 包成 suspend 函数 private suspend fun getSlotsPage(): CabinetSlotsRespVo? = suspendCancellableCoroutine { cont -> NetApi.getIsLockCabinetSlotsPage { slots -> cont.resume(slots) } } private suspend fun getLocksPage(): LockPageRespVO? = suspendCancellableCoroutine { cont -> NetApi.getIsLockPage { locks -> cont.resume(locks) } } private suspend fun getKeyPage(): KeyPageRespVO? = suspendCancellableCoroutine { cont -> NetApi.getIsKeyPage { keys -> cont.resume(keys) cont.cancel() } } // 2. 把原本同步的字典查询留在 IO 线程 private suspend fun fetchDict(key: String): List = withContext(Dispatchers.IO) { @Suppress("UNCHECKED_CAST") NetApi.getDictData(key) as List } // 3. 重写 checkEquipCount fun checkEquipCount( needLockCount: Int, isNeedKey: Boolean, callBack: (Pair?, MutableMap>) -> Unit ) { // 你可以改成接收 CoroutineScope 或者直接在全局 Scope 启动 ThreadUtils.runOnMain { sendLoadingEventMsg(MyApplication.instance?.applicationContext!!.getString(R.string.check_key_and_lock)) try { // —— 串行请求1 & 2 —— val slotsPage = getSlotsPage() val locksPage = getLocksPage() // —— 并行加载字典(或按需串行也行) —— val lockStatus = async { fetchDict(DictAndSystemConstants.KEY_PAD_LOCK_STATUS) } val slotStatus = async { fetchDict(DictAndSystemConstants.KEY_SLOT_STATUS) } val slotType = async { fetchDict(DictAndSystemConstants.KEY_SLOT_TYPE) } val keyStatus = async { fetchDict(DictAndSystemConstants.KEY_KEY_STATUS) } // 等待字典加载完成 val lockStatusList = lockStatus.await() val slotStatusList = slotStatus.await() val slotTypeList = slotType.await() val keyStatusList = keyStatus.await() // —— 在 Default 线程做计算密集操作 —— val lockMap = withContext(Dispatchers.Default) { ModBusController.getLocks( needLockCount, slotsPage?.records?.filter { it.slotType == slotTypeList.find { d -> d.dictLabel == "锁" }?.dictValue && it.status == slotStatusList.find { d -> d.dictLabel == "异常" }?.dictValue }?.toMutableList() ?: mutableListOf(), locksPage?.records?.filter { it.exStatus == lockStatusList.find { d -> d.dictLabel == "异常" }?.dictValue } ?.map { it.lockNfc ?: "" }?.toMutableList() ?: mutableListOf() ) } val actualLockCount = lockMap.values.sumBy { it.size } // 如果锁不够,提前清空并立刻返回 if (actualLockCount < needLockCount) { ToastUtils.tip( MyApplication.instance!!.getString(R.string.lock_is_not_enough) ) callBack(null, mutableMapOf()) return@runOnMain } // —— 如果需钥匙,再请求并计算 —— var keyPair: Pair? = null if (isNeedKey) { val keyPage = withContext(Dispatchers.IO) { getKeyPage() } keyPair = withContext(Dispatchers.Default) { getOneKey( slotsPage?.records?.filter { it.slotType == slotTypeList.find { d -> d.dictLabel == "钥匙" }?.dictValue && it.status == slotStatusList.find { d -> d.dictLabel == "异常" }?.dictValue }?.toMutableList() ?: mutableListOf(), keyPage?.records?.filter { it.exStatus == keyStatusList.find { d -> d.dictLabel == "异常" }?.dictValue } ?.map { it.keyNfc ?: "" }?.toMutableList() ?: mutableListOf() ) } if (keyPair == null) { ToastUtils.tip( MyApplication.instance!!.getString(R.string.no_available_key) ) } } // —— 全部计算完毕,在主线程一次性回调 —— callBack(keyPair, lockMap) } catch (e: Exception) { // 根据需求处理异常,或把异常信息也通过 callback 返回 sendLoadingEventMsg(null, false) e.printStackTrace() ToastUtils.tip("检查设备异常:${e.message}") } } } /** * 获取开关量数据 */ fun getSwitchData(): MutableList { return ModBusController.getSwitchData() } /****************************************** 蓝牙 ******************************************/ /******************************************蓝牙通用准备******************************************/ /** * 注册连接监听 */ fun registerConnectListener( mac: String, connectNow: Boolean = false, callBack: (( Boolean, BleBean? ) -> Unit)? = null ) { BleConnectionManager.registerConnectListener(mac, connectNow, callBack) } /** * 连接监听反注册 */ fun unregisterConnectListener(mac: String, bleBean: BleBean? = null) { BleConnectionManager.unregisterConnectListener(mac, bleBean) } /******************************************蓝牙通用准备结束******************************************/ fun getBleDeviceByMac(mac: String?): BleBean? { return deviceList.find { it.bleDevice.mac == mac } } fun getBleBeanByRfid(nfc: String?): BleBean? { nfc ?: return null ModBusController.getKeyByRfid(nfc)?.mac?.let { itMac -> return getBleDeviceByMac(itMac) } return null } /** * 下发工作票 */ private fun sendTicketBusiness( isLock: Boolean, mac: String, ticketDetail: TicketDetailRespVO, lockList: MutableList?, activity: AppCompatActivity, isNeedLoading: Boolean = false, ) { registerConnectListener(mac, true) { isDone, bleBean -> if (!isDone) { sendTicketBusiness(isLock, mac, ticketDetail, lockList, activity, isNeedLoading) return@registerConnectListener } if (bleBean == null) { // ToastUtils.tip(R.string.simple_key_is_not_connected) LogUtil.e("sendTicketBusiness fail : $mac, bleBean is null") return@registerConnectListener } // 单bleBean json赋值 bleBean.retryCount = 0 bleBean.ticketSend = generateTicketSendJson(isLock, ticketDetail, lockList) bleBean.ticketSend?.let { itJson -> sendTicketWithRetry(itJson, bleBean.bleDevice, isNeedLoading) } } } /** * 带重试的下发工作票,重试次数3,间隔500ms */ private fun sendTicketWithRetry( json: String, bleDevice: BleDevice, isNeedLoading: Boolean = false, maxRetries: Int = 3, delayMillis: Long = 500 ) { var retryCount = 0 fun attemptSend() { sendTicket(json, bleDevice, isNeedLoading) { sendRst -> if (!sendRst && retryCount < maxRetries) { retryCount++ // 等待一段时间后再次尝试 Executor.delayOnMain(delayMillis) { LogUtil.i("Retry attempt, mac : ${bleDevice.mac}, retryCount : $retryCount") attemptSend() } } } } attemptSend() } /** * 读取工作票完成情况 */ private fun getTicketStatusBusiness( mac: String, isNeedLoading: Boolean = false ) { registerConnectListener(mac, true) { isDone, bleBean -> if (isDone) { Executor.delayOnMain(500) { getTicketStatusWithRetry(bleBean!!.bleDevice, isNeedLoading) } } else { if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, null, false) ) ) } } } private fun getTicketStatusWithRetry( bleDevice: BleDevice, isNeedLoading: Boolean = false, maxRetries: Int = 3, delayMillis: Long = 500 ) { var retryCount = 0 fun attemptSend() { getTicketStatus(bleDevice, isNeedLoading) { sendRst -> if (!sendRst && retryCount < maxRetries) { retryCount++ // 等待一段时间后再次尝试 Executor.delayOnMain(delayMillis) { LogUtil.i("Retry attempt, mac : ${bleDevice.mac}, retryCount : $retryCount") attemptSend() } } } } attemptSend() } private fun sendTicket( jsonStr: String, bleDevice: BleDevice, isNeedLoading: Boolean = false, processCallback: ((Boolean) -> Unit)? = null ) { if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(true, CommonUtils.getStr(R.string.start_to_send_ticket), null) ) ) BleCmdManager.sendWorkTicket( jsonStr, bleDevice = bleDevice, callback = object : CustomBleWriteCallback() { override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { LogUtil.i("sendTicket success") if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(true, CommonUtils.getStr(R.string.sending_ticket), null) ) ) } override fun onWriteFailure(exception: BleException?) { LogUtil.e("sendTicket fail : ${bleDevice.mac}") if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, CommonUtils.getStr(R.string.send_ticket_fail), null) ) ) processCallback?.invoke(false) } }) } /** * 生成下发工作票Json * * @param vo 工作票详情 */ private fun generateTicketSendJson( isLock: Boolean, vo: TicketDetailRespVO, lockList: MutableList? ): String { LogUtil.i("generateTicketSendJson : $lockList") val bo = WorkTicketSendBO( cardNo = SPUtils.getLoginUser(MyApplication.instance!!.applicationContext)?.userCardList?.get( 0 ), ) CommonUtils.getDiffHours(vo.ticketEndTime)?.let { bo.effectiveTime = it } // 有配置则用配置,没用则填充默认密码 bo.password = SPUtils.getLoginUser(MyApplication.instance!!.applicationContext)?.keyCode ?: "123456" val dataBO = WorkTicketSendBO.DataBO( taskCode = vo.ticketId.toString(), codeId = 1 ) val taskList = ArrayList() vo.ticketPointsVOList?.let { itList -> itList.forEach { pointVO -> if (vo.noUnlockTicketPointsVOSet?.any { it.pointId == pointVO.pointId } == true && isLock == false) { return@forEach } val task = WorkTicketSendBO.DataBO.DataListBO( dataId = pointVO.pointId?.toInt(), equipRfidNo = pointVO.pointNfc, equipName = pointVO.pointName, target = if (isLock) 0 else 1 ) if (!isLock) { task.infoRfidNo = pointVO.lockNfc } pointVO.prePointId?.let { task.prevId = it.toInt() } // TODO partCode待补充 taskList.add(task) } } dataBO.dataList = taskList bo.data = mutableListOf(dataBO) if (isLock) { // TODO 挂锁数组 if (!lockList.isNullOrEmpty()) { bo.lockList = mutableListOf() lockList.forEachIndexed { index, s -> if (s.isNullOrEmpty()) { LogUtil.w("Lock nfc is null or empty") return@forEachIndexed } bo.lockList?.add(LockListBO(index + 1, s)) } } } // TODO partList 待补充 val jsonStr = Gson().toJson(bo) LogUtil.i("json : $jsonStr") return jsonStr } /** * 生成下空发工作票Json * * @param vo 工作票详情 */ fun generateEmptyTicketSendJson(): String { // 构造一个所有字段都为空/默认值的 WorkTicketSendBO val bo = WorkTicketSendBO( cardNo = "", // 空卡号 effectiveTime = 0, // 默认有效时长 password = "" // 空密码 ).apply { // data 列表留空 data = mutableListOf() // lockList 留空(如果字段非空,再设置为 emptyList()) lockList = mutableListOf() } // 转成 JSON 并返回 val jsonStr = Gson().toJson(bo) LogUtil.i("generateEmptyTicketJson: $jsonStr") return jsonStr } fun handleRsp( bleBean: BleBean, byteArray: ByteArray, isNeedLoading: Boolean = false, prepareDoneCallBack: ((Boolean, BleBean?) -> Unit)? ) { // TODO Token校验 // val len = byteArray[2].toInt() // val token = byteArray.copyOfRange(len + 7, len + 11) // if (token.contentEquals(bleBean.token)) { // LogUtil.i("Token is right") // } else { // LogUtil.e("Token is wrong") // } when { // 获取令牌 byteArray.startsWith(BleConst.RSP_GET_TOKEN) -> BleCmdManager.handleToken( bleBean.bleDevice, byteArray ) { isSuccess -> if (isSuccess) { prepareDoneCallBack?.invoke(true, bleBean) } } // 工作模式切换 byteArray.startsWith(BleConst.RSP_SWITCH_MODE) -> { handleSwitchModeResult(byteArray, isNeedLoading) { res, job -> sendEventMsg( MsgEvent( MSG_EVENT_SWITCH_MODE, SwitchModeMsg(job.toInt(), res.toInt(), bleBean) ) ) } } // 工作票下发 byteArray.startsWith(BleConst.RSP_SEND_WORK_TICKET) -> handleWorkTicketResult( bleBean, byteArray, isNeedLoading ) // 获取设备当前状态 byteArray.startsWith(BleConst.RSP_CURRENT_STATUS) -> BleCmdManager.handleCurrentStatus( byteArray ) { sendEventMsg(MsgEvent(MSG_EVENT_CURRENT_MODE, CurrentModeMsg(bleBean, it))) } // 获取设备工作票完成情况 byteArray.startsWith(BleConst.RSP_WORK_TICKET_RESULT) && byteArray[3] == 0x02.toByte() -> handleTicketStatus( bleBean.bleDevice, byteArray, isNeedLoading ) byteArray.startsWith(BleConst.RSP_POWER_STATUS) -> { val power = byteArray[4].toInt() if (power < 50) {//如果电量小于50就打开仓位充电 ModBusController.controlKeyCharge(true, bleBean.bleDevice.mac) { LogUtil.i("钥匙: ${bleBean.bleDevice.mac} 开始充电") } } else { ModBusController.controlKeyCharge(false, bleBean.bleDevice.mac) { LogUtil.i("钥匙: ${bleBean.bleDevice.mac} 关闭充电") } } } } } /** * 工作模式切换结果 * job : 0x01:工作模式 0x02:待机模式 * res : 0x01:成功 0x02:失败 */ private fun handleSwitchModeResult( byteArray: ByteArray, isNeedLoading: Boolean = false, callBack: ((Byte, Byte) -> Unit)? = null ) { BleCmdManager.handleSwitchModeResult(byteArray) { job, res -> if (res == 0x01.toByte() && job == 0x01.toByte()) { LogUtil.i("切换工作模式成功") if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, "切换工作模式成功", null) ) ) } else if (res == 0x01.toByte() && job == 0x02.toByte()) { LogUtil.i("切换待机模式成功") if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, "切换待机模式成功", null) ) ) } else { LogUtil.e("切换模式失败 : ${job.toInt()} - ${res.toInt()}") if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, null, null) ) ) } callBack?.invoke(res, job) } } /** * 工作票下发结果 * res:0x00:成功 0x01:失败 0x02:传输超时 0x0D:当前IDX超出范围 0x0E:当前数据CRC校验失败 0x14:JSON结构错误 0x63:未知错误 */ private fun handleWorkTicketResult( bleBean: BleBean, byteArray: ByteArray, isNeedLoading: Boolean = false ) { BleCmdManager.handleWorkTicketResult(bleBean, byteArray) { isSuccess, rst -> if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, null, null) ) ) if (isSuccess) { // 下发完毕,切换工作模式 LogUtil.i("工作票下发完毕") if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(true, "切换钥匙为工作模式", null) ) ) Executor.delayOnIO(800) { //切换到工作模式 switchWorkMode(bleBean.bleDevice, isNeedLoading) } } else { sendLoadingEventMsg(null, false) if (bleBean.retryCount < 3) { Executor.delayOnMain(500) { bleBean.retryCount++ sendLoadingEventMsg(MyApplication.instance!!.getString(R.string.start_to_send_ticket)) sendTicketWithRetry(bleBean.ticketSend!!, bleBean.bleDevice, isNeedLoading) } } else { ToastUtils.tip(R.string.send_ticket_fail) LogUtil.e("Send ticket fail") ModBusController.getKeyByMac(bleBean.bleDevice.mac)?.let { itKey -> mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_KEY && it.nfc == itKey.rfid } } } } } } /** * 获取当前钥匙的状态 */ fun getCurrentStatus( from: Int, bleDevice: BleDevice, retryCount: Int = 0, timeoutCallBack: ((Boolean) -> Unit)? = null ) { LogUtil.i("getCurrentStatus - ${bleDevice.mac} - from : $from") var isTimeout = true // 加1秒防止早于onWriteFailure开始处理导致多次处理 Executor.delayOnMain((BleUtil.OPERATE_TIMEOUT + 1).toLong()) { if (isTimeout) { LogUtil.e("getCurrentStatus timeout : mac = ${bleDevice.mac}, retryCount = $retryCount") if (retryCount > 0) { Executor.delayOnMain(1000) { getCurrentStatus(from, bleDevice, retryCount - 1, timeoutCallBack) } } else { ModBusController.getKeyByMac(bleDevice.mac)?.rfid?.let { addExceptionKey(it) timeoutCallBack?.invoke(true) } } } } BleCmdManager.getCurrentStatus(bleDevice, object : CustomBleWriteCallback() { override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { LogUtil.i("getCurrentStatus success : ${bleDevice.mac}") isTimeout = false timeoutCallBack?.invoke(false) } override fun onWriteFailure(exception: BleException?) { LogUtil.i("getCurrentStatus fail : ${bleDevice.mac}") isTimeout = false Executor.delayOnMain(1000) { getCurrentStatus(from, bleDevice, timeoutCallBack = timeoutCallBack) } } }) } /** * 获取电池电量 */ fun getBatteryPower(bleDevice: BleDevice) { LogUtil.i("获取电池电量:${bleDevice.mac}") BleCmdManager.getPower(bleDevice.mac, object : CustomBleWriteCallback() { override fun onWriteSuccess(p0: Int, p1: Int, p2: ByteArray?) { LogUtil.i("发送获取电池电量命令成功:${bleDevice.mac}") } override fun onWriteFailure(p0: BleException?) { ThreadUtils.runOnIODelayed(500) { LogUtil.i("发送获取电池电量命令失败:${bleDevice.mac}") getBatteryPower(bleDevice) } } }) } /** * 切换工作模式 */ private fun switchWorkMode(bleDevice: BleDevice, isNeedLoading: Boolean = false) { LogUtil.i("switchWorkMode - ${bleDevice.mac}") BleCmdManager.switchMode(STATUS_WORK, bleDevice, object : CustomBleWriteCallback() { override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { LogUtil.i("switch mode work success : ${bleDevice.mac}") } override fun onWriteFailure(exception: BleException?) { LogUtil.e("switch mode work fail : ${exception?.code} - ${exception?.description}") Executor.delayOnMain(500) { switchWorkMode(bleDevice, isNeedLoading) } } }) } /** * 处理工作票完成情况 */ private fun handleTicketStatus( bleDevice: BleDevice, byteArray: ByteArray, isNeedLoading: Boolean = false ) { BleCmdManager.handleTicketStatus(bleDevice, byteArray) { ticketJson -> if (ticketJson.isNullOrEmpty()) { return@handleTicketStatus } if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, "工作票完成状态读取完成", null) ) ) LogUtil.i("Get ticket status complete : ${bleDevice.mac}") // TD:Ticket Done if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, "TD$ticketJson}", true) ) ) val workTicketGetBO = try { Gson().fromJson(ticketJson, WorkTicketGetBO::class.java) } catch (e: Exception) { null } if (workTicketGetBO == null) { ToastUtils.tip(R.string.ticket_data_error) return@handleTicketStatus } // 判断workTicketGetBO里是否有未完成的 ThreadUtils.runOnIO { val finishedStatus = workTicketGetBO.hasFinished() LogUtil.i("作业票结束情况:${finishedStatus}") if (finishedStatus.first) { Executor.delayOnMain(500) { handleKeyReturn(bleDevice, workTicketGetBO, finishedStatus.second) } } else { // 当前策略:作业票未完成禁止归还钥匙 withContext(Dispatchers.Main) { showKeyReturnDialog { sendLoadingEventMsg(null, false) ToastUtils.tip(R.string.continue_the_ticket) BleManager.getInstance().disconnect(bleDevice) // 打开卡扣,防止初始化的时候选择不处理钥匙导致无法使用 val dock = ModBusController.getDockByKeyMac(bleDevice.mac) val keyBean = dock?.getKeyList()?.find { it.mac == bleDevice.mac } keyBean?.let { ModBusController.controlKeyBuckle(true, keyBean.isLeft, dock.addr) } } } } } } } /** * ticketFinished主要是后端的作业票是否已经结束,结束了,就直接修改状态就好了 */ private fun handleKeyReturn( bleDevice: BleDevice, workTicketGetBO: WorkTicketGetBO?, ticketFinished: Boolean ) { val dock = ModBusController.getDockByKeyMac(bleDevice.mac) val keyBean = dock?.getKeyList()?.find { it.mac == bleDevice.mac } keyBean?.let { ModBusController.controlKeyBuckle(false, keyBean.isLeft, dock.addr) } if (ticketFinished) { mDeviceTakeList.removeIf { it.nfc == keyBean?.rfid } switchReadyMode(bleDevice) } else { // 上报隔离点状态 val keyNfc = ModBusController.getKeyByMac(bleDevice.mac)?.rfid ?: "key rfid lost" workTicketGetBO?.data?.forEach { data -> val updateList = mutableListOf() data.dataList?.forEach { dataListDTO -> data.taskCode?.toLong()?.let { SPUtils.returnKey(it) } val updateVO = LockPointUpdateReqVO( data.taskCode?.toLong(), dataListDTO.infoRfidNo, dataListDTO.equipRfidNo, keyNfc, dataListDTO.target, dataListDTO.status ) updateList.add(updateVO) } sendLoadingEventMsg(null, false) if (CAN_RETURN) { // 上报点位钥匙绑定 NetApi.updateLockPointBatch(updateList) { isSuccess, msg, code -> LogUtil.i("还锁操作:${isSuccess},${msg},${code}") if (isSuccess) { // 上报钥匙归还 NetApi.updateKeyReturn( data.taskCode?.toLong()!!, keyNfc!!, MyApplication.instance!!.serialNo() ) { isSuccess, msg, code -> if (!isSuccess && msg != MyApplication.instance?.applicationContext!!.getString( R.string.ticket_lost ) ) { SPUtils.saveUpdateKeyReturn( MyApplication.instance!!, UpdateKeyReturnBO(data.taskCode?.toLong()!!, keyNfc!!) ) if (msg == MyApplication.instance?.applicationContext!!.getString( R.string.ticket_lost ) ) { sendEventMsg( MsgEvent( MsgEventConstants.MSG_EVENT_TICKET_FINISHED, null ) ) } ToastUtils.tip(R.string.key_return_success) } else { ToastUtils.tip(R.string.key_return_success) } } data.taskCode?.toLong()?.let { sendEventMsg( MsgEvent( MSG_EVENT_UPDATE_TICKET_PROGRESS, UpdateTicketProgressMsg(it) ) ) } // 确认归还,切换为待机模式 switchReadyMode(bleDevice) } else { ThreadUtils.runOnMain { // 当前策略:作业票未完成禁止归还钥匙 showKeyReturnDialog { sendLoadingEventMsg(null, false) ToastUtils.tip(R.string.continue_the_ticket) BleManager.getInstance().disconnect(bleDevice) // 打开卡扣,防止初始化的时候选择不处理钥匙导致无法使用 if (workTicketGetBO.data?.all { it.dataList?.all { it.closed == 1 } == true } == true) { workTicketGetBO.data?.firstOrNull()?.taskCode?.toLong() ?.let { checkStepAndTicketDetailThenSendTicket( it, bleDevice.mac ) } } else { val dock = ModBusController.getDockByKeyMac(bleDevice.mac) val keyBean = dock?.getKeyList()?.find { it.mac == bleDevice.mac } keyBean?.let { ModBusController.controlKeyBuckle( true, keyBean.isLeft, dock.addr ) } } } } SPUtils.clearUpdateKeyReturn(MyApplication.instance!!) SPUtils.clearUpdateLockPoint(MyApplication.instance!!) } } } else { SPUtils.saveUpdateLockPoint(MyApplication.instance!!, updateList) SPUtils.saveUpdateKeyReturn( MyApplication.instance!!, UpdateKeyReturnBO(data.taskCode?.toLong()!!, keyNfc!!) ) // 保存待发数据,切换为待机模式 switchReadyMode(bleDevice) } } } } /** * 处理虚拟钥匙取出,如果作业的全部点位已经上锁更新钥匙的状态使用 */ fun handleVirtualKeyGive(taskCode: Long, keyNfc: String, done: () -> Unit) { // 上报钥匙归还 NetApi.updateKeyTake(taskCode, keyNfc, MyApplication.instance!!.serialNo()) { isSuccess -> if (isSuccess) { done() } } } /** * 处理虚拟钥匙归还,如果作业的全部点位已经上锁更新钥匙的状态使用 */ fun handleVirtualKeyReturn(taskCode: Long, keyNfc: String, done: () -> Unit) { // 上报钥匙归还 NetApi.updateKeyReturn( taskCode, keyNfc, MyApplication.instance!!.serialNo() ) { isSuccess, msg, code -> if (!isSuccess && msg != MyApplication.instance?.applicationContext!!.getString( R.string.ticket_lost ) ) { SPUtils.saveUpdateKeyReturn( MyApplication.instance!!, UpdateKeyReturnBO(taskCode, keyNfc) ) if (msg == MyApplication.instance?.applicationContext!!.getString( R.string.ticket_lost ) ) { sendEventMsg( MsgEvent( MsgEventConstants.MSG_EVENT_TICKET_FINISHED, null ) ) } } else { done() sendEventMsg( MsgEvent( MSG_EVENT_UPDATE_TICKET_PROGRESS, UpdateTicketProgressMsg(taskCode) ) ) } } } fun switchReadyMode(bleDevice: BleDevice) { BleCmdManager.switchMode(STATUS_READY, bleDevice, object : CustomBleWriteCallback() { override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { LogUtil.i("switch mode ready success : ${bleDevice.mac}") } override fun onWriteFailure(exception: BleException?) { LogUtil.e("switch mode ready fail : ${bleDevice.mac}") Executor.delayOnMain(300) { switchReadyMode(bleDevice) } } }) } /** * 获取工作票完成情况 */ private fun getTicketStatus( bleDevice: BleDevice, isNeedLoading: Boolean = false, processCallback: ((Boolean) -> Unit)? = null ) { if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(true, "开始获取工作票", null) ) ) BleCmdManager.getTicketStatus(bleDevice, object : CustomBleWriteCallback() { override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) { if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, "工作票获取成功", null) ) ) LogUtil.i("getTicketStatus success") } override fun onWriteFailure(exception: BleException?) { if (isNeedLoading) sendEventMsg( MsgEvent( MSG_EVENT_LOADING, LoadingMsg(false, "工作票获取失败", false) ) ) processCallback?.invoke(false) LogUtil.e("getTicketStatus fail") } }) } /** * 添加待更新取出状态的设备 */ fun addDeviceTake(deviceType: Int, ticketId: Long, nfc: String?) { LogUtil.i("addDeviceTake : $deviceType - $ticketId - $nfc") mDeviceTakeList.removeIf { it.deviceType == deviceType && it.nfc == nfc } mDeviceTakeList.add(DeviceTakeUpdateBO(deviceType, ticketId, nfc!!)) } fun removeDeviceTake(deviceType: Int, nfc: String?) { LogUtil.i("removeDeviceTake : $deviceType - $nfc") mDeviceTakeList.removeIf { it.deviceType == deviceType && it.nfc == nfc } } private fun handleDeviceTake(deviceTakeUpdateBO: DeviceTakeUpdateMsg, rfid: String? = null) { LogUtil.i("$deviceTakeUpdateBO") when (deviceTakeUpdateBO.deviceType) { // 钥匙 0 -> { 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, MyApplication.instance?.serialNo()!! ) { isSuccess -> if (isSuccess) { mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_KEY && it.nfc == info.nfc } sendEventMsg( MsgEvent( MSG_EVENT_UPDATE_TICKET_PROGRESS, UpdateTicketProgressMsg(info.ticketId) ) ) //钥匙取出之后重新再连一把钥匙待机 ModBusController.getKeyByRfid( info.nfc )?.mac?.let { unregisterConnectListener(it) } //待机数不够就再连一把,但不能是原来那把 if (BleManager.getInstance().allConnectedDevice.size < BleConst.MAX_KEY_STAND_BY) { ModBusController.getKeyByRfid( info.nfc )?.mac?.let { connectExistsKey( it ) } } } } } ?: sendLoadingEventMsg(null, false) } // 挂锁 1 -> { mDeviceTakeList.find { it.deviceType == DEVICE_TYPE_LOCK && it.nfc == deviceTakeUpdateBO.nfc } ?.let { info -> NetApi.updateLockTake( mutableListOf( LockTakeUpdateReqVO( info.ticketId, info.nfc, MyApplication.instance?.serialNo()!! ) ) ) { isSuccess -> Executor.runOnMain { if (isSuccess == false) { LogUtil.e("Lock take report fail") ToastUtils.tip(R.string.lock_take_report_fail) SPUtils.saveTicketTakeLockException(info.ticketId) mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_LOCK && it.nfc == info.nfc } mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_KEY && it.ticketId == info.ticketId } sendLoadingEventMsg(null, false) return@runOnMain } // 检查是不是要发钥匙了 mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_LOCK && it.nfc == info.nfc } // 检查当前工作票是否取完挂锁 if (mDeviceTakeList.any { it.deviceType == DEVICE_TYPE_LOCK && it.ticketId == info.ticketId }) { LogUtil.i("Waiting all locks to take out") sendLoadingEventMsg( MyApplication.instance?.applicationContext?.getString( R.string.take_out_lock_tip, mDeviceTakeList.count { it.deviceType == DEVICE_TYPE_LOCK && it.ticketId == info.ticketId }) ) ToastUtils.tip(R.string.take_out_rest_locks) return@runOnMain } else { LogUtil.i("All locks are taken") sendLoadingEventMsg(null, false) } if (SPUtils.getTicketTakeLockException(info.ticketId)) { ToastUtils.tip(R.string.current_ticket_report_lock_take_exception_tip) return@runOnMain } // 检查有无当前工作票的钥匙 mDeviceTakeList.find { it.deviceType == DEVICE_TYPE_KEY && it.ticketId == info.ticketId } ?.let { itKey -> sendLoadingEventMsg( MyApplication.instance?.applicationContext!!.getString( R.string.ble_connecting ) ) handleGiveKey(itKey) } } } } } } } /** * 分配钥匙 */ private fun handleGiveKey(deviceTakeUpdateBO: DeviceTakeUpdateBO) { getBleDeviceByMac(ModBusController.getKeyByRfid(deviceTakeUpdateBO.nfc)?.mac)?.let { getCurrentStatus( 2, getBleDeviceByMac(ModBusController.getKeyByRfid(deviceTakeUpdateBO.nfc)?.mac)!!.bleDevice ) { if (!it) { return@getCurrentStatus } LogUtil.w("handleGiveKey timeout") removeDeviceTake(DEVICE_TYPE_KEY, deviceTakeUpdateBO.nfc) checkEquipCount(0, true) { keyPair, lockMap -> if (keyPair == null) { ThreadUtils.runOnMain { val tipDialog = TipDialog(SIKCore.getApplication()) tipDialog.setTip( SIKCore.getApplication().getString(R.string.key_take_error_tip) ) tipDialog.setConfirmListener { tipDialog.dismiss() sendEventMsg( MsgEvent( MSG_EVENT_DEVICE_EXCEPTION, DeviceExceptionMsg(DEVICE_TYPE_KEY, deviceTakeUpdateBO.nfc) ) ) } tipDialog.show() } } else { addDeviceTake( DEVICE_TYPE_KEY, deviceTakeUpdateBO.ticketId, keyPair.second?.rfid!! ) handleGiveKey( DeviceTakeUpdateBO( DEVICE_TYPE_KEY, deviceTakeUpdateBO.ticketId, keyPair.second?.rfid!! ) ) } } } } ?: run { ThreadUtils.runOnMain { val tipDialog = TipDialog(SIKCore.getApplication()) tipDialog.setTip(SIKCore.getApplication().getString(R.string.key_take_error_tip)) tipDialog.setConfirmListener { tipDialog.dismiss() sendEventMsg( MsgEvent( MSG_EVENT_DEVICE_EXCEPTION, DeviceExceptionMsg(DEVICE_TYPE_KEY, deviceTakeUpdateBO.nfc) ) ) } tipDialog.show() } } } /** * 根据当前模式进行处理 */ private fun handleCurrentMode(currentModeMsg: CurrentModeMsg) { when (currentModeMsg.mode) { // 工作模式 0x01.toByte() -> { // 读工作票 getTicketStatusBusiness(currentModeMsg.bleBean.bleDevice.mac) } // 待机模式 0x02.toByte() -> { // 根据情况看是否需要下发工作票 ModBusController.getKeyByMac(currentModeMsg.bleBean.bleDevice.mac)?.let { key -> // 判断是否有待取的钥匙 val updateBo = mDeviceTakeList.find { it.deviceType == DEVICE_TYPE_KEY && key.rfid == it.nfc } if (mDeviceTakeList.any { it.deviceType == DEVICE_TYPE_LOCK && it.ticketId == updateBo?.ticketId }) { //todo 如果有钥匙待取但是对应的作业票的锁还有的,就不发 return } updateBo?.let { itBO -> NetApi.getStepDetail(itBO.ticketId) { var step = 0 it?.filter { it.stepStatus == "1" } ?.maxByOrNull { it.stepIndex!! }?.stepIndex?.let { step = it } NetApi.getTicketDetail(itBO.ticketId) { ticketDetail, _ -> if (ticketDetail == null) { return@getTicketDetail } val role = ticketDetail?.ticketUserVOList?.find { it.userId == SPUtils.getLoginUser(MyApplication.instance?.applicationContext!!)?.userId && it.userType == USER_TYPE_LOCKER } if (role == null) { ToastUtils.tip(R.string.you_are_not_locker_tip) return@getTicketDetail } if (step == 4) { // 上锁工作票 sendTicketBusiness( true, currentModeMsg.bleBean.bleDevice.mac, ticketDetail, ticketDetail.ticketLockVOList?.filter { it.lockStatus != "2" } ?.map { it.lockNfc }?.toMutableList(), ActivityUtils.currentActivity() as BaseActivity<*>, true ) } else if (step == 7) { // 解锁工作票 sendTicketBusiness( false, currentModeMsg.bleBean.bleDevice.mac, ticketDetail, null, ActivityUtils.currentActivity() as BaseActivity<*>, true ) } } } } ?: let { ModBusController.updateKeyReadyStatus( currentModeMsg.bleBean.bleDevice.mac, true, 4 ) sendLoadingEventMsg(null, false) //连上之后没有工作票要下发就断开 看是否还有设备等待连接,没有就不断开,有就让路,一般是初始化的时候 if (BleConnectionManager.hasConnectWait()) { BleManager.getInstance().disconnect(currentModeMsg.bleBean.bleDevice) } } } } // 故障模式 0x03.toByte() -> { // TODO 上报? ToastUtils.tip( "${currentModeMsg.bleBean.bleDevice.mac} : " + "${CommonUtils.getStr(R.string.key_is_in_failure_mode)}" ) } } } /** * 检查步骤和作业票详情并且下发作业票 */ private fun checkStepAndTicketDetailThenSendTicket(ticketId: Long, mac: String) { NetApi.getStepDetail(ticketId) { var step = 0 it?.filter { it.stepStatus == "1" } ?.maxByOrNull { it.stepIndex!! }?.stepIndex?.let { step = it } NetApi.getTicketDetail(ticketId) { ticketDetail, _ -> if (ticketDetail == null) { return@getTicketDetail } val role = ticketDetail?.ticketUserVOList?.find { it.userId == SPUtils.getLoginUser(MyApplication.instance?.applicationContext!!)?.userId && it.userType == USER_TYPE_LOCKER } if (role == null) { ToastUtils.tip(R.string.you_are_not_locker_tip) return@getTicketDetail } if (step == 4) { // 上锁工作票 sendTicketBusiness( true, mac, ticketDetail, ticketDetail.ticketLockVOList?.filter { it.lockStatus != "2" } ?.map { it.lockNfc }?.toMutableList(), ActivityUtils.currentActivity() as BaseActivity<*>, true ) } else if (step == 7) { // 解锁工作票 sendTicketBusiness( false, mac, ticketDetail, null, ActivityUtils.currentActivity() as BaseActivity<*>, true ) } } } } fun submitKeyData(context: Context) { if (!CAN_RETURN) return val updateList = SPUtils.getUpdateLockPoint(context) if (updateList.isNotEmpty()) { NetApi.updateLockPointBatch(updateList) { isSuccess, msg, code -> LogUtil.i("submitKeyData还锁操作:${isSuccess},${msg},${code}") if (isSuccess || code == 500) { SPUtils.clearUpdateLockPoint(context) SPUtils.clearUpdateKeyReturn(context) } } } val returnList = SPUtils.getUpdateKeyReturn(context).filter { it.keyNfc.isNotEmpty() }.toMutableList() if (returnList.isEmpty()) { return } val itemsToRemove = returnList.toList() var count = 0 itemsToRemove.forEach { itData -> NetApi.updateKeyReturn( itData.ticketId, itData.keyNfc, context.serialNo() ) { isSuccess, msg, code -> count++ if (isSuccess || msg == MyApplication.instance?.applicationContext!!.getString( R.string.ticket_lost ) ) { returnList.remove(itData) getBleBeanByRfid(itData.keyNfc)?.bleDevice?.let { switchReadyMode(it) } if (msg == MyApplication.instance?.applicationContext!!.getString( R.string.ticket_lost ) ) { sendEventMsg( MsgEvent( MsgEventConstants.MSG_EVENT_TICKET_FINISHED, null ) ) } } if (count == itemsToRemove.size) { if (returnList.isEmpty()) { SPUtils.clearUpdateKeyReturn(context) } else { returnList.forEach { SPUtils.saveUpdateKeyReturn(context, it) } } } } } } fun sendLoadingEventMsg(str: String?, isShow: Boolean = true) { sendEventMsg(MsgEvent(MSG_EVENT_LOADING, LoadingMsg(isShow, str, false))) } fun logout(context: Context) { reConnectKey() NetApi.logout() // 关所有有设备的卡扣 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 val noLockIdxList = dockBean.getLockList().filter { !it.isExist }.map { it.idx } as MutableList ModBusController.controlLockBuckle(false, dockBean.addr, hasLockIdxList) ModBusController.controlLockBuckle(true, dockBean.addr, noLockIdxList) } dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE } .forEach { dockBean -> dockBean.getKeyList().forEach { key -> if (key.isExist) { NetApi.getKeyInfo(key.rfid.toString()) { if (it != null && !it.macAddress.isNullOrEmpty()) { ModBusController.updateKeyMac( dockBean.addr, key.isLeft, it.macAddress ) } else { ModBusController.controlKeyBuckle( true, key.isLeft, dockBean.addr ) } } } else { ModBusController.controlKeyBuckle(true, key.isLeft, dockBean.addr) } } } sendLoadingEventMsg(null, false) context.startActivity(Intent(context, LoginActivity::class.java)) } /** * 钥匙重新连接,清除内部作业票,清除所有代取设备重新分配 */ private fun reConnectKey() { val keyList = mDeviceTakeList.filter { it.deviceType == DEVICE_TYPE_KEY } // 不拿的设备不归你,下次登录重新按需分配 // 尽早clear,防止触发handleCurrentMode导致重新下发作业票 mDeviceTakeList.clear() // 连接后直接切换待机模式,让钥匙作业票失效并且重新准备完毕 keyList.forEach { val mac = ModBusController.getKeyByRfid(it.nfc)?.mac if (mac == null) { NetApi.getKeyInfo(it.nfc) { keyInfo -> keyInfo?.macAddress?.let { itMac -> registerConnectListener(itMac) { isDone, bleBean -> if (isDone && bleBean != null) { switchReadyMode(bleBean.bleDevice) } } } } } else { registerConnectListener(mac) { isDone, bleBean -> if (isDone && bleBean != null) { switchReadyMode(bleBean.bleDevice) } } } } } /** * 强制使用setValue,防止postValue造成数据丢失 */ fun sendEventMsg(msgEvent: MsgEvent) { Executor.runOnMain { mEventBus.value = msgEvent } } fun addExceptionKey(rfid: String) { LogUtil.w("addExceptionKey: $rfid") if (mExceptionKeyList.contains(rfid)) { return } mExceptionKeyList.add(rfid) } fun removeExceptionKey(key: String) { LogUtil.i("removeExceptionKey: $key") mExceptionKeyList.remove(key) } }