diff --git a/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/bluetooth/BleManager.kt b/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/bluetooth/BleManager.kt index 76cf608d51..ee444bb364 100644 --- a/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/bluetooth/BleManager.kt +++ b/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/bluetooth/BleManager.kt @@ -3,7 +3,10 @@ package com.mogo.och.common.module.manager.bluetooth import android.annotation.SuppressLint import android.bluetooth.le.BluetoothLeScanner import android.bluetooth.le.ScanCallback +import android.bluetooth.le.ScanFilter import android.bluetooth.le.ScanResult +import android.bluetooth.le.ScanSettings +import android.os.ParcelUuid import android.util.Log import com.mogo.och.common.module.utils.RxUtils import java.util.UUID @@ -11,7 +14,8 @@ import java.util.UUID object BleManager : BaseBluetoothManager() { - val UUID_SERVICE: UUID = UUID.fromString("000018F0-0000-1000-8000-00805F9BDDFB") //自定义UUID + val UUID_SERVICE_String:String = "000018F0-0000-1000-8000-00805F9BDDFB" + val UUID_SERVICE: UUID = UUID.fromString(UUID_SERVICE_String) //自定义UUID val UUID_CHAR_READ_NOTIFY: UUID = UUID.fromString("00002AF0-0000-1000-8000-00805F9BEEFB") val UUID_DESC_NOTITY: UUID = UUID.fromString("00002AF1-0000-1000-8000-00805F9BCCFB") val UUID_CHAR_WRITE: UUID = UUID.fromString("00002AF1-0000-1000-8000-00805F9BFFFB") @@ -24,6 +28,7 @@ object BleManager : BaseBluetoothManager() { private val SCAN_PERIOD = 60_000L private const val TAG = "BleManager" + private const val TAG1 = "GattCallback" // 未连接到的设备 val scanList = mutableListOf() @@ -41,6 +46,7 @@ object BleManager : BaseBluetoothManager() { val bleDevItem = OchBluetoothGattCallback(result.device) if(!scanList.contains(bleDevItem)){ scanList.add(bleDevItem) + Log.d(TAG1, "添加item scanList个数:${scanList.size}") } Log.d(TAG, "callbackType:${callbackType}_____result:${result}") } @@ -49,6 +55,7 @@ object BleManager : BaseBluetoothManager() { } + @SuppressLint("MissingPermission") fun scanLeDevice() { when (isEnable()) { @@ -69,7 +76,13 @@ object BleManager : BaseBluetoothManager() { bluetoothLeScanner?.stopScan(leScanCallback) } scanning = true - bluetoothLeScanner?.startScan(leScanCallback) + + val filters = mutableListOf() + val filter = ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(UUID_SERVICE_String)).build() + filters.add(filter); + val scanSettings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build() + + bluetoothLeScanner?.startScan(filters,scanSettings,leScanCallback) scanList.clear() } }catch (e:SecurityException){ @@ -81,6 +94,13 @@ object BleManager : BaseBluetoothManager() { @SuppressLint("MissingPermission") fun sendData2Wx(devName: String, data: String): Boolean { var needDev:OchBluetoothGattCallback?=null + sendDataIntDev.forEach { + if(it.device.name==devName){ + it.connectGattAndSend(data) + return true + } + } + scanList.forEach { if(it.device.name==devName){ needDev = it @@ -90,7 +110,10 @@ object BleManager : BaseBluetoothManager() { needDev?.let { it.connectGattAndSend(data) scanList.remove(it) - sendDataIntDev.add(it) + if(!sendDataIntDev.contains(it)) { + sendDataIntDev.add(it) + } + Log.d(TAG, "发送数据 scanList个数:${scanList.size}___sendDataIntDev个数:${sendDataIntDev.size}") if (sendDataIntDev.size > maxConnectCount) { sendDataIntDev.first().tryCloseConn() } @@ -101,7 +124,16 @@ object BleManager : BaseBluetoothManager() { fun copy2ScanList(ochBluetoothGattCallback: OchBluetoothGattCallback) { sendDataIntDev.remove(ochBluetoothGattCallback) - scanList.add(ochBluetoothGattCallback) + if(!scanList.contains(ochBluetoothGattCallback)){ + scanList.add(ochBluetoothGattCallback) + } + Log.d(TAG1, "超时返回 scanList个数:${scanList.size}___sendDataIntDev个数:${sendDataIntDev.size}") + } + + fun removeFromScandAndWrite(ochBluetoothGattCallback: OchBluetoothGattCallback) { + sendDataIntDev.remove(ochBluetoothGattCallback) + scanList.remove(ochBluetoothGattCallback) + Log.d(TAG1, "链接超次 scanList个数:${scanList.size}___sendDataIntDev个数:${sendDataIntDev.size}") } } \ No newline at end of file diff --git a/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/bluetooth/OchBluetoothGattCallback.kt b/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/bluetooth/OchBluetoothGattCallback.kt index 1fe3fe86b9..95e6dfa707 100644 --- a/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/bluetooth/OchBluetoothGattCallback.kt +++ b/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/bluetooth/OchBluetoothGattCallback.kt @@ -2,6 +2,7 @@ package com.mogo.och.common.module.manager.bluetooth import android.annotation.SuppressLint import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothDevice.TRANSPORT_LE import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCallback import android.bluetooth.BluetoothGattCharacteristic @@ -20,6 +21,10 @@ import kotlin.properties.Delegates @SuppressLint("MissingPermission") class OchBluetoothGattCallback(device: BluetoothDevice) : BluetoothGattCallback() { + + // 连接超时 + // 写超时 + private val _device = device val device:BluetoothDevice get() { @@ -36,16 +41,36 @@ class OchBluetoothGattCallback(device: BluetoothDevice) : BluetoothGattCallback( private var timeoutCLose: Disposable? = null + private var writeOut: Disposable? = null + + private var isDestory = false + + var isConnected:Boolean by Delegates.observable(false) { _, oldValue, newValue -> if (oldValue != newValue) { Log.i(TAG,"${device.name}:链接状态发生变化${newValue}") + if(!newValue){ + OchChainLogManager.writeChainLogBluetooth("连接","连接断开") + closeAndDestory() + } } } private val TAG = "GattCallback" + fun closeAndDestory(){ + isDestory = true + closeConn(false) + BleManager.removeFromScandAndWrite(this) + } + /** - * @param + * @param gatt [android.bluetooth.BluetoothProfile.STATE_CONNECTED] 连接成功 + * [android.bluetooth.BluetoothProfile.STATE_CONNECTING] 蓝牙连接中 + * [android.bluetooth.BluetoothProfile.STATE_DISCONNECTED] 连接失败 + * [android.bluetooth.BluetoothProfile.STATE_DISCONNECTING] 连接失败 + * @param status + * @param newState */ override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { val dev = gatt.device @@ -57,12 +82,21 @@ class OchBluetoothGattCallback(device: BluetoothDevice) : BluetoothGattCallback( gatt.discoverServices() //启动服务发现 }else{ isConnected = false + closeConn(false) connect() Log.i(TAG,"与[${dev.name}]连接断开") } }else{ Log.i(TAG,"与[${dev.name}]连接出错,错误码:$status") - connect() + when (status) { + 8 -> { + closeAndDestory() + } + else -> { + connect() + } + } + } } @@ -71,9 +105,7 @@ class OchBluetoothGattCallback(device: BluetoothDevice) : BluetoothGattCallback( if (status == BluetoothGatt.GATT_SUCCESS) { //BLE服务发现成功 // 遍历获取BLE服务Services/Characteristics/Descriptors的全部UUID // 链接成功 服务发现也成功后 - waitSendData.forEach { - writeData2UUID(it.key,it.value) - } + sendData() } } @@ -99,14 +131,16 @@ class OchBluetoothGattCallback(device: BluetoothDevice) : BluetoothGattCallback( "onCharacteristicWrite:${gatt.device.name},${gatt.device.address},$uuid,$valueStr,$status" ) if(status==BluetoothGatt.GATT_SUCCESS){ - OchChainLogManager.writeChainLogWriteOff("司机端核销成功","${device.name}:发送数据${valueStr} 小程序接受成功") + OchChainLogManager.writeChainLogBluetooth("写入","${device.name}:发送数据${valueStr} 小程序接受成功") waitSendData.remove(valueStr) if(waitSendData.isEmpty()){ RxUtils.disposeSubscribe(timeoutCLose) timeoutCLose = RxUtils.createSubscribeOnOwnThread(10_000) { - closeConn() + closeConn(true) } } + }else{ + OchChainLogManager.writeChainLogBluetooth("写入","${device.name}:发送数据${valueStr} 写入失败") } } @@ -125,20 +159,22 @@ class OchBluetoothGattCallback(device: BluetoothDevice) : BluetoothGattCallback( if (waitSendData.isNotEmpty()) { return }else{ - closeConn() + closeConn(true) } } } // BLE中心设备连接外围设备的数量有限(大概2~7个),在建立新连接之前必须释放旧连接资源,否则容易出现连接错误133 - fun closeConn() { + fun closeConn(needCopy:Boolean) { if (mBluetoothGatt != null) { mBluetoothGatt?.disconnect() mBluetoothGatt?.close() mBluetoothGatt = null currentTryCount = 0 isConnected = false - BleManager.copy2ScanList(this) + if(needCopy&&!isDestory) { + BleManager.copy2ScanList(this) + } } } @@ -161,6 +197,20 @@ class OchBluetoothGattCallback(device: BluetoothDevice) : BluetoothGattCallback( val characteristic = service.getCharacteristic(uuid) //通过UUID获取可写的Characteristic characteristic.setValue(text.toByteArray()) //单次最多20个字节 mBluetoothGatt!!.writeCharacteristic(characteristic) + // 设置写超时 + RxUtils.disposeSubscribe(timeoutCLose) + timeoutCLose = RxUtils.createSubscribeOnOwnThread(15_000) { + // 删除正在发送的 + waitSendData.remove(text) + if(waitSendData.isEmpty()){ + OchChainLogManager.writeChainLogBluetooth("写数据","写15s超时") + closeAndDestory() + } + sendData() + } + }else{ + OchChainLogManager.writeChainLogBluetooth("发现服务","server 为null 没有链接服务") + closeAndDestory() } } @@ -169,20 +219,63 @@ class OchBluetoothGattCallback(device: BluetoothDevice) : BluetoothGattCallback( if(isConnected){ ToastUtils.showShort("已连接成功") OchChainLogManager.writeChainLogWriteOff("司机端核销成功","已连接成功") - writeData2UUID(data,BleManager.UUID_CHAR_WRITE) + waitSendData[data] = BleManager.UUID_CHAR_WRITE + sendData() }else { + if (isDestory) { + OchChainLogManager.writeChainLogBluetooth("连接","已销毁") + closeAndDestory() + return + } connect() waitSendData[data] = BleManager.UUID_CHAR_WRITE } } + /** + * 链接成功后发送 + * 写超时后 写下一条 + */ + private fun sendData() { + waitSendData.firstNotNullOf { + if(RxUtils.isNotDisposed(writeOut)){ + return + } + writeData2UUID(it.key,it.value) + } + } + + /** + * 连接失败 + * 连接失败 + * 发送数据 + */ private fun connect(){ Log.i(TAG,"connect:第${currentTryCount}次") // 重试5次 - if(currentTryCount>maxTryCount){ + if(currentTryCount>maxTryCount||isDestory){ + // 重新扫描该设备 + OchChainLogManager.writeChainLogBluetooth("连接","超过次数") + closeAndDestory() return } currentTryCount += 1 - mBluetoothGatt = _device.connectGatt(AbsMogoApplication.getApp(), false, this) + val temp = currentTryCount + closeConn(false) + currentTryCount = temp + mBluetoothGatt = _device.connectGatt(AbsMogoApplication.getApp(), false, this, TRANSPORT_LE) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as OchBluetoothGattCallback + + return _device.name == other._device.name && _device.address== other._device.address + } + + override fun hashCode(): Int { + return _device.hashCode() } } \ No newline at end of file diff --git a/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/logchainanalytic/OchChainLogManager.kt b/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/logchainanalytic/OchChainLogManager.kt index a5de2226a9..164bc09204 100644 --- a/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/logchainanalytic/OchChainLogManager.kt +++ b/OCH/common/common/src/main/java/com/mogo/och/common/module/manager/logchainanalytic/OchChainLogManager.kt @@ -60,10 +60,15 @@ object OchChainLogManager { const val EVENT_KEY_INFO_WRITEOFF = "analytics_event_och_writeoff" + const val EVENT_KEY_INFO_BLUETOOTH = "analytics_event_och_bluetooth" + fun writeChainLogDb(title: String, info: String) { writeChainLog(title, info, true, EVENT_KEY_INFO_DB) } + fun writeChainLogBluetooth(title: String, info: String) { + writeChainLog(title, info, true, EVENT_KEY_INFO_BLUETOOTH) + } fun writeChainLogWriteOff(title: String, info: String) { writeChainLog(title, info, true, EVENT_KEY_INFO_WRITEOFF) } diff --git a/OCH/shuttle/passenger_weaknet/src/main/java/m2/com/mogo/och/shuttle/weaknet/passenger/ui/widget/M2StatusBarView.kt b/OCH/shuttle/passenger_weaknet/src/main/java/m2/com/mogo/och/shuttle/weaknet/passenger/ui/widget/M2StatusBarView.kt index 808ae4f02a..f46d22882d 100644 --- a/OCH/shuttle/passenger_weaknet/src/main/java/m2/com/mogo/och/shuttle/weaknet/passenger/ui/widget/M2StatusBarView.kt +++ b/OCH/shuttle/passenger_weaknet/src/main/java/m2/com/mogo/och/shuttle/weaknet/passenger/ui/widget/M2StatusBarView.kt @@ -18,8 +18,11 @@ import com.mogo.eagle.core.function.call.setting.CallerSkinModeListenerManager import com.mogo.eagle.core.utilcode.kotlin.* import com.mogo.eagle.core.utilcode.util.ClickUtils import com.mogo.eagle.core.utilcode.util.ThreadUtils +import com.mogo.och.common.module.manager.bluetooth.BleManager +import com.mogo.och.common.module.manager.loop.BizLoopManager import com.mogo.och.shuttle.weaknet.passenger.R import kotlinx.android.synthetic.main.shuttle_p_m2_view_status_bar.view.aciv_wait_ele +import kotlinx.android.synthetic.main.shuttle_p_m2_view_status_bar.view.iv_logon import kotlinx.android.synthetic.main.shuttle_p_m2_view_status_bar.view.progress import kotlinx.android.synthetic.main.shuttle_p_m2_view_status_bar.view.tv_power_cos import kotlinx.coroutines.* @@ -71,6 +74,16 @@ class M2StatusBarView @JvmOverloads constructor( tv_power_cos?.also { it.text = "检测中" } + iv_logon.onClick { + BizLoopManager.runInMainThread{ + BleManager.scanLeDevice() + } + } + tv_power_cos.onClick { + BizLoopManager.runInIoThread { + BleManager.sendData2Wx("1889480", "00,${System.currentTimeMillis()}") + } + } } override fun onSkinModeChange(skinMode: Int) {