更新100J

This commit is contained in:
微微一笑
2026-03-27 10:13:52 +08:00
parent 553e24886f
commit 4c6704ba8a
2 changed files with 99 additions and 30 deletions

View File

@ -663,7 +663,20 @@ class HBY100JProtocol {
const DELAY_PACKET = 80; // 数据包间延时(ms)参考6155 const DELAY_PACKET = 80; // 数据包间延时(ms)参考6155
const toHex = (arr) => Array.from(arr).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' '); const toHex = (arr) => Array.from(arr).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ');
console.log('[100J-蓝牙] 语音下发总大小:', total, '字节, fileType=', ft); console.log('[100J-蓝牙] 语音下发总大小:', total, '字节, fileType=', ft);
if (onProgress) onProgress(1); // 进度单调递增:前段固定 2→8数据段占 8~95结束包 99→100避免先 5% 再掉回 1% 的错觉
let progressPeak = 0;
const emitProgress = (raw) => {
const n = Math.round(Number(raw));
if (!Number.isFinite(n)) return;
const v = Math.min(100, Math.max(progressPeak, n));
progressPeak = v;
if (onProgress) onProgress(v);
};
if (total <= 0) {
emitProgress(100);
return Promise.resolve({ code: 200, msg: '语音文件已通过蓝牙上传' });
}
emitProgress(2);
const bleToolPromise = import('@/utils/BleHelper.js').then(m => m.default.getBleTool()); const bleToolPromise = import('@/utils/BleHelper.js').then(m => m.default.getBleTool());
let bleRef = null; let bleRef = null;
const send = (dataBytes, label = '') => { const send = (dataBytes, label = '') => {
@ -688,26 +701,30 @@ class HBY100JProtocol {
bleRef = ble; bleRef = ble;
ble.setVoiceUploading(true); ble.setVoiceUploading(true);
return send(startData, ' 开始包') return send(startData, ' 开始包')
.then(() => { if (onProgress) onProgress(3); return waitPromise; }) .then(() => waitPromise)
.then(() => { if (onProgress) onProgress(5); return delay(DELAY_AFTER_START); }) .then(() => delay(DELAY_AFTER_START))
.then(() => { .then(() => {
emitProgress(8);
let seq = 0; let seq = 0;
const sendNext = (offset) => { const sendNext = (offset) => {
if (offset >= total) { if (offset >= total) {
return delay(DELAY_PACKET).then(() => send([ft, 2], ' 结束包')); return delay(DELAY_PACKET)
.then(() => send([ft, 2], ' 结束包'))
.then(() => { emitProgress(99); });
} }
const chunk = bytes.slice(offset, Math.min(offset + chunkSize, total)); const chunk = bytes.slice(offset, Math.min(offset + chunkSize, total));
const chunkData = [ft, 1, seq & 0xFF, (seq >> 8) & 0xFF, ...chunk]; const chunkData = [ft, 1, seq & 0xFF, (seq >> 8) & 0xFF, ...chunk];
return send(chunkData, ` #${seq} 数据包`).then(() => { return send(chunkData, ` #${seq} 数据包`).then(() => {
seq++; seq++;
if (onProgress) onProgress(Math.min(100, Math.floor((offset + chunk.length) / total * 100))); const doneRatio = (offset + chunk.length) / total;
emitProgress(8 + Math.round(doneRatio * 87));
return delay(DELAY_PACKET).then(() => sendNext(offset + chunk.length)); return delay(DELAY_PACKET).then(() => sendNext(offset + chunk.length));
}); });
}; };
return sendNext(0); return sendNext(0);
}) })
.then(() => { .then(() => {
if (onProgress) onProgress(100); emitProgress(100);
return { code: 200, msg: '语音文件已通过蓝牙上传' }; return { code: 200, msg: '语音文件已通过蓝牙上传' };
}); });
}; };

View File

@ -233,12 +233,37 @@
console.log("页面返回") console.log("页面返回")
}, },
onUnload() { onUnload() {
// 页面卸载时断开MQTT连接 this.clearVoiceApplyTimers();
if (this.mqttClient) { if (this.mqttClient) {
this.mqttClient.disconnect(); this.mqttClient.disconnect();
} }
}, },
methods: { methods: {
/** 清除「使用」语音相关的全部定时器,避免返回上一页后仍触发 toast / 二次 navigateBack */
clearVoiceApplyTimers() {
if (this._applyOverallTimer) {
clearTimeout(this._applyOverallTimer);
this._applyOverallTimer = null;
}
if (this.upgradeTimer) {
clearTimeout(this.upgradeTimer);
this.upgradeTimer = null;
}
if (this._applyNavigateTimer) {
clearTimeout(this._applyNavigateTimer);
this._applyNavigateTimer = null;
}
},
scheduleNavigateBackAfterVoice(delayMs = 1500) {
if (this._applyNavigateTimer) {
clearTimeout(this._applyNavigateTimer);
this._applyNavigateTimer = null;
}
this._applyNavigateTimer = setTimeout(() => {
this._applyNavigateTimer = null;
uni.navigateBack();
}, delayMs);
},
//语音管理列表(合并云端 + 本地无网络保存的语音) //语音管理列表(合并云端 + 本地无网络保存的语音)
getinitData(val, isLoadMore = false) { getinitData(val, isLoadMore = false) {
const deviceId = this.device.deviceId; const deviceId = this.device.deviceId;
@ -525,10 +550,7 @@
}, },
Apply(item, index) { Apply(item, index) {
this.updateProgress = 0; this.updateProgress = 0;
if (this.upgradeTimer) { this.clearVoiceApplyTimers();
clearTimeout(this.upgradeTimer);
this.upgradeTimer = null;
}
// 本地项在无网时禁止下发仅弹窗isUpdating 在确认可执行后再置 true // 本地项在无网时禁止下发仅弹窗isUpdating 在确认可执行后再置 true
// 本地项优先用 localPath云端项用 fileUrl兼容多种字段名相对路径补全 baseURL // 本地项优先用 localPath云端项用 fileUrl兼容多种字段名相对路径补全 baseURL
let fileUrl = ''; let fileUrl = '';
@ -548,21 +570,35 @@
// 本地合并项 mergeLocal 会把路径写在 fileUrl需带给接口层做 effectiveLocal 兜底 // 本地合并项 mergeLocal 会把路径写在 fileUrl需带给接口层做 effectiveLocal 兜底
fileUrl: item._isLocal ? (typeof item.fileUrl === 'string' ? item.fileUrl : '') : fileUrl, fileUrl: item._isLocal ? (typeof item.fileUrl === 'string' ? item.fileUrl : '') : fileUrl,
localPath, localPath,
onProgress: (p) => { this.updateProgress = p; }, onProgress: (p) => {
const n = Math.min(100, Math.max(0, Math.round(Number(p) || 0)));
const cur = Number(this.updateProgress) || 0;
this.updateProgress = Math.max(cur, n);
},
// 不传「蓝牙连接中」类提示:关蓝牙走 4G 时易误导;进度条 + 必要时全局请稍候即可 // 不传「蓝牙连接中」类提示:关蓝牙走 4G 时易误导;进度条 + 必要时全局请稍候即可
onWaiting: () => {} onWaiting: () => {}
}; };
const runDeviceUpdate = () => { const runDeviceUpdate = () => {
const overallTimer = setTimeout(() => { // 大文件蓝牙分片耗时可远超 2 分钟,整体超时放宽到 10 分钟(挂到实例上,便于 onUnload / 成功时清除)
const OVERALL_MS = 600000;
if (this._applyOverallTimer) {
clearTimeout(this._applyOverallTimer);
this._applyOverallTimer = null;
}
this._applyOverallTimer = setTimeout(() => {
this._applyOverallTimer = null;
if (this.isUpdating) { if (this.isUpdating) {
uni.showToast({ title: '操作时', icon: 'none', duration: 2000 }); uni.showToast({ title: '操作时间过长已中断,请重试或检查蓝牙连接', icon: 'none', duration: 2500 });
this.isUpdating = false; this.isUpdating = false;
this.updateProgress = 0; this.updateProgress = 0;
} }
}, 120000); }, OVERALL_MS);
// 进入列表时的蓝牙快照可能过期;与 HBY100 详情页一致,从 BleHelper 按 MAC 再对齐一次 // 进入列表时的蓝牙快照可能过期;与 HBY100 详情页一致,从 BleHelper 按 MAC 再对齐一次
sync100JBleProtocolFromHelper(this.device).then(() => deviceUpdateVoice(data)).then((RES) => { sync100JBleProtocolFromHelper(this.device).then(() => deviceUpdateVoice(data)).then((RES) => {
clearTimeout(overallTimer); if (this._applyOverallTimer) {
clearTimeout(this._applyOverallTimer);
this._applyOverallTimer = null;
}
if (RES.code == 200) { if (RES.code == 200) {
// 蓝牙上传:进度已由 onProgress 更新,直接完成 // 蓝牙上传:进度已由 onProgress 更新,直接完成
if (RES._channel === 'ble') { if (RES._channel === 'ble') {
@ -576,37 +612,50 @@
this.syncVoiceListUseStatus(item); this.syncVoiceListUseStatus(item);
uni.showToast({ title, icon: RES._updateVoiceAfterBleFailed ? 'none' : 'success', duration: 2000 }); uni.showToast({ title, icon: RES._updateVoiceAfterBleFailed ? 'none' : 'success', duration: 2000 });
this.isUpdating = false; this.isUpdating = false;
setTimeout(() => { uni.navigateBack(); }, 1500); this.scheduleNavigateBackAfterVoice(1500);
return; return;
} }
// 4G订阅 MQTT 获取设备端进度6 秒超时 // 4GMQTT 进度可能数十秒才上报,用「自上次进度起」滑动超时,避免误报
const MQTT_IDLE_MS = 120000;
const armMqttIdle = () => {
if (this.upgradeTimer) clearTimeout(this.upgradeTimer);
this.upgradeTimer = setTimeout(() => { this.upgradeTimer = setTimeout(() => {
if (this.isUpdating) { if (!this.isUpdating) return;
uni.showToast({ title: '音频进度同步超时', icon: 'none', duration: 2000 }); uni.showToast({
title: '长时间未收到设备进度,若语音已生效可返回查看',
icon: 'none',
duration: 3500
});
this.isUpdating = false; this.isUpdating = false;
this.updateProgress = 0; this.updateProgress = 0;
} }, MQTT_IDLE_MS);
}, 6000); };
armMqttIdle();
this.mqttClient = this.mqttClient || new MqttClient(); this.mqttClient = this.mqttClient || new MqttClient();
this.mqttClient.connect(() => { this.mqttClient.connect(() => {
const statusTopic = `status/894078/HBY100/${this.device.deviceImei}`; const statusTopic = `status/894078/HBY100/${this.device.deviceImei}`;
this.mqttClient.subscribe(statusTopic, (payload) => { this.mqttClient.subscribe(statusTopic, (payload) => {
try { try {
const payloadObj = typeof payload === 'string' ? JSON.parse(payload) : payload; const payloadObj = typeof payload === 'string' ? JSON.parse(payload) : payload;
const progress = payloadObj.data?.progress; const progress = payloadObj.data != null && payloadObj.data.progress !== undefined
? payloadObj.data.progress
: payloadObj.progress;
if (progress !== undefined && !isNaN(progress) && progress >= 0 && progress <= 100) { if (progress !== undefined && !isNaN(progress) && progress >= 0 && progress <= 100) {
this.updateProgress = progress; armMqttIdle();
if (progress === 100) { const cur = Number(this.updateProgress) || 0;
clearTimeout(this.upgradeTimer); this.updateProgress = Math.max(cur, Math.round(progress));
if (Number(progress) === 100) {
if (this.upgradeTimer) clearTimeout(this.upgradeTimer);
this.upgradeTimer = null;
this.syncVoiceListUseStatus(item); this.syncVoiceListUseStatus(item);
uni.showToast({ title: '音频上传成功', icon: 'success', duration: 2000 }); uni.showToast({ title: '音频上传成功', icon: 'success', duration: 2000 });
this.isUpdating = false; this.isUpdating = false;
setTimeout(() => { uni.navigateBack(); }, 1500); this.scheduleNavigateBackAfterVoice(1500);
} }
} }
} catch (e) { } catch (e) {
clearTimeout(this.upgradeTimer);
console.error('解析MQTT payload失败', e); console.error('解析MQTT payload失败', e);
armMqttIdle();
} }
}); });
}); });
@ -615,7 +664,10 @@
uni.showToast({ title: RES.msg || '操作失败', icon: 'none', duration: 1000 }); uni.showToast({ title: RES.msg || '操作失败', icon: 'none', duration: 1000 });
} }
}).catch((err) => { }).catch((err) => {
clearTimeout(overallTimer); if (this._applyOverallTimer) {
clearTimeout(this._applyOverallTimer);
this._applyOverallTimer = null;
}
this.isUpdating = false; this.isUpdating = false;
this.updateProgress = 0; this.updateProgress = 0;
uni.showToast({ title: err.message || '操作失败', icon: 'none', duration: 2500 }); uni.showToast({ title: err.message || '操作失败', icon: 'none', duration: 2500 });