Przeglądaj źródła

1. 移植并优化蓝牙通信部分的代码逻辑

bjb 2 tygodni temu
rodzic
commit
a1143f7b1a

+ 2 - 2
app/src/main/java/com/iscs/bozzys/Entry.kt

@@ -5,7 +5,7 @@ import com.alibaba.sdk.android.push.noonesdk.PushInitConfig
 import com.alibaba.sdk.android.push.noonesdk.PushServiceFactory
 import com.alibaba.sdk.android.push.noonesdk.PushServiceFactory
 import com.iscs.bozzys.utils.LogUtil
 import com.iscs.bozzys.utils.LogUtil
 import com.iscs.bozzys.utils.Storage
 import com.iscs.bozzys.utils.Storage
-import com.iscs.bozzys.utils.ble.BleTask
+import com.iscs.bozzys.utils.ble.KeyManager
 
 
 /**
 /**
  * App主入口
  * App主入口
@@ -26,7 +26,7 @@ class Entry : Application() {
         // 初始化消息推送
         // 初始化消息推送
         initPush()
         initPush()
         // 初始化蓝牙设备任务处理器
         // 初始化蓝牙设备任务处理器
-        BleTask.init(this)
+        KeyManager.init(this)
     }
     }
 
 
     private fun initPush() {
     private fun initPush() {

+ 5 - 5
app/src/main/java/com/iscs/bozzys/ui/pages/vm/VMDetailTask.kt

@@ -23,7 +23,7 @@ import com.iscs.bozzys.ui.pages.compose.checkCanCommitReturnTips
 import com.iscs.bozzys.ui.pages.compose.getFormListByJsonList
 import com.iscs.bozzys.ui.pages.compose.getFormListByJsonList
 import com.iscs.bozzys.utils.LogUtil
 import com.iscs.bozzys.utils.LogUtil
 import com.iscs.bozzys.utils.SystemUtil
 import com.iscs.bozzys.utils.SystemUtil
-import com.iscs.bozzys.utils.ble.BleTask
+import com.iscs.bozzys.utils.ble.KeyManager
 import com.iscs.bozzys.utils.ble.OnTaskStatusChangeListener
 import com.iscs.bozzys.utils.ble.OnTaskStatusChangeListener
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -279,7 +279,7 @@ class VMDetailTask : VMBase() {
             keys.find { it.keyNfc == id }?.let {
             keys.find { it.keyNfc == id }?.let {
                 // 找到钥匙,匹配到钥匙中
                 // 找到钥匙,匹配到钥匙中
                 _state.value = _state.value.copy(keys = mutableListOf(it))
                 _state.value = _state.value.copy(keys = mutableListOf(it))
-                BleTask.markDeviceUsed(it.macAddress, "")
+                KeyManager.markDeviceUsed(it.macAddress, "")
                 return@launch
                 return@launch
             }
             }
             // 查找挂锁
             // 查找挂锁
@@ -312,16 +312,16 @@ class VMDetailTask : VMBase() {
             val mac = _state.value.keys.getOrNull(0)?.macAddress ?: ""
             val mac = _state.value.keys.getOrNull(0)?.macAddress ?: ""
             val node = _state.value.node
             val node = _state.value.node
             val locks = _state.value.locks
             val locks = _state.value.locks
-            BleTask.markDeviceUsed(mac, node.toKeyTicket(locks))
+            KeyManager.markDeviceUsed(mac, node.toKeyTicket(locks))
         }
         }
     }
     }
 
 
     fun onAddTaskListener() {
     fun onAddTaskListener() {
-        BleTask.addOnTaskChangeListener(onTaskChangeListener)
+        KeyManager.addOnTaskChangeListener(onTaskChangeListener)
     }
     }
 
 
     fun onRemoveTaskListener() {
     fun onRemoveTaskListener() {
-        BleTask.removeOnTaskChangeListener(onTaskChangeListener)
+        KeyManager.removeOnTaskChangeListener(onTaskChangeListener)
     }
     }
 
 
     /**
     /**

+ 0 - 3
app/src/main/java/com/iscs/bozzys/utils/ble/BleConnectResult.kt

@@ -1,3 +0,0 @@
-package com.iscs.bozzys.utils.ble
-
-data class BleConnectResult(val connected: Boolean = false)

+ 0 - 20
app/src/main/java/com/iscs/bozzys/utils/ble/BleFrame.kt

@@ -1,20 +0,0 @@
-package com.iscs.bozzys.utils.ble
-
-/**
- * 构建蓝牙通信数据帧
- *
- * @param reqCode   请求码
- * @param data      请求或响应数据
- * @param rspCode   响应码
- */
-data class BleFrame(val reqCode: ByteArray, val data: ByteArray, val rspCode: ByteArray, val writeUUID: String = "")
-
-/**
- * 蓝牙作业票数据包信息
- *
- * @param pkgIdx    当前包索引
- * @param pkgSize   当前包大小
- * @param pkgData   当前包数据
- * @param pkgTotal  总包数
- */
-data class BleTicketDataPackage(val pkgIdx: Int, val pkgSize: Int, val pkgData: ByteArray, val pkgTotal: Int)

+ 0 - 204
app/src/main/java/com/iscs/bozzys/utils/ble/BleFrameExt.kt

@@ -1,204 +0,0 @@
-package com.iscs.bozzys.utils.ble
-
-import com.iscs.bozzys.utils.crc16
-import com.iscs.bozzys.utils.toByteArray
-import java.nio.ByteBuffer
-import java.nio.ByteOrder
-
-/**
- * 用于蓝牙钥匙通信的帧数据封装,以及数据响应后的解析处理
- */
-object BleFrameExt {
-
-    /**
-     * 构建蓝牙设备获取设备token的操作
-     */
-    fun buildBLEGetTokenCMD(): BleFrame {
-        return BleFrame(BleProtocol.REQ_GET_TOKEN, getUnixTime(), BleProtocol.RSP_GET_TOKEN)
-    }
-
-    /**
-     * 构建蓝牙设备获取蓝牙设备电量
-     */
-    fun ByteArray.buildBLEGetPowerCMD(): BleFrame {
-        return BleFrame(BleProtocol.REQ_POWER_STATUS, getUnixTime() + this, BleProtocol.RSP_POWER_STATUS)
-    }
-
-    /**
-     * 构建蓝牙获取当前设备状态
-     */
-    fun ByteArray.buildBLEGetStatusCMD(): BleFrame {
-        return BleFrame(BleProtocol.REQ_CURRENT_STATUS, getUnixTime() + this, BleProtocol.RSP_CURRENT_STATUS)
-    }
-
-    /**
-     * 切换当前工作模式
-     *
-     * @param runMode 有效运行模式 WORK STBY
-     */
-    fun ByteArray.buildBLESwitchRunModeCMD(runMode: BleRunMode): BleFrame {
-        var mode = 0x00
-        if (runMode == BleRunMode.WORK) {
-            mode = 0x01
-        } else if (runMode == BleRunMode.STBY) {
-            mode = 0x02
-        }
-        return BleFrame(BleProtocol.REQ_SWITCH_MODE, byteArrayOf(mode.toByte()) + getUnixTime() + this, BleProtocol.RSP_SWITCH_MODE)
-    }
-
-    /**
-     * 构建用于作业票下发的指令集合
-     */
-    fun ByteArray.buildBLETicketDataCMDList(json: String): List<BleFrame> {
-        val cmds = ArrayList<BleFrame>()
-        // 先将数据转换为字节数组
-        val pkg = json.toByteArray()
-        // 包大小
-        val pkgSize = pkg.size
-        // 分包数
-        val pkgCount = (pkgSize + 128 - 1) / 128
-        for (idx in 0 until pkgCount) {
-            val endIdx = if ((idx + 1) == pkgCount) pkgSize else ((idx + 1) * 128)
-            // 将包做切片操作
-            val subPkg = pkg.copyOfRange(idx * 128, endIdx)
-            // 封装数据包操作
-            val data = pkgCount.toByteArray() + idx.toByteArray() + subPkg.crc16(0, subPkg.size) + subPkg.size.toByteArray() + subPkg
-            // 装包处理
-            cmds.add(
-                BleFrame(
-                    BleProtocol.REQ_SEND_WORK_TICKET,
-                    (data.size + 1).toByteArray(1) + 0x02.toByteArray(1) + data + getUnixTime() + this,
-                    BleProtocol.RSP_SEND_WORK_TICKET,
-                    BleProtocol.WRITE_UUID
-                )
-            )
-        }
-        return cmds
-    }
-
-    /**
-     * 构建获取作业票信息命令
-     */
-    fun ByteArray.buildBLEGetTicketInfoCMD(idx: Int = 0, pkgTotal: Int = 0): BleFrame {
-        if (idx > 0) {
-            return BleFrame(
-                BleProtocol.REQ_WORK_TICKET_RESULT_PART,
-                idx.toByteArray() + pkgTotal.toByteArray() + byteArrayOf(0x00) + getUnixTime() + this,
-                BleProtocol.RSP_WORK_TICKET_RESULT
-            )
-        }
-        return BleFrame(BleProtocol.REQ_WORK_TICKET_RESULT, getUnixTime() + this, BleProtocol.RSP_WORK_TICKET_RESULT)
-    }
-
-    /**
-     * 断开蓝牙操作
-     */
-    fun ByteArray.buildBLEDisconnectCMD(): BleFrame {
-        return BleFrame(BleProtocol.REQ_DISCONNECT, getUnixTime() + this, BleProtocol.RSP_DISCONNECT)
-    }
-
-    /**
-     * 获取token
-     */
-    fun BleFrame.getToken(): ByteArray {
-        if (this.rspCode.contentEquals(BleProtocol.RSP_GET_TOKEN)) {
-            return this.data
-        }
-        return byteArrayOf()
-    }
-
-    /**
-     * 获取当前运行模式
-     */
-    fun BleFrame.getRunMode(): BleRunMode {
-        if (this.rspCode.contentEquals(BleProtocol.RSP_CURRENT_STATUS)) {
-            return when (this.data[0]) {
-                0x01.toByte() -> BleRunMode.WORK
-                0x02.toByte() -> BleRunMode.STBY
-                0x03.toByte() -> BleRunMode.FAILURE
-                else -> BleRunMode.NONE
-            }
-        }
-        return BleRunMode.NONE
-    }
-
-    /**
-     * 获取当前设备电量
-     */
-    fun BleFrame.getPower(): Int {
-        if (this.rspCode.contentEquals(BleProtocol.RSP_POWER_STATUS)) {
-            return this.data[0].toInt()
-        }
-        return -1
-    }
-
-    /**
-     * 获取当前工作模式切换是否成功
-     */
-    fun BleFrame.getSwitchRunModeResult(): Boolean {
-        if (this.rspCode.contentEquals(BleProtocol.RSP_SWITCH_MODE)) {
-            return this.data[1].toInt() == 1
-        }
-        return false
-    }
-
-    /**
-     * 响应发送工作票结果
-     *
-     * @return 0-成功 1-失败 2-超时 13-idx超出范围
-     */
-    fun BleFrame.getSendTicketResult(): Int {
-        if (this.rspCode.contentEquals(BleProtocol.RSP_SEND_WORK_TICKET)) {
-            return this.data[4].toInt()
-        }
-        return 99
-    }
-
-    /**
-     * 获取包数据信息
-     *
-     * @return 接收到的包数据信息
-     */
-    fun BleFrame.getTicketPackageInfo(): BleTicketDataPackage {
-        if (this.rspCode.contentEquals(BleProtocol.RSP_WORK_TICKET_RESULT)) {
-            // 解析数据
-            val pkgCount = this.data[2] + this.data[3]
-            val pkgIdx = this.data[4] + this.data[5]
-            val pkgSize = this.data[8].toUByte() + this.data[9].toUByte()
-            val pkgData = this.data.copyOfRange(10, pkgSize.toInt() + 10)
-            return BleTicketDataPackage(pkgIdx, pkgSize.toInt(), pkgData, pkgCount)
-        }
-        return BleTicketDataPackage(0, 0, byteArrayOf(), 0)
-    }
-
-    /**
-     * 获取断开连接结果
-     *
-     * @param 1-成功 2-失败
-     */
-    fun BleFrame.getDisconnectResult(): Int {
-        if (this.rspCode.contentEquals(BleProtocol.RSP_DISCONNECT)) {
-            return this.data[0].toInt()
-        }
-        return 99
-    }
-
-}
-
-/**
- * 获取手机当前时间,提供设备使用
- */
-private fun getUnixTime(): ByteArray {
-    val tempArr = (System.currentTimeMillis() / 1000).toByteArray()
-    val timeStampArr = byteArrayOf(tempArr[0], tempArr[1], tempArr[2], tempArr[3])
-    return timeStampArr
-}
-
-/**
- * 字节数据转换,将长整型数据转换为字节数组
- */
-private fun Long.toByteArray(): ByteArray {
-    return ByteBuffer.allocate(java.lang.Long.BYTES)
-        .order(ByteOrder.LITTLE_ENDIAN)
-        .putLong(this).array()
-}

+ 0 - 352
app/src/main/java/com/iscs/bozzys/utils/ble/BleManager.kt

@@ -1,352 +0,0 @@
-package com.iscs.bozzys.utils.ble
-
-import android.annotation.SuppressLint
-import android.app.Application
-import android.bluetooth.BluetoothDevice
-import android.bluetooth.BluetoothGatt
-import android.bluetooth.BluetoothGattCallback
-import android.bluetooth.BluetoothGattCharacteristic
-import android.bluetooth.BluetoothGattDescriptor
-import android.bluetooth.BluetoothManager
-import android.bluetooth.BluetoothProfile
-import android.bluetooth.BluetoothStatusCodes
-import android.bluetooth.le.ScanCallback
-import android.bluetooth.le.ScanFilter
-import android.bluetooth.le.ScanResult
-import android.bluetooth.le.ScanSettings
-import android.os.Build
-import android.os.SystemClock
-import com.iscs.bozzys.utils.LogUtil
-import com.iscs.bozzys.utils.byteArrayToHexString
-import com.iscs.bozzys.utils.hexToByteArray
-import kotlinx.coroutines.CancellableContinuation
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.TimeoutCancellationException
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
-import kotlinx.coroutines.withContext
-import kotlinx.coroutines.withTimeout
-import java.util.UUID
-import java.util.concurrent.ConcurrentHashMap
-
-/**
- * 蓝牙连接管理器
- */
-class BleManager(
-    val app: Application,
-    val mac: String = "",
-    val mtu: Int = 500,
-    val needIndicate: Boolean = true,
-    val writeUUID: String = BleProtocol.INDICATE_UUID,
-    val serviceUUID: String = BleProtocol.SERVICE_UUID,
-) {
-
-    companion object {
-        private const val TAG = "BLEManager"
-    }
-
-    // 蓝牙管理器
-    private val bm = app.getSystemService(BluetoothManager::class.java)
-
-    // 蓝牙适配器
-    private val ba = bm.adapter
-
-    // 当前连接的设备
-    private var device: BluetoothDevice? = null
-
-    // gatt连接对象
-    private var gatt: BluetoothGatt? = null
-
-    // 连接回调
-    private var doneConnect: CancellableContinuation<BleConnectResult>? = null
-
-    // 待响应式的请求数据接收池
-    private val receiverPool = ConcurrentHashMap<String, CompletableDeferred<BleFrame>>()
-
-    // 是否已经连接
-    private var isConnected = false
-
-    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
-
-    // 蓝牙扫描回调
-    private val scanCallback = object : ScanCallback() {
-
-        override fun onScanResult(callbackType: Int, result: ScanResult?) {
-            super.onScanResult(callbackType, result)
-            LogUtil.i(TAG, "scan() onScanResult() ${result?.device?.address}")
-            if (result != null && result.device != null) {
-                device = result.device
-                // 查找到设备后,停止扫描并且执行连接操作
-                stopScan()
-                // 执行连接蓝牙设备
-                innerConnect()
-            }
-        }
-
-        override fun onScanFailed(errorCode: Int) {
-            super.onScanFailed(errorCode)
-            LogUtil.i(TAG, "scan() onScanFailed() $errorCode")
-            // 扫描失败重新执行扫描
-            scan()
-        }
-
-    }
-
-    /**
-     * 协程方式的连接
-     */
-    suspend fun connect(device: BluetoothDevice? = null): BleConnectResult = suspendCancellableCoroutine { block ->
-        this.doneConnect = block
-        if (device == null) {
-            // 开始执行扫描连接
-            scan()
-        } else {
-            this.device = device
-            innerConnect()
-            timeoutDisconnect()
-        }
-    }
-
-    /**
-     * 超时未连接处理
-     */
-    private fun timeoutDisconnect() {
-        scope.launch {
-            delay(5000)
-            // 未连接成功执行断开连接操作
-            if (!isConnected) {
-                if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
-                    this@BleManager.doneConnect?.resume(BleConnectResult(false), null)
-                }
-                release()
-            }
-        }
-    }
-
-    /**
-     * 查找设备
-     */
-    @SuppressLint("MissingPermission")
-    private fun scan() {
-        if (ba == null || !ba.isEnabled) {
-            // 蓝牙不可用
-            LogUtil.e(TAG, "scan() bluetooth unused")
-            if (!(this.doneConnect?.isCompleted ?: false)) this.doneConnect?.resume(BleConnectResult(false), null)
-            return
-        }
-        LogUtil.i(TAG, "scan() bluetooth started")
-        // 获取扫描对象
-        val scan = ba.bluetoothLeScanner
-        // 设置扫描过滤
-        val filters = ArrayList<ScanFilter>()
-        if (mac.isNotEmpty()) filters.add(ScanFilter.Builder().setDeviceAddress(mac).build())
-        // 设置蓝牙扫描频率高 SCAN_MODE_LOW_LATENCY
-        // 设置蓝牙扫描频率中 SCAN_MODE_BALANCED
-        val settings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build()
-        scan.startScan(filters, settings, scanCallback)
-    }
-
-    @SuppressLint("MissingPermission")
-    fun stopScan() {
-        try {
-            ba.bluetoothLeScanner.stopScan(scanCallback)
-        } catch (e: Exception) {
-            LogUtil.e(TAG, "stopScan() stop scan failed $e")
-        }
-    }
-
-    /**
-     * 连接设备,内部方法
-     */
-    @SuppressLint("MissingPermission")
-    private fun innerConnect() {
-        try {
-            gatt = device?.connectGatt(app, false, object : BluetoothGattCallback() {
-
-                @SuppressLint("MissingPermission")
-                override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
-                    if (newState == BluetoothProfile.STATE_CONNECTED) {
-                        LogUtil.i(TAG, "$gatt gattCallback onConnectionStateChange() gatt connected")
-                        gatt.discoverServices()
-                    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
-                        LogUtil.i(TAG, "$gatt gattCallback onConnectionStateChange() gatt disconnected")
-                        isConnected = false
-                        if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
-                            this@BleManager.doneConnect?.resume(BleConnectResult(false), null)
-                        }
-                        release()
-                    }
-                }
-
-                @SuppressLint("MissingPermission")
-                override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
-                    if (status == BluetoothGatt.GATT_SUCCESS) {
-                        LogUtil.i(TAG, "gattCallback onServicesDiscovered() find services success")
-                        // 执行设置mtu操作
-                        gatt.requestMtu(mtu)
-                    }
-                }
-
-                override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
-                    super.onMtuChanged(gatt, mtu, status)
-                    LogUtil.i(TAG, "gattCallback onMtuChanged() mtu $mtu")
-                    Thread {
-                        val indicateOk = enableIndicate()
-                        SystemClock.sleep(1000)
-                        if (needIndicate) {
-                            isConnected = indicateOk
-                            if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
-                                this@BleManager.doneConnect?.resume(BleConnectResult(indicateOk), null)
-                            }
-                        } else {
-                            isConnected = false
-                            if (!(this@BleManager.doneConnect?.isCompleted ?: false)) {
-                                this@BleManager.doneConnect?.resume(BleConnectResult(true), null)
-                            }
-                        }
-                    }.start()
-                }
-
-                @SuppressLint("MissingPermission")
-                override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) {
-                    super.onCharacteristicRead(gatt, characteristic, value, status)
-                    LogUtil.i(TAG, "gattCallback onCharacteristicRead()")
-                }
-
-                override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {
-                    super.onDescriptorRead(gatt, descriptor, status, value)
-                    LogUtil.i(TAG, "gattCallback onDescriptorRead() data write success")
-                }
-
-                override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
-                    super.onCharacteristicChanged(gatt, characteristic)
-                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) return
-                    LogUtil.w(TAG, "gattCallback <--- ${characteristic?.value?.byteArrayToHexString(" ")}")
-                    val data = characteristic?.value ?: byteArrayOf()
-                    var key = ""
-                    receiverPool.forEach { item ->
-                        if (data.byteArrayToHexString().startsWith(item.key.split("_")[1])) {
-                            // 找到指定响应体
-                            key = item.key
-                            return@forEach
-                        }
-                    }
-                    if (key.isNotEmpty()) {
-                        val deferred = receiverPool.remove(key)
-                        if (deferred != null && !deferred.isCompleted) {
-                            val spl = key.split("_")
-                            val rspCodeLen = spl[1].length / 2
-                            deferred.complete(BleFrame(spl[0].hexToByteArray(), data.copyOfRange(rspCodeLen, data.size), spl[1].hexToByteArray()))
-                        }
-                    }
-                }
-
-                override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {
-                    super.onCharacteristicChanged(gatt, characteristic, value)
-                    LogUtil.w(TAG, "gattCallback <--- ${value.byteArrayToHexString(" ")}")
-                    var key = ""
-                    receiverPool.forEach { item ->
-                        if (value.byteArrayToHexString().startsWith(item.key.split("_")[1])) {
-                            // 找到指定响应体
-                            key = item.key
-                            return@forEach
-                        }
-                    }
-                    if (key.isNotEmpty()) {
-                        val deferred = receiverPool.remove(key)
-                        if (deferred != null && !deferred.isCompleted) {
-                            val spl = key.split("_")
-                            val rspCodeLen = spl[1].length / 2
-                            deferred.complete(BleFrame(spl[0].hexToByteArray(), value.copyOfRange(rspCodeLen, value.size), spl[1].hexToByteArray()))
-                        }
-                    }
-                }
-            }, BluetoothDevice.TRANSPORT_LE)
-            LogUtil.d(TAG, "$gatt gatt created")
-        } catch (e: Exception) {
-            LogUtil.e(TAG, "connect() connect to device ${device?.address} failed $e")
-        }
-    }
-
-    /**
-     * 断开连接
-     */
-    @SuppressLint("MissingPermission")
-    fun disconnect() {
-        device?.let { gatt?.disconnect() }
-    }
-
-    /**
-     * 资源释放
-     */
-    @SuppressLint("MissingPermission")
-    private fun release() {
-        try {
-            // 执行断开连接操作
-            gatt?.close()
-        } finally {
-            gatt = null
-            device = null
-            doneConnect = null
-            isConnected = false
-        }
-    }
-
-    /**
-     * 是否使能Indicate
-     */
-    @SuppressLint("MissingPermission")
-    private fun enableIndicate(uuid: String = writeUUID): Boolean {
-        gatt?.getService(UUID.fromString(serviceUUID))?.getCharacteristic(UUID.fromString(uuid))?.let {
-            // 开启通知
-            val open = gatt?.setCharacteristicNotification(it, true)
-            LogUtil.i(TAG, "enableIndicate() open notification $open")
-            val descriptor = it.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))
-            LogUtil.i(TAG, "enableIndicate() get descriptor $descriptor")
-            if (open != true || descriptor == null) return false
-
-            // 写入desc
-            val enable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-                gatt?.writeDescriptor(descriptor, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE) == BluetoothStatusCodes.SUCCESS
-            } else {
-                descriptor.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
-                gatt?.writeDescriptor(descriptor)
-            }
-            LogUtil.i(TAG, "enableIndicate() indicate enable $enable")
-            return enable == true
-        }
-        return false
-    }
-
-    /**
-     * 写数据,带响应
-     */
-    @SuppressLint("MissingPermission")
-    suspend fun writeByResponse(frame: BleFrame) = withContext(Dispatchers.IO) {
-        val deferred = CompletableDeferred<BleFrame>()
-        val writeUUID = if (frame.writeUUID.isNotEmpty()) UUID.fromString(frame.writeUUID) else UUID.fromString(writeUUID)
-        receiverPool["${frame.reqCode.byteArrayToHexString()}_${frame.rspCode.byteArrayToHexString()}"] = deferred
-        LogUtil.w(TAG, "gattCallback ---> ${frame.reqCode.byteArrayToHexString(" ")} ${frame.data.byteArrayToHexString(" ")}")
-        // 发送数据方法兼容处理
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-            gatt?.getService(UUID.fromString(serviceUUID))?.getCharacteristic(writeUUID)?.let {
-                gatt?.writeCharacteristic(it, frame.reqCode + frame.data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
-            }
-        } else {
-            val characteristic = gatt?.getService(UUID.fromString(serviceUUID))?.getCharacteristic(writeUUID)
-            characteristic?.value = frame.reqCode + frame.data
-            characteristic?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
-            gatt?.writeCharacteristic(characteristic)
-        }
-        try {
-            withTimeout(1000) { deferred.await() }
-        } catch (_: TimeoutCancellationException) {
-            BleFrame(byteArrayOf(), byteArrayOf(), byteArrayOf())
-        }
-    }
-
-}

+ 0 - 62
app/src/main/java/com/iscs/bozzys/utils/ble/BleProtocol.kt

@@ -1,62 +0,0 @@
-package com.iscs.bozzys.utils.ble
-
-/**
- * 封装用于蓝牙通信的协议
- */
-object BleProtocol {
-
-    // 服务特征
-    const val SERVICE_UUID = "0000FEE7-0000-1000-8000-00805F9B34FB"
-
-    // 写入其他数据特征
-    const val INDICATE_UUID = "0000FED1-0000-1000-8000-00805F9B34FB"
-
-    // 写作业票数据特征
-    const val WRITE_UUID = "0000FED2-0000-1000-8000-00805F9B34FB"
-
-    // 获取令牌,需增加4字节的时间戳,总长8个字节长度
-    val REQ_GET_TOKEN = byteArrayOf(0x01, 0x01, 0x05, 0x00)
-
-    // 获取令牌响应,最后4个是token,总长15个字节长度
-    val RSP_GET_TOKEN = byteArrayOf(0x01, 0x02, 0x04)
-
-    // 获取钥匙电量
-    val REQ_POWER_STATUS = byteArrayOf(0x03, 0x01, 0x01, 0x03)
-
-    // 获取钥匙电量响应
-    val RSP_POWER_STATUS = byteArrayOf(0x03, 0x02, 0x03, 0x03)
-
-    // 获取当前设备请求
-    val REQ_CURRENT_STATUS = byteArrayOf(0x03, 0x01, 0x01, 0x01)
-
-    // 获取当前设备响应
-    val RSP_CURRENT_STATUS = byteArrayOf(0x03, 0x02, 0x02, 0x01)
-
-    // 工作模式切换请求
-    val REQ_SWITCH_MODE = byteArrayOf(0x02, 0x01, 0x02, 0x01)
-
-    // 工作模式切换响应
-    val RSP_SWITCH_MODE = byteArrayOf(0x02, 0x02, 0x03, 0x01)
-
-    // 断开蓝牙请求
-    val REQ_DISCONNECT = byteArrayOf(0x02, 0x01, 0x01, 0xEA.toByte())
-
-    // 断开蓝牙响应
-    val RSP_DISCONNECT = byteArrayOf(0x02, 0x02, 0x02, 0xEA.toByte())
-
-    // 工作票下发
-    val REQ_SEND_WORK_TICKET = byteArrayOf(0x02, 0x01)
-
-    // 工作票下发响应
-    val RSP_SEND_WORK_TICKET = byteArrayOf(0x02, 0x02, 0x06, 0x02)
-
-    // 获取设备工作票完成情况
-    val REQ_WORK_TICKET_RESULT = byteArrayOf(0x03, 0x01, 0x01, 0x02)
-
-    // 获取设备工作票完成情况响应
-    val RSP_WORK_TICKET_RESULT = byteArrayOf(0x03, 0x02)
-
-    // 获取设备工作票完成情况分包
-    val REQ_WORK_TICKET_RESULT_PART = byteArrayOf(0x03, 0x01, 0x06, 0x02)
-
-}

+ 0 - 8
app/src/main/java/com/iscs/bozzys/utils/ble/BleRunMode.kt

@@ -1,8 +0,0 @@
-package com.iscs.bozzys.utils.ble
-
-enum class BleRunMode {
-    WORK,       // 工作模式
-    STBY,       // 待机模式
-    FAILURE,    // 故障模式
-    NONE,       // 未知
-}

+ 282 - 0
app/src/main/java/com/iscs/bozzys/utils/ble/GattClient.kt

@@ -0,0 +1,282 @@
+package com.iscs.bozzys.utils.ble
+
+import android.annotation.SuppressLint
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothGatt
+import android.bluetooth.BluetoothGattCallback
+import android.bluetooth.BluetoothGattCharacteristic
+import android.bluetooth.BluetoothGattDescriptor
+import android.bluetooth.BluetoothProfile
+import android.bluetooth.BluetoothStatusCodes
+import android.content.Context
+import android.os.Build
+import com.iscs.bozzys.utils.LogUtil
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withTimeout
+import java.util.UUID
+
+/**
+ * 用于蓝牙设备操作
+ *
+ * @param ctx           上下文
+ * @param device        被操作的蓝牙设备
+ * @param sDataUUID     写数据的服务特征
+ * @param wDataUUID     写数据的特征
+ * @param indicate      是否开启指示(通知),是指发送数据后的回调是否开启
+ * @param wIndicateUUID 写指示(通知)特征值
+ * @param scope         协程
+ * @param mtu           设置通信的频率
+ */
+class GattClient(
+    private val ctx: Context,
+    private val device: BluetoothDevice,
+    private val sDataUUID: String = "0000FEE7-0000-1000-8000-00805F9B34FB",
+    private val wDataUUID: String = "0000FED2-0000-1000-8000-00805F9B34FB",
+    private val indicate: Boolean = true,
+    private val wIndicateUUID: String = "0000FED1-0000-1000-8000-00805F9B34FB",
+    private val dIndicateUUID: String = "00002902-0000-1000-8000-00805F9B34FB",
+    private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO),
+    private val mtu: Int = 500
+) {
+
+
+    companion object {
+        private const val TAG = "GattClient"
+    }
+
+    // 低功耗蓝牙连接对象
+    private var gatt: BluetoothGatt? = null
+
+    // 当前连接状态
+    private var isConnected = false
+
+    // 2. 增加队列和当前挂起的响应
+    private val commandQueue = Channel<GattFrame>(Channel.UNLIMITED)
+    private var pendingResponse: CompletableDeferred<ByteArray>? = null
+
+    init {
+        // 3. 开启后台处理循环
+        startProcessQueue()
+    }
+
+    private fun startProcessQueue() {
+        scope.launch {
+            for (command in commandQueue) {
+                if (!isConnected) {
+                    if (!command.receive.isCompleted) command.receive.complete(byteArrayOf())
+                    continue
+                }
+
+                // 执行写操作
+                val success = writeData(command.send)
+                if (success) {
+                    pendingResponse = command.receive
+                    // 设置超时处理,防止外设不回数据导致队列死锁
+                    try {
+                        withTimeout(3000) { command.receive.await() }
+                    } catch (_: Exception) {
+                        LogUtil.e(TAG, "Wait response timeout")
+                        if (!command.receive.isCompleted) command.receive.complete(byteArrayOf())
+                    } finally {
+                        pendingResponse = null
+                    }
+                } else {
+                    if (!command.receive.isCompleted) command.receive.complete(byteArrayOf())
+                }
+            }
+        }
+    }
+
+    /**
+     * 连接蓝牙低功耗设备
+     *
+     * @param connectCallback 连接回调
+     */
+    @SuppressLint("MissingPermission")
+    suspend fun connect(timeout: Long = 5000): Boolean = suspendCancellableCoroutine { block ->
+        try {
+            gatt = device.connectGatt(ctx, false, object : BluetoothGattCallback() {
+
+                @SuppressLint("MissingPermission")
+                override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
+                    if (newState == BluetoothProfile.STATE_CONNECTED) {
+                        LogUtil.i(TAG, "$gatt onConnectionStateChange() gatt connected")
+                        gatt.discoverServices()
+                    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+                        LogUtil.i(TAG, "$gatt onConnectionStateChange() gatt disconnected")
+                        isConnected = false
+                        if (!block.isCompleted) block.resumeWith(Result.success(false))
+                        release()
+                    }
+                }
+
+                @SuppressLint("MissingPermission")
+                override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
+                    if (status == BluetoothGatt.GATT_SUCCESS) {
+                        LogUtil.i(TAG, "$gatt onServicesDiscovered() find services success")
+                        // 执行设置mtu操作
+                        gatt.requestMtu(mtu)
+                    }
+                }
+
+                override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
+                    super.onMtuChanged(gatt, mtu, status)
+                    LogUtil.i(TAG, "$gatt onMtuChanged() mtu $mtu")
+                    scope.launch {
+                        val enable = enableIndicate()
+                        delay(500)
+                        if (indicate) {
+                            isConnected = enable
+                            if (enable) {
+                                if (!block.isCompleted) block.resumeWith(Result.success(true))
+                            } else {
+                                disconnect()
+                            }
+                        } else {
+                            isConnected = true
+                            if (!block.isCompleted) block.resumeWith(Result.success(true))
+                        }
+                    }
+                }
+
+                override fun onCharacteristicChanged(
+                    gatt: BluetoothGatt,
+                    characteristic: BluetoothGattCharacteristic,
+                    value: ByteArray
+                ) {
+                    // 5. 收到硬件主动上报的数据时,如果是我们正在等待的响应,则填充它
+                    LogUtil.i(TAG, "onCharacteristicChanged: ${value.contentToString()}")
+                    pendingResponse?.complete(value)
+                }
+
+                // 兼容旧版本 API
+                @Deprecated("Deprecated in Java")
+                override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
+                    val value = characteristic.value
+                    LogUtil.i(TAG, "onCharacteristicChanged (Legacy): ${value.contentToString()}")
+                    pendingResponse?.complete(value)
+                }
+
+            }, BluetoothDevice.TRANSPORT_LE)
+            timeout(timeout) { if (!block.isCompleted) block.resumeWith(Result.success(false)) }
+        } catch (e: Exception) {
+            timeout(timeout) { if (!block.isCompleted) block.resumeWith(Result.success(false)) }
+            release()
+        }
+    }
+
+    // 4. 提供给外部调用的发送方法
+    suspend fun sendDataSync(data: ByteArray): ByteArray {
+        val deferred = CompletableDeferred<ByteArray>()
+        commandQueue.send(GattFrame(data, deferred))
+        return deferred.await()
+    }
+
+    @SuppressLint("MissingPermission")
+    private fun writeData(data: ByteArray): Boolean {
+        val service = gatt?.getService(UUID.fromString(sDataUUID))
+        val char = service?.getCharacteristic(UUID.fromString(wDataUUID)) ?: return false
+
+        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            gatt?.writeCharacteristic(
+                char,
+                data,
+                BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
+            ) == BluetoothStatusCodes.SUCCESS
+        } else {
+            char.value = data
+            gatt?.writeCharacteristic(char) == true
+        }
+    }
+
+    @SuppressLint("MissingPermission")
+    fun disconnect() {
+        try {
+            gatt?.disconnect()
+        } catch (_: Exception) {
+            release()
+        }
+    }
+
+    @SuppressLint("MissingPermission")
+    private fun release() {
+        try {
+            // 执行断开连接操作
+            gatt?.close()
+        } finally {
+            gatt = null
+            isConnected = false
+        }
+    }
+
+    /**
+     * 配置连接超时回调
+     */
+    private fun timeout(timeout: Long, callback: () -> Unit) {
+        scope.launch {
+            delay(timeout)
+            if (!isConnected) {
+                callback()
+                release()
+            }
+        }
+    }
+
+    @SuppressLint("MissingPermission")
+    private fun enableIndicate(): Boolean {
+        gatt?.getService(UUID.fromString(sDataUUID))?.getCharacteristic(UUID.fromString(wIndicateUUID))?.let {
+            // 启用通知
+            val setNotification = gatt?.setCharacteristicNotification(it, true)
+            LogUtil.i(TAG, "enableIndicate() enable notification $setNotification")
+            val descriptor = it.getDescriptor(UUID.fromString(dIndicateUUID))
+            LogUtil.i(TAG, "enableIndicate() get descriptor $descriptor")
+            if (setNotification != true || descriptor == null) return false
+            // 开始写入使能
+            val enable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                gatt?.writeDescriptor(
+                    descriptor,
+                    BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
+                ) == BluetoothStatusCodes.SUCCESS
+            } else {
+                descriptor.value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
+                gatt?.writeDescriptor(descriptor)
+            }
+            LogUtil.i(TAG, "enableIndicate() indicate enable $enable")
+            return enable == true
+        }
+        return false
+    }
+
+}
+
+/**
+ * 封装通信数据
+ */
+data class GattFrame(val send: ByteArray, val receive: CompletableDeferred<ByteArray>) {
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as GattFrame
+
+        if (!send.contentEquals(other.send)) return false
+        if (receive != other.receive) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = send.contentHashCode()
+        result = 31 * result + receive.hashCode()
+        return result
+    }
+
+}

+ 166 - 0
app/src/main/java/com/iscs/bozzys/utils/ble/KeyHelper.kt

@@ -0,0 +1,166 @@
+package com.iscs.bozzys.utils.ble
+
+import com.iscs.bozzys.utils.crc16
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+
+/**
+ * 用于蓝牙钥匙的通信协议封装,通信层
+ *
+ * @param gattClient 蓝牙通信对象
+ */
+class KeyHelper(private val gattClient: GattClient) {
+
+    // 本次蓝牙连接的token
+    private var token: ByteArray = byteArrayOf()
+
+    /**
+     * 获取钥匙连接的token
+     */
+    private suspend fun getToken(): ByteArray {
+        if (token.isEmpty()) {
+            val ret = gattClient.sendDataSync(byteArrayOf(0x01, 0x01, 0x05, 0x00) + getUnixTime())
+            if (ret.isNotEmpty()) token = ret.copyOfRange(3, ret.size)
+        }
+        return token
+    }
+
+    /**
+     * 获取当前钥匙的工作模式
+     *
+     * @return WORK 工作模式,STBY 待机模式,空 未知模式
+     */
+    suspend fun getWorkMode(): String {
+        val ret = gattClient.sendDataSync(byteArrayOf(0x03, 0x01, 0x01, 0x01) + getUnixTime() + getToken())
+        return if (ret.isNotEmpty()) {
+            if (ret[4].toInt() == 1) "WORK"
+            else if (ret[4].toInt() == 2) "STBY"
+            else ""
+        } else ""
+    }
+
+    /**
+     * 设置工作模式
+     *
+     * @param mode WORK 工作模式,STBY 待机模式
+     */
+    suspend fun setWorkMode(mode: String): Boolean {
+        val modeByte = if (mode == "WORK") 0x01 else 0x02
+        val ret =
+            gattClient.sendDataSync(byteArrayOf(0x02, 0x01, 0x02, 0x01, modeByte.toByte()) + getUnixTime() + getToken())
+        return if (ret.isNotEmpty()) {
+            ret[5].toInt() == 1
+        } else false
+    }
+
+    /**
+     * 获取设备电量
+     *
+     * @return 0-100 设备电量
+     */
+    suspend fun getPower(): Int {
+        val ret = gattClient.sendDataSync(byteArrayOf(0x03, 0x01, 0x01, 0x03) + getUnixTime() + getToken())
+        return if (ret.isNotEmpty()) ret[4].toInt() else 0
+    }
+
+    /**
+     * 获取作业票数据
+     */
+    suspend fun getTicketData(): String {
+        var data = byteArrayOf()
+        // 解析数据
+        var pkgIdx = 0
+        var pkgCount = 1
+        while (pkgIdx < pkgCount) {
+            val cmd = if (pkgIdx == 0) byteArrayOf(0x03, 0x01, 0x01, 0x02) else {
+                byteArrayOf(0x03, 0x01, 0x06, 0x02) + pkgIdx.toByteArray() + pkgCount.toByteArray() + byteArrayOf(0x00)
+            }
+            val ret = gattClient.sendDataSync(cmd + getUnixTime() + getToken())
+            if (ret.isEmpty()) {
+                data = byteArrayOf()
+                break
+            }
+            pkgCount = ret[4] + ret[5]
+            pkgIdx = ret[6] + ret[7]
+            val pkgSize = ret[10].toUByte() + ret[11].toUByte()
+            data += ret.copyOfRange(12, pkgSize.toInt() + 12)
+            pkgIdx++
+        }
+        return if (data.isNotEmpty()) String(data) else ""
+    }
+
+    /**
+     * 发送作业票数据给钥匙
+     *
+     * @param json  作业票数据
+     */
+    suspend fun sendTicketData(json: String): Boolean {
+        // 先将数据转换为字节数组
+        val pkg = json.toByteArray()
+        // 包大小
+        val pkgSize = pkg.size
+        // 分包数
+        val pkgCount = (pkgSize + 128 - 1) / 128
+        for (idx in 0 until pkgCount) {
+            val endIdx = if ((idx + 1) == pkgCount) pkgSize else ((idx + 1) * 128)
+            // 将包做切片操作
+            val subPkg = pkg.copyOfRange(idx * 128, endIdx)
+            // 封装数据包操作
+            val data = pkgCount.toByteArray() + idx.toByteArray() +
+                    subPkg.crc16(0, subPkg.size) + subPkg.size.toByteArray() + subPkg
+            // 装包处理
+            val ret = gattClient.sendDataSync(
+                byteArrayOf(0x02, 0x01) +
+                        (data.size + 1).toByteArray(bit = 1) +
+                        0x02.toByteArray(bit = 1) +
+                        data + getUnixTime() + getToken()
+            )
+            if (ret[8].toInt() == 0) continue else return false
+        }
+        return true
+    }
+
+    /**
+     * 获取手机当前时间,提供设备使用
+     */
+    private fun getUnixTime(): ByteArray {
+        val tempArr = (System.currentTimeMillis() / 1000).toByteArray()
+        val timeStampArr = byteArrayOf(tempArr[0], tempArr[1], tempArr[2], tempArr[3])
+        return timeStampArr
+    }
+
+    /**
+     * 将Int转为ByteArray数组
+     *
+     * @param bo    ByteOrder.LITTLE_ENDIAN 小端模式,ByteOrder.BIG_ENDIAN 大端模式
+     * @param bit   保留位数
+     */
+    fun Int.toByteArray(bo: ByteOrder = ByteOrder.LITTLE_ENDIAN, bit: Int = 2): ByteArray {
+        val idx = if (bo == ByteOrder.BIG_ENDIAN) 2 else 0
+        return ByteBuffer.allocate(4)
+            .order(bo)
+            .putInt(this)
+            .array()
+            .copyOfRange(idx, idx + bit)
+    }
+
+    /**
+     * 将Long转为ByteArray数组
+     *
+     * @param bo ByteOrder.LITTLE_ENDIAN 小端模式,ByteOrder.BIG_ENDIAN 大端模式
+     */
+    private fun Long.toByteArray(bo: ByteOrder = ByteOrder.LITTLE_ENDIAN): ByteArray {
+        return ByteBuffer.allocate(8)
+            .order(bo)
+            .putLong(this)
+            .array()
+    }
+
+    /**
+     * 将字节数组转换为16进制字符串
+     */
+    private fun ByteArray.toString(sep: String = ""): String {
+        return joinToString(sep) { "%02X".format(it) }
+    }
+
+}

+ 49 - 90
app/src/main/java/com/iscs/bozzys/utils/ble/BleTask.kt → app/src/main/java/com/iscs/bozzys/utils/ble/KeyManager.kt

@@ -1,7 +1,6 @@
 package com.iscs.bozzys.utils.ble
 package com.iscs.bozzys.utils.ble
 
 
 import android.annotation.SuppressLint
 import android.annotation.SuppressLint
-import android.app.Application
 import android.bluetooth.BluetoothAdapter
 import android.bluetooth.BluetoothAdapter
 import android.bluetooth.BluetoothDevice
 import android.bluetooth.BluetoothDevice
 import android.bluetooth.BluetoothManager
 import android.bluetooth.BluetoothManager
@@ -9,30 +8,16 @@ import android.bluetooth.le.ScanCallback
 import android.bluetooth.le.ScanFilter
 import android.bluetooth.le.ScanFilter
 import android.bluetooth.le.ScanResult
 import android.bluetooth.le.ScanResult
 import android.bluetooth.le.ScanSettings
 import android.bluetooth.le.ScanSettings
+import android.content.Context
 import com.iscs.bozzys.Entry
 import com.iscs.bozzys.Entry
 import com.iscs.bozzys.api.ApiRequest
 import com.iscs.bozzys.api.ApiRequest
 import com.iscs.bozzys.api.ApiRequest.getResponse
 import com.iscs.bozzys.api.ApiRequest.getResponse
 import com.iscs.bozzys.api.KeyTicket
 import com.iscs.bozzys.api.KeyTicket
 import com.iscs.bozzys.utils.LogUtil
 import com.iscs.bozzys.utils.LogUtil
-import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLEDisconnectCMD
-import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLEGetPowerCMD
-import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLEGetStatusCMD
-import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLEGetTicketInfoCMD
-import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLESwitchRunModeCMD
-import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLETicketDataCMDList
-import com.iscs.bozzys.utils.ble.BleFrameExt.getDisconnectResult
-import com.iscs.bozzys.utils.ble.BleFrameExt.getPower
-import com.iscs.bozzys.utils.ble.BleFrameExt.getRunMode
-import com.iscs.bozzys.utils.ble.BleFrameExt.getSendTicketResult
-import com.iscs.bozzys.utils.ble.BleFrameExt.getSwitchRunModeResult
-import com.iscs.bozzys.utils.ble.BleFrameExt.getTicketPackageInfo
-import com.iscs.bozzys.utils.ble.BleFrameExt.getToken
-import com.iscs.bozzys.utils.byteArrayToHexString
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.Json
 
 
@@ -43,7 +28,9 @@ import kotlinx.serialization.json.Json
  * 2. 循环连接钥匙设备,读取设备任务完成情况
  * 2. 循环连接钥匙设备,读取设备任务完成情况
  * 3. 使用中的设备享有优先权,被标记为使用中的设备不进行作业任务的读取操作,等待进行作业任务的下发操作
  * 3. 使用中的设备享有优先权,被标记为使用中的设备不进行作业任务的读取操作,等待进行作业任务的下发操作
  */
  */
-object BleTask {
+object KeyManager {
+
+    private const val TAG = "KeyManager"
 
 
     private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
     private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
 
 
@@ -88,10 +75,10 @@ object BleTask {
     /**
     /**
      * 蓝牙任务管理初始化
      * 蓝牙任务管理初始化
      *
      *
-     * @param app
+     * @param ctx
      */
      */
-    fun init(app: Application) {
-        bm = app.getSystemService(BluetoothManager::class.java)
+    fun init(ctx: Context) {
+        bm = ctx.getSystemService(BluetoothManager::class.java)
         ba = bm.adapter
         ba = bm.adapter
         scan()
         scan()
         loop()
         loop()
@@ -151,6 +138,7 @@ object BleTask {
      * 循环发现设备
      * 循环发现设备
      */
      */
     private fun loop() {
     private fun loop() {
+        LogUtil.d(TAG, "功能模块 - 发现钥匙 已开启")
         scope.launch {
         scope.launch {
             for (device in deviceQueue) {
             for (device in deviceQueue) {
                 connectDeviceAndDealData(device)
                 connectDeviceAndDealData(device)
@@ -170,69 +158,51 @@ object BleTask {
     private suspend fun connectDeviceAndDealData(device: BluetoothDevice) {
     private suspend fun connectDeviceAndDealData(device: BluetoothDevice) {
         if (usedDevices.contains(device.address)) {
         if (usedDevices.contains(device.address)) {
             if (usedDevices[device.address].isNullOrEmpty()) {
             if (usedDevices[device.address].isNullOrEmpty()) {
-                LogUtil.w("BleTask", "[${device.address}] 设备即将被使用,不做状态获取处理")
+                LogUtil.w(TAG, "[${device.address}] 设备即将被使用,不做状态获取处理")
             } else {
             } else {
                 // 执行作业任务的下发操作
                 // 执行作业任务的下发操作
                 sendTicket2Device(device)
                 sendTicket2Device(device)
             }
             }
             return
             return
         }
         }
-        LogUtil.d("BleTask", "[${device.address}] 连接开始")
-        val bm = BleManager(Entry.app, mac = device.address)
-        val result = bm.connect(device)
-        if (result.connected) {
-            // 获取设备token
-            val token = bm.writeByResponse(BleFrameExt.buildBLEGetTokenCMD()).getToken()
-            LogUtil.i("BleTask", "[${device.address}] 设备密钥 -> ${token.byteArrayToHexString()}")
+        LogUtil.d(TAG, "[${device.address}] 连接开始")
+        val gc = GattClient(Entry.app, device)
+        if (gc.connect()) {
+            val kh = KeyHelper(gc)
             // 获取设备电量
             // 获取设备电量
-            val power = bm.writeByResponse(token.buildBLEGetPowerCMD()).getPower()
-            LogUtil.i("BleTask", "[${device.address}] 设备电量 -> $power")
+            val power = kh.getPower()
+            LogUtil.i(TAG, "[${device.address}] 设备电量 -> $power")
             // 获取当前设备运行模式
             // 获取当前设备运行模式
-            val runMode = bm.writeByResponse(token.buildBLEGetStatusCMD()).getRunMode()
-            LogUtil.i("BleTask", "[${device.address}] 设备模式 -> $runMode")
-            if (runMode == BleRunMode.WORK) {
-                // 执行作业票的获取
-                val pkgList = ArrayList<BleTicketDataPackage>()
-                val mainPkg = bm.writeByResponse(token.buildBLEGetTicketInfoCMD()).getTicketPackageInfo()
-                pkgList.add(mainPkg)
-                // 校验是否有子包,如果有,继续读取子包数据
-                for (idx in 1 until mainPkg.pkgTotal) {
-                    val subPkg =
-                        bm.writeByResponse(token.buildBLEGetTicketInfoCMD(idx, mainPkg.pkgTotal)).getTicketPackageInfo()
-                    pkgList.add(subPkg)
-                }
-                var datas = byteArrayOf()
-                pkgList.forEach { datas += it.pkgData }
-                val ticketJson = String(datas)
-                LogUtil.i("BleTask", "[${device.address}] 设备数据 -> $ticketJson")
+            val runMode = kh.getWorkMode()
+            LogUtil.i(TAG, "[${device.address}] 设备模式 -> $runMode")
+            if (runMode == "WORK") {
+                val ticketJson = kh.getTicketData()
+                LogUtil.i(TAG, "[${device.address}] 设备数据 -> $ticketJson")
                 // 解析作业票
                 // 解析作业票
                 val json = Json { ignoreUnknownKeys = true }
                 val json = Json { ignoreUnknownKeys = true }
                 try {
                 try {
                     // 处理作业票完成情况
                     // 处理作业票完成情况
                     val ticket = json.decodeFromString<KeyTicket>(ticketJson)
                     val ticket = json.decodeFromString<KeyTicket>(ticketJson)
-                    // LogUtil.i("BleTask", "[${device.address}] 解析作业")
+                    // LogUtil.i(TAG, "[${device.address}] 解析作业")
                     val params = ticket.getReturnParams(device.address)
                     val params = ticket.getReturnParams(device.address)
                     // 执行归还数据核对
                     // 执行归还数据核对
-                    LogUtil.i("BleTask", "[${device.address}] 提交数据 -> $params")
+                    LogUtil.i(TAG, "[${device.address}] 提交数据 -> $params")
                     val keyRsp = ApiRequest.returnKey(params).getOrElse { it.getResponse() }
                     val keyRsp = ApiRequest.returnKey(params).getOrElse { it.getResponse() }
-                    LogUtil.i("BleTask", "[${device.address}] 提交返回 -> ${keyRsp.data}")
+                    LogUtil.i(TAG, "[${device.address}] 提交返回 -> ${keyRsp.data}")
                     if (keyRsp.data?.keyBack == "1") {
                     if (keyRsp.data?.keyBack == "1") {
                         // 切换钥匙为待机模式
                         // 切换钥匙为待机模式
-                        val switch = bm.writeByResponse(token.buildBLESwitchRunModeCMD(BleRunMode.STBY))
-                            .getSwitchRunModeResult()
-                        LogUtil.i("BleTask", "[${device.address}] 切换模式 -> ${if (switch) "成功" else "失败"}")
+                        val switch = kh.setWorkMode("STBY")
+                        LogUtil.i(TAG, "[${device.address}] 切换模式 -> ${if (switch) "成功" else "失败"}")
                     }
                     }
                     // 归还钥匙接口调用结果验证
                     // 归还钥匙接口调用结果验证
                     listener.forEach { it.onReadTicketSuccess(device.address, ticket) }
                     listener.forEach { it.onReadTicketSuccess(device.address, ticket) }
                 } catch (e: Exception) {
                 } catch (e: Exception) {
-                    LogUtil.i("BleTask", "[${device.address}] 解析异常 -> $e")
+                    LogUtil.i(TAG, "[${device.address}] 解析异常 -> $e")
                 }
                 }
             }
             }
-            val disRet = bm.writeByResponse(token.buildBLEDisconnectCMD()).getDisconnectResult()
-            LogUtil.i("BleTask", "[${device.address}] 设备断开 -> ${if (disRet == 1) "成功" else "失败"}")
         }
         }
-        bm.disconnect()
-        LogUtil.d("BleTask", "[${device.address}] 连接断开")
+        gc.disconnect()
+        LogUtil.d(TAG, "[${device.address}] 连接断开")
     }
     }
 
 
     /**
     /**
@@ -242,43 +212,32 @@ object BleTask {
      */
      */
     private suspend fun sendTicket2Device(device: BluetoothDevice) {
     private suspend fun sendTicket2Device(device: BluetoothDevice) {
         val json = usedDevices[device.address] ?: "{}"
         val json = usedDevices[device.address] ?: "{}"
-        LogUtil.d("BleTask", "[${device.address}] 下发作业")
-        LogUtil.d("BleTask", "[${device.address}] 连接开始")
-        val bm = BleManager(Entry.app, mac = device.address)
-        val result = bm.connect(device)
-        if (result.connected) {
-            // 获取设备token
-            val token = bm.writeByResponse(BleFrameExt.buildBLEGetTokenCMD()).getToken()
-            LogUtil.i("BleTask", "[${device.address}] 设备密钥 -> ${token.byteArrayToHexString()}")
+        LogUtil.d(TAG, "[${device.address}] 下发作业")
+        LogUtil.d(TAG, "[${device.address}] 连接开始")
+        val gc = GattClient(Entry.app, device)
+        if (gc.connect()) {
+            val kh = KeyHelper(gc)
             // 获取设备电量
             // 获取设备电量
-            val power = bm.writeByResponse(token.buildBLEGetPowerCMD()).getPower()
-            LogUtil.i("BleTask", "[${device.address}] 设备电量 -> $power")
+            val power = kh.getPower()
+            LogUtil.i(TAG, "[${device.address}] 设备电量 -> $power")
             // 获取当前设备运行模式
             // 获取当前设备运行模式
-            val runMode = bm.writeByResponse(token.buildBLEGetStatusCMD()).getRunMode()
-            LogUtil.i("BleTask", "[${device.address}] 设备模式 -> $runMode")
+            val runMode = kh.getWorkMode()
+            LogUtil.i(TAG, "[${device.address}] 设备模式 -> $runMode")
             // 下发作业票
             // 下发作业票
-            val tickets = token.buildBLETicketDataCMDList(json)
-            var ticketSendOk = true
-            tickets.forEach {
-                delay(200)
-                val ticket = bm.writeByResponse(it).getSendTicketResult()
-                if (ticket != 0) {
-                    ticketSendOk = false
-                    return@forEach
-                }
+            val tickets = kh.sendTicketData(json)
+            if (tickets) {
+                LogUtil.i(TAG, "[${device.address}] 下发作业 -> 成功")
+                // 作业票下发成功,修改设备运行模式为工作模式
+                val switch = kh.setWorkMode("WORK")
+                LogUtil.i(TAG, "[${device.address}] 切换模式 -> ${if (switch) "成功" else "失败"}")
+                removeDeviceUsed(device.address)
+                listener.forEach { it.onSendTicketSuccess(device.address) }
+            } else {
+                LogUtil.i(TAG, "[${device.address}] 下发作业 -> 失败")
             }
             }
-            LogUtil.i("BleTask", "[${device.address}] 下发作业 -> ${if (ticketSendOk) "成功" else "失败"}")
-            // 作业票下发成功,修改设备运行模式为工作模式
-            val switch = bm.writeByResponse(token.buildBLESwitchRunModeCMD(BleRunMode.WORK))
-                .getSwitchRunModeResult()
-            LogUtil.i("BleTask", "[${device.address}] 切换模式 -> ${if (switch) "成功" else "失败"}")
-            val disRet = bm.writeByResponse(token.buildBLEDisconnectCMD()).getDisconnectResult()
-            LogUtil.i("BleTask", "[${device.address}] 设备断开 -> ${if (disRet == 1) "成功" else "失败"}")
-            removeDeviceUsed(device.address)
-            listener.forEach { it.onSendTicketSuccess(device.address) }
         }
         }
-        bm.disconnect()
-        LogUtil.d("BleTask", "[${device.address}] 断开连接")
+        gc.disconnect()
+        LogUtil.d(TAG, "[${device.address}] 断开连接")
     }
     }
 
 
 }
 }