85 lines
2.4 KiB
JavaScript
85 lines
2.4 KiB
JavaScript
import { splitUint8Array, toUint8Array } from '../utils/bytes'
|
||
import { utf8ToBytes } from '../utils/hex'
|
||
|
||
/**
|
||
* 按协议将 payload 拆成 A7 分包:
|
||
* 第 1 字节固定包头 0xA7,第 2 字节高4位为总包数、低4位为包序号。
|
||
*/
|
||
export function buildA7Packets(payload, packetConfig = {}) {
|
||
const {
|
||
head = 0xA7,
|
||
chunkSize = 18,
|
||
maxChunk = 15
|
||
} = packetConfig
|
||
|
||
const bytes = typeof payload === 'string' ? utf8ToBytes(payload) : toUint8Array(payload)
|
||
const chunks = splitUint8Array(bytes, chunkSize)
|
||
|
||
if (chunks.length > maxChunk) {
|
||
throw new Error(`分包数量超过上限: ${chunks.length} > ${maxChunk}`)
|
||
}
|
||
|
||
return chunks.map((chunk, index) => {
|
||
const packetIndex = index + 1
|
||
const lenByte = ((chunks.length & 0x0F) << 4) | (packetIndex & 0x0F)
|
||
const packet = new Uint8Array(2 + chunk.length)
|
||
|
||
packet[0] = head
|
||
packet[1] = lenByte
|
||
packet.set(chunk, 2)
|
||
|
||
return packet
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 将 payload 按 A7 规则分包后顺序写入目标特征。
|
||
* 统一 auth/wifi 分包发送逻辑,避免多处重复实现。
|
||
*/
|
||
export async function writeA7Payload(ble, characteristicId, payload, packetConfig = {}, options = {}) {
|
||
if (!ble || typeof ble.writeCharacteristic !== 'function') {
|
||
throw new Error('writeA7Payload 缺少有效的 ble 实例')
|
||
}
|
||
|
||
if (!characteristicId) {
|
||
throw new Error('writeA7Payload 缺少 characteristicId')
|
||
}
|
||
|
||
const { packetInterval = 0 } = options
|
||
const packets = buildA7Packets(payload, packetConfig)
|
||
|
||
for (const packet of packets) {
|
||
await ble.writeCharacteristic(characteristicId, packet)
|
||
|
||
if (packetInterval > 0) {
|
||
// 部分固件在连续写入时需要极短间隔,默认 0 兼容原行为。
|
||
await new Promise((resolve) => setTimeout(resolve, packetInterval))
|
||
}
|
||
}
|
||
|
||
return packets.length
|
||
}
|
||
|
||
/**
|
||
* 合并多个 A7 分包数据体(去掉前两字节头信息后拼接)。
|
||
*/
|
||
export function concatA7PacketData(packets = []) {
|
||
const sorted = [...packets].sort((a, b) => {
|
||
const indexA = a[1] & 0x0F
|
||
const indexB = b[1] & 0x0F
|
||
return indexA - indexB
|
||
})
|
||
|
||
const totalLength = sorted.reduce((sum, packet) => sum + Math.max(packet.length - 2, 0), 0)
|
||
const merged = new Uint8Array(totalLength)
|
||
let offset = 0
|
||
|
||
sorted.forEach((packet) => {
|
||
const body = packet.slice(2)
|
||
merged.set(body, offset)
|
||
offset += body.length
|
||
})
|
||
|
||
return merged
|
||
}
|