diff --git a/api/100J/HBY100-J.js b/api/100J/HBY100-J.js index ad9604c..4f8fd44 100644 --- a/api/100J/HBY100-J.js +++ b/api/100J/HBY100-J.js @@ -210,7 +210,7 @@ class HBY100JProtocol { } break; case 0x05: - // 05: 文件更新响应 FB 05 [fileType] [status] FF,status: 1=成功 2=失败 + // 05: 文件更新响应 FB 05 [fileType] [status] FF,status: 1=成功 2=失败(含开始/结束等阶段应答,以固件为准) if (data.length >= 1) result.fileType = data[0]; if (data.length >= 2) result.fileStatus = data[1]; // 1=Success, 2=Failure if (!skipSideEffects && this._fileResponseResolve) this._fileResponseResolve(result); @@ -704,6 +704,16 @@ class HBY100JProtocol { return bleToolPromise.then(ble => ble.sendData(this.bleDeviceId, buf, this.SERVICE_UUID, this.WRITE_UUID)); }; const delay = (ms) => new Promise(r => setTimeout(r, ms)); + const showTailWriteLoading = () => { + try { + uni.showLoading({ title: '设备写入中…', mask: true }); + } catch (e) {} + }; + const hideTailWriteLoading = () => { + try { + uni.hideLoading(); + } catch (e) {} + }; // 开始包: FA 05 [fileType] [phase=0] [size 4B LE] FF const startData = [ft, 0, total & 0xFF, (total >> 8) & 0xFF, (total >> 16) & 0xFF, (total >> 24) & 0xFF]; // 单包约 507B(500 负载),依赖 MTU;Android 上为整包 write @@ -720,9 +730,30 @@ class HBY100JProtocol { let seq = 0; const sendNext = (offset) => { if (offset >= total) { + // 结束包 FA 05 01 02 FF:协议(10) 设备应答 FB 05;尾包后设备需落盘,按约 7KB/s 估算等待,避免未写完就超时误走 4G + const WRITE_BPS = 7 * 1024; + const END_ACK_MS = Math.min(600000, Math.max(25000, Math.ceil(total / WRITE_BPS) * 1000 + 25000)); + console.log('[100J-蓝牙] 尾包后等待设备写入应答,超时', END_ACK_MS, 'ms(按约 7KB/s 估算落盘)'); return delay(DELAY_PACKET) .then(() => send([ft, 2], ' 结束包')) - .then(() => { emitProgress(99); }); + .then(() => { + showTailWriteLoading(); + return this.waitForFileResponse(END_ACK_MS); + }) + .then((ack) => { + if (ack && ack.fileStatus === 2) { + return Promise.reject(new Error('设备写入失败,请重试')); + } + if (ack && ack.fileStatus === 1) { + console.log('[100J-蓝牙] 结束包后设备确认写入成功 FB 05'); + emitProgress(99); + return; + } + return Promise.reject(new Error('设备写入确认超时,请稍后重试或靠近设备')); + }) + .finally(() => { + hideTailWriteLoading(); + }); } const chunk = bytes.slice(offset, Math.min(offset + chunkSize, total)); const chunkData = [ft, 1, seq & 0xFF, (seq >> 8) & 0xFF, ...chunk]; @@ -1119,6 +1150,12 @@ function execWithBleFirst(bleExec, httpExec, logName, onWaiting, opts = {}) { if (no4G) { return Promise.reject(e instanceof Error && e.message ? e : new Error(String((e && e.message) || '蓝牙发送语音失败,请靠近设备后重试'))); } + const msg = (e && e.message) ? String(e.message) : String(e || ''); + // 分片已发完、仅尾包后等设备落盘应答失败:不应再 4G updateVoice(文件已走蓝牙,重复下发会误导) + if (msg.indexOf('设备写入确认超时') !== -1 || msg.indexOf('设备写入失败') !== -1) { + console.log('[100J]', logName || '指令', '蓝牙语音尾包阶段失败,不回退4G:', msg); + return Promise.reject(e instanceof Error && e.message ? e : new Error(msg)); + } console.log('[100J]', logName || '指令', '蓝牙失败,回退4G'); return do4G(); }; diff --git a/pages/100J/audioManager/AudioList.vue b/pages/100J/audioManager/AudioList.vue index 8666259..827c890 100644 --- a/pages/100J/audioManager/AudioList.vue +++ b/pages/100J/audioManager/AudioList.vue @@ -567,9 +567,10 @@ clearTimeout(this.upgradeTimer); this.upgradeTimer = null; } + // 蓝牙链:尾包 FA 05 01 02 FF 后已收到 FB 05 成功应答,再调 updateVoice,此处再 toast 并返回上一页 const title = RES._updateVoiceAfterBleFailed ? '蓝牙已下发,云端同步失败可稍后重试' - : '音频上传成功'; + : '设备已确认写入'; this.syncVoiceListUseStatus(item); uni.showToast({ title, icon: RES._updateVoiceAfterBleFailed ? 'none' : 'success', duration: 2000 }); this.isUpdating = false;