SEC_Warehouse/utils/ble/core/stateWait.js

100 lines
2.9 KiB
JavaScript

/**
* 统一等待 UUID_STATE 通知。
*
* @param {object} ble createBleCore 实例
* @param {object} options 等待配置
* @param {number[]} options.allowedCodes 允许通过的状态码;为空则接收任意状态码
* @param {boolean} options.rejectOnUnexpected 收到非 allowedCodes 状态码时是否立刻失败
* @param {number} options.timeout 超时时间(ms)
* @param {string} options.timeoutMessage 超时提示
* @param {string} options.unexpectedMessage 非预期状态提示前缀
* @param {number[]} options.refreshOnCodes 收到这些中间态状态码时重置超时计时器
* @param {number} options.refreshTimeout 重置后的窗口时长(ms),未传则回退到 timeout
*
* 中间态刷新机制:设备连接 WiFi 时会先发 STATE=4(连接中) 再发最终态,
* 若总超时短于真实 DHCP 时长会误判超时。收到 refreshOnCodes 内的中间态时
* 重置倒计时,最多延长至 refreshTimeout 指定的单次窗口时长。
*/
export function waitForProtocolState(ble, options = {}) {
const {
allowedCodes = [],
rejectOnUnexpected = false,
timeout = 6000,
timeoutMessage = '等待 UUID_STATE 超时',
unexpectedMessage = '收到非预期 UUID_STATE 状态码',
refreshOnCodes = [],
refreshTimeout = timeout
} = options
return new Promise((resolve, reject) => {
let timer = null
let off = null
try {
console.debug('[BLE][STATEWAIT]', 'start', {
allowed: allowedCodes.join(',') || 'ANY',
timeout,
refreshOn: refreshOnCodes.join(',') || 'NONE'
})
} catch (e) {
}
const finish = (callback) => {
if (timer) {
clearTimeout(timer)
}
if (off) {
off()
}
callback()
}
const armTimer = (duration) => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
try {
console.debug('[BLE][STATEWAIT]', 'timeout')
} catch (e) {
}
finish(() => reject(new Error(timeoutMessage)))
}, duration)
}
armTimer(timeout)
off = ble.on('protocol:state', (payload) => {
const stateCode = Number(payload?.stateCode)
if (!allowedCodes.length || allowedCodes.includes(stateCode)) {
try {
console.debug('[BLE][STATEWAIT]', 'accept', stateCode)
} catch (e) {
}
finish(() => resolve(payload))
return
}
// 命中中间态刷新:重置超时窗口,继续等待后续最终态
if (refreshOnCodes.length && refreshOnCodes.includes(stateCode)) {
try {
console.debug('[BLE][STATEWAIT]', 'refresh', stateCode)
} catch (e) {
}
armTimer(refreshTimeout)
return
}
if (rejectOnUnexpected) {
try {
console.debug('[BLE][STATEWAIT]', 'reject', stateCode)
} catch (e) {
}
finish(() => reject(new Error(`${unexpectedMessage}: ${stateCode}`)))
}
})
})
}