BusinessManager.kt 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. package com.grkj.iscs
  2. import android.bluetooth.BluetoothGatt
  3. import android.os.Build
  4. import android.util.Log
  5. import androidx.appcompat.app.AppCompatActivity
  6. import com.clj.fastble.BleManager
  7. import com.clj.fastble.data.BleDevice
  8. import com.clj.fastble.exception.BleException
  9. import com.google.gson.Gson
  10. import com.grkj.iscs.ble.BleBean
  11. import com.grkj.iscs.ble.BleCmdManager
  12. import com.grkj.iscs.ble.BleConst
  13. import com.grkj.iscs.ble.BleConst.REQ_WORK_TICKET_RESULT_PART
  14. import com.grkj.iscs.ble.BleConst.STATUS_READY
  15. import com.grkj.iscs.ble.BleConst.STATUS_WORK
  16. import com.grkj.iscs.ble.BleUtil
  17. import com.grkj.iscs.ble.CustomBleGattCallback
  18. import com.grkj.iscs.ble.CustomBleIndicateCallback
  19. import com.grkj.iscs.ble.CustomBleScanCallback
  20. import com.grkj.iscs.ble.CustomBleWriteCallback
  21. import com.grkj.iscs.extentions.removeLeadingZeros
  22. import com.grkj.iscs.extentions.serialNo
  23. import com.grkj.iscs.extentions.startsWith
  24. import com.grkj.iscs.extentions.toByteArray
  25. import com.grkj.iscs.extentions.toHexStrings
  26. import com.grkj.iscs.modbus.DockBean
  27. import com.grkj.iscs.modbus.ModBusController
  28. import com.grkj.iscs.model.Constants
  29. import com.grkj.iscs.model.Constants.PERMISSION_REQUEST_CODE
  30. import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_CARD
  31. import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_FINGERPRINT
  32. import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_KEY
  33. import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_LOCK
  34. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_ELEC_LOCK_BOARD
  35. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_KEY
  36. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_LOCK
  37. import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_PORTABLE
  38. import com.grkj.iscs.model.bo.WorkTicketSendBO
  39. import com.grkj.iscs.model.vo.ticket.TicketDetailRespVO
  40. import com.grkj.iscs.util.CommonUtils
  41. import com.grkj.iscs.util.Executor
  42. import com.grkj.iscs.util.NetApi
  43. import com.grkj.iscs.util.SPUtils
  44. import com.grkj.iscs.util.ToastUtils
  45. import com.grkj.iscs.util.log.LogUtil
  46. import pub.devrel.easypermissions.AfterPermissionGranted
  47. /**
  48. * 业务层管理
  49. */
  50. object BusinessManager {
  51. var NEED_AUTH = true
  52. // 归还设备是否需要登录及角色验证
  53. var CAN_RETURN = true
  54. get() {
  55. // TODO 临时注掉,方便调试
  56. // val loginUser = SPUtils.getLoginUser(MyApplication.instance!!.applicationContext!!)
  57. // return !(NEED_AUTH && (loginUser == null || loginUser.userId == 0L))
  58. return true
  59. }
  60. /**
  61. * 下发工作票还是读取工作票完成状态
  62. */
  63. var isSendTicket = true
  64. /****************************************** ModBus ******************************************/
  65. fun connectDock(isNeedInit: Boolean = false) {
  66. // 暂定100上限
  67. ModBusController.setSlaveCount(100)
  68. ModBusController.interruptReadTrashBinStatus(false)
  69. ModBusController.start(MyApplication.instance!!.applicationContext)
  70. ModBusController.unregisterListener(MyApplication.instance!!.applicationContext)
  71. if (isNeedInit) {
  72. ModBusController.initDevicesStatus()
  73. }
  74. }
  75. fun disconnectDock() {
  76. ModBusController.stop()
  77. }
  78. /**
  79. * @param key 可null,null时用applicationContext做全局监听
  80. */
  81. fun registerStatusListener(key: Any?, callBack: (DockBean) -> Unit) {
  82. ModBusController.registerStatusListener(key ?: MyApplication.instance!!.applicationContext) { res ->
  83. LogUtil.i("设备状态:${(res as List<ByteArray>).map { it.toHexStrings() }}")
  84. res.forEach { bytes ->
  85. val dockBean = ModBusController.updateStatus(bytes) ?: return@forEach
  86. if (!CAN_RETURN) {
  87. return@forEach
  88. }
  89. when (dockBean.type) {
  90. DOCK_TYPE_KEY -> {
  91. dockBean.getKeyList().forEach { keyBean ->
  92. if (keyBean.isExist) {
  93. // 放回钥匙,读取rfid
  94. ModBusController.readKeyRfid(dockBean.addr.toInt() - 1, if (keyBean.isLeft) 0 else 1) { isLeft, res ->
  95. val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
  96. ModBusController.updateKeyRfid(dockBean.addr.toInt(), keyBean.isLeft, rfid)
  97. NetApi.getKeyInfo(rfid) {
  98. if (it != null && !it.macAddress.isNullOrEmpty()) {
  99. ModBusController.updateKeyMac(dockBean.addr.toInt(), keyBean.isLeft, it.macAddress)
  100. } else {
  101. ToastUtils.tip(R.string.get_key_info_fail)
  102. }
  103. }
  104. // TODO 蓝牙通信
  105. }
  106. ModBusController.controlKeyBuckle(false, isLeft = true, dockBean.addr.toInt() - 1)
  107. }
  108. }
  109. }
  110. DOCK_TYPE_LOCK -> {
  111. dockBean.getLockList().forEach { lockBean ->
  112. if (lockBean.isExist) {
  113. ModBusController.readLockRfid(dockBean.addr.toInt() - 1, lockBean.idx) { res ->
  114. val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
  115. ModBusController.updateLockRfid(dockBean.addr.toInt(), lockBean.idx, rfid)
  116. NetApi.getLockInfo(rfid) {
  117. if (it != null) {
  118. // TODO 考虑快速拿取
  119. ModBusController.controlLockBuckle(false, dockBean.addr.toInt() - 1, lockBean.idx) { itRst ->
  120. if (itRst.isNotEmpty()) {
  121. // 上报锁具信息
  122. NetApi.updateLockReturn(rfid, MyApplication.instance!!.serialNo()) {}
  123. }
  124. }
  125. }
  126. }
  127. }
  128. }
  129. }
  130. }
  131. DOCK_TYPE_ELEC_LOCK_BOARD -> {
  132. // TODO 占位
  133. }
  134. DOCK_TYPE_PORTABLE -> {
  135. // TODO 便携式待完善
  136. dockBean.deviceList.forEach { deviceBean ->
  137. if (deviceBean.isExist) {
  138. when (deviceBean.type) {
  139. DEVICE_TYPE_KEY -> {
  140. ModBusController.readKeyRfid(dockBean.addr.toInt() - 1, deviceBean.idx) { isLeft, res ->
  141. val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
  142. ModBusController.updateKeyRfid(dockBean.addr.toInt(), true, rfid)
  143. NetApi.getKeyInfo(rfid) {
  144. if (it != null && !it.macAddress.isNullOrEmpty()) {
  145. ModBusController.updateKeyMac(dockBean.addr.toInt(), isLeft, it.macAddress)
  146. } else {
  147. ToastUtils.tip(R.string.get_key_info_fail)
  148. }
  149. }
  150. // TODO 蓝牙通信
  151. }
  152. ModBusController.controlKeyBuckle(false, isLeft = true, dockBean.addr.toInt() - 1)
  153. }
  154. DEVICE_TYPE_LOCK -> {
  155. ModBusController.readLockRfid(dockBean.addr.toInt() - 1, deviceBean.idx) { res ->
  156. val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
  157. ModBusController.updateLockRfid(dockBean.addr.toInt(), deviceBean.idx, rfid)
  158. NetApi.getLockInfo(rfid) {
  159. if (it != null) {
  160. // TODO 考虑快速拿取
  161. ModBusController.controlLockBuckle(false, dockBean.addr.toInt() - 1, deviceBean.idx) { itRst ->
  162. if (itRst.isNotEmpty()) {
  163. // 上报锁具信息
  164. NetApi.updateLockReturn(rfid, MyApplication.instance!!.serialNo()) {}
  165. }
  166. }
  167. }
  168. }
  169. }
  170. }
  171. DEVICE_TYPE_CARD -> {
  172. ModBusController.readPortalCaseCardRfid(dockBean.addr.toInt() - 1) { res ->
  173. val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
  174. println("卡片RFID : $rfid")
  175. }
  176. }
  177. DEVICE_TYPE_FINGERPRINT -> {
  178. }
  179. }
  180. }
  181. }
  182. }
  183. }
  184. callBack.invoke(dockBean)
  185. }
  186. }
  187. }
  188. fun readLockBuckleStatus() {
  189. // TODO slaveIdx暂时写死,调试用
  190. ModBusController.readBuckleStatus(true, 0) { type, res ->
  191. LogUtil.i("单slave卡扣状态 : $type - ${res.toHexStrings()}")
  192. when (type) {
  193. 0 -> {
  194. val isLeftLock = (res[4].toInt() shr 0) and 0x1 == 1
  195. val isRightLock = (res[4].toInt() shr 4) and 0x1 == 1
  196. println("锁具底座卡扣状态 : $isLeftLock - $isRightLock")
  197. }
  198. 1 -> {
  199. val tempList = mutableListOf<Boolean>()
  200. for (i in 0..7) {
  201. tempList.add((res[4].toInt() shr i) and 0x1 == 1)
  202. }
  203. println("锁具底座卡扣1-8状态 : $tempList")
  204. }
  205. 2 -> {
  206. val lock9Status = (res[4].toInt() shr 0) and 0x1 == 1
  207. val lock10Status = (res[4].toInt() shr 1) and 0x1 == 1
  208. println("锁具底座卡扣9、10状态 : $lock9Status - $lock10Status")
  209. }
  210. }
  211. }
  212. }
  213. fun readKeyBuckleStatus() {
  214. // TODO slaveIdx暂时写死,调试用
  215. ModBusController.readBuckleStatus(false, 1) { type, res ->
  216. LogUtil.i("单slave卡扣状态 : $type - ${res.toHexStrings()}")
  217. // TODO 待验证
  218. when (type) {
  219. 0 -> {
  220. val isLeftLock = (res[4].toInt() shr 0) and 0x1 == 1
  221. val isRightLock = (res[4].toInt() shr 4) and 0x1 == 1
  222. println("钥匙底座卡扣状态 : $isLeftLock - $isRightLock")
  223. }
  224. 1 -> {
  225. val tempList = mutableListOf<Boolean>()
  226. for (i in 0..7) {
  227. tempList.add((res[4].toInt() shr i) and 0x1 == 1)
  228. }
  229. println("锁具底座卡扣1-8状态 : $tempList")
  230. }
  231. 2 -> {
  232. val lock9Status = (res[4].toInt() shr 0) and 0x1 == 1
  233. val lock10Status = (res[4].toInt() shr 1) and 0x1 == 1
  234. println("锁具底座卡扣9、10状态 : $lock9Status - $lock10Status")
  235. }
  236. }
  237. }
  238. }
  239. /**
  240. * 检查钥匙和锁具数量
  241. *
  242. * @param needLockCount 需要的锁具的数量(可能在别的机柜取过)
  243. */
  244. fun checkEquipCount(needLockCount: Int, callBack: (Pair<Byte, DockBean.KeyBean?>?, MutableMap<Byte, MutableList<DockBean.LockBean>>) -> Unit) {
  245. var lockCount = 0
  246. val lockMap = ModBusController.getLocks(needLockCount)
  247. lockMap.forEach { (_, rfidList) ->
  248. lockCount += rfidList.size
  249. }
  250. val key = ModBusController.getOneKey()
  251. var tipStr = ""
  252. if (lockCount < needLockCount) {
  253. val msg = MyApplication.instance!!.applicationContext.resources.getString(R.string.lock_is_not_enough)
  254. LogUtil.w(msg)
  255. tipStr = msg
  256. }
  257. if (key == null) {
  258. val msg = MyApplication.instance!!.applicationContext.resources.getString(R.string.key_is_not_enough)
  259. LogUtil.w(msg)
  260. tipStr = if (tipStr.isEmpty()) {
  261. msg
  262. } else {
  263. tipStr + "\n" + msg
  264. }
  265. }
  266. if (tipStr.isNotEmpty()) {
  267. ToastUtils.tip(tipStr)
  268. }
  269. callBack.invoke(if (lockCount < needLockCount) null else key, lockMap)
  270. }
  271. /****************************************** 蓝牙 ******************************************/
  272. /******************************************蓝牙通用准备******************************************/
  273. private fun prepareBle(
  274. mac: String,
  275. activity: AppCompatActivity,
  276. loadingCallBack: (Boolean, String?) -> Unit,
  277. prepareDoneCallBack: (Boolean, BleBean?) -> Unit
  278. ) {
  279. CommonUtils.checkBlePermission(activity) {
  280. doScanBle(mac, loadingCallBack, prepareDoneCallBack)
  281. }
  282. }
  283. @AfterPermissionGranted(PERMISSION_REQUEST_CODE)
  284. fun doScanBle(mac: String, loadingCallBack: (Boolean, String?) -> Unit, prepareDoneCallBack: (Boolean, BleBean?) -> Unit) {
  285. LogUtil.d("扫描开始:$mac")
  286. loadingCallBack(true, "正在扫描设备...")
  287. BleUtil.instance?.scan(object : CustomBleScanCallback() {
  288. override fun onPrompt(promptStr: String?) {
  289. BleManager.getInstance().enableBluetooth()
  290. }
  291. override fun onScanStarted(success: Boolean) {
  292. LogUtil.d("扫描开始:${success}")
  293. BleUtil.instance?.deviceList?.clear()
  294. }
  295. override fun onScanning(bleDevice: BleDevice?) {
  296. bleDevice?.let {
  297. Log.d("doScanBle", "扫描到的设备:${it.mac}")
  298. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
  299. if (!it.name.isNullOrBlank()) {
  300. BleUtil.instance?.deviceList?.add(BleBean(it))
  301. } else {
  302. }
  303. } else {
  304. BleUtil.instance?.deviceList?.add(BleBean(it))
  305. }
  306. }
  307. }
  308. override fun onScanFinished(scanResultList: MutableList<BleDevice>?) {
  309. loadingCallBack.invoke(false, null)
  310. if (BleUtil.instance?.deviceList?.isEmpty() == true) {
  311. ToastUtils.tip(R.string.ble_no_device_found)
  312. return
  313. }
  314. BleUtil.instance?.getBleDeviceByMac(mac)?.bleDevice?.let {
  315. doConnect(it, loadingCallBack, prepareDoneCallBack)
  316. }
  317. }
  318. })
  319. }
  320. /**
  321. * 连接蓝牙设备
  322. */
  323. fun doConnect(
  324. bleDevice: BleDevice,
  325. loadingCallBack: (Boolean, String?) -> Unit,
  326. prepareDoneCallBack: (Boolean, BleBean?) -> Unit
  327. ) {
  328. loadingCallBack.invoke(true, CommonUtils.getStr(R.string.ble_connecting))
  329. BleManager.getInstance().disconnect(bleDevice)
  330. BleUtil.instance?.connectBySelect(bleDevice,
  331. object : CustomBleGattCallback() {
  332. override fun onPrompt(promptStr: String?) {
  333. loadingCallBack.invoke(false, promptStr)
  334. }
  335. override fun onStartConnect() {}
  336. override fun onConnectFail(bleDevice: BleDevice?, exception: BleException?) {
  337. loadingCallBack.invoke(false, CommonUtils.getStr(R.string.ble_connect_fail))
  338. prepareDoneCallBack.invoke(false, null)
  339. }
  340. override fun onConnectSuccess(bleDevice: BleDevice?, gatt: BluetoothGatt?, status: Int) {
  341. loadingCallBack.invoke(false, null)
  342. LogUtil.i("onConnectSuccess : $bleDevice")
  343. bleDevice?.let {
  344. val bleBean = BleBean(it)
  345. ToastUtils.tip("连接成功")
  346. BleUtil.instance?.deviceList?.add(bleBean)
  347. // 设置MTU
  348. Executor.delayOnMain(200) {
  349. BleUtil.instance?.setMtu(it)
  350. }
  351. // 监听
  352. Executor.delayOnMain(500) {
  353. indicate(bleBean, loadingCallBack, prepareDoneCallBack)
  354. }
  355. }
  356. }
  357. override fun onDisConnected(isActiveDisConnected: Boolean, device: BleDevice?, gatt: BluetoothGatt?, status: Int) {
  358. loadingCallBack.invoke(false, null)
  359. ToastUtils.tip(CommonUtils.getStr(R.string.ble_disconnect))
  360. BleUtil.instance?.getBleDeviceByMac(device?.mac)?.let {
  361. BleUtil.instance?.deviceList?.remove(it)
  362. }
  363. }
  364. })
  365. }
  366. /**
  367. * 监听蓝牙设备
  368. */
  369. private fun indicate(
  370. bleBean: BleBean?,
  371. loadingCallBack: (Boolean, String?) -> Unit,
  372. prepareDoneCallBack: (Boolean, BleBean?) -> Unit
  373. ) {
  374. loadingCallBack.invoke(true, "开始监听...")
  375. bleBean?.let {
  376. BleUtil.instance?.indicate(it.bleDevice, indicateCallback = object : CustomBleIndicateCallback() {
  377. override fun onPrompt(promptStr: String?) {
  378. LogUtil.i("监听onPrompt : $promptStr")
  379. }
  380. override fun onConnectPrompt(promptStr: String?) {
  381. LogUtil.i("监听onConnectPrompt : $promptStr")
  382. }
  383. override fun onDisConnectPrompt(promptStr: String?) {
  384. LogUtil.i("监听onDisConnectPrompt : $promptStr")
  385. }
  386. override fun onIndicateSuccess() {
  387. LogUtil.i("监听成功")
  388. // val testStr = "{\"cardNo\":\"80A8C0F4EA\",\"password\":\"12345678\",\"effectiveTime\":24,\"data\":[{\"taskCode\":\"202401020001\",\"taskId\":\"71b49baa49b343bc84d7e6b829ac1bdc\",\"codeId\":1,\"dataList\":[{\"dataId\":1,\"equipRfidNo\":\"049648B2E31690\",\"infoRfidNo\":\"04E3BCCA201290\",\"target\":1},{\"dataId\":2,\"equipRfidNo\":\"0405982414C563\",\"target\":0,\"prevId\":1}]},{\"taskCode\":\"202401020002\",\"taskId\":\"145b5a4cc38c41e19943f4c8b48d12b0\",\"codeId\":2,\"dataList\":[{\"dataId\":1,\"equipRfidNo\":\"045460F7F4F438\",\"infoRfidNo\":\"04BC6584C65009\",\"target\":1},{\"dataId\":2,\"equipRfidNo\":\"042B99E449E795\",\"target\":0,\"prevId\":1},{\"dataId\":3,\"equipRfidNo\":\"04A312EE848B62\",\"infoRfidNo\":\"04220E86831289\",\"target\":1,\"prevId\":2}]}],\"lockList\":[{\"lockId\":\"1\",\"rfid\":\"040E21443010E9\"},{\"lockId\":\"2\",\"rfid\":\"0457505E5861C2\"}]}"
  389. // sendTicket(testStr, it.bleDevice, loadingCallBack)
  390. getToken(bleBean, loadingCallBack, prepareDoneCallBack)
  391. }
  392. override fun onIndicateFailure(exception: BleException?) {
  393. loadingCallBack.invoke(false, null)
  394. ToastUtils.tip("监听失败")
  395. LogUtil.i("监听失败")
  396. prepareDoneCallBack.invoke(false, null)
  397. }
  398. override fun onCharacteristicChanged(data: ByteArray?) {
  399. LogUtil.i("监听数据 : ${data?.toHexStrings()}")
  400. data?.let { itData ->
  401. handleRsp(it, itData, loadingCallBack)
  402. }
  403. }
  404. })
  405. }
  406. }
  407. /**
  408. * 获取蓝牙钥匙token
  409. */
  410. private fun getToken(
  411. bleBean: BleBean?,
  412. loadingCallBack: (Boolean, String?) -> Unit,
  413. prepareDoneCallBack: (Boolean, BleBean?) -> Unit
  414. ) {
  415. loadingCallBack.invoke(true, "开始获取token...")
  416. bleBean?.let {
  417. BleCmdManager.getToken(it.bleDevice.mac, object : CustomBleWriteCallback() {
  418. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  419. loadingCallBack.invoke(false, "token获取成功")
  420. LogUtil.i("getToken success : ${bleBean.bleDevice.mac}")
  421. prepareDoneCallBack.invoke(true, bleBean)
  422. }
  423. override fun onWriteFailure(exception: BleException?) {
  424. loadingCallBack.invoke(false, "token获取失败")
  425. LogUtil.e("getToken fail : ${bleBean.bleDevice.mac}")
  426. prepareDoneCallBack.invoke(false, null)
  427. }
  428. })
  429. }
  430. }
  431. /******************************************蓝牙通用准备结束******************************************/
  432. /**
  433. * 下发工作票
  434. */
  435. fun sendTicketBusiness(
  436. mac: String,
  437. ticketDetail: TicketDetailRespVO,
  438. activity: AppCompatActivity,
  439. loadingCallBack: (Boolean, String?) -> Unit,
  440. ) {
  441. prepareBle(mac, activity, loadingCallBack) { done, bleBean ->
  442. if (done) {
  443. Executor.delayOnMain(500) {
  444. // 单bleBean json赋值
  445. bleBean?.ticketSend = generateTicketSendJson(ticketDetail)
  446. bleBean?.ticketSend?.let { itJson ->
  447. sendTicket(itJson, bleBean.bleDevice, loadingCallBack)
  448. }
  449. }
  450. } else {
  451. loadingCallBack.invoke(false, null)
  452. }
  453. }
  454. }
  455. /**
  456. * 读取工作票完成情况
  457. */
  458. fun getTicketStatusBusiness(mac: String, activity: AppCompatActivity, loadingCallBack: (Boolean, String?) -> Unit) {
  459. prepareBle(mac, activity, loadingCallBack) { done, bleBean ->
  460. if (done) {
  461. Executor.delayOnMain(500) {
  462. getTicketStatus(bleBean!!.bleDevice, loadingCallBack)
  463. }
  464. } else {
  465. loadingCallBack.invoke(false, null)
  466. }
  467. }
  468. }
  469. private fun sendTicket(jsonStr: String, bleDevice: BleDevice, loadingCallBack: (Boolean, String?) -> Unit) {
  470. loadingCallBack.invoke(true, "开始下发工作票...")
  471. BleCmdManager.sendWorkTicket(jsonStr, bleDevice = bleDevice, callback = object : CustomBleWriteCallback() {
  472. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  473. println("sendTicket success")
  474. loadingCallBack.invoke(true, "工作票下发中...")
  475. }
  476. override fun onWriteFailure(exception: BleException?) {
  477. LogUtil.e("sendTicket fail : ${bleDevice.mac}")
  478. loadingCallBack.invoke(false, "工作票下发失败")
  479. }
  480. })
  481. }
  482. /**
  483. * 生成下发工作票Json
  484. *
  485. * @param vo 工作票详情
  486. */
  487. private fun generateTicketSendJson(vo: TicketDetailRespVO): String {
  488. // 用ticketStatus的"待上锁"进行判断
  489. val isLock = vo.ticketStatus == Constants.TICKET_STATUS_READY_TO_LOCK
  490. val bo = WorkTicketSendBO(
  491. cardNo = SPUtils.getLoginUser(MyApplication.instance!!.applicationContext)?.cardNfc,
  492. )
  493. CommonUtils.getDiffHours(vo.ticketEndTime)?.let {
  494. bo.effectiveTime = it
  495. }
  496. val dataBO = WorkTicketSendBO.DataBO(
  497. taskCode = vo.ticketCode,
  498. taskId = vo.ticketId.toString(),
  499. codeId = 1
  500. )
  501. val taskList = ArrayList<WorkTicketSendBO.DataBO.DataListBO>()
  502. vo.pointDetailVOList?.let { itList ->
  503. itList.forEach { pointVO ->
  504. val task = WorkTicketSendBO.DataBO.DataListBO(
  505. dataId = pointVO.pointId?.toInt(),
  506. equipRfidNo = pointVO.pointNfc,
  507. infoRfidNo = pointVO.pointName,
  508. target = if (isLock) 0 else 1
  509. )
  510. pointVO.prePointId?.let {
  511. task.prevId = it.toInt()
  512. }
  513. if (!isLock) {
  514. task.infoRfidNo = pointVO.lockId.toString()
  515. }
  516. // TODO partCode待补充
  517. taskList.add(task)
  518. }
  519. }
  520. dataBO.dataList = taskList
  521. bo.data = mutableListOf(dataBO)
  522. if (isLock) {
  523. bo.lockList = mutableListOf()
  524. }
  525. // TODO partList 待补充
  526. val jsonStr = Gson().toJson(bo)
  527. return jsonStr
  528. }
  529. fun handleRsp(bleBean: BleBean, byteArray: ByteArray, loadingCallBack: (Boolean, String?) -> Unit) {
  530. // TODO Token校验
  531. // val len = byteArray[2].toInt()
  532. // val token = byteArray.copyOfRange(len + 7, len + 11)
  533. // if (token.contentEquals(bleBean.token)) {
  534. // LogUtil.i("Token is right")
  535. // } else {
  536. // LogUtil.e("Token is wrong")
  537. // }
  538. when {
  539. // 获取令牌
  540. byteArray.startsWith(BleConst.RSP_GET_TOKEN) -> handleToken(bleBean.bleDevice, byteArray)
  541. // 工作模式切换
  542. byteArray.startsWith(BleConst.RSP_SWITCH_MODE) -> handleSwitchModeResult(byteArray, loadingCallBack)
  543. // 工作票下发
  544. byteArray.startsWith(BleConst.RSP_SEND_WORK_TICKET) -> handleWorkTicketResult(bleBean, byteArray, loadingCallBack)
  545. // 获取设备工作票完成情况
  546. byteArray.startsWith(BleConst.RSP_WORK_TICKET_RESULT) && byteArray[3] == 0x02.toByte() ->
  547. handleTicketStatus(bleBean.bleDevice, byteArray, loadingCallBack)
  548. }
  549. }
  550. /**
  551. * 令牌处理
  552. */
  553. private fun handleToken(bleDevice: BleDevice, byteArray: ByteArray) {
  554. LogUtil.i("handleToken : ${byteArray.toHexStrings()}")
  555. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  556. it.token = byteArrayOf(byteArray[11], byteArray[12], byteArray[13], byteArray[14])
  557. println("Token 赋值 ${it.token?.toHexStrings()} : ${bleDevice.mac}")
  558. }
  559. }
  560. /**
  561. * 工作模式切换结果
  562. * job : 0x01:工作模式 0x02:待机模式
  563. * res : 0x01:成功 0x02:失败
  564. */
  565. private fun handleSwitchModeResult(byteArray: ByteArray, loadingCallBack: (Boolean, String?) -> Unit) {
  566. LogUtil.i("handleSwitchModeResult : ${byteArray.toHexStrings()}")
  567. val job = byteArray[4]
  568. val res = byteArray[5]
  569. loadingCallBack.invoke(false, null)
  570. if (res == 0x01.toByte() && job == 0x01.toByte()) {
  571. loadingCallBack.invoke(false, "切换工作模式成功")
  572. } else if (res == 0x02.toByte() && job == 0x01.toByte()) {
  573. loadingCallBack.invoke(false, "切换待机模式成功")
  574. }
  575. BleManager.getInstance().disconnectAllDevice()
  576. }
  577. /**
  578. * 工作票下发结果
  579. * res:0x00:成功 0x01:失败 0x02:传输超时 0x0D:当前IDX超出范围 0x0E:当前数据CRC校验失败 0x14:JSON结构错误 0x63:未知错误
  580. */
  581. private fun handleWorkTicketResult(bleBean: BleBean, byteArray: ByteArray, loadingCallBack: (Boolean, String?) -> Unit) {
  582. LogUtil.i("handleWorkTicketResult : ${byteArray.toHexStrings()}")
  583. loadingCallBack.invoke(false, null)
  584. val idx = byteArray[4] + byteArray[5]
  585. val total = byteArray[6] + byteArray[7]
  586. val res = byteArray[8]
  587. if (idx != total - 1) {
  588. if ((res == 0x00.toByte() || res == 0x02.toByte())) {
  589. // TODO 要判断res
  590. BleCmdManager.sendWorkTicket(
  591. BleUtil.instance?.getBleDeviceByMac(bleBean.bleDevice.mac)?.ticketSend!!,
  592. if (res == 0x00.toByte()) idx + 1 else idx,
  593. bleBean.bleDevice,
  594. object : CustomBleWriteCallback() {
  595. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {}
  596. override fun onWriteFailure(exception: BleException?) {}
  597. })
  598. }
  599. } else {
  600. LogUtil.i("Work ticket is done")
  601. // 下发完毕,切换工作模式
  602. loadingCallBack.invoke(true, "切换钥匙为工作模式")
  603. BleCmdManager.switchMode(STATUS_WORK, bleBean.bleDevice, object : CustomBleWriteCallback() {
  604. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  605. println("switch mode 1 success")}
  606. override fun onWriteFailure(exception: BleException?) {
  607. println("switch mode 1 fail")}
  608. })
  609. // 打开钥匙卡扣
  610. val keyBean = ModBusController.getKeyByRfid(bleBean.bleDevice.mac)
  611. if (keyBean == null) {
  612. loadingCallBack.invoke(false, "未找到钥匙信息")
  613. ToastUtils.tip(R.string.key_not_exists)
  614. } else {
  615. val dock = ModBusController.getDockByKeyMac(bleBean.bleDevice.mac)
  616. ModBusController.controlKeyBuckle(true, keyBean.isLeft, dock?.addr?.toInt())
  617. }
  618. }
  619. }
  620. /**
  621. * 处理工作票完成情况
  622. */
  623. private fun handleTicketStatus(bleDevice: BleDevice, byteArray: ByteArray, loadingCallBack: (Boolean, String?) -> Unit) {
  624. // TODO 需要有超时重传机制
  625. LogUtil.i("handleTicketStatus : ${byteArray.toHexStrings()}")
  626. val total = byteArray[4] + byteArray[5]
  627. val idx = byteArray[6] + byteArray[7]
  628. val crc = byteArray[8] + byteArray[9]
  629. val size = byteArray[10].toUByte() + byteArray[11].toUByte()
  630. println("工作票数据 : $total : $idx : $size")
  631. // 数据组装
  632. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  633. it.ticketStatus += byteArray.copyOfRange(12, 12 + size.toInt())
  634. }
  635. // TODO 缺少res处理
  636. if (idx != total - 1) {
  637. loadingCallBack.invoke(true, "获取工作票分包")
  638. getTicketStatusPart((idx + 1).toByteArray(), total.toByteArray(), byteArrayOf(0x01.toByte()), bleDevice, object : CustomBleWriteCallback() {
  639. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  640. println("getTicketStatusPart success")
  641. }
  642. override fun onWriteFailure(exception: BleException?) {
  643. println("getTicketStatusPart fail")
  644. }
  645. })
  646. } else {
  647. loadingCallBack.invoke(false, "工作票完成状态读取完成")
  648. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  649. println("工作票完成接收 : ${String(it.ticketStatus)}")
  650. loadingCallBack.invoke(false, "工作票完成接收${String(it.ticketStatus)}")
  651. // TODO 清空ticket
  652. it.ticketStatus = byteArrayOf()
  653. // TODO 根据工作票完成情况,切换为待机模式
  654. BleCmdManager.switchMode(STATUS_READY, bleDevice, object : CustomBleWriteCallback() {
  655. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  656. println("switch mode 1 success")}
  657. override fun onWriteFailure(exception: BleException?) {
  658. println("switch mode 1 fail")}
  659. })
  660. }
  661. }
  662. }
  663. /**
  664. * 获取工作票完成情况分包
  665. */
  666. private fun getTicketStatusPart(idx: ByteArray, total: ByteArray, res: ByteArray, bleDevice: BleDevice, callback: CustomBleWriteCallback?) {
  667. BleUtil.instance?.getBleDeviceByMac(bleDevice.mac)?.let {
  668. BleUtil.instance?.write(it.bleDevice, cmd = BleCmdManager.assembleData(it, REQ_WORK_TICKET_RESULT_PART + idx + total + res), writeCallback = callback)
  669. }
  670. }
  671. /**
  672. * 获取工作票完成情况
  673. */
  674. private fun getTicketStatus(bleDevice: BleDevice, loadingCallBack: (Boolean, String?) -> Unit) {
  675. loadingCallBack.invoke(true, "开始获取工作票")
  676. BleCmdManager.getTicketStatus(bleDevice, object : CustomBleWriteCallback() {
  677. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  678. loadingCallBack.invoke(false, "工作票获取成功")
  679. println("getTicketStatus success")}
  680. override fun onWriteFailure(exception: BleException?) {
  681. loadingCallBack.invoke(false, "工作票获取失败")
  682. println("getTicketStatus fail")}
  683. })
  684. }
  685. }