diff --git a/docs/unified-ble-flow.md b/docs/unified-ble-flow.md index 73686e4..13ab2c7 100644 --- a/docs/unified-ble-flow.md +++ b/docs/unified-ble-flow.md @@ -132,6 +132,14 @@ async function runStandardFlow({ key, ssid, password }) { ## 6. 修改记录 +### 2026-09-11 回滚到迁移完成基线(真机仍搜不到设备) + +- 2026-09-09/09-10 的全部搜索策略改动(`ac4cb3b` 节流、`01fefe3` 全频段扫描、`2238e96` 恢复过滤+排障日志)与 `58d0ee8`(ble-config 页 useDidHide/超时兜底/safeDisconnect)逐一尝试后真机仍无法搜出设备,`git checkout 8494285 --` 整体回滚到"Taro 迁移收尾提交"的基线状态: + - `src/ble/discovery.js`:恢复过滤扫描优先 + 4s 兜底全扫的原始实现,移除 300ms UI 刷新节流与 `[BLE][SCAN]`/`[BLE][FOUND]` 排障日志,`stopSearch` 恢复"先停原生扫描再翻转状态"的原顺序 + - `src/constants/bluetooth.js`:恢复原注释(内容未变) + - `src/pages/device/ble-config.tsx`:恢复 `1f3c9c8` 初版——仅 useEffect 卸载清理(无 useDidHide)、`prepareWifiConfig` 无超时兜底、连接失败不补 disconnect +- 回滚后基线 = 迁移完成时用户尚未报告任何搜索问题的状态,用于隔离"迁移本身引入"与"后续修复引入"两类原因 + ### 2026-09-10 恢复过滤扫描优先 + 搜索排障日志 #### src/ble/discovery.js diff --git a/src/ble/discovery.js b/src/ble/discovery.js index 696810b..56d595d 100644 --- a/src/ble/discovery.js +++ b/src/ble/discovery.js @@ -11,14 +11,10 @@ import {DEVICE_PROFILE, createUnifiedBleController, shortUuid, uuidInList} from import {showToast} from '@/utils/toast' import Taro from '@tarojs/taro' +const FILTER_SCAN_TIMEOUT_MS = 4000 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 const TARGET_NAME_KEYWORDS = Object.freeze(['ED713', '713WQ', 'ED719', 'EP832']) function promisifyTaroApi(apiName, params = {}) { @@ -81,10 +77,9 @@ export function createBluetoothDiscovery(options = {}) { let searchSessionId = 0 let acceptingDeviceEvents = false + let filterFallbackTimer = null let discoveryPollTimer = null let autoStopTimer = null - let deviceListEmitTimer = null - let filterFallbackTimer = null let offDeviceFound = null let offAdapterState = null @@ -121,41 +116,14 @@ export function createBluetoothDiscovery(options = {}) { onDeviceListChange(getSortedDeviceList()) } - // 广播包持续到达时(RSSI 每包都变,签名判重挡不住),节流 UI 刷新; - // 数据仍实时合入 deviceMap,只是通知 UI 的频率被压到每 300ms 一次。 - // 末包到达后补一次尾部刷新,保证最后一帧数据不丢 - function emitDeviceListThrottled() { - if (deviceListEmitTimer) { - return - } - - deviceListEmitTimer = setTimeout(() => { - deviceListEmitTimer = null - emitDeviceList() - }, DEVICE_LIST_EMIT_INTERVAL_MS) - } - - function clearDeviceListEmitTimer() { - if (!deviceListEmitTimer) { - return - } - - clearTimeout(deviceListEmitTimer) - deviceListEmitTimer = null - } - function setDeviceMap(nextMap) { deviceMap = nextMap onDeviceMapChange({...deviceMap}) - emitDeviceListThrottled() + emitDeviceList() } function resetDeviceMap() { - clearDeviceListEmitTimer() - deviceMap = {} - onDeviceMapChange({...deviceMap}) - // 重置列表必须立即通知:清空操作不能等 300ms 节流窗口 - emitDeviceList() + setDeviceMap({}) } function normalizeUuid(uuid) { @@ -273,6 +241,10 @@ export function createBluetoothDiscovery(options = {}) { } } + function hasTargetDeviceInMap() { + return Object.values(deviceMap).some((item) => item.isTargetDevice) + } + function getDeviceStableSignature(device = {}) { return [ device.deviceId || '', @@ -286,10 +258,6 @@ export function createBluetoothDiscovery(options = {}) { ].join('#') } - function hasTargetDeviceInMap() { - return Object.values(deviceMap).some((item) => item.isTargetDevice) - } - function clearFallbackTimer() { if (!filterFallbackTimer) { return @@ -367,12 +335,12 @@ export function createBluetoothDiscovery(options = {}) { function upsertDevice(rawDevice) { if (!rawDevice || !rawDevice.deviceId) { - return false + return } const matched = matchTargetDevice(rawDevice) if (!matched.isTarget) { - return false + return } const existing = deviceMap[rawDevice.deviceId] || {} @@ -391,14 +359,13 @@ export function createBluetoothDiscovery(options = {}) { const nextSignature = getDeviceStableSignature(merged) if (prevSignature === nextSignature) { - return false + return } setDeviceMap({ ...deviceMap, [rawDevice.deviceId]: merged }) - return true } function getBluetoothErrorText(error) { @@ -433,19 +400,13 @@ export function createBluetoothDiscovery(options = {}) { } const currentSession = searchSessionId - let hitCount = 0 devices.forEach((device) => { if (!acceptingDeviceEvents || currentSession !== searchSessionId) { return } - if (upsertDevice(device)) { - hitCount += 1 - } + upsertDevice(device) }) - if (hitCount) { - console.debug('[BLE][FOUND]', 'event devices=', devices.length, 'newHits=', hitCount) - } }) offAdapterState = controller.ble.on('adapter:state', (state) => { @@ -586,18 +547,13 @@ 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 } - if (upsertDevice(device)) { - hitCount += 1 - } + upsertDevice(device) }) - console.debug('[BLE][SCAN]', 'native devices=', list.length, 'newHits=', hitCount) } catch (error) { } } @@ -622,9 +578,6 @@ export function createBluetoothDiscovery(options = {}) { discoveryPollTimer = setTimeout(run, 300) } - // 过滤扫描超过 4s 仍无目标设备:停扫 → 等待 → 切无过滤全频段扫描。 - // 微信 services 过滤只按 128 位完整 UUID 匹配广播包,16 位短 UUID 设备 - // 会被过滤掉,必须靠兜底全扫 + JS 层 matchTargetDevice 兜住 async function fallbackToUnfilteredDiscoveryIfNeeded(sessionId) { clearFallbackTimer() @@ -634,7 +587,6 @@ export function createBluetoothDiscovery(options = {}) { } try { - console.debug('[BLE][SCAN]', 'filtered scan found nothing, fallback to unfiltered') await stopDiscoverySilently() await delay(RESTART_SCAN_DELAY_MS) @@ -650,8 +602,7 @@ export function createBluetoothDiscovery(options = {}) { /** * 设备搜索: - * 优先按 0xFFF4/0x00F4/0xF400 过滤扫描(快、列表干净); - * 4s 无结果自动切到无过滤全扫(兜住 16 位短 UUID 设备)并轮询 getBluetoothDevices。 + * 优先按 0xFFF4/0x00F4/0xF400 过滤;若无结果自动切到兜底扫描并轮询 getBluetoothDevices。 */ async function startSearch() { if (isSearching) { @@ -692,11 +643,8 @@ export function createBluetoothDiscovery(options = {}) { 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) + } catch (error) { await startDiscoveryWithServices([]) - console.debug('[BLE][SCAN]', 'unfiltered discovery started (after filter fail), sessionId=', sessionId) } if (sessionId !== searchSessionId) { @@ -729,20 +677,13 @@ export function createBluetoothDiscovery(options = {}) { searchSessionId += 1 acceptingDeviceEvents = false - // 先同步翻转搜索态,再异步停原生扫描: - // stopBluetoothDevicesDiscovery 在部分机型回调慢,若等它完成才 setSearching(false), - // 期间按钮仍显示"搜索中"、连接/二次搜索等后续操作全部被串行拖住(感知为卡顿)。 - // searchSessionId 已递增、事件已关闭,翻转状态不会引入新的扫描事件 + await stopDiscoverySilently() + + // 暂停搜索保留已发现的设备数据,不做 resetDeviceMap setSearching(false) if (showStopToast) { showToast('已停止搜索') } - - await stopDiscoverySilently() - - // 停搜后立即冲刷最后一帧列表,避免 pending 的节流定时器延迟刷新 - clearDeviceListEmitTimer() - emitDeviceList() } function pickPreferredDeviceId(deviceId) { @@ -776,11 +717,7 @@ export function createBluetoothDiscovery(options = {}) { async function connectDevice(deviceId) { await ensureInitialized() - // 搜索进行中点击"网络配置":立即停搜索(状态同步翻转,不等原生回调), - // 否则 createBLEConnection 会被扫描信道占用拖慢甚至失败 - if (isSearching) { - await stopSearch(false) - } + await stopSearch(false) const targetDeviceId = pickPreferredDeviceId(deviceId) if (!targetDeviceId) { @@ -930,7 +867,6 @@ export function createBluetoothDiscovery(options = {}) { function cleanupDiscovery() { clearFallbackTimer() clearDiscoveryPollTimer() - clearDeviceListEmitTimer() searchSessionId += 1 acceptingDeviceEvents = false diff --git a/src/constants/bluetooth.js b/src/constants/bluetooth.js index 4984659..3e512fb 100644 --- a/src/constants/bluetooth.js +++ b/src/constants/bluetooth.js @@ -16,10 +16,6 @@ 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, diff --git a/src/pages/device/ble-config.tsx b/src/pages/device/ble-config.tsx index 5121b8b..a134a25 100644 --- a/src/pages/device/ble-config.tsx +++ b/src/pages/device/ble-config.tsx @@ -1,35 +1,20 @@ import { useEffect, useState } from 'react' -import Taro, { useDidHide } from '@tarojs/taro' +import Taro from '@tarojs/taro' import { View, Text } from '@tarojs/components' import { Navbar } from '@/components/navbar' import { useBle } from '@/ble/hooks/useBle' import { navigateTo } from '@/utils/navigation' import { ROUTES } from '@/constants/routes' import { getErrorMessage } from '@/utils/error' -import { showToast } from '@/utils/toast' import { DEVICE_ACTION_KEYS, ACTION_REGISTRY, isEp832Device } from '@/constants/deviceActions' import type { BleDevice } from '@/ble/hooks/useBle' import styles from './ble-config.module.scss' -// createBLEConnection 最多 3 次尝试(15s 超时 + 1.2s 重试间隔)约 47s, -// 兜底覆盖 discoverServices/notify 无原生超时的场景,保证 loading 遮罩必然撤除 -const PREPARE_WIFI_TIMEOUT_MS = 60000 - -function withTimeout(promise: Promise, ms: number, message: string): Promise { - return Promise.race([ - promise, - new Promise((_, reject) => { - setTimeout(() => reject(new Error(message)), ms) - }) - ]) -} - export default function DeviceBleConfig() { const { state, discovery } = useBle() const [preparingId, setPreparingId] = useState('') - // 页面隐藏(navigateTo 配网页/切后台)与卸载(返回)时停搜索,连接保持供配网页使用。 - // useEffect 的清理只在卸载触发,压栈后台时组件不卸载,必须补 useDidHide(对齐旧版 onHide/onUnload) + // 页面隐藏/卸载时清理搜索(对齐旧版 onHide/onUnload),连接保持供配网页使用 useEffect(() => { return () => { if (discovery.getState().isSearching) { @@ -39,12 +24,6 @@ export default function DeviceBleConfig() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []) - useDidHide(() => { - if (discovery.getState().isSearching) { - discovery.stopSearch(false) - } - }) - const startScan = async () => { if (state.searching) { await discovery.stopSearch() @@ -67,19 +46,10 @@ export default function DeviceBleConfig() { let prepError: unknown = null try { - // createBLEConnection 部分机型失败后不回调(微信基础库缺陷), - // discoverServices/notify 也无原生超时;不设兜底会导致 loading 永不消失、按钮永久失效 - await withTimeout( - discovery.prepareWifiConfig(deviceId), - PREPARE_WIFI_TIMEOUT_MS, - `连接设备超时(${PREPARE_WIFI_TIMEOUT_MS / 1000}s),请重试` - ) + await discovery.prepareWifiConfig(deviceId) navigateTo(ROUTES.DEVICE_CONFIG) } catch (error) { prepError = error - // 对齐旧版 safeDisconnect:连接半途失败时清掉 OS 层残留连接, - // 否则后续重试会被"已连接(10010)"错误卡死 - discovery.disconnectDevice().catch(() => {}) } finally { setPreparingId('') // showLoading 与 showToast 共用同一遮罩层,必须先 hideLoading 再弹提示 @@ -94,8 +64,6 @@ export default function DeviceBleConfig() { showCancel: false, confirmText: '知道了' }) - } else { - showToast('设备已连接,请填写WiFi信息') } }