fix(ble): 恢复过滤扫描优先+4s兜底全扫,并加搜索排障日志
真机验证"直接全频段扫描"方案后设备反而搜不出(修改前过滤扫描可搜到), 说明该机型下无过滤扫描路径存在问题(可能为机型对无 services 参数的 startBluetoothDevicesDiscovery 兼容差异)。回退为迁移原版策略: - 恢复 DISCOVERY_FILTER_SERVICE_UUIDS 常量与 fallbackToUnfilteredDiscoveryIfNeeded 兜底机制:先按 0xFFF4/0x00F4/0xF400 过滤扫描,4s 无结果自动切全扫 - upsertDevice 改为返回布尔值,event/poll 两条通道均打印排障日志 ([BLE][SCAN] 原生采集数与命中数、[BLE][FOUND] 事件命中数、 启动/兜底切换日志),真机 vConsole 可直接观察各阶段状态
This commit is contained in:
parent
5e5cd2bf47
commit
2238e965b9
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
BLUETOOTH_ERROR_MESSAGES,
|
||||
DEFAULT_BLUETOOTH_ERROR_MESSAGE,
|
||||
DISCOVERY_FILTER_SERVICE_UUIDS,
|
||||
TARGET_SERVICE_UUID_FULL,
|
||||
TARGET_SERVICE_UUID_ALT_FULL,
|
||||
TARGET_SERVICE_UUID_COMPAT_FULL,
|
||||
|
|
@ -13,6 +14,8 @@ import Taro from '@tarojs/taro'
|
|||
const RESTART_SCAN_DELAY_MS = 120
|
||||
const DISCOVERY_POLL_INTERVAL_MS = 1200
|
||||
const SCAN_DURATION_MS = 30000
|
||||
// 过滤扫描无结果多久后切到无过滤全扫
|
||||
const FILTER_SCAN_TIMEOUT_MS = 4000
|
||||
// 设备列表 UI 刷新最小间隔:搜索中 allowDuplicatesKey=true,目标设备每秒广播
|
||||
// 数次,不节流会以每秒数十次 setState 打满小程序渲染线程,点击被排队卡死
|
||||
const DEVICE_LIST_EMIT_INTERVAL_MS = 300
|
||||
|
|
@ -81,6 +84,7 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
let discoveryPollTimer = null
|
||||
let autoStopTimer = null
|
||||
let deviceListEmitTimer = null
|
||||
let filterFallbackTimer = null
|
||||
|
||||
let offDeviceFound = null
|
||||
let offAdapterState = null
|
||||
|
|
@ -282,6 +286,19 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
].join('#')
|
||||
}
|
||||
|
||||
function hasTargetDeviceInMap() {
|
||||
return Object.values(deviceMap).some((item) => item.isTargetDevice)
|
||||
}
|
||||
|
||||
function clearFallbackTimer() {
|
||||
if (!filterFallbackTimer) {
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(filterFallbackTimer)
|
||||
filterFallbackTimer = null
|
||||
}
|
||||
|
||||
function clearDiscoveryPollTimer() {
|
||||
if (!discoveryPollTimer) {
|
||||
return
|
||||
|
|
@ -350,12 +367,12 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
|
||||
function upsertDevice(rawDevice) {
|
||||
if (!rawDevice || !rawDevice.deviceId) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
const matched = matchTargetDevice(rawDevice)
|
||||
if (!matched.isTarget) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
const existing = deviceMap[rawDevice.deviceId] || {}
|
||||
|
|
@ -374,13 +391,14 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
const nextSignature = getDeviceStableSignature(merged)
|
||||
|
||||
if (prevSignature === nextSignature) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
setDeviceMap({
|
||||
...deviceMap,
|
||||
[rawDevice.deviceId]: merged
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
function getBluetoothErrorText(error) {
|
||||
|
|
@ -415,13 +433,19 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
}
|
||||
|
||||
const currentSession = searchSessionId
|
||||
let hitCount = 0
|
||||
devices.forEach((device) => {
|
||||
if (!acceptingDeviceEvents || currentSession !== searchSessionId) {
|
||||
return
|
||||
}
|
||||
|
||||
upsertDevice(device)
|
||||
if (upsertDevice(device)) {
|
||||
hitCount += 1
|
||||
}
|
||||
})
|
||||
if (hitCount) {
|
||||
console.debug('[BLE][FOUND]', 'event devices=', devices.length, 'newHits=', hitCount)
|
||||
}
|
||||
})
|
||||
|
||||
offAdapterState = controller.ble.on('adapter:state', (state) => {
|
||||
|
|
@ -562,13 +586,18 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
try {
|
||||
const result = await promisifyTaroApi('getBluetoothDevices')
|
||||
const list = Array.isArray(result?.devices) ? result.devices : []
|
||||
// 排障日志:原生层到底采到了多少设备、命中了多少目标(真机 vConsole 可见)
|
||||
let hitCount = 0
|
||||
list.forEach((device) => {
|
||||
if (!isSearching || !acceptingDeviceEvents || sessionId !== searchSessionId) {
|
||||
return
|
||||
}
|
||||
|
||||
upsertDevice(device)
|
||||
if (upsertDevice(device)) {
|
||||
hitCount += 1
|
||||
}
|
||||
})
|
||||
console.debug('[BLE][SCAN]', 'native devices=', list.length, 'newHits=', hitCount)
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
|
|
@ -593,10 +622,36 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
discoveryPollTimer = setTimeout(run, 300)
|
||||
}
|
||||
|
||||
// 过滤扫描超过 4s 仍无目标设备:停扫 → 等待 → 切无过滤全频段扫描。
|
||||
// 微信 services 过滤只按 128 位完整 UUID 匹配广播包,16 位短 UUID 设备
|
||||
// 会被过滤掉,必须靠兜底全扫 + JS 层 matchTargetDevice 兜住
|
||||
async function fallbackToUnfilteredDiscoveryIfNeeded(sessionId) {
|
||||
clearFallbackTimer()
|
||||
|
||||
filterFallbackTimer = setTimeout(async () => {
|
||||
if (!isSearching || !acceptingDeviceEvents || sessionId !== searchSessionId || hasTargetDeviceInMap()) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
console.debug('[BLE][SCAN]', 'filtered scan found nothing, fallback to unfiltered')
|
||||
await stopDiscoverySilently()
|
||||
await delay(RESTART_SCAN_DELAY_MS)
|
||||
|
||||
if (!isSearching || !acceptingDeviceEvents || sessionId !== searchSessionId) {
|
||||
return
|
||||
}
|
||||
|
||||
await startDiscoveryWithServices([])
|
||||
} catch (error) {
|
||||
}
|
||||
}, FILTER_SCAN_TIMEOUT_MS)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备搜索:先无过滤全频段扫描(onBluetoothDeviceFound 事件驱动 + getBluetoothDevices 轮询双通道),
|
||||
* 命中关键词/服务 UUID 即入列。微信的 services 过滤只匹配 128 位完整 UUID,
|
||||
* 对 16 位广播 UUID(0x00F4 等)的设备经常过滤过头导致搜不到,故不再走过滤扫描。
|
||||
* 设备搜索:
|
||||
* 优先按 0xFFF4/0x00F4/0xF400 过滤扫描(快、列表干净);
|
||||
* 4s 无结果自动切到无过滤全扫(兜住 16 位短 UUID 设备)并轮询 getBluetoothDevices。
|
||||
*/
|
||||
async function startSearch() {
|
||||
if (isSearching) {
|
||||
|
|
@ -606,6 +661,7 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
// 立即标记搜索中,防止 await 期间用户重复点击或 stopSearch 被覆盖
|
||||
isSearching = true
|
||||
onSearchingChange(true)
|
||||
clearFallbackTimer()
|
||||
clearDiscoveryPollTimer()
|
||||
clearAutoStopTimer()
|
||||
|
||||
|
|
@ -634,13 +690,21 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
|
||||
acceptingDeviceEvents = true
|
||||
|
||||
await startDiscoveryWithServices([])
|
||||
try {
|
||||
await startDiscoveryWithServices(DISCOVERY_FILTER_SERVICE_UUIDS)
|
||||
console.debug('[BLE][SCAN]', 'filtered discovery started, sessionId=', sessionId)
|
||||
} catch (startError) {
|
||||
console.debug('[BLE][SCAN]', 'filtered start failed, retry unfiltered', startError)
|
||||
await startDiscoveryWithServices([])
|
||||
console.debug('[BLE][SCAN]', 'unfiltered discovery started (after filter fail), sessionId=', sessionId)
|
||||
}
|
||||
|
||||
if (sessionId !== searchSessionId) {
|
||||
return
|
||||
}
|
||||
|
||||
startDiscoveryPolling(sessionId)
|
||||
await fallbackToUnfilteredDiscoveryIfNeeded(sessionId)
|
||||
|
||||
// 30s 自动停止搜索
|
||||
autoStopTimer = setTimeout(() => {
|
||||
|
|
@ -658,6 +722,7 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
}
|
||||
|
||||
async function stopSearch(showStopToast = true) {
|
||||
clearFallbackTimer()
|
||||
clearDiscoveryPollTimer()
|
||||
clearAutoStopTimer()
|
||||
|
||||
|
|
@ -863,6 +928,7 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
}
|
||||
|
||||
function cleanupDiscovery() {
|
||||
clearFallbackTimer()
|
||||
clearDiscoveryPollTimer()
|
||||
clearDeviceListEmitTimer()
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,16 @@ export const TARGET_SHORT_UUID_CANDIDATES = Object.freeze([
|
|||
TARGET_SERVICE_UUID_COMPAT
|
||||
])
|
||||
|
||||
/**
|
||||
* 过滤扫描用的 128 位完整 UUID 列表(与 advertisServiceUUIDs 匹配主服务)。
|
||||
* 微信 services 过滤只认完整 UUID;广播 16 位短 UUID 的机型由无过滤兜底全扫 + JS 层匹配覆盖。
|
||||
*/
|
||||
export const DISCOVERY_FILTER_SERVICE_UUIDS = Object.freeze([
|
||||
TARGET_SERVICE_UUID_COMPAT_FULL,
|
||||
TARGET_SERVICE_UUID_FULL,
|
||||
TARGET_SERVICE_UUID_ALT_FULL
|
||||
])
|
||||
|
||||
/**
|
||||
* 蓝牙常见错误提示映射。
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue