BleConnectionManager.kt 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. package com.grkj.iscs.ble
  2. import android.bluetooth.BluetoothGatt
  3. import androidx.appcompat.app.AppCompatActivity
  4. import com.clj.fastble.BleManager
  5. import com.clj.fastble.data.BleDevice
  6. import com.clj.fastble.exception.BleException
  7. import com.grkj.iscs.BusinessManager
  8. import com.grkj.iscs.BusinessManager.deviceList
  9. import com.grkj.iscs.BusinessManager.getBleDeviceByMac
  10. import com.grkj.iscs.BusinessManager.getCurrentStatus
  11. import com.grkj.iscs.BusinessManager.isTestMode
  12. import com.grkj.iscs.BusinessManager.removeExceptionKey
  13. import com.grkj.iscs.BusinessManager.sendEventMsg
  14. import com.grkj.iscs.BusinessManager.sendLoadingEventMsg
  15. import com.grkj.iscs.R
  16. import com.grkj.iscs.extentions.toHexStrings
  17. import com.grkj.iscs.modbus.ModBusController
  18. import com.grkj.iscs.modbus.ModBusController.controlKeyCharge
  19. import com.grkj.iscs.model.Constants.PERMISSION_REQUEST_CODE
  20. import com.grkj.iscs.model.eventmsg.LoadingMsg
  21. import com.grkj.iscs.model.eventmsg.MsgEvent
  22. import com.grkj.iscs.model.eventmsg.MsgEventConstants.MSG_EVENT_LOADING
  23. import com.grkj.iscs.util.ActivityUtils
  24. import com.grkj.iscs.util.CommonUtils
  25. import com.grkj.iscs.util.Executor
  26. import com.grkj.iscs.util.ToastUtils
  27. import com.grkj.iscs.util.log.LogUtil
  28. import com.grkj.iscs.view.base.BaseActivity
  29. import com.sik.sikcore.activity.ActivityTracker
  30. import com.sik.sikcore.thread.ThreadUtils
  31. import kotlinx.coroutines.Dispatchers
  32. import kotlinx.coroutines.delay
  33. import kotlinx.coroutines.suspendCancellableCoroutine
  34. import kotlinx.coroutines.withContext
  35. import pub.devrel.easypermissions.AfterPermissionGranted
  36. import java.util.LinkedList
  37. import kotlin.coroutines.resume
  38. import kotlin.coroutines.suspendCoroutine
  39. /**
  40. * BLE 连接管理工具:保持原有扫描、连接、监听、取 Token 流程,
  41. * 并增加“最大待机数”功能,超出后断开最旧连接,保证同时在线设备数不超过阈值。
  42. */
  43. object BleConnectionManager {
  44. /**
  45. * 最大待机连接数,超过则断开最旧设备。
  46. * 默认为业务常量 MAX_KEY_STAND_BY,可根据需求调整。
  47. */
  48. @Volatile
  49. var maxStandbyCount: Int = BleConst.MAX_KEY_STAND_BY
  50. @Volatile
  51. var maxConnectCount: Int = BleConst.MAX_KEY_CONNECT_COUNT
  52. // 按连接完成顺序维护待机队列
  53. private val standbyQueue = LinkedList<String>()
  54. // 原有回调管理
  55. private val connectListeners = mutableListOf<ConnectListener>()
  56. private var isPreparing: Boolean = false
  57. @Volatile
  58. private var currentConnectingMac: String? = null
  59. /**
  60. * 注册连接监听:
  61. * - 如果设备已在 deviceList 且拥有 token,立即回调并返回
  62. * - 如果 mac 已在待连接队列或正在连接,忽略重复请求
  63. * - 否则将 mac 添加到队列并触发连接流程
  64. */
  65. fun registerConnectListener(
  66. mac: String,
  67. connectNow: Boolean = false,
  68. callBack: ((Boolean, BleBean?) -> Unit)? = null
  69. ) {
  70. LogUtil.i("蓝牙连接-开始连接 : $mac")
  71. // 已连接且已获取 token
  72. deviceList.find {
  73. it.bleDevice.mac == mac && BleManager.getInstance().isConnected(mac) && it.token != null
  74. }?.let { bean ->
  75. LogUtil.i("蓝牙连接-设备已连接")
  76. callBack?.invoke(true, bean)
  77. return
  78. }
  79. if (connectNow) {
  80. LogUtil.w("蓝牙连接-立即连接 mac: $mac")
  81. unregisterConnectListener(mac)
  82. }
  83. // 重复注册检查
  84. if (connectListeners.any { it.mac == mac } || currentConnectingMac == mac) {
  85. LogUtil.w("蓝牙连接-忽略重复注册 mac: $mac")
  86. callBack?.invoke(false, null)
  87. return
  88. }
  89. // 加入队列并启动连接
  90. fun checkAndConnect(isDisconnectAll: Boolean = false) {
  91. LogUtil.w("蓝牙连接-开始检查连接 mac: $mac")
  92. if (BleManager.getInstance().allConnectedDevice.size < maxConnectCount) {
  93. connectListeners.add(ConnectListener(mac, callBack))
  94. connectKey()
  95. } else {
  96. if (connectNow && !isDisconnectAll) {
  97. LogUtil.i("蓝牙连接-超过最大连接数,但是需要立即连接,断开所有连接进行连接")
  98. BleManager.getInstance().disconnectAllDevice()
  99. checkAndConnect(true)
  100. } else {
  101. ThreadUtils.runOnIODelayed(500) {
  102. checkAndConnect()
  103. }
  104. }
  105. }
  106. }
  107. checkAndConnect()
  108. }
  109. /**
  110. * 连接监听反注册
  111. */
  112. fun unregisterConnectListener(mac: String, bleBean: BleBean? = null) {
  113. LogUtil.i("蓝牙连接-unregisterConnectListener : $mac")
  114. connectListeners.removeAll { it.mac == mac }
  115. currentConnectingMac = ""
  116. }
  117. /**
  118. * 检查是否能进行蓝牙连接准备的下一步,防止未准备完但是已经取消订阅
  119. */
  120. private fun checkProcess(mac: String?, hideLoading: Boolean = true): Boolean {
  121. val canProcess = connectListeners.any { it.mac == mac }
  122. if (!canProcess && hideLoading) sendLoadingEventMsg(null, false)
  123. return canProcess
  124. }
  125. /**
  126. * 连接钥匙,单个mac走完prepare再进行下一个
  127. */
  128. private fun connectKey() {
  129. if (connectListeners.isEmpty()) return
  130. if (isPreparing || BleManager.getInstance().allConnectedDevice.size > maxStandbyCount) {
  131. LogUtil.i("暂时不能连接:${isPreparing},${BleManager.getInstance().allConnectedDevice.size > maxStandbyCount}")
  132. Executor.delayOnMain(1000) { connectKey() }
  133. return
  134. }
  135. val listener = connectListeners.first()
  136. currentConnectingMac = listener.mac
  137. ThreadUtils.runOnIODelayed(10 * 1000) {
  138. isPreparing = false
  139. }
  140. isPreparing = true
  141. if (ActivityTracker.getCurrentActivity() == null) {
  142. LogUtil.w("蓝牙连接-Ignore connectKey : ${listener.mac} no current activity")
  143. isPreparing = false
  144. currentConnectingMac = null
  145. return
  146. }
  147. prepareBle(
  148. listener.mac, ActivityUtils.currentActivity() as BaseActivity<*>, false
  149. ) { isDone, bleBean ->
  150. Executor.runOnMain {
  151. isPreparing = false
  152. currentConnectingMac = null
  153. if (!isDone) {
  154. // 判断是否仍然待连,防止拿走;移到末尾,防止循环影响
  155. if (checkProcess(listener.mac, false)) {
  156. listener.callBack?.invoke(false, null)
  157. unregisterConnectListener(listener.mac)
  158. }
  159. ModBusController.controlKeyBuckle(true, listener.mac)
  160. LogUtil.i("蓝牙连接-连接钥匙失败")
  161. return@runOnMain
  162. }
  163. // 判断是否仍然待连,防止拿走
  164. // TODO 暂时只处理准备成功
  165. if (connectListeners.contains(listener)) {
  166. listener.callBack?.invoke(true, bleBean)
  167. unregisterConnectListener(listener.mac)
  168. }
  169. if (connectListeners.isNotEmpty()) connectKey()
  170. }
  171. }
  172. }
  173. /**
  174. * @param loadingCallBack 是否显示loading、loading文字、流程是否结束
  175. * @param prepareDoneCallBack 蓝牙连接是否成功、蓝牙连接对象
  176. */
  177. private fun prepareBle(
  178. mac: String,
  179. activity: AppCompatActivity,
  180. isNeedLoading: Boolean = false,
  181. prepareDoneCallBack: ((Boolean, BleBean?) -> Unit)?
  182. ) {
  183. if (!checkProcess(mac, false)) {
  184. LogUtil.e("蓝牙连接-Prepare is canceled : $mac")
  185. return
  186. }
  187. Executor.runOnMain {
  188. CommonUtils.checkBlePermission(activity) {
  189. doScanBle(mac, isNeedLoading, prepareDoneCallBack)
  190. }
  191. }
  192. }
  193. @AfterPermissionGranted(PERMISSION_REQUEST_CODE)
  194. private fun doScanBle(
  195. mac: String,
  196. isNeedLoading: Boolean = false,
  197. prepareDoneCallBack: ((Boolean, BleBean?) -> Unit)?
  198. ) {
  199. LogUtil.i("蓝牙连接-doScanBle:$mac")
  200. if (!checkProcess(mac, false)) {
  201. LogUtil.e("蓝牙连接-Prepare is canceled : $mac")
  202. return
  203. }
  204. if (isNeedLoading) sendEventMsg(
  205. MsgEvent(
  206. MSG_EVENT_LOADING, LoadingMsg(true, "正在扫描设备...", null)
  207. )
  208. )
  209. BleUtil.instance?.scan(object : CustomBleScanCallback() {
  210. override fun onPrompt(promptStr: String?) {
  211. // 蓝牙未启动重试
  212. LogUtil.i("蓝牙连接-参数:${promptStr}")
  213. BleManager.getInstance().enableBluetooth()
  214. doScanBle(mac, isNeedLoading, prepareDoneCallBack)
  215. }
  216. override fun onScanStarted(success: Boolean) {
  217. LogUtil.i("蓝牙连接-onScanStarted:${success}")
  218. if (!success) {
  219. if (isNeedLoading) sendEventMsg(
  220. MsgEvent(
  221. MSG_EVENT_LOADING, LoadingMsg(false, null, null)
  222. )
  223. )
  224. prepareDoneCallBack?.invoke(false, null)
  225. }
  226. }
  227. override fun onScanning(bleDevice: BleDevice?) {
  228. val mac = bleDevice?.mac ?: return
  229. LogUtil.i("蓝牙连接-onScanning:$mac")
  230. if (mac.equals(mac, ignoreCase = true)) {
  231. // 找到目标设备,马上停止扫描
  232. LogUtil.i("找到目标设备 $mac,停止扫描并尝试连接")
  233. BleManager.getInstance().cancelScan()
  234. // 立刻调用 doConnect,下一步进入连接流程
  235. doConnect(bleDevice, isNeedLoading, prepareDoneCallBack)
  236. }
  237. }
  238. override fun onScanFinished(scanResultList: MutableList<BleDevice>?) {
  239. LogUtil.i("蓝牙连接-onScanFinished: $mac - ${scanResultList?.none { it.mac == mac }}")
  240. if (isNeedLoading) sendEventMsg(
  241. MsgEvent(
  242. MSG_EVENT_LOADING, LoadingMsg(false, null, null)
  243. )
  244. )
  245. // 没有扫描到
  246. if (scanResultList?.none { it.mac == mac } == true) {
  247. LogUtil.w("$mac is not scanned")
  248. prepareDoneCallBack?.invoke(false, null)
  249. }
  250. }
  251. })
  252. }
  253. /**
  254. * 连接蓝牙设备
  255. */
  256. private fun doConnect(
  257. bleDevice: BleDevice,
  258. isNeedLoading: Boolean = false,
  259. prepareDoneCallBack: ((Boolean, BleBean?) -> Unit)?
  260. ) {
  261. LogUtil.i("蓝牙连接-doConnect : ${bleDevice.mac}")
  262. if (!checkProcess(bleDevice.mac, false)) {
  263. LogUtil.e("蓝牙连接-Prepare is canceled : ${bleDevice.mac}")
  264. return
  265. }
  266. if (isNeedLoading) sendEventMsg(
  267. MsgEvent(
  268. MSG_EVENT_LOADING,
  269. LoadingMsg(true, CommonUtils.getStr(R.string.ble_connecting), null)
  270. )
  271. )
  272. ThreadUtils.runOnIO {
  273. BleManager.getInstance().disconnect(bleDevice)
  274. delay(300)
  275. BleUtil.instance?.connectBySelect(
  276. bleDevice, object : CustomBleGattCallback() {
  277. override fun onPrompt(promptStr: String?) {
  278. if (isNeedLoading) sendEventMsg(
  279. MsgEvent(
  280. MSG_EVENT_LOADING, LoadingMsg(false, promptStr, null)
  281. )
  282. )
  283. }
  284. override fun onStartConnect() {}
  285. override fun onConnectFail(bleDevice: BleDevice?, exception: BleException?) {
  286. if (isNeedLoading) sendEventMsg(
  287. MsgEvent(
  288. MSG_EVENT_LOADING,
  289. LoadingMsg(
  290. false,
  291. CommonUtils.getStr(R.string.ble_connect_fail),
  292. false
  293. )
  294. )
  295. )
  296. LogUtil.e("蓝牙连接-onConnectFail : ${bleDevice?.mac} - ${exception?.description}")
  297. prepareDoneCallBack?.invoke(false, null)
  298. }
  299. override fun onConnectSuccess(
  300. bleDevice: BleDevice?, gatt: BluetoothGatt?, status: Int
  301. ) {
  302. if (isNeedLoading) sendEventMsg(
  303. MsgEvent(
  304. MSG_EVENT_LOADING, LoadingMsg(false, null, null)
  305. )
  306. )
  307. LogUtil.i("蓝牙连接-onConnectSuccess : ${bleDevice?.mac}")
  308. bleDevice?.let {
  309. deviceList.removeIf { it.bleDevice.mac == bleDevice.mac }
  310. val bleBean = BleBean(it)
  311. deviceList.add(bleBean)
  312. removeExceptionKey(it.mac)
  313. // 设置MTU
  314. Executor.delayOnMain(200) {
  315. if (!checkProcess(bleDevice.mac, false)) {
  316. LogUtil.e("Prepare is canceled : ${bleDevice.mac}")
  317. return@delayOnMain
  318. }
  319. BleUtil.instance?.setMtu(it)
  320. }
  321. // 监听
  322. Executor.delayOnMain(500) {
  323. indicate(bleBean, isNeedLoading, prepareDoneCallBack)
  324. }
  325. }
  326. }
  327. override fun onDisConnected(
  328. isActiveDisConnected: Boolean,
  329. device: BleDevice?,
  330. gatt: BluetoothGatt?,
  331. status: Int
  332. ) {
  333. if (isNeedLoading) sendEventMsg(
  334. MsgEvent(
  335. MSG_EVENT_LOADING, LoadingMsg(false, null, false)
  336. )
  337. )
  338. LogUtil.i("蓝牙连接-onDisConnected : ${device?.mac} - $isActiveDisConnected")
  339. getBleDeviceByMac(device?.mac)?.let {
  340. deviceList.remove(it)
  341. it.token = null
  342. }
  343. bleDevice.mac?.let { itMac ->
  344. unregisterConnectListener(itMac)
  345. }
  346. if (!isActiveDisConnected) {
  347. // 测试模式下不重连
  348. if (isTestMode) {
  349. return
  350. }
  351. // 断开和重连之间最好间隔一段时间,否则可能会出现长时间连接不上的情况
  352. Executor.delayOnMain(300) {
  353. registerConnectListener(bleDevice.mac) { isDone, bleBean ->
  354. if (isDone && bleBean != null) {
  355. Executor.delayOnMain(300) {
  356. getCurrentStatus(6, bleBean.bleDevice)
  357. }
  358. }
  359. }
  360. }
  361. } else {
  362. ModBusController.updateKeyReadyStatus(bleDevice.mac, false, 3)
  363. }
  364. }
  365. })
  366. }
  367. }
  368. /**
  369. * 监听蓝牙设备
  370. */
  371. private fun indicate(
  372. bleBean: BleBean?,
  373. isNeedLoading: Boolean = false,
  374. prepareDoneCallBack: ((Boolean, BleBean?) -> Unit)?
  375. ) {
  376. if (!checkProcess(bleBean?.bleDevice?.mac, false)) {
  377. LogUtil.e("蓝牙连接-Prepare is canceled : ${bleBean?.bleDevice?.mac}")
  378. return
  379. }
  380. if (isNeedLoading) sendEventMsg(
  381. MsgEvent(
  382. MSG_EVENT_LOADING, LoadingMsg(true, "开始监听...", null)
  383. )
  384. )
  385. bleBean?.let {
  386. var isIndicateSuccess = false
  387. BleUtil.instance?.indicate(
  388. it.bleDevice, indicateCallback = object : CustomBleIndicateCallback() {
  389. override fun onPrompt(promptStr: String?) {
  390. LogUtil.i("蓝牙连接-indicate onPrompt : $promptStr")
  391. }
  392. override fun onConnectPrompt(promptStr: String?) {
  393. LogUtil.i("蓝牙连接-indicate onConnectPrompt : $promptStr")
  394. }
  395. override fun onDisConnectPrompt(promptStr: String?) {
  396. LogUtil.i("蓝牙连接-indicate onDisConnectPrompt : $promptStr")
  397. }
  398. override fun onIndicateSuccess() {
  399. LogUtil.i("蓝牙连接-onIndicateSuccess")
  400. isIndicateSuccess = true
  401. getToken(bleBean, isNeedLoading, prepareDoneCallBack)
  402. }
  403. override fun onIndicateFailure(exception: BleException?) {
  404. if (isNeedLoading) sendEventMsg(
  405. MsgEvent(
  406. MSG_EVENT_LOADING, LoadingMsg(false, null, false)
  407. )
  408. )
  409. LogUtil.e("蓝牙连接-onIndicateFailure : ${bleBean.bleDevice.mac} - ${exception?.description}")
  410. Executor.delayOnIO(500) {
  411. if (isIndicateSuccess) {
  412. return@delayOnIO
  413. }
  414. prepareDoneCallBack?.invoke(false, null)
  415. }
  416. }
  417. override fun onCharacteristicChanged(data: ByteArray?) {
  418. LogUtil.i("蓝牙连接-onCharacteristicChanged : ${data?.toHexStrings()}")
  419. data?.let { itData ->
  420. BusinessManager.handleRsp(
  421. it,
  422. itData,
  423. isNeedLoading,
  424. prepareDoneCallBack
  425. )
  426. }
  427. }
  428. })
  429. }
  430. }
  431. /**
  432. * 获取蓝牙钥匙token
  433. */
  434. private fun getToken(
  435. bleBean: BleBean?,
  436. isNeedLoading: Boolean = false,
  437. prepareDoneCallBack: ((Boolean, BleBean?) -> Unit)?
  438. ) {
  439. if (!checkProcess(bleBean?.bleDevice?.mac)) {
  440. LogUtil.e("蓝牙连接-Prepare is canceled : ${bleBean?.bleDevice?.mac}")
  441. return
  442. }
  443. if (isNeedLoading) sendEventMsg(
  444. MsgEvent(
  445. MSG_EVENT_LOADING, LoadingMsg(true, "开始获取token...", null)
  446. )
  447. )
  448. bleBean?.let {
  449. BleCmdManager.getToken(it.bleDevice.mac, object : CustomBleWriteCallback() {
  450. override fun onWriteSuccess(current: Int, total: Int, justWrite: ByteArray?) {
  451. if (isNeedLoading) sendEventMsg(
  452. MsgEvent(
  453. MSG_EVENT_LOADING, LoadingMsg(false, "token获取成功", null)
  454. )
  455. )
  456. LogUtil.i("蓝牙连接-getToken success : ${bleBean.bleDevice.mac}")
  457. }
  458. override fun onWriteFailure(exception: BleException?) {
  459. if (isNeedLoading) sendEventMsg(
  460. MsgEvent(
  461. MSG_EVENT_LOADING, LoadingMsg(false, "token获取失败", false)
  462. )
  463. )
  464. LogUtil.e("蓝牙连接-getToken fail : ${bleBean.bleDevice.mac}")
  465. prepareDoneCallBack?.invoke(false, null)
  466. }
  467. })
  468. }
  469. }
  470. /**
  471. * 对单个 MAC 做下面两步:
  472. * 1. 先尝试不充电连接,若成功就返回 true;
  473. * 2. 否则开启“充电”,等 500ms,再尝试一次连接,连接成功后断电并返回 true;否则返回 false。
  474. */
  475. suspend fun tryConnectWithOptionalCharge(mac: String, withOpenCharge: Boolean = true): Boolean =
  476. withContext(Dispatchers.IO) {
  477. // -------- 第一次尝试 --------
  478. LogUtil.i("蓝牙连接-第一次尝试")
  479. val firstTry = suspendCancellableCoroutine<Boolean> { cont ->
  480. // 1. 定义一个 flag,确保只 resume 一次
  481. var isCalled = false
  482. BusinessManager.registerConnectListener(mac, true) { isDone, _ ->
  483. if (isCalled) {
  484. return@registerConnectListener
  485. }
  486. isCalled = true
  487. if (isDone) {
  488. // 连接成功后,把电关掉(以防万一)
  489. controlKeyCharge(false, mac) { }
  490. cont.resume(true)
  491. } else {
  492. cont.resume(false)
  493. }
  494. cont.cancel()
  495. }
  496. }
  497. LogUtil.i("蓝牙连接-第一次连接:${firstTry},是否继续尝试上电连接${withOpenCharge}")
  498. if (firstTry) {
  499. return@withContext true
  500. }
  501. if (!withOpenCharge) {
  502. return@withContext false
  503. }
  504. // -------- 第二次尝试:先开电,再连 --------
  505. // 开电,并等待回调
  506. suspendCoroutine<Unit> { unitCont ->
  507. controlKeyCharge(false, mac) {
  508. ThreadUtils.runOnIO {
  509. delay(500)
  510. controlKeyCharge(true, mac) {
  511. unitCont.resume(Unit)
  512. }
  513. }
  514. }
  515. }
  516. LogUtil.i("蓝牙连接-开启充电并等待500ms")
  517. // 等 500ms 保证硬件电源稳定
  518. delay(500)
  519. // 再次注册连接监听
  520. val secondTry = suspendCancellableCoroutine<Boolean> { cont ->
  521. var isCalled = false
  522. BusinessManager.registerConnectListener(mac, true) { isDone, _ ->
  523. if (isCalled) {
  524. return@registerConnectListener
  525. }
  526. isCalled = true
  527. // 无论成功或失败,都先把电关掉
  528. controlKeyCharge(false, mac) { }
  529. cont.resume(isDone)
  530. cont.cancel()
  531. }
  532. }
  533. return@withContext secondTry
  534. }
  535. /**
  536. * 扫描在线的蓝牙钥匙并发送指令关机
  537. */
  538. suspend fun scanOnlineKeyLockMacAndSwitchModeToClose(): Boolean {
  539. return suspendCancellableCoroutine { parentCont ->
  540. BleUtil.instance?.scan(object : CustomBleScanCallback() {
  541. override fun onPrompt(promptStr: String?) {
  542. // 蓝牙未启动重试
  543. LogUtil.i("设备录入-参数:${promptStr}")
  544. BleManager.getInstance().enableBluetooth()
  545. ThreadUtils.runOnMainDelayed(300) {
  546. scanOnlineKeyLockMacAndSwitchModeToClose()
  547. }
  548. }
  549. override fun onScanStarted(success: Boolean) {
  550. LogUtil.i("设备录入-onScanStarted:${success}")
  551. if (!success) {
  552. ThreadUtils.runOnMainDelayed(300) {
  553. scanOnlineKeyLockMacAndSwitchModeToClose()
  554. }
  555. }
  556. }
  557. override fun onScanning(bleDevice: BleDevice?) {
  558. val mac = bleDevice?.mac ?: return
  559. LogUtil.i("设备录入-onScanning:$mac")
  560. }
  561. override fun onScanFinished(scanResultList: MutableList<BleDevice>?) {
  562. val devicesSnapshot = scanResultList?.toList().orEmpty()
  563. ThreadUtils.runOnIO {
  564. devicesSnapshot.forEach {
  565. val connected = tryConnectWithOptionalCharge(it.mac, false)
  566. if (connected) {
  567. val sendSuccess = sendEmptyTicketJson(it)
  568. LogUtil.i("设备录入-发送切换工作模式:${it.mac},${sendSuccess}")
  569. }
  570. }
  571. parentCont.resume(true)
  572. }
  573. }
  574. })
  575. }
  576. }
  577. /**
  578. * 发送空作业票
  579. */
  580. private suspend fun sendEmptyTicketJson(bleDevice: BleDevice): Boolean {
  581. return suspendCancellableCoroutine<Boolean> { cont ->
  582. BleCmdManager.sendWorkTicket(
  583. BusinessManager.generateEmptyTicketSendJson(),
  584. bleDevice = bleDevice,
  585. callback = object : CustomBleWriteCallback() {
  586. override fun onWriteSuccess(
  587. current: Int,
  588. total: Int,
  589. justWrite: ByteArray?
  590. ) {
  591. ThreadUtils.runOnIO {
  592. delay(3000)
  593. cont.resume(switchWorkMode(bleDevice))
  594. }
  595. }
  596. override fun onWriteFailure(exception: BleException?) {
  597. ThreadUtils.runOnMainDelayed(300) {
  598. cont.resume(sendEmptyTicketJson(bleDevice))
  599. }
  600. }
  601. })
  602. }
  603. }
  604. /**
  605. * 切换工作模式
  606. */
  607. private suspend fun switchWorkMode(bleDevice: BleDevice): Boolean {
  608. return suspendCancellableCoroutine<Boolean> { cont ->
  609. BleCmdManager.switchMode(
  610. BleConst.STATUS_WORK,
  611. bleDevice,
  612. object : CustomBleWriteCallback() {
  613. override fun onWriteSuccess(
  614. current: Int,
  615. total: Int,
  616. justWrite: ByteArray?
  617. ) {
  618. BleManager.getInstance().disconnect(bleDevice)
  619. ThreadUtils.runOnIO {
  620. delay(800)
  621. cont.resume(true)
  622. }
  623. LogUtil.i("设备录入-切换模式发送成功 : ${bleDevice.mac}")
  624. }
  625. override fun onWriteFailure(exception: BleException?) {
  626. LogUtil.e("设备录入-切换模式发送失败 : ${bleDevice.mac}")
  627. ThreadUtils.runOnMainDelayed(300) {
  628. cont.resume(sendEmptyTicketJson(bleDevice))
  629. }
  630. }
  631. })
  632. }
  633. }
  634. /**
  635. * 切换待机模式
  636. */
  637. fun switchReadyMode(bleDevice: BleDevice) {
  638. BleCmdManager.switchMode(
  639. BleConst.STATUS_READY,
  640. bleDevice,
  641. object : CustomBleWriteCallback() {
  642. override fun onWriteSuccess(
  643. current: Int,
  644. total: Int,
  645. justWrite: ByteArray?
  646. ) {
  647. BleManager.getInstance().disconnect(bleDevice)
  648. LogUtil.i("设备录入-切换模式发送成功 : ${bleDevice.mac}")
  649. }
  650. override fun onWriteFailure(exception: BleException?) {
  651. LogUtil.e("设备录入-切换模式发送失败 : ${bleDevice.mac}")
  652. }
  653. })
  654. }
  655. /**
  656. * 扫描在线的蓝牙
  657. */
  658. fun scanOnlineKeyLockMac(callback: (List<BleDevice>?) -> Unit) {
  659. BleUtil.instance?.scan(object : CustomBleScanCallback() {
  660. override fun onPrompt(promptStr: String?) {
  661. // 蓝牙未启动重试
  662. LogUtil.d("设备录入-参数:${promptStr}")
  663. BleManager.getInstance().enableBluetooth()
  664. ThreadUtils.runOnMainDelayed(300) {
  665. scanOnlineKeyLockMac(callback)
  666. }
  667. }
  668. override fun onScanStarted(success: Boolean) {
  669. LogUtil.d("设备录入-onScanStarted:${success}")
  670. if (!success) {
  671. ThreadUtils.runOnMainDelayed(300) {
  672. scanOnlineKeyLockMac(callback)
  673. }
  674. }
  675. }
  676. override fun onScanning(bleDevice: BleDevice?) {
  677. val mac = bleDevice?.mac ?: return
  678. LogUtil.d("设备录入-onScanning:$mac")
  679. }
  680. override fun onScanFinished(scanResultList: MutableList<BleDevice>?) {
  681. LogUtil.d("设备录入-扫描完成:$scanResultList")
  682. callback(scanResultList?.toList())
  683. }
  684. })
  685. }
  686. // 蓝牙连接准备监听
  687. data class ConnectListener(
  688. val mac: String,
  689. val callBack: ((Boolean, BleBean?) -> Unit)? = null
  690. )
  691. }