BusinessManager.kt 85 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031
  1. package com.grkj.iscs
  2. //todo 所有蓝牙包替换com.clj. -> com.clj.
  3. import android.content.Context
  4. import android.content.Intent
  5. import androidx.appcompat.app.AppCompatActivity
  6. import androidx.lifecycle.MutableLiveData
  7. import com.clj.fastble.BleManager
  8. import com.clj.fastble.data.BleDevice
  9. import com.clj.fastble.exception.BleException
  10. import com.google.gson.Gson
  11. import com.grkj.iscs.ble.BleBean
  12. import com.grkj.iscs.ble.BleCmdManager
  13. import com.grkj.iscs.ble.BleConnectionManager
  14. import com.grkj.iscs.ble.BleConst
  15. import com.grkj.iscs.ble.BleConst.STATUS_READY
  16. import com.grkj.iscs.ble.BleConst.STATUS_WORK
  17. import com.grkj.iscs.ble.BleUtil
  18. import com.grkj.iscs.ble.CustomBleWriteCallback
  19. import com.grkj.iscs.extentions.removeLeadingZeros
  20. import com.grkj.iscs.extentions.serialNo
  21. import com.grkj.iscs.extentions.startsWith
  22. import com.grkj.iscs.extentions.toHexStrings
  23. import com.grkj.iscs.modbus.DockBean
  24. import com.grkj.iscs.modbus.ModBusController
  25. import com.grkj.iscs.modbus.ModBusController.dockList
  26. import com.grkj.iscs.modbus.ModBusController.getOneKey
  27. import com.grkj.iscs.model.Constants.USER_TYPE_LOCKER
  28. import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_CARD
  29. import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_FINGERPRINT
  30. import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_KEY
  31. import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_LOCK
  32. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_ELEC_LOCK_BOARD
  33. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_KEY
  34. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_LOCK
  35. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_PORTABLE
  36. import com.grkj.iscs.model.DictAndSystemConstants
  37. import com.grkj.iscs.model.ISCSDomainData
  38. import com.grkj.iscs.model.bo.DeviceTakeUpdateBO
  39. import com.grkj.iscs.model.bo.UpdateKeyReturnBO
  40. import com.grkj.iscs.model.bo.WorkTicketGetBO
  41. import com.grkj.iscs.model.bo.WorkTicketSendBO
  42. import com.grkj.iscs.model.bo.WorkTicketSendBO.LockListBO
  43. import com.grkj.iscs.model.eventmsg.CurrentModeMsg
  44. import com.grkj.iscs.model.eventmsg.DeviceExceptionMsg
  45. import com.grkj.iscs.model.eventmsg.DeviceTakeUpdateMsg
  46. import com.grkj.iscs.model.eventmsg.LoadingMsg
  47. import com.grkj.iscs.model.eventmsg.MsgEvent
  48. import com.grkj.iscs.model.eventmsg.MsgEventConstants
  49. import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_CURRENT_MODE
  50. import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_DEVICE_EXCEPTION
  51. import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_DEVICE_TAKE_UPDATE
  52. import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_LOADING
  53. import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_SWITCH_COLLECTION_UPDATE
  54. import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_SWITCH_MODE
  55. import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_UPDATE_TICKET_PROGRESS
  56. import com.grkj.iscs.model.eventmsg.SwitchModeMsg
  57. import com.grkj.iscs.model.eventmsg.UpdateTicketProgressMsg
  58. import com.grkj.iscs.model.vo.dict.CommonDictRespVO
  59. import com.grkj.iscs.model.vo.hardware.CabinetSlotsRespVo
  60. import com.grkj.iscs.model.vo.hardware.SwitchListReqVO
  61. import com.grkj.iscs.model.vo.key.KeyPageRespVO
  62. import com.grkj.iscs.model.vo.lock.LockPageRespVO
  63. import com.grkj.iscs.model.vo.lock.LockTakeUpdateReqVO
  64. import com.grkj.iscs.model.vo.ticket.LockPointUpdateReqVO
  65. import com.grkj.iscs.model.vo.ticket.TicketDetailRespVO
  66. import com.grkj.iscs.util.ActivityUtils
  67. import com.grkj.iscs.util.CommonUtils
  68. import com.grkj.iscs.util.Executor
  69. import com.grkj.iscs.util.NetApi
  70. import com.grkj.iscs.util.SPUtils
  71. import com.grkj.iscs.util.ToastUtils
  72. import com.grkj.iscs.util.log.LogUtil
  73. import com.grkj.iscs.view.activity.LoginActivity
  74. import com.grkj.iscs.view.base.BaseActivity
  75. import com.grkj.iscs.view.dialog.TipDialog
  76. import com.sik.sikcore.SIKCore
  77. import com.sik.sikcore.date.TimeUtils
  78. import com.sik.sikcore.thread.ThreadUtils
  79. import kotlinx.coroutines.Dispatchers
  80. import kotlinx.coroutines.async
  81. import kotlinx.coroutines.suspendCancellableCoroutine
  82. import kotlinx.coroutines.withContext
  83. import kotlin.coroutines.resume
  84. /**
  85. * 业务层管理
  86. */
  87. object BusinessManager {
  88. // 消息总线
  89. val mEventBus = MutableLiveData<MsgEvent>()
  90. @JvmStatic
  91. @Volatile
  92. // 已连接的蓝牙钥匙集合
  93. var deviceList: MutableList<BleBean> = mutableListOf()
  94. // Modbus数据页面监听
  95. class DeviceListener(
  96. val key: Any, val callBack: (DockBean) -> Unit
  97. )
  98. private val listeners = ArrayList<DeviceListener>()
  99. private var initListener: (() -> Unit)? = null
  100. // 归还设备是否需要登录
  101. var NEED_AUTH = true
  102. // 归还设备是否需要登录及角色验证
  103. var CAN_RETURN = true
  104. get() {
  105. val loginUser = SPUtils.getLoginUser(MyApplication.instance!!.applicationContext!!)
  106. return (NEED_AUTH && loginUser != null) || !NEED_AUTH
  107. }
  108. // 设备待取列表(需要报给后台的列表,等实际取完再上报)
  109. @JvmStatic
  110. val mDeviceTakeList = mutableListOf<DeviceTakeUpdateBO>()
  111. // 是否是测试人员登录的
  112. var isTestMode = false
  113. // 有问题的钥匙的列表 - rfid
  114. var mExceptionKeyList = mutableListOf<String>()
  115. /**
  116. * 初始化消息总线
  117. */
  118. fun initMsgEventBus() {
  119. mEventBus.observeForever {
  120. LogUtil.i("msgEvent : $it")
  121. when (it.code) {
  122. // loading消息
  123. MSG_EVENT_LOADING -> {
  124. Executor.runOnMain {
  125. val loadingMsg = it.data as LoadingMsg
  126. (ActivityUtils.currentActivity() as BaseActivity<*>).handleLoading(
  127. loadingMsg.isShow, loadingMsg.loadingText
  128. )
  129. }
  130. }
  131. // 设备取出
  132. MSG_EVENT_DEVICE_TAKE_UPDATE -> {
  133. handleDeviceTake(it.data as DeviceTakeUpdateMsg)
  134. }
  135. // 钥匙当前模式
  136. MSG_EVENT_CURRENT_MODE -> {
  137. handleCurrentMode(it.data as CurrentModeMsg)
  138. }
  139. // 钥匙切换模式结果
  140. MSG_EVENT_SWITCH_MODE -> {
  141. when ((it.data as SwitchModeMsg).job) {
  142. // 工作模式
  143. 1 -> {
  144. if (it.data.res == 1) {
  145. // 只能在这里断开,不能全部断开
  146. BleManager.getInstance().disconnect(it.data.bleBean.bleDevice)
  147. // 打开钥匙卡扣
  148. val keyBean =
  149. ModBusController.getKeyByMac(it.data.bleBean.bleDevice.mac)
  150. if (keyBean == null) {
  151. sendEventMsg(
  152. MsgEvent(
  153. MSG_EVENT_LOADING,
  154. LoadingMsg(false, "未找到钥匙信息", false)
  155. )
  156. )
  157. ToastUtils.tip(R.string.key_not_exists)
  158. } else {
  159. sendLoadingEventMsg(CommonUtils.getStr(R.string.take_out_key_tip))
  160. val dock =
  161. ModBusController.getDockByKeyMac(it.data.bleBean.bleDevice.mac)
  162. keyBean.isReady = false
  163. ModBusController.controlKeyBuckle(
  164. true, keyBean.isLeft, dock?.addr
  165. )
  166. ModBusController.updateKeyReadyStatus(
  167. it.data.bleBean.bleDevice.mac, false, 1
  168. )
  169. ToastUtils.tip(R.string.take_out_key)
  170. }
  171. } else {
  172. LogUtil.e("切换工作模式失败 : ${it.data.bleBean.bleDevice.mac}")
  173. Executor.delayOnMain(500) {
  174. switchWorkMode(it.data.bleBean.bleDevice, false)
  175. }
  176. }
  177. }
  178. // 待机模式
  179. 2 -> {
  180. if (it.data.res == 1) {
  181. ModBusController.updateKeyReadyStatus(
  182. it.data.bleBean.bleDevice.mac, true, 2
  183. )
  184. // 延时再次获取当前状态,触发handleCurrentMode里工作票下发状态检查
  185. Executor.delayOnMain(500) {
  186. getCurrentStatus(1, it.data.bleBean.bleDevice)
  187. }
  188. } else {
  189. LogUtil.e("切换待机模式失败 : ${it.data.bleBean.bleDevice.mac}")
  190. Executor.delayOnMain(500) {
  191. switchReadyMode(it.data.bleBean.bleDevice)
  192. }
  193. }
  194. }
  195. }
  196. }
  197. MSG_EVENT_SWITCH_COLLECTION_UPDATE -> {
  198. ThreadUtils.runOnIO {
  199. val switchStatus =
  200. NetApi.getDictData(DictAndSystemConstants.KEY_SWITCH_STATUS)
  201. val switchListReqVOS = ModBusController.getSwitchData().map {
  202. SwitchListReqVO(
  203. it.idx.toString(),
  204. if (it.enabled) switchStatus?.find { it.dictLabel == "打开" }?.dictValue else switchStatus?.find { it.dictLabel == "关闭" }?.dictValue,
  205. TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
  206. )
  207. }
  208. NetApi.updateSwitchList(switchListReqVOS) {
  209. LogUtil.i("开关更新完成")
  210. }
  211. }
  212. }
  213. }
  214. }
  215. }
  216. /**
  217. * 连接一把存在的可连接的钥匙
  218. */
  219. private fun connectExistsKey(exceptKeyMac: String) {
  220. ThreadUtils.runOnIO {
  221. // —— 串行请求1 & 2 ——
  222. val slotsPage = getSlotsPage()
  223. // —— 并行加载字典(或按需串行也行) ——
  224. val slotStatus =
  225. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_SLOT_STATUS) }
  226. val keyStatus =
  227. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_KEY_STATUS) }
  228. val slotType =
  229. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_SLOT_TYPE) }
  230. // 等待字典加载完成
  231. val slotStatusList = slotStatus.await()
  232. val keyStatusList = keyStatus.await()
  233. val slotTypeList = slotType.await()
  234. withContext(Dispatchers.Default) {
  235. val keyPage = withContext(Dispatchers.IO) { getKeyPage() }
  236. getOneKey(
  237. slotsPage?.records?.filter {
  238. it.slotType == slotTypeList.find { d -> d.dictLabel == "钥匙" }?.dictValue && it.status == slotStatusList.find { d -> d.dictLabel == "异常" }?.dictValue
  239. }?.toMutableList() ?: mutableListOf(),
  240. (keyPage?.records?.filter { it.exStatus == keyStatusList.find { d -> d.dictLabel == "异常" }?.dictValue }
  241. ?.map { it.keyNfc ?: "" }?.toMutableList() ?: mutableListOf()),
  242. mutableListOf(exceptKeyMac)
  243. )
  244. }
  245. }
  246. }
  247. /****************************************** ModBus ******************************************/
  248. /**
  249. * 链接底座
  250. */
  251. fun connectDock(isNeedInit: Boolean = false) {
  252. ModBusController.interruptReadTrashBinStatus(false)
  253. ModBusController.start(MyApplication.instance!!.applicationContext)
  254. ModBusController.unregisterListener(MyApplication.instance!!.applicationContext)
  255. if (isNeedInit) {
  256. ModBusController.initDevicesStatus()
  257. }
  258. }
  259. /**
  260. * 断开底座链接
  261. */
  262. fun disconnectDock() {
  263. ModBusController.stop()
  264. }
  265. /**
  266. * 注册状态监听
  267. */
  268. fun registerStatusListener(key: Any, listener: (DockBean) -> Unit) {
  269. listeners.add(DeviceListener(key, listener))
  270. }
  271. /**
  272. * 注册初始化监听
  273. */
  274. fun registerInitListener(listener: () -> Unit) {
  275. this.initListener = listener
  276. }
  277. /**
  278. * 取消注册初始化监听
  279. */
  280. fun unRegisterInitListener() {
  281. this.initListener = null
  282. }
  283. /**
  284. * 取消注册状态监听
  285. */
  286. fun unregisterListener(key: Any) {
  287. val it = listeners.iterator()
  288. while (it.hasNext()) {
  289. if (it.next().key == key) {
  290. it.remove()
  291. }
  292. }
  293. }
  294. /**
  295. * 总的监听,做预处理,其余的所有监听均使用本监听处理后的数据,只允许调用一次
  296. */
  297. fun registerMainListener() {
  298. ModBusController.registerStatusListener(this) { res ->
  299. deviceStatusHandle(res)
  300. }
  301. }
  302. /**
  303. * 硬件状态
  304. * 1、检测到有钥匙
  305. * 2、上锁
  306. * 3、开启充电
  307. * 4、蓝牙连接
  308. * 5、蓝牙数据通讯
  309. */
  310. private fun deviceStatusHandle(res: Any) {
  311. LogUtil.i("硬件状态:${(res as List<ByteArray>).map { it.toHexStrings() }}")
  312. if (res.isEmpty() || res.any { it.isEmpty() }) {
  313. var tipStr = CommonUtils.getStr(R.string.no_response_board_exists) + " : "
  314. val addressList = mutableListOf<String>()
  315. ModBusController.modBusManager?.mSlaveAddressList?.forEach { itDock ->
  316. if (res.none { it.isNotEmpty() && it[0] == itDock }) {
  317. addressList.add("0x${String.format("%02X", itDock)}")
  318. }
  319. }
  320. tipStr += addressList
  321. ToastUtils.tip(tipStr)
  322. }
  323. res.forEachIndexed { index, bytes ->
  324. val dockBean = ModBusController.updateStatus(bytes) ?: return@forEachIndexed
  325. ModBusController.isInitReady = true
  326. if (!CAN_RETURN) {
  327. return@forEachIndexed
  328. }
  329. when (dockBean.type) {
  330. DOCK_TYPE_KEY -> {
  331. dockBean.getKeyList().forEach { keyBean ->
  332. deviceKeyHandler(dockBean, keyBean)
  333. }
  334. }
  335. DOCK_TYPE_LOCK -> {
  336. dockBean.getLockList().forEach { lockBean ->
  337. deviceLockHandler(dockBean, lockBean)
  338. }
  339. }
  340. DOCK_TYPE_ELEC_LOCK_BOARD -> {
  341. // TODO 占位
  342. }
  343. DOCK_TYPE_PORTABLE -> {
  344. // TODO 便携式待完善
  345. dockBean.deviceList.forEach { deviceBean ->
  346. if (deviceBean.isExist) {
  347. when (deviceBean.type) {
  348. DEVICE_TYPE_KEY -> {
  349. deviceKeyHandler(dockBean, deviceBean as DockBean.KeyBean)
  350. }
  351. DEVICE_TYPE_LOCK -> {
  352. deviceLockHandler(dockBean, deviceBean as DockBean.LockBean)
  353. }
  354. DEVICE_TYPE_CARD -> {
  355. ModBusController.readPortalCaseCardRfid(dockBean.addr) { res ->
  356. if (res.size < 11) {
  357. LogUtil.e("Portal Case card rfid error")
  358. return@readPortalCaseCardRfid
  359. }
  360. val rfid = res.copyOfRange(3, 11).toHexStrings(false)
  361. .removeLeadingZeros()
  362. LogUtil.i("卡片RFID : $rfid")
  363. }
  364. }
  365. DEVICE_TYPE_FINGERPRINT -> {
  366. }
  367. }
  368. }
  369. }
  370. }
  371. }
  372. Executor.delayOnMain(200) {
  373. if (!ISCSDomainData.isDeviceRegistration) {
  374. listeners.forEach { it.callBack(dockBean) }
  375. }
  376. }
  377. }
  378. Executor.delayOnMain(200) {
  379. if (ISCSDomainData.isDeviceRegistration) {
  380. initListener?.invoke()
  381. }
  382. }
  383. }
  384. /**
  385. * 挂锁处理
  386. */
  387. private fun deviceLockHandler(
  388. dockBean: DockBean,
  389. lockBean: DockBean.LockBean
  390. ) {
  391. if (lockBean.isExist) {
  392. ModBusController.readLockRfid(dockBean.addr, lockBean.idx) { res ->
  393. if (res.size < 11) {
  394. LogUtil.e("Lock rfid error")
  395. return@readLockRfid
  396. }
  397. val rfid =
  398. res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
  399. ModBusController.updateLockRfid(
  400. dockBean.addr, lockBean.idx, rfid
  401. )
  402. ThreadUtils.runOnIO {
  403. val lockStatusReq =
  404. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_PAD_LOCK_STATUS) }
  405. val slotStatus =
  406. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_SLOT_STATUS) }
  407. val slotType =
  408. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_SLOT_TYPE) }
  409. val slotsPageReq = async { getSlotsPage() }
  410. var lockStatus = lockStatusReq.await()
  411. val slotsPage = slotsPageReq.await()
  412. val slotStatusList = slotStatus.await()
  413. val slotTypeList = slotType.await()
  414. NetApi.getIsLockPage { lockData ->
  415. //锁rfid未异常正常请求锁数据,关锁
  416. if (rfid in (lockData?.records?.filter { it.exStatus == lockStatus.find { it.dictLabel == "异常" }?.dictValue }
  417. ?.map { it.lockNfc }?.toMutableList()
  418. ?: mutableListOf())
  419. ) {
  420. ToastUtils.tip(
  421. MyApplication.instance?.applicationContext!!.getString(
  422. R.string.lock_exception_tag
  423. )
  424. )
  425. } else if (slotsPage?.records?.filter {
  426. it.slotType == slotTypeList.find { d -> d.dictLabel == "锁" }?.dictValue && it.status == slotStatusList.find { d -> d.dictLabel == "异常" }?.dictValue
  427. }
  428. ?.find { it.row?.toInt() == dockBean.row && (lockBean.idx + 1) == it.col?.toInt() } != null) {
  429. ToastUtils.tip(
  430. MyApplication.instance?.applicationContext!!.getString(
  431. R.string.slot_exception_tag
  432. )
  433. )
  434. } else {
  435. NetApi.getLockInfo(rfid) {
  436. if (it != null) {
  437. // TODO 考虑快速拿取
  438. ModBusController.controlLockBuckle(
  439. false, dockBean.addr, lockBean.idx
  440. ) { itRst ->
  441. if (itRst.isNotEmpty()) {
  442. // 上报锁具信息
  443. NetApi.updateLockReturn(
  444. rfid,
  445. MyApplication.instance!!.serialNo()
  446. ) {}
  447. }
  448. }
  449. }
  450. }
  451. }
  452. }
  453. }
  454. }
  455. } else {
  456. LogUtil.i("挂锁取出-:${lockBean.rfid}")
  457. sendEventMsg(
  458. MsgEvent(
  459. MSG_EVENT_DEVICE_TAKE_UPDATE,
  460. DeviceTakeUpdateMsg(DEVICE_TYPE_LOCK, lockBean.rfid)
  461. )
  462. )
  463. }
  464. }
  465. private fun deviceKeyHandler(dockBean: DockBean, keyBean: DockBean.KeyBean) {
  466. if (keyBean.isExist) {
  467. // 放回钥匙,读取rfid
  468. ModBusController.readKeyRfid(
  469. dockBean.addr, if (keyBean.isLeft) 0 else 1
  470. ) { isLeft, res ->
  471. if (!ISCSDomainData.isDeviceRegistration) {
  472. ModBusController.controlKeyCharge(
  473. true, keyBean.isLeft, dockBean.addr
  474. )
  475. }
  476. if (res.size < 11) {
  477. LogUtil.e("Key rfid error")
  478. return@readKeyRfid
  479. }
  480. val rfid =
  481. res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
  482. ThreadUtils.runOnIO {
  483. val slotStatus =
  484. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_SLOT_STATUS) }
  485. val slotType =
  486. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_SLOT_TYPE) }
  487. val slotsPageReq = async { getSlotsPage() }
  488. val keyStatusReq =
  489. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_KEY_STATUS) }
  490. val keyPageReq = async { getKeyPage() }
  491. var keyStatus = keyStatusReq.await()
  492. var keyData = keyPageReq.await()
  493. val slotsPage = slotsPageReq.await()
  494. val slotStatusList = slotStatus.await()
  495. val slotTypeList = slotType.await()
  496. //锁钥匙未异常正常请求锁数据,关锁
  497. if (rfid in (keyData?.records?.filter { it.exStatus == keyStatus.find { it.dictLabel == "异常" }?.dictValue }
  498. ?.map { it.keyNfc }?.toMutableList()
  499. ?: mutableListOf())
  500. ) {
  501. ToastUtils.tip(
  502. MyApplication.instance?.applicationContext!!.getString(
  503. R.string.key_exception_tag
  504. )
  505. )
  506. } else if (slotsPage?.records?.filter {
  507. it.slotType == slotTypeList.find { d -> d.dictLabel == "钥匙" }?.dictValue && it.status == slotStatusList.find { d -> d.dictLabel == "异常" }?.dictValue
  508. }
  509. ?.find { it.row?.toInt() == dockBean.row && it.col?.toInt() == (dockBean.col + (if (keyBean.isLeft) 0 else 1) * 2) } != null) {
  510. ToastUtils.tip(
  511. MyApplication.instance?.applicationContext!!.getString(
  512. R.string.slot_exception_tag
  513. )
  514. )
  515. } else {
  516. ModBusController.updateKeyRfid(
  517. dockBean.addr, keyBean.isLeft, rfid
  518. )
  519. // 放回钥匙,上锁
  520. ModBusController.controlKeyBuckle(
  521. false, keyBean.isLeft, dockBean.addr
  522. ) {
  523. NetApi.getKeyInfo(rfid) {
  524. ModBusController.updateKeyNewHardware(
  525. dockBean.addr, true, it == null
  526. )
  527. if (it != null && !it.macAddress.isNullOrEmpty()) {
  528. ModBusController.updateKeyMac(
  529. dockBean.addr, keyBean.isLeft, it.macAddress
  530. )
  531. ModBusController.updateKeyReadyStatus(
  532. it.macAddress, false, 5
  533. )
  534. } else {
  535. LogUtil.e("Get key info fail : $rfid")
  536. if (!ISCSDomainData.isDeviceRegistration) {
  537. ToastUtils.tip(R.string.get_key_info_fail)
  538. }
  539. ModBusController.controlKeyBuckle(
  540. true, keyBean.isLeft, dockBean.addr
  541. )
  542. }
  543. }
  544. }
  545. }
  546. }
  547. }
  548. } else if (!keyBean.isCharging) {//增加充电判断,防止无线充电干扰锁仓状态导致判断为取出
  549. // 移出待连监听集合,防止connectKey循环失败
  550. keyBean.mac?.let {
  551. unregisterConnectListener(it)
  552. }
  553. sendEventMsg(
  554. MsgEvent(
  555. MSG_EVENT_DEVICE_TAKE_UPDATE,
  556. DeviceTakeUpdateMsg(DEVICE_TYPE_KEY, keyBean.rfid)
  557. )
  558. )
  559. }
  560. }
  561. /**
  562. * 更新所有锁仓状态
  563. */
  564. fun updateAllBuckleStatus(done: () -> Unit) {
  565. ModBusController.updateAllBuckleStatus(done)
  566. }
  567. /**
  568. * 更新开关状态
  569. */
  570. fun updateSwitchStatus(done: () -> Unit) {
  571. ModBusController.updateSwitchStatus(done)
  572. }
  573. /**
  574. * 钥匙归还提示确认弹框,当前策略:作业票未完成禁止归还钥匙
  575. */
  576. private fun showKeyReturnDialog(onConfirm: () -> Unit) {
  577. val ctx = ActivityUtils.currentActivity() as BaseActivity<*>
  578. val dlg = TipDialog(ctx)
  579. dlg.setTip(ctx.getString(R.string.key_return_tip))
  580. dlg.setType(TipDialog.TYPE_CONFIRM)
  581. // 加个选择判断,如果是直接取消弹框而不是点击“确定”,当成确定
  582. var state = 0
  583. dlg.setConfirmListener {
  584. state = 1
  585. onConfirm.invoke()
  586. }
  587. dlg.setOnDismissListener {
  588. if (state == 0) {
  589. onConfirm.invoke()
  590. }
  591. }
  592. dlg.show()
  593. }
  594. fun readLockBuckleStatus() {
  595. // TODO slaveIdx暂时写死,调试用
  596. ModBusController.readBuckleStatus(true, 0) { type, res ->
  597. LogUtil.i("单slave卡扣状态 : $type - ${res.toHexStrings()}")
  598. when (type) {
  599. 0 -> {
  600. val isLeftLock = (res[4].toInt() shr 0) and 0x1 == 1
  601. val isRightLock = (res[4].toInt() shr 4) and 0x1 == 1
  602. LogUtil.i("锁具底座卡扣状态 : $isLeftLock - $isRightLock")
  603. }
  604. 1 -> {
  605. val tempList = mutableListOf<Boolean>()
  606. for (i in 0..7) {
  607. tempList.add((res[4].toInt() shr i) and 0x1 == 1)
  608. }
  609. LogUtil.i("锁具底座卡扣1-8状态 : $tempList")
  610. }
  611. 2 -> {
  612. val lock9Status = (res[4].toInt() shr 0) and 0x1 == 1
  613. val lock10Status = (res[4].toInt() shr 1) and 0x1 == 1
  614. LogUtil.i("锁具底座卡扣9、10状态 : $lock9Status - $lock10Status")
  615. }
  616. }
  617. }
  618. }
  619. fun readKeyBuckleStatus() {
  620. // TODO slaveIdx暂时写死,调试用
  621. ModBusController.readBuckleStatus(false, 1) { type, res ->
  622. LogUtil.i("单slave卡扣状态 : $type - ${res.toHexStrings()}")
  623. // TODO 待验证
  624. when (type) {
  625. 0 -> {
  626. val isLeftLock = (res[4].toInt() shr 0) and 0x1 == 1
  627. val isRightLock = (res[4].toInt() shr 4) and 0x1 == 1
  628. LogUtil.i("钥匙底座卡扣状态 : $isLeftLock - $isRightLock")
  629. }
  630. 1 -> {
  631. val tempList = mutableListOf<Boolean>()
  632. for (i in 0..7) {
  633. tempList.add((res[4].toInt() shr i) and 0x1 == 1)
  634. }
  635. LogUtil.i("锁具底座卡扣1-8状态 : $tempList")
  636. }
  637. 2 -> {
  638. val lock9Status = (res[4].toInt() shr 0) and 0x1 == 1
  639. val lock10Status = (res[4].toInt() shr 1) and 0x1 == 1
  640. LogUtil.i("锁具底座卡扣9、10状态 : $lock9Status - $lock10Status")
  641. }
  642. }
  643. }
  644. }
  645. // 1. 把 NetApi.get…Page 包成 suspend 函数
  646. private suspend fun getSlotsPage(): CabinetSlotsRespVo? = suspendCancellableCoroutine { cont ->
  647. NetApi.getIsLockCabinetSlotsPage { slots ->
  648. cont.resume(slots)
  649. }
  650. }
  651. private suspend fun getLocksPage(): LockPageRespVO? = suspendCancellableCoroutine { cont ->
  652. NetApi.getIsLockPage { locks ->
  653. cont.resume(locks)
  654. }
  655. }
  656. private suspend fun getKeyPage(): KeyPageRespVO? = suspendCancellableCoroutine { cont ->
  657. NetApi.getIsKeyPage { keys ->
  658. cont.resume(keys)
  659. cont.cancel()
  660. }
  661. }
  662. // 2. 把原本同步的字典查询留在 IO 线程
  663. private suspend fun <T> fetchDict(key: String): List<T> = withContext(Dispatchers.IO) {
  664. @Suppress("UNCHECKED_CAST") NetApi.getDictData(key) as List<T>
  665. }
  666. // 3. 重写 checkEquipCount
  667. fun checkEquipCount(
  668. needLockCount: Int,
  669. isNeedKey: Boolean,
  670. callBack: (Pair<Byte, DockBean.KeyBean?>?, MutableMap<Byte, MutableList<DockBean.LockBean>>) -> Unit
  671. ) {
  672. // 你可以改成接收 CoroutineScope 或者直接在全局 Scope 启动
  673. ThreadUtils.runOnMain {
  674. sendLoadingEventMsg(MyApplication.instance?.applicationContext!!.getString(R.string.check_key_and_lock))
  675. try {
  676. // —— 串行请求1 & 2 ——
  677. val slotsPage = getSlotsPage()
  678. val locksPage = getLocksPage()
  679. // —— 并行加载字典(或按需串行也行) ——
  680. val lockStatus =
  681. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_PAD_LOCK_STATUS) }
  682. val slotStatus =
  683. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_SLOT_STATUS) }
  684. val slotType =
  685. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_SLOT_TYPE) }
  686. val keyStatus =
  687. async { fetchDict<CommonDictRespVO>(DictAndSystemConstants.KEY_KEY_STATUS) }
  688. // 等待字典加载完成
  689. val lockStatusList = lockStatus.await()
  690. val slotStatusList = slotStatus.await()
  691. val slotTypeList = slotType.await()
  692. val keyStatusList = keyStatus.await()
  693. // —— 在 Default 线程做计算密集操作 ——
  694. val lockMap = withContext(Dispatchers.Default) {
  695. ModBusController.getLocks(
  696. needLockCount,
  697. slotsPage?.records?.filter {
  698. it.slotType == slotTypeList.find { d -> d.dictLabel == "锁" }?.dictValue && it.status == slotStatusList.find { d -> d.dictLabel == "异常" }?.dictValue
  699. }?.toMutableList() ?: mutableListOf(),
  700. locksPage?.records?.filter { it.exStatus == lockStatusList.find { d -> d.dictLabel == "异常" }?.dictValue }
  701. ?.map { it.lockNfc ?: "" }?.toMutableList() ?: mutableListOf()
  702. )
  703. }
  704. val actualLockCount = lockMap.values.sumBy { it.size }
  705. // 如果锁不够,提前清空并立刻返回
  706. if (actualLockCount < needLockCount) {
  707. ToastUtils.tip(
  708. MyApplication.instance!!.getString(R.string.lock_is_not_enough)
  709. )
  710. callBack(null, mutableMapOf())
  711. return@runOnMain
  712. }
  713. // —— 如果需钥匙,再请求并计算 ——
  714. var keyPair: Pair<Byte, DockBean.KeyBean?>? = null
  715. if (isNeedKey) {
  716. val keyPage = withContext(Dispatchers.IO) { getKeyPage() }
  717. keyPair = withContext(Dispatchers.Default) {
  718. getOneKey(
  719. slotsPage?.records?.filter {
  720. it.slotType == slotTypeList.find { d -> d.dictLabel == "钥匙" }?.dictValue && it.status == slotStatusList.find { d -> d.dictLabel == "异常" }?.dictValue
  721. }?.toMutableList() ?: mutableListOf(),
  722. keyPage?.records?.filter { it.exStatus == keyStatusList.find { d -> d.dictLabel == "异常" }?.dictValue }
  723. ?.map { it.keyNfc ?: "" }?.toMutableList() ?: mutableListOf()
  724. )
  725. }
  726. if (keyPair == null) {
  727. ToastUtils.tip(
  728. MyApplication.instance!!.getString(R.string.no_available_key)
  729. )
  730. }
  731. }
  732. // —— 全部计算完毕,在主线程一次性回调 ——
  733. callBack(keyPair, lockMap)
  734. } catch (e: Exception) {
  735. // 根据需求处理异常,或把异常信息也通过 callback 返回
  736. sendLoadingEventMsg(null, false)
  737. e.printStackTrace()
  738. ToastUtils.tip("检查设备异常:${e.message}")
  739. }
  740. }
  741. }
  742. /**
  743. * 获取开关量数据
  744. */
  745. fun getSwitchData(): MutableList<DockBean.SwitchBean> {
  746. return ModBusController.getSwitchData()
  747. }
  748. /****************************************** 蓝牙 ******************************************/
  749. /******************************************蓝牙通用准备******************************************/
  750. /**
  751. * 注册连接监听
  752. */
  753. fun registerConnectListener(
  754. mac: String, connectNow: Boolean = false, callBack: ((
  755. Boolean, BleBean?
  756. ) -> Unit)? = null
  757. ) {
  758. BleConnectionManager.registerConnectListener(mac, connectNow, callBack)
  759. }
  760. /**
  761. * 连接监听反注册
  762. */
  763. fun unregisterConnectListener(mac: String, bleBean: BleBean? = null) {
  764. BleConnectionManager.unregisterConnectListener(mac, bleBean)
  765. }
  766. /******************************************蓝牙通用准备结束******************************************/
  767. fun getBleDeviceByMac(mac: String?): BleBean? {
  768. return deviceList.find { it.bleDevice.mac == mac }
  769. }
  770. fun getBleBeanByRfid(nfc: String?): BleBean? {
  771. nfc ?: return null
  772. ModBusController.getKeyByRfid(nfc)?.mac?.let { itMac ->
  773. return getBleDeviceByMac(itMac)
  774. }
  775. return null
  776. }
  777. /**
  778. * 下发工作票
  779. */
  780. private fun sendTicketBusiness(
  781. isLock: Boolean,
  782. mac: String,
  783. ticketDetail: TicketDetailRespVO,
  784. lockList: MutableList<String?>?,
  785. activity: AppCompatActivity,
  786. isNeedLoading: Boolean = false,
  787. ) {
  788. registerConnectListener(mac, true) { isDone, bleBean ->
  789. if (!isDone) {
  790. sendTicketBusiness(isLock, mac, ticketDetail, lockList, activity, isNeedLoading)
  791. return@registerConnectListener
  792. }
  793. if (bleBean == null) {
  794. // ToastUtils.tip(R.string.simple_key_is_not_connected)
  795. LogUtil.e("sendTicketBusiness fail : $mac, bleBean is null")
  796. return@registerConnectListener
  797. }
  798. // 单bleBean json赋值
  799. bleBean.retryCount = 0
  800. bleBean.ticketSend = generateTicketSendJson(isLock, ticketDetail, lockList)
  801. bleBean.ticketSend?.let { itJson ->
  802. sendTicketWithRetry(itJson, bleBean.bleDevice, isNeedLoading)
  803. }
  804. }
  805. }
  806. /**
  807. * 带重试的下发工作票,重试次数3,间隔500ms
  808. */
  809. private fun sendTicketWithRetry(
  810. json: String,
  811. bleDevice: BleDevice,
  812. isNeedLoading: Boolean = false,
  813. maxRetries: Int = 3,
  814. delayMillis: Long = 500
  815. ) {
  816. var retryCount = 0
  817. fun attemptSend() {
  818. sendTicket(json, bleDevice, isNeedLoading) { sendRst ->
  819. if (!sendRst && retryCount < maxRetries) {
  820. retryCount++
  821. // 等待一段时间后再次尝试
  822. Executor.delayOnMain(delayMillis) {
  823. LogUtil.i("Retry attempt, mac : ${bleDevice.mac}, retryCount : $retryCount")
  824. attemptSend()
  825. }
  826. }
  827. }
  828. }
  829. attemptSend()
  830. }
  831. /**
  832. * 读取工作票完成情况
  833. */
  834. private fun getTicketStatusBusiness(
  835. mac: String, isNeedLoading: Boolean = false
  836. ) {
  837. registerConnectListener(mac, true) { isDone, bleBean ->
  838. if (isDone) {
  839. Executor.delayOnMain(500) {
  840. getTicketStatusWithRetry(bleBean!!.bleDevice, isNeedLoading)
  841. }
  842. } else {
  843. if (isNeedLoading) sendEventMsg(
  844. MsgEvent(
  845. MSG_EVENT_LOADING, LoadingMsg(false, null, false)
  846. )
  847. )
  848. }
  849. }
  850. }
  851. private fun getTicketStatusWithRetry(
  852. bleDevice: BleDevice,
  853. isNeedLoading: Boolean = false,
  854. maxRetries: Int = 3,
  855. delayMillis: Long = 500
  856. ) {
  857. var retryCount = 0
  858. fun attemptSend() {
  859. getTicketStatus(bleDevice, isNeedLoading) { sendRst ->
  860. if (!sendRst && retryCount < maxRetries) {
  861. retryCount++
  862. // 等待一段时间后再次尝试
  863. Executor.delayOnMain(delayMillis) {
  864. LogUtil.i("Retry attempt, mac : ${bleDevice.mac}, retryCount : $retryCount")
  865. attemptSend()
  866. }
  867. }
  868. }
  869. }
  870. attemptSend()
  871. }
  872. private fun sendTicket(
  873. jsonStr: String,
  874. bleDevice: BleDevice,
  875. isNeedLoading: Boolean = false,
  876. processCallback: ((Boolean) -> Unit)? = null
  877. ) {
  878. if (isNeedLoading) sendEventMsg(
  879. MsgEvent(
  880. MSG_EVENT_LOADING,
  881. LoadingMsg(true, CommonUtils.getStr(R.string.start_to_send_ticket), null)
  882. )
  883. )
  884. BleCmdManager.sendWorkTicket(
  885. jsonStr, bleDevice = bleDevice, callback = object : CustomBleWriteCallback() {
  886. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  887. LogUtil.i("sendTicket success")
  888. if (isNeedLoading) sendEventMsg(
  889. MsgEvent(
  890. MSG_EVENT_LOADING,
  891. LoadingMsg(true, CommonUtils.getStr(R.string.sending_ticket), null)
  892. )
  893. )
  894. }
  895. override fun onWriteFailure(exception: BleException?) {
  896. LogUtil.e("sendTicket fail : ${bleDevice.mac}")
  897. if (isNeedLoading) sendEventMsg(
  898. MsgEvent(
  899. MSG_EVENT_LOADING,
  900. LoadingMsg(false, CommonUtils.getStr(R.string.send_ticket_fail), null)
  901. )
  902. )
  903. processCallback?.invoke(false)
  904. }
  905. })
  906. }
  907. /**
  908. * 生成下发工作票Json
  909. *
  910. * @param vo 工作票详情
  911. */
  912. private fun generateTicketSendJson(
  913. isLock: Boolean, vo: TicketDetailRespVO, lockList: MutableList<String?>?
  914. ): String {
  915. LogUtil.i("generateTicketSendJson : $lockList")
  916. val bo = WorkTicketSendBO(
  917. cardNo = SPUtils.getLoginUser(MyApplication.instance!!.applicationContext)?.userCardList?.get(
  918. 0
  919. ),
  920. )
  921. CommonUtils.getDiffHours(vo.ticketEndTime)?.let {
  922. bo.effectiveTime = it
  923. }
  924. // 有配置则用配置,没用则填充默认密码
  925. bo.password =
  926. SPUtils.getLoginUser(MyApplication.instance!!.applicationContext)?.keyCode ?: "123456"
  927. val dataBO = WorkTicketSendBO.DataBO(
  928. taskCode = vo.ticketId.toString(), codeId = 1
  929. )
  930. val taskList = ArrayList<WorkTicketSendBO.DataBO.DataListBO>()
  931. vo.ticketPointsVOList?.let { itList ->
  932. itList.forEach { pointVO ->
  933. if (vo.noUnlockTicketPointsVOSet?.any { it.pointId == pointVO.pointId } == true && isLock == false) {
  934. return@forEach
  935. }
  936. val task = WorkTicketSendBO.DataBO.DataListBO(
  937. dataId = pointVO.pointId?.toInt(),
  938. equipRfidNo = pointVO.pointNfc,
  939. equipName = pointVO.pointName,
  940. target = if (isLock) 0 else 1
  941. )
  942. if (!isLock) {
  943. task.infoRfidNo = pointVO.lockNfc
  944. }
  945. pointVO.prePointId?.let {
  946. task.prevId = it.toInt()
  947. }
  948. // TODO partCode待补充
  949. taskList.add(task)
  950. }
  951. }
  952. dataBO.dataList = taskList
  953. bo.data = mutableListOf(dataBO)
  954. if (isLock) {
  955. // TODO 挂锁数组
  956. if (!lockList.isNullOrEmpty()) {
  957. bo.lockList = mutableListOf()
  958. lockList.forEachIndexed { index, s ->
  959. if (s.isNullOrEmpty()) {
  960. LogUtil.w("Lock nfc is null or empty")
  961. return@forEachIndexed
  962. }
  963. bo.lockList?.add(LockListBO(index + 1, s))
  964. }
  965. }
  966. }
  967. // TODO partList 待补充
  968. val jsonStr = Gson().toJson(bo)
  969. LogUtil.i("json : $jsonStr")
  970. return jsonStr
  971. }
  972. /**
  973. * 生成下空发工作票Json
  974. *
  975. * @param vo 工作票详情
  976. */
  977. fun generateEmptyTicketSendJson(): String {
  978. // 构造一个所有字段都为空/默认值的 WorkTicketSendBO
  979. val bo = WorkTicketSendBO(
  980. cardNo = "", // 空卡号
  981. effectiveTime = 0, // 默认有效时长
  982. password = "" // 空密码
  983. ).apply {
  984. // data 列表留空
  985. data = mutableListOf()
  986. // lockList 留空(如果字段非空,再设置为 emptyList())
  987. lockList = mutableListOf()
  988. }
  989. // 转成 JSON 并返回
  990. val jsonStr = Gson().toJson(bo)
  991. LogUtil.i("generateEmptyTicketJson: $jsonStr")
  992. return jsonStr
  993. }
  994. fun handleRsp(
  995. bleBean: BleBean,
  996. byteArray: ByteArray,
  997. isNeedLoading: Boolean = false,
  998. prepareDoneCallBack: ((Boolean, BleBean?) -> Unit)?
  999. ) {
  1000. // TODO Token校验
  1001. // val len = byteArray[2].toInt()
  1002. // val token = byteArray.copyOfRange(len + 7, len + 11)
  1003. // if (token.contentEquals(bleBean.token)) {
  1004. // LogUtil.i("Token is right")
  1005. // } else {
  1006. // LogUtil.e("Token is wrong")
  1007. // }
  1008. when {
  1009. // 获取令牌
  1010. byteArray.startsWith(BleConst.RSP_GET_TOKEN) -> BleCmdManager.handleToken(
  1011. bleBean.bleDevice, byteArray
  1012. ) { isSuccess ->
  1013. if (isSuccess) {
  1014. prepareDoneCallBack?.invoke(true, bleBean)
  1015. }
  1016. }
  1017. // 工作模式切换
  1018. byteArray.startsWith(BleConst.RSP_SWITCH_MODE) -> {
  1019. handleSwitchModeResult(byteArray, isNeedLoading) { res, job ->
  1020. sendEventMsg(
  1021. MsgEvent(
  1022. MSG_EVENT_SWITCH_MODE, SwitchModeMsg(job.toInt(), res.toInt(), bleBean)
  1023. )
  1024. )
  1025. }
  1026. }
  1027. // 工作票下发
  1028. byteArray.startsWith(BleConst.RSP_SEND_WORK_TICKET) -> handleWorkTicketResult(
  1029. bleBean, byteArray, isNeedLoading
  1030. )
  1031. // 获取设备当前状态
  1032. byteArray.startsWith(BleConst.RSP_CURRENT_STATUS) -> BleCmdManager.handleCurrentStatus(
  1033. byteArray
  1034. ) {
  1035. sendEventMsg(MsgEvent(MSG_EVENT_CURRENT_MODE, CurrentModeMsg(bleBean, it)))
  1036. }
  1037. // 获取设备工作票完成情况
  1038. byteArray.startsWith(BleConst.RSP_WORK_TICKET_RESULT) && byteArray[3] == 0x02.toByte() -> handleTicketStatus(
  1039. bleBean.bleDevice, byteArray, isNeedLoading
  1040. )
  1041. byteArray.startsWith(BleConst.RSP_POWER_STATUS) -> {
  1042. val power = byteArray[4].toInt()
  1043. if (power < 50) {//如果电量小于50就打开仓位充电
  1044. ModBusController.controlKeyCharge(true, bleBean.bleDevice.mac) {
  1045. LogUtil.i("钥匙: ${bleBean.bleDevice.mac} 开始充电")
  1046. }
  1047. } else {
  1048. ModBusController.controlKeyCharge(false, bleBean.bleDevice.mac) {
  1049. LogUtil.i("钥匙: ${bleBean.bleDevice.mac} 关闭充电")
  1050. }
  1051. }
  1052. }
  1053. }
  1054. }
  1055. /**
  1056. * 工作模式切换结果
  1057. * job : 0x01:工作模式 0x02:待机模式
  1058. * res : 0x01:成功 0x02:失败
  1059. */
  1060. private fun handleSwitchModeResult(
  1061. byteArray: ByteArray,
  1062. isNeedLoading: Boolean = false,
  1063. callBack: ((Byte, Byte) -> Unit)? = null
  1064. ) {
  1065. BleCmdManager.handleSwitchModeResult(byteArray) { job, res ->
  1066. if (res == 0x01.toByte() && job == 0x01.toByte()) {
  1067. LogUtil.i("切换工作模式成功")
  1068. if (isNeedLoading) sendEventMsg(
  1069. MsgEvent(
  1070. MSG_EVENT_LOADING, LoadingMsg(false, "切换工作模式成功", null)
  1071. )
  1072. )
  1073. } else if (res == 0x01.toByte() && job == 0x02.toByte()) {
  1074. LogUtil.i("切换待机模式成功")
  1075. if (isNeedLoading) sendEventMsg(
  1076. MsgEvent(
  1077. MSG_EVENT_LOADING, LoadingMsg(false, "切换待机模式成功", null)
  1078. )
  1079. )
  1080. } else {
  1081. LogUtil.e("切换模式失败 : ${job.toInt()} - ${res.toInt()}")
  1082. if (isNeedLoading) sendEventMsg(
  1083. MsgEvent(
  1084. MSG_EVENT_LOADING, LoadingMsg(false, null, null)
  1085. )
  1086. )
  1087. }
  1088. callBack?.invoke(res, job)
  1089. }
  1090. }
  1091. /**
  1092. * 工作票下发结果
  1093. * res:0x00:成功 0x01:失败 0x02:传输超时 0x0D:当前IDX超出范围 0x0E:当前数据CRC校验失败 0x14:JSON结构错误 0x63:未知错误
  1094. */
  1095. private fun handleWorkTicketResult(
  1096. bleBean: BleBean, byteArray: ByteArray, isNeedLoading: Boolean = false
  1097. ) {
  1098. BleCmdManager.handleWorkTicketResult(bleBean, byteArray) { isSuccess, rst ->
  1099. if (isNeedLoading) sendEventMsg(
  1100. MsgEvent(
  1101. MSG_EVENT_LOADING, LoadingMsg(false, null, null)
  1102. )
  1103. )
  1104. if (isSuccess) {
  1105. // 下发完毕,切换工作模式
  1106. LogUtil.i("工作票下发完毕")
  1107. if (isNeedLoading) sendEventMsg(
  1108. MsgEvent(
  1109. MSG_EVENT_LOADING, LoadingMsg(true, "切换钥匙为工作模式", null)
  1110. )
  1111. )
  1112. Executor.delayOnIO(800) {
  1113. //切换到工作模式
  1114. switchWorkMode(bleBean.bleDevice, isNeedLoading)
  1115. }
  1116. } else {
  1117. sendLoadingEventMsg(null, false)
  1118. if (bleBean.retryCount < 3) {
  1119. Executor.delayOnMain(500) {
  1120. bleBean.retryCount++
  1121. sendLoadingEventMsg(MyApplication.instance!!.getString(R.string.start_to_send_ticket))
  1122. sendTicketWithRetry(bleBean.ticketSend!!, bleBean.bleDevice, isNeedLoading)
  1123. }
  1124. } else {
  1125. ToastUtils.tip(R.string.send_ticket_fail)
  1126. LogUtil.e("Send ticket fail")
  1127. ModBusController.getKeyByMac(bleBean.bleDevice.mac)?.let { itKey ->
  1128. mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_KEY && it.nfc == itKey.rfid }
  1129. }
  1130. }
  1131. }
  1132. }
  1133. }
  1134. /**
  1135. * 获取当前钥匙的状态
  1136. */
  1137. fun getCurrentStatus(
  1138. from: Int,
  1139. bleDevice: BleDevice,
  1140. retryCount: Int = 0,
  1141. timeoutCallBack: ((Boolean) -> Unit)? = null
  1142. ) {
  1143. LogUtil.i("getCurrentStatus - ${bleDevice.mac} - from : $from")
  1144. var isTimeout = true
  1145. // 加1秒防止早于onWriteFailure开始处理导致多次处理
  1146. Executor.delayOnMain((BleUtil.OPERATE_TIMEOUT + 1).toLong()) {
  1147. if (isTimeout) {
  1148. LogUtil.e("getCurrentStatus timeout : mac = ${bleDevice.mac}, retryCount = $retryCount")
  1149. if (retryCount > 0) {
  1150. Executor.delayOnMain(1000) {
  1151. getCurrentStatus(from, bleDevice, retryCount - 1, timeoutCallBack)
  1152. }
  1153. } else {
  1154. ModBusController.getKeyByMac(bleDevice.mac)?.rfid?.let {
  1155. addExceptionKey(it)
  1156. timeoutCallBack?.invoke(true)
  1157. }
  1158. }
  1159. }
  1160. }
  1161. BleCmdManager.getCurrentStatus(bleDevice, object : CustomBleWriteCallback() {
  1162. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  1163. LogUtil.i("getCurrentStatus success : ${bleDevice.mac}")
  1164. isTimeout = false
  1165. timeoutCallBack?.invoke(false)
  1166. }
  1167. override fun onWriteFailure(exception: BleException?) {
  1168. LogUtil.i("getCurrentStatus fail : ${bleDevice.mac}")
  1169. isTimeout = false
  1170. Executor.delayOnMain(1000) {
  1171. getCurrentStatus(from, bleDevice, timeoutCallBack = timeoutCallBack)
  1172. }
  1173. }
  1174. })
  1175. }
  1176. /**
  1177. * 获取电池电量
  1178. */
  1179. fun getBatteryPower(bleDevice: BleDevice) {
  1180. LogUtil.i("获取电池电量:${bleDevice.mac}")
  1181. BleCmdManager.getPower(bleDevice.mac, object : CustomBleWriteCallback() {
  1182. override fun onWriteSuccess(p0: Int, p1: Int, p2: ByteArray?) {
  1183. LogUtil.i("发送获取电池电量命令成功:${bleDevice.mac}")
  1184. }
  1185. override fun onWriteFailure(p0: BleException?) {
  1186. ThreadUtils.runOnIODelayed(500) {
  1187. LogUtil.i("发送获取电池电量命令失败:${bleDevice.mac}")
  1188. getBatteryPower(bleDevice)
  1189. }
  1190. }
  1191. })
  1192. }
  1193. /**
  1194. * 切换工作模式
  1195. */
  1196. private fun switchWorkMode(bleDevice: BleDevice, isNeedLoading: Boolean = false) {
  1197. LogUtil.i("switchWorkMode - ${bleDevice.mac}")
  1198. BleCmdManager.switchMode(STATUS_WORK, bleDevice, object : CustomBleWriteCallback() {
  1199. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  1200. LogUtil.i("switch mode work success : ${bleDevice.mac}")
  1201. }
  1202. override fun onWriteFailure(exception: BleException?) {
  1203. LogUtil.e("switch mode work fail : ${exception?.code} - ${exception?.description}")
  1204. Executor.delayOnMain(500) {
  1205. switchWorkMode(bleDevice, isNeedLoading)
  1206. }
  1207. }
  1208. })
  1209. }
  1210. /**
  1211. * 处理工作票完成情况
  1212. */
  1213. private fun handleTicketStatus(
  1214. bleDevice: BleDevice, byteArray: ByteArray, isNeedLoading: Boolean = false
  1215. ) {
  1216. BleCmdManager.handleTicketStatus(bleDevice, byteArray) { ticketJson ->
  1217. if (ticketJson.isNullOrEmpty()) {
  1218. return@handleTicketStatus
  1219. }
  1220. if (isNeedLoading) sendEventMsg(
  1221. MsgEvent(
  1222. MSG_EVENT_LOADING, LoadingMsg(false, "工作票完成状态读取完成", null)
  1223. )
  1224. )
  1225. LogUtil.i("Get ticket status complete : ${bleDevice.mac}")
  1226. // TD:Ticket Done
  1227. if (isNeedLoading) sendEventMsg(
  1228. MsgEvent(
  1229. MSG_EVENT_LOADING, LoadingMsg(false, "TD$ticketJson}", true)
  1230. )
  1231. )
  1232. val workTicketGetBO = try {
  1233. Gson().fromJson(ticketJson, WorkTicketGetBO::class.java)
  1234. } catch (e: Exception) {
  1235. null
  1236. }
  1237. if (workTicketGetBO == null) {
  1238. ToastUtils.tip(R.string.ticket_data_error)
  1239. return@handleTicketStatus
  1240. }
  1241. // 判断workTicketGetBO里是否有未完成的
  1242. ThreadUtils.runOnIO {
  1243. val finishedStatus = workTicketGetBO.hasFinished()
  1244. LogUtil.i("作业票结束情况:${finishedStatus}")
  1245. if (finishedStatus.first) {
  1246. Executor.delayOnMain(500) {
  1247. handleKeyReturn(bleDevice, workTicketGetBO, finishedStatus.second)
  1248. }
  1249. } else {
  1250. // 当前策略:作业票未完成禁止归还钥匙
  1251. withContext(Dispatchers.Main) {
  1252. showKeyReturnDialog {
  1253. sendLoadingEventMsg(null, false)
  1254. ToastUtils.tip(R.string.continue_the_ticket)
  1255. BleManager.getInstance().disconnect(bleDevice)
  1256. // 打开卡扣,防止初始化的时候选择不处理钥匙导致无法使用
  1257. val dock = ModBusController.getDockByKeyMac(bleDevice.mac)
  1258. val keyBean = dock?.getKeyList()?.find { it.mac == bleDevice.mac }
  1259. keyBean?.let {
  1260. ModBusController.controlKeyBuckle(true, keyBean.isLeft, dock.addr)
  1261. }
  1262. }
  1263. }
  1264. }
  1265. }
  1266. }
  1267. }
  1268. /**
  1269. * ticketFinished主要是后端的作业票是否已经结束,结束了,就直接修改状态就好了
  1270. */
  1271. private fun handleKeyReturn(
  1272. bleDevice: BleDevice, workTicketGetBO: WorkTicketGetBO?, ticketFinished: Boolean
  1273. ) {
  1274. val dock = ModBusController.getDockByKeyMac(bleDevice.mac)
  1275. val keyBean = dock?.getKeyList()?.find { it.mac == bleDevice.mac }
  1276. keyBean?.let {
  1277. ModBusController.controlKeyBuckle(false, keyBean.isLeft, dock.addr)
  1278. }
  1279. if (ticketFinished) {
  1280. mDeviceTakeList.removeIf { it.nfc == keyBean?.rfid }
  1281. switchReadyMode(bleDevice)
  1282. } else {
  1283. // 上报隔离点状态
  1284. val keyNfc = ModBusController.getKeyByMac(bleDevice.mac)?.rfid ?: "key rfid lost"
  1285. workTicketGetBO?.data?.forEach { data ->
  1286. val updateList = mutableListOf<LockPointUpdateReqVO>()
  1287. data.dataList?.forEach { dataListDTO ->
  1288. data.taskCode?.toLong()?.let {
  1289. SPUtils.returnKey(it)
  1290. }
  1291. val updateVO = LockPointUpdateReqVO(
  1292. data.taskCode?.toLong(),
  1293. dataListDTO.infoRfidNo,
  1294. dataListDTO.equipRfidNo,
  1295. keyNfc,
  1296. dataListDTO.target,
  1297. dataListDTO.status
  1298. )
  1299. updateList.add(updateVO)
  1300. }
  1301. sendLoadingEventMsg(null, false)
  1302. if (CAN_RETURN) {
  1303. // 上报点位钥匙绑定
  1304. NetApi.updateLockPointBatch(updateList) { isSuccess, msg, code ->
  1305. LogUtil.i("还锁操作:${isSuccess},${msg},${code}")
  1306. if (isSuccess) {
  1307. // 上报钥匙归还
  1308. NetApi.updateKeyReturn(
  1309. data.taskCode?.toLong()!!,
  1310. keyNfc!!,
  1311. MyApplication.instance!!.serialNo()
  1312. ) { isSuccess, msg, code ->
  1313. if (!isSuccess && msg != MyApplication.instance?.applicationContext!!.getString(
  1314. R.string.ticket_lost
  1315. )
  1316. ) {
  1317. SPUtils.saveUpdateKeyReturn(
  1318. MyApplication.instance!!,
  1319. UpdateKeyReturnBO(data.taskCode?.toLong()!!, keyNfc!!)
  1320. )
  1321. if (msg == MyApplication.instance?.applicationContext!!.getString(
  1322. R.string.ticket_lost
  1323. )
  1324. ) {
  1325. sendEventMsg(
  1326. MsgEvent(
  1327. MsgEventConstants.MSG_EVENT_TICKET_FINISHED, null
  1328. )
  1329. )
  1330. }
  1331. ToastUtils.tip(R.string.key_return_success)
  1332. } else {
  1333. ToastUtils.tip(R.string.key_return_success)
  1334. }
  1335. }
  1336. data.taskCode?.toLong()?.let {
  1337. sendEventMsg(
  1338. MsgEvent(
  1339. MSG_EVENT_UPDATE_TICKET_PROGRESS,
  1340. UpdateTicketProgressMsg(it)
  1341. )
  1342. )
  1343. }
  1344. // 确认归还,切换为待机模式
  1345. switchReadyMode(bleDevice)
  1346. } else {
  1347. ThreadUtils.runOnMain {
  1348. // 当前策略:作业票未完成禁止归还钥匙
  1349. showKeyReturnDialog {
  1350. sendLoadingEventMsg(null, false)
  1351. ToastUtils.tip(R.string.continue_the_ticket)
  1352. BleManager.getInstance().disconnect(bleDevice)
  1353. // 打开卡扣,防止初始化的时候选择不处理钥匙导致无法使用
  1354. if (workTicketGetBO.data?.all { it.dataList?.all { it.closed == 1 } == true } == true) {
  1355. workTicketGetBO.data?.firstOrNull()?.taskCode?.toLong()
  1356. ?.let {
  1357. checkStepAndTicketDetailThenSendTicket(
  1358. it,
  1359. bleDevice.mac
  1360. )
  1361. }
  1362. } else {
  1363. val dock = ModBusController.getDockByKeyMac(bleDevice.mac)
  1364. val keyBean =
  1365. dock?.getKeyList()?.find { it.mac == bleDevice.mac }
  1366. keyBean?.let {
  1367. ModBusController.controlKeyBuckle(
  1368. true, keyBean.isLeft, dock.addr
  1369. )
  1370. }
  1371. }
  1372. }
  1373. }
  1374. SPUtils.clearUpdateKeyReturn(MyApplication.instance!!)
  1375. SPUtils.clearUpdateLockPoint(MyApplication.instance!!)
  1376. }
  1377. }
  1378. } else {
  1379. SPUtils.saveUpdateLockPoint(MyApplication.instance!!, updateList)
  1380. SPUtils.saveUpdateKeyReturn(
  1381. MyApplication.instance!!,
  1382. UpdateKeyReturnBO(data.taskCode?.toLong()!!, keyNfc!!)
  1383. )
  1384. // 保存待发数据,切换为待机模式
  1385. switchReadyMode(bleDevice)
  1386. }
  1387. }
  1388. }
  1389. }
  1390. /**
  1391. * 处理虚拟钥匙取出,如果作业的全部点位已经上锁更新钥匙的状态使用
  1392. */
  1393. fun handleVirtualKeyGive(taskCode: Long, keyNfc: String, done: () -> Unit) {
  1394. // 上报钥匙归还
  1395. NetApi.updateKeyTake(taskCode, keyNfc, MyApplication.instance!!.serialNo()) { isSuccess ->
  1396. if (isSuccess) {
  1397. done()
  1398. }
  1399. }
  1400. }
  1401. /**
  1402. * 处理虚拟钥匙归还,如果作业的全部点位已经上锁更新钥匙的状态使用
  1403. */
  1404. fun handleVirtualKeyReturn(taskCode: Long, keyNfc: String, done: () -> Unit) {
  1405. // 上报钥匙归还
  1406. NetApi.updateKeyReturn(
  1407. taskCode, keyNfc, MyApplication.instance!!.serialNo()
  1408. ) { isSuccess, msg, code ->
  1409. if (!isSuccess && msg != MyApplication.instance?.applicationContext!!.getString(
  1410. R.string.ticket_lost
  1411. )
  1412. ) {
  1413. SPUtils.saveUpdateKeyReturn(
  1414. MyApplication.instance!!, UpdateKeyReturnBO(taskCode, keyNfc)
  1415. )
  1416. if (msg == MyApplication.instance?.applicationContext!!.getString(
  1417. R.string.ticket_lost
  1418. )
  1419. ) {
  1420. sendEventMsg(
  1421. MsgEvent(
  1422. MsgEventConstants.MSG_EVENT_TICKET_FINISHED, null
  1423. )
  1424. )
  1425. }
  1426. } else {
  1427. done()
  1428. sendEventMsg(
  1429. MsgEvent(
  1430. MSG_EVENT_UPDATE_TICKET_PROGRESS, UpdateTicketProgressMsg(taskCode)
  1431. )
  1432. )
  1433. }
  1434. }
  1435. }
  1436. fun switchReadyMode(bleDevice: BleDevice) {
  1437. BleCmdManager.switchMode(STATUS_READY, bleDevice, object : CustomBleWriteCallback() {
  1438. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  1439. LogUtil.i("switch mode ready success : ${bleDevice.mac}")
  1440. }
  1441. override fun onWriteFailure(exception: BleException?) {
  1442. LogUtil.e("switch mode ready fail : ${bleDevice.mac}")
  1443. Executor.delayOnMain(300) {
  1444. switchReadyMode(bleDevice)
  1445. }
  1446. }
  1447. })
  1448. }
  1449. /**
  1450. * 获取工作票完成情况
  1451. */
  1452. private fun getTicketStatus(
  1453. bleDevice: BleDevice,
  1454. isNeedLoading: Boolean = false,
  1455. processCallback: ((Boolean) -> Unit)? = null
  1456. ) {
  1457. if (isNeedLoading) sendEventMsg(
  1458. MsgEvent(
  1459. MSG_EVENT_LOADING, LoadingMsg(true, "开始获取工作票", null)
  1460. )
  1461. )
  1462. BleCmdManager.getTicketStatus(bleDevice, object : CustomBleWriteCallback() {
  1463. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  1464. if (isNeedLoading) sendEventMsg(
  1465. MsgEvent(
  1466. MSG_EVENT_LOADING, LoadingMsg(false, "工作票获取成功", null)
  1467. )
  1468. )
  1469. LogUtil.i("getTicketStatus success")
  1470. }
  1471. override fun onWriteFailure(exception: BleException?) {
  1472. if (isNeedLoading) sendEventMsg(
  1473. MsgEvent(
  1474. MSG_EVENT_LOADING, LoadingMsg(false, "工作票获取失败", false)
  1475. )
  1476. )
  1477. processCallback?.invoke(false)
  1478. LogUtil.e("getTicketStatus fail")
  1479. }
  1480. })
  1481. }
  1482. /**
  1483. * 添加待更新取出状态的设备
  1484. */
  1485. fun addDeviceTake(deviceType: Int, ticketId: Long, nfc: String?) {
  1486. LogUtil.i("addDeviceTake : $deviceType - $ticketId - $nfc")
  1487. mDeviceTakeList.removeIf { it.deviceType == deviceType && it.nfc == nfc }
  1488. mDeviceTakeList.add(DeviceTakeUpdateBO(deviceType, ticketId, nfc!!))
  1489. }
  1490. fun removeDeviceTake(deviceType: Int, nfc: String?) {
  1491. LogUtil.i("removeDeviceTake : $deviceType - $nfc")
  1492. mDeviceTakeList.removeIf { it.deviceType == deviceType && it.nfc == nfc }
  1493. }
  1494. private fun handleDeviceTake(deviceTakeUpdateBO: DeviceTakeUpdateMsg, rfid: String? = null) {
  1495. LogUtil.i("$deviceTakeUpdateBO")
  1496. when (deviceTakeUpdateBO.deviceType) {
  1497. // 钥匙
  1498. 0 -> {
  1499. mDeviceTakeList.find { it.deviceType == DEVICE_TYPE_KEY && it.nfc == deviceTakeUpdateBO.nfc }
  1500. ?.let { info ->
  1501. sendLoadingEventMsg(null, false)
  1502. SPUtils.takeKey(info.ticketId)
  1503. NetApi.updateKeyTake(
  1504. info.ticketId, info.nfc, MyApplication.instance?.serialNo()!!
  1505. ) { isSuccess ->
  1506. if (isSuccess) {
  1507. mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_KEY && it.nfc == info.nfc }
  1508. sendEventMsg(
  1509. MsgEvent(
  1510. MSG_EVENT_UPDATE_TICKET_PROGRESS,
  1511. UpdateTicketProgressMsg(info.ticketId)
  1512. )
  1513. )
  1514. //钥匙取出之后重新再连一把钥匙待机
  1515. ModBusController.getKeyByRfid(
  1516. info.nfc
  1517. )?.mac?.let {
  1518. unregisterConnectListener(it)
  1519. }
  1520. //待机数不够就再连一把,但不能是原来那把
  1521. if (BleManager.getInstance().allConnectedDevice.size < BleConst.MAX_KEY_STAND_BY) {
  1522. ModBusController.getKeyByRfid(
  1523. info.nfc
  1524. )?.mac?.let {
  1525. connectExistsKey(
  1526. it
  1527. )
  1528. }
  1529. }
  1530. }
  1531. }
  1532. } ?: sendLoadingEventMsg(null, false)
  1533. }
  1534. // 挂锁
  1535. 1 -> {
  1536. mDeviceTakeList.find { it.deviceType == DEVICE_TYPE_LOCK && it.nfc == deviceTakeUpdateBO.nfc }
  1537. ?.let { info ->
  1538. NetApi.updateLockTake(
  1539. mutableListOf(
  1540. LockTakeUpdateReqVO(
  1541. info.ticketId, info.nfc, MyApplication.instance?.serialNo()!!
  1542. )
  1543. )
  1544. ) { isSuccess ->
  1545. Executor.runOnMain {
  1546. if (isSuccess == false) {
  1547. LogUtil.e("Lock take report fail")
  1548. ToastUtils.tip(R.string.lock_take_report_fail)
  1549. SPUtils.saveTicketTakeLockException(info.ticketId)
  1550. mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_LOCK && it.nfc == info.nfc }
  1551. mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_KEY && it.ticketId == info.ticketId }
  1552. sendLoadingEventMsg(null, false)
  1553. return@runOnMain
  1554. }
  1555. // 检查是不是要发钥匙了
  1556. mDeviceTakeList.removeIf { it.deviceType == DEVICE_TYPE_LOCK && it.nfc == info.nfc }
  1557. // 检查当前工作票是否取完挂锁
  1558. if (mDeviceTakeList.any { it.deviceType == DEVICE_TYPE_LOCK && it.ticketId == info.ticketId }) {
  1559. LogUtil.i("Waiting all locks to take out")
  1560. sendLoadingEventMsg(
  1561. MyApplication.instance?.applicationContext?.getString(
  1562. R.string.take_out_lock_tip,
  1563. mDeviceTakeList.count { it.deviceType == DEVICE_TYPE_LOCK && it.ticketId == info.ticketId })
  1564. )
  1565. ToastUtils.tip(R.string.take_out_rest_locks)
  1566. return@runOnMain
  1567. } else {
  1568. LogUtil.i("All locks are taken")
  1569. sendLoadingEventMsg(null, false)
  1570. }
  1571. if (SPUtils.getTicketTakeLockException(info.ticketId)) {
  1572. ToastUtils.tip(R.string.current_ticket_report_lock_take_exception_tip)
  1573. return@runOnMain
  1574. }
  1575. // 检查有无当前工作票的钥匙
  1576. mDeviceTakeList.find { it.deviceType == DEVICE_TYPE_KEY && it.ticketId == info.ticketId }
  1577. ?.let { itKey ->
  1578. sendLoadingEventMsg(
  1579. MyApplication.instance?.applicationContext!!.getString(
  1580. R.string.ble_connecting
  1581. )
  1582. )
  1583. handleGiveKey(itKey)
  1584. }
  1585. }
  1586. }
  1587. }
  1588. }
  1589. }
  1590. }
  1591. /**
  1592. * 分配钥匙
  1593. */
  1594. private fun handleGiveKey(deviceTakeUpdateBO: DeviceTakeUpdateBO) {
  1595. getBleDeviceByMac(ModBusController.getKeyByRfid(deviceTakeUpdateBO.nfc)?.mac)?.let {
  1596. getCurrentStatus(
  1597. 2,
  1598. getBleDeviceByMac(ModBusController.getKeyByRfid(deviceTakeUpdateBO.nfc)?.mac)!!.bleDevice
  1599. ) {
  1600. if (!it) {
  1601. return@getCurrentStatus
  1602. }
  1603. LogUtil.w("handleGiveKey timeout")
  1604. removeDeviceTake(DEVICE_TYPE_KEY, deviceTakeUpdateBO.nfc)
  1605. checkEquipCount(0, true) { keyPair, lockMap ->
  1606. if (keyPair == null) {
  1607. ThreadUtils.runOnMain {
  1608. val tipDialog = TipDialog(SIKCore.getApplication())
  1609. tipDialog.setTip(
  1610. SIKCore.getApplication().getString(R.string.key_take_error_tip)
  1611. )
  1612. tipDialog.setConfirmListener {
  1613. tipDialog.dismiss()
  1614. sendEventMsg(
  1615. MsgEvent(
  1616. MSG_EVENT_DEVICE_EXCEPTION,
  1617. DeviceExceptionMsg(DEVICE_TYPE_KEY, deviceTakeUpdateBO.nfc)
  1618. )
  1619. )
  1620. }
  1621. tipDialog.show()
  1622. }
  1623. } else {
  1624. addDeviceTake(
  1625. DEVICE_TYPE_KEY, deviceTakeUpdateBO.ticketId, keyPair.second?.rfid!!
  1626. )
  1627. handleGiveKey(
  1628. DeviceTakeUpdateBO(
  1629. DEVICE_TYPE_KEY, deviceTakeUpdateBO.ticketId, keyPair.second?.rfid!!
  1630. )
  1631. )
  1632. }
  1633. }
  1634. }
  1635. } ?: run {
  1636. ThreadUtils.runOnMain {
  1637. val tipDialog = TipDialog(SIKCore.getApplication())
  1638. tipDialog.setTip(SIKCore.getApplication().getString(R.string.key_take_error_tip))
  1639. tipDialog.setConfirmListener {
  1640. tipDialog.dismiss()
  1641. sendEventMsg(
  1642. MsgEvent(
  1643. MSG_EVENT_DEVICE_EXCEPTION,
  1644. DeviceExceptionMsg(DEVICE_TYPE_KEY, deviceTakeUpdateBO.nfc)
  1645. )
  1646. )
  1647. }
  1648. tipDialog.show()
  1649. }
  1650. }
  1651. }
  1652. /**
  1653. * 根据当前模式进行处理
  1654. */
  1655. private fun handleCurrentMode(currentModeMsg: CurrentModeMsg) {
  1656. when (currentModeMsg.mode) {
  1657. // 工作模式
  1658. 0x01.toByte() -> {
  1659. // 读工作票
  1660. getTicketStatusBusiness(currentModeMsg.bleBean.bleDevice.mac)
  1661. }
  1662. // 待机模式
  1663. 0x02.toByte() -> {
  1664. // 根据情况看是否需要下发工作票
  1665. ModBusController.getKeyByMac(currentModeMsg.bleBean.bleDevice.mac)?.let { key ->
  1666. // 判断是否有待取的钥匙
  1667. val updateBo =
  1668. mDeviceTakeList.find { it.deviceType == DEVICE_TYPE_KEY && key.rfid == it.nfc }
  1669. if (mDeviceTakeList.any { it.deviceType == DEVICE_TYPE_LOCK && it.ticketId == updateBo?.ticketId }) {
  1670. //todo 如果有钥匙待取但是对应的作业票的锁还有的,就不发
  1671. return
  1672. }
  1673. updateBo?.let { itBO ->
  1674. NetApi.getStepDetail(itBO.ticketId) {
  1675. var step = 0
  1676. it?.filter { it.stepStatus == "1" }
  1677. ?.maxByOrNull { it.stepIndex!! }?.stepIndex?.let {
  1678. step = it
  1679. }
  1680. NetApi.getTicketDetail(itBO.ticketId) { ticketDetail, _ ->
  1681. if (ticketDetail == null) {
  1682. return@getTicketDetail
  1683. }
  1684. val role = ticketDetail?.ticketUserVOList?.find {
  1685. it.userId == SPUtils.getLoginUser(MyApplication.instance?.applicationContext!!)?.userId && it.userType == USER_TYPE_LOCKER
  1686. }
  1687. if (role == null) {
  1688. ToastUtils.tip(R.string.you_are_not_locker_tip)
  1689. return@getTicketDetail
  1690. }
  1691. if (step == 4) { // 上锁工作票
  1692. sendTicketBusiness(
  1693. true,
  1694. currentModeMsg.bleBean.bleDevice.mac,
  1695. ticketDetail,
  1696. ticketDetail.ticketLockVOList?.filter { it.lockStatus != "2" }
  1697. ?.map { it.lockNfc }?.toMutableList(),
  1698. ActivityUtils.currentActivity() as BaseActivity<*>,
  1699. true
  1700. )
  1701. } else if (step == 7) { // 解锁工作票
  1702. sendTicketBusiness(
  1703. false,
  1704. currentModeMsg.bleBean.bleDevice.mac,
  1705. ticketDetail,
  1706. null,
  1707. ActivityUtils.currentActivity() as BaseActivity<*>,
  1708. true
  1709. )
  1710. }
  1711. }
  1712. }
  1713. } ?: let {
  1714. ModBusController.updateKeyReadyStatus(
  1715. currentModeMsg.bleBean.bleDevice.mac, true, 4
  1716. )
  1717. sendLoadingEventMsg(null, false)
  1718. //连上之后没有工作票要下发就断开 看是否还有设备等待连接,没有就不断开,有就让路,一般是初始化的时候
  1719. if (BleConnectionManager.hasConnectWait()) {
  1720. BleManager.getInstance().disconnect(currentModeMsg.bleBean.bleDevice)
  1721. }
  1722. }
  1723. }
  1724. }
  1725. // 故障模式
  1726. 0x03.toByte() -> {
  1727. // TODO 上报?
  1728. ToastUtils.tip(
  1729. "${currentModeMsg.bleBean.bleDevice.mac} : " + "${CommonUtils.getStr(R.string.key_is_in_failure_mode)}"
  1730. )
  1731. }
  1732. }
  1733. }
  1734. /**
  1735. * 检查步骤和作业票详情并且下发作业票
  1736. */
  1737. private fun checkStepAndTicketDetailThenSendTicket(ticketId: Long, mac: String) {
  1738. NetApi.getStepDetail(ticketId) {
  1739. var step = 0
  1740. it?.filter { it.stepStatus == "1" }
  1741. ?.maxByOrNull { it.stepIndex!! }?.stepIndex?.let {
  1742. step = it
  1743. }
  1744. NetApi.getTicketDetail(ticketId) { ticketDetail, _ ->
  1745. if (ticketDetail == null) {
  1746. return@getTicketDetail
  1747. }
  1748. val role = ticketDetail?.ticketUserVOList?.find {
  1749. it.userId == SPUtils.getLoginUser(MyApplication.instance?.applicationContext!!)?.userId && it.userType == USER_TYPE_LOCKER
  1750. }
  1751. if (role == null) {
  1752. ToastUtils.tip(R.string.you_are_not_locker_tip)
  1753. return@getTicketDetail
  1754. }
  1755. if (step == 4) { // 上锁工作票
  1756. sendTicketBusiness(
  1757. true,
  1758. mac,
  1759. ticketDetail,
  1760. ticketDetail.ticketLockVOList?.filter { it.lockStatus != "2" }
  1761. ?.map { it.lockNfc }?.toMutableList(),
  1762. ActivityUtils.currentActivity() as BaseActivity<*>,
  1763. true
  1764. )
  1765. } else if (step == 7) { // 解锁工作票
  1766. sendTicketBusiness(
  1767. false,
  1768. mac,
  1769. ticketDetail,
  1770. null,
  1771. ActivityUtils.currentActivity() as BaseActivity<*>,
  1772. true
  1773. )
  1774. }
  1775. }
  1776. }
  1777. }
  1778. fun submitKeyData(context: Context) {
  1779. if (!CAN_RETURN) return
  1780. val updateList = SPUtils.getUpdateLockPoint(context)
  1781. if (updateList.isNotEmpty()) {
  1782. NetApi.updateLockPointBatch(updateList) { isSuccess, msg, code ->
  1783. LogUtil.i("submitKeyData还锁操作:${isSuccess},${msg},${code}")
  1784. if (isSuccess || code == 500) {
  1785. SPUtils.clearUpdateLockPoint(context)
  1786. SPUtils.clearUpdateKeyReturn(context)
  1787. }
  1788. }
  1789. }
  1790. val returnList =
  1791. SPUtils.getUpdateKeyReturn(context).filter { it.keyNfc.isNotEmpty() }.toMutableList()
  1792. if (returnList.isEmpty()) {
  1793. return
  1794. }
  1795. val itemsToRemove = returnList.toList()
  1796. var count = 0
  1797. itemsToRemove.forEach { itData ->
  1798. NetApi.updateKeyReturn(
  1799. itData.ticketId, itData.keyNfc, context.serialNo()
  1800. ) { isSuccess, msg, code ->
  1801. count++
  1802. if (isSuccess || msg == MyApplication.instance?.applicationContext!!.getString(
  1803. R.string.ticket_lost
  1804. )
  1805. ) {
  1806. returnList.remove(itData)
  1807. getBleBeanByRfid(itData.keyNfc)?.bleDevice?.let {
  1808. switchReadyMode(it)
  1809. }
  1810. if (msg == MyApplication.instance?.applicationContext!!.getString(
  1811. R.string.ticket_lost
  1812. )
  1813. ) {
  1814. sendEventMsg(
  1815. MsgEvent(
  1816. MsgEventConstants.MSG_EVENT_TICKET_FINISHED, null
  1817. )
  1818. )
  1819. }
  1820. }
  1821. if (count == itemsToRemove.size) {
  1822. if (returnList.isEmpty()) {
  1823. SPUtils.clearUpdateKeyReturn(context)
  1824. } else {
  1825. returnList.forEach {
  1826. SPUtils.saveUpdateKeyReturn(context, it)
  1827. }
  1828. }
  1829. }
  1830. }
  1831. }
  1832. }
  1833. fun sendLoadingEventMsg(str: String?, isShow: Boolean = true) {
  1834. sendEventMsg(MsgEvent(MSG_EVENT_LOADING, LoadingMsg(isShow, str, false)))
  1835. }
  1836. fun logout(context: Context) {
  1837. reConnectKey()
  1838. NetApi.logout()
  1839. // 关所有有设备的卡扣
  1840. dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }
  1841. .forEach { dockBean ->
  1842. val hasLockIdxList =
  1843. dockBean.getLockList().filter { it.isExist }.map { it.idx } as MutableList<Int>
  1844. val noLockIdxList =
  1845. dockBean.getLockList().filter { !it.isExist }.map { it.idx } as MutableList<Int>
  1846. ModBusController.controlLockBuckle(false, dockBean.addr, hasLockIdxList)
  1847. ModBusController.controlLockBuckle(true, dockBean.addr, noLockIdxList)
  1848. }
  1849. dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }
  1850. .forEach { dockBean ->
  1851. dockBean.getKeyList().forEach { key ->
  1852. if (key.isExist) {
  1853. NetApi.getKeyInfo(key.rfid.toString()) {
  1854. if (it != null && !it.macAddress.isNullOrEmpty()) {
  1855. ModBusController.updateKeyMac(
  1856. dockBean.addr, key.isLeft, it.macAddress
  1857. )
  1858. } else {
  1859. ModBusController.controlKeyBuckle(
  1860. true, key.isLeft, dockBean.addr
  1861. )
  1862. }
  1863. }
  1864. } else {
  1865. ModBusController.controlKeyBuckle(true, key.isLeft, dockBean.addr)
  1866. }
  1867. }
  1868. }
  1869. sendLoadingEventMsg(null, false)
  1870. context.startActivity(Intent(context, LoginActivity::class.java))
  1871. }
  1872. /**
  1873. * 钥匙重新连接,清除内部作业票,清除所有代取设备重新分配
  1874. */
  1875. private fun reConnectKey() {
  1876. val keyList = mDeviceTakeList.filter { it.deviceType == DEVICE_TYPE_KEY }
  1877. // 不拿的设备不归你,下次登录重新按需分配
  1878. // 尽早clear,防止触发handleCurrentMode导致重新下发作业票
  1879. mDeviceTakeList.clear()
  1880. // 连接后直接切换待机模式,让钥匙作业票失效并且重新准备完毕
  1881. keyList.forEach {
  1882. val mac = ModBusController.getKeyByRfid(it.nfc)?.mac
  1883. if (mac == null) {
  1884. NetApi.getKeyInfo(it.nfc) { keyInfo ->
  1885. keyInfo?.macAddress?.let { itMac ->
  1886. registerConnectListener(itMac) { isDone, bleBean ->
  1887. if (isDone && bleBean != null) {
  1888. switchReadyMode(bleBean.bleDevice)
  1889. }
  1890. }
  1891. }
  1892. }
  1893. } else {
  1894. registerConnectListener(mac) { isDone, bleBean ->
  1895. if (isDone && bleBean != null) {
  1896. switchReadyMode(bleBean.bleDevice)
  1897. }
  1898. }
  1899. }
  1900. }
  1901. }
  1902. /**
  1903. * 强制使用setValue,防止postValue造成数据丢失
  1904. */
  1905. fun sendEventMsg(msgEvent: MsgEvent) {
  1906. Executor.runOnMain {
  1907. mEventBus.value = msgEvent
  1908. }
  1909. }
  1910. fun addExceptionKey(rfid: String) {
  1911. LogUtil.w("addExceptionKey: $rfid")
  1912. if (mExceptionKeyList.contains(rfid)) {
  1913. return
  1914. }
  1915. mExceptionKeyList.add(rfid)
  1916. }
  1917. fun removeExceptionKey(key: String) {
  1918. LogUtil.i("removeExceptionKey: $key")
  1919. mExceptionKeyList.remove(key)
  1920. }
  1921. }