Procházet zdrojové kódy

1. 蓝牙相关数据获取封装

bjb před 1 týdnem
rodič
revize
361288e68f

+ 12 - 1
app/src/main/java/com/iscs/comm/MainActivity.kt

@@ -27,6 +27,12 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import com.iscs.comm.entity.device.Device
 import com.iscs.comm.extension.CommFrameExt
+import com.iscs.comm.extension.CommFrameExt.buildBLEGetPowerCMD
+import com.iscs.comm.extension.CommFrameExt.buildBLEGetStatusCMD
+import com.iscs.comm.extension.CommFrameExt.getPower
+import com.iscs.comm.extension.CommFrameExt.getRunMode
+import com.iscs.comm.extension.CommFrameExt.getToken
+import com.iscs.comm.extension.byteArrayToHexString
 import com.iscs.comm.intf.IDeviceListener
 import com.iscs.comm.manager.BLEManager
 import com.iscs.comm.ui.theme.CommDemoTheme
@@ -65,7 +71,12 @@ class MainActivity : ComponentActivity() {
             val bm = BLEManager(this@MainActivity.application, mac = "CC:BA:97:21:71:E6")
             val result = bm.connect()
             if (result.connected) {
-                bm.writeByResponse(CommFrameExt.buildBLEGetTokenCMD())
+                val token = bm.writeByResponse(CommFrameExt.buildBLEGetTokenCMD()).getToken()
+                Log.i("xiaoming", "获取设备token ${token.byteArrayToHexString()}")
+                val power = bm.writeByResponse(token.buildBLEGetPowerCMD()).getPower()
+                Log.i("xiaoming", "当前设备电量:$power")
+                val runMode = bm.writeByResponse(token.buildBLEGetStatusCMD()).getRunMode()
+                Log.i("xiaoming", "当前工作模式:$runMode")
             }
         }
         DisposableEffect("") {

+ 1 - 1
settings.gradle.kts

@@ -19,6 +19,6 @@ dependencyResolutionManagement {
     }
 }
 
-rootProject.name = "CommDemo"
+rootProject.name = "ISCS_SDK"
 include(":app")
 include(":transport")

+ 8 - 0
transport/src/main/java/com/iscs/comm/enums/RunMode.kt

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

+ 50 - 0
transport/src/main/java/com/iscs/comm/extension/CommFrameExt.kt

@@ -2,6 +2,7 @@ package com.iscs.comm.extension
 
 import com.iscs.comm.entity.BleFrame
 import com.iscs.comm.entity.Frame
+import com.iscs.comm.enums.RunMode
 import com.iscs.comm.protocol.BLEProtocol
 import com.iscs.comm.protocol.CanProtocol.CAN_ID_BASE
 import com.iscs.comm.protocol.CanProtocol.CAN_LOCK_RFID
@@ -103,6 +104,55 @@ object CommFrameExt {
         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)
+    }
+
+    /**
+     * 获取token
+     */
+    fun BleFrame.getToken(): ByteArray {
+        if (this.rspCode.contentEquals(BLEProtocol.RSP_GET_TOKEN)) {
+            return this.data
+        }
+        return byteArrayOf()
+    }
+
+    /**
+     * 获取当前运行模式
+     */
+    fun BleFrame.getRunMode(): RunMode {
+        if (this.rspCode.contentEquals(BLEProtocol.RSP_CURRENT_STATUS)) {
+            return when (this.data[0]) {
+                0x01.toByte() -> RunMode.WORK
+                0x02.toByte() -> RunMode.STBY
+                0x03.toByte() -> RunMode.FAILURE
+                else -> RunMode.NONE
+            }
+        }
+        return RunMode.NONE
+    }
+
+    /**
+     * 获取当前设备电量
+     */
+    fun BleFrame.getPower(): Int {
+        if (this.rspCode.contentEquals(BLEProtocol.RSP_POWER_STATUS)) {
+            return this.data[0].toInt()
+        }
+        return -1
+    }
+
 }
 
 private fun getUnixTime(): ByteArray {

+ 10 - 1
transport/src/main/java/com/iscs/comm/extension/DataConvertExt.kt

@@ -46,4 +46,13 @@ fun List<Byte>.byteListToHexString(sep: String = ""): String {
  */
 fun Byte.toBinaryString(): String {
     return this.toInt().and(0xFF).toString(2).padStart(8, '0')
-}
+}
+
+/**
+ * 16进制转自己数组
+ */
+fun String.hexToByteArray(): ByteArray {
+    return this.chunked(2)
+        .map { it.toInt(16).toByte() }
+        .toByteArray()
+}

+ 42 - 1
transport/src/main/java/com/iscs/comm/manager/BLEManager.kt

@@ -18,12 +18,19 @@ import android.os.SystemClock
 import com.iscs.comm.entity.BleConnectResult
 import com.iscs.comm.entity.BleFrame
 import com.iscs.comm.extension.byteArrayToHexString
+import com.iscs.comm.extension.hexToByteArray
 import com.iscs.comm.protocol.BLEProtocol
 import com.iscs.comm.utils.ISCSLog
 import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeout
 import java.util.UUID
+import java.util.concurrent.ConcurrentHashMap
 
 /**
  * 蓝牙连接管理器
@@ -56,6 +63,9 @@ class BLEManager(
     // 连接回调
     private var doneConnect: CancellableContinuation<BleConnectResult>? = null
 
+    // 待响应式的请求数据接收池
+    private val receiverPool = ConcurrentHashMap<String, CompletableDeferred<BleFrame>>()
+
     // 蓝牙扫描回调
     private val scanCallback = object : ScanCallback() {
 
@@ -84,6 +94,7 @@ class BLEManager(
     @OptIn(ExperimentalCoroutinesApi::class)
     private val gattCallback = object : BluetoothGattCallback() {
 
+        @SuppressLint("MissingPermission")
         override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
             if (newState == BluetoothProfile.STATE_CONNECTED) {
                 ISCSLog.i(TAG, "gattCallback onConnectionStateChange() gatt connected")
@@ -93,6 +104,7 @@ class BLEManager(
             }
         }
 
+        @SuppressLint("MissingPermission")
         override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
             if (status == BluetoothGatt.GATT_SUCCESS) {
                 ISCSLog.i(TAG, "gattCallback onServicesDiscovered() find services success")
@@ -115,6 +127,7 @@ class BLEManager(
             }.start()
         }
 
+        @SuppressLint("MissingPermission")
         override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) {
             super.onCharacteristicRead(gatt, characteristic, value, status)
             ISCSLog.i(TAG, "gattCallback onCharacteristicRead() ${gatt.readCharacteristic(characteristic)} ${value.contentToString()}")
@@ -128,6 +141,23 @@ class BLEManager(
         override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
             super.onCharacteristicChanged(gatt, characteristic)
             ISCSLog.i(TAG, "gattCallback onCharacteristicChanged() get notification data ${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) {
@@ -178,6 +208,7 @@ class BLEManager(
     /**
      * 连接设备,内部方法
      */
+    @SuppressLint("MissingPermission")
     private fun innerConnect() {
         // context -> app
         // autoConnect -> false 避免设备连接异常
@@ -191,6 +222,7 @@ class BLEManager(
     /**
      * 断开连接
      */
+    @SuppressLint("MissingPermission")
     fun disconnect() {
         device?.let { gatt?.close() }
     }
@@ -198,6 +230,7 @@ class BLEManager(
     /**
      * 是否使能Indicate
      */
+    @SuppressLint("MissingPermission")
     private fun enableIndicate(): Boolean {
         gatt?.getService(UUID.fromString(serviceUUID))?.getCharacteristic(UUID.fromString(indicateUUID))?.let {
             // 开启通知
@@ -223,7 +256,10 @@ class BLEManager(
     /**
      * 写数据,带响应
      */
-    fun writeByResponse(frame: BleFrame) {
+    @SuppressLint("MissingPermission")
+    suspend fun writeByResponse(frame: BleFrame) = withContext(Dispatchers.IO) {
+        val deferred = CompletableDeferred<BleFrame>()
+        receiverPool["${frame.reqCode.byteArrayToHexString()}_${frame.rspCode.byteArrayToHexString()}"] = deferred
         // 发送数据方法兼容处理
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
             gatt?.getService(UUID.fromString(serviceUUID))?.getCharacteristic(UUID.fromString(indicateUUID))?.let {
@@ -237,6 +273,11 @@ class BLEManager(
             characteristic?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
             gatt?.writeCharacteristic(characteristic)
         }
+        try {
+            withTimeout(1000) { deferred.await() }
+        } catch (_: TimeoutCancellationException) {
+            BleFrame(byteArrayOf(), byteArrayOf(), byteArrayOf())
+        }
     }
 
 }

+ 12 - 0
transport/src/main/java/com/iscs/comm/protocol/BLEProtocol.kt

@@ -17,4 +17,16 @@ object BLEProtocol {
     // 获取令牌响应,最后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)
+
 }