const transport = require('../../transport/ble-core.js') const { alignBootloaderBuffer, getBootloaderExpectedLength, parseBootloaderResponse } = require('../../protocols/bootloader/index.js') const RESPONSE_TIMEOUT_MS = 3000 let activeResponseWaiter = null function abortActiveResponseWaiter(message) { if (!activeResponseWaiter) return false const waiter = activeResponseWaiter activeResponseWaiter = null waiter.abort(new Error(message || '蓝牙已断开')) return true } function waitForResponse(kind, timeout, options = {}) { const expectedLength = getBootloaderExpectedLength(kind) const buffer = [] return new Promise((resolve, reject) => { let settled = false let timer = null let unsubscribe = () => {} const waiter = { abort: (error) => { cleanup() reject(error) } } abortActiveResponseWaiter('新的 BootLoader 响应等待已开始') activeResponseWaiter = waiter unsubscribe = transport.subscribeRawResponse((bytes) => { buffer.push.apply(buffer, bytes) alignBootloaderBuffer(buffer) if (buffer.length < expectedLength) return const frame = buffer.slice(0, expectedLength) try { const response = parseBootloaderResponse(frame, kind) cleanup() resolve(response) } catch (error) { if (options.ignoreInvalid) { buffer.shift() return } cleanup() reject(error) } }) timer = setTimeout(() => { cleanup() reject(new Error(`${kind} 响应超时`)) }, timeout || RESPONSE_TIMEOUT_MS) function cleanup() { if (settled) return settled = true clearTimeout(timer) if (activeResponseWaiter === waiter) { activeResponseWaiter = null } unsubscribe() } }) } async function sendRawFrame(frame, label) { return transport.sendRawFrameExact(frame, label) } async function sendFrame(frame, label, kind, timeout) { const responsePromise = kind ? waitForResponse(kind, timeout) : null const sent = await sendRawFrame(frame, label) if (!sent) { if (responsePromise) { responsePromise.catch(() => {}) abortActiveResponseWaiter(`${label}发送失败`) } throw new Error(`${label}发送失败`) } return responsePromise ? responsePromise : true } module.exports = { RESPONSE_TIMEOUT_MS, abortActiveResponseWaiter, sendFrame, sendRawFrame, waitForResponse }