feat(ota): 实现GD32 OTA包间背压机制,优化传输节奏控制
移除固定的包间延迟配置 `OTA_PACKET_DELAY_MS`,采用基于SPI Link v2 CREDIT背压的动态节奏控制,在SpiMaster中通过remoteCredit==0时wait/拒发 实现流量控制,提高OTA传输稳定性。 refactor(stack): 统一任务栈大小配置并添加CPU统计功能 - 在app_config.h中集中定义所有任务栈大小常量,基于实测水位优化分配 - 新增monitor/cpu_stats.cpp组件,提供1Hz FreeRTOS CPU占用采样 - 更新main.cpp中的任务创建使用新的栈大小常量 - 调整health_monitor和ota_relay等任务的栈分配 perf(ota): 改进ESP32和GD32 OTA分区预擦策略 - ESP32 OTA从边写边擦改为整分区预擦模式,避免写入时的扇区擦除阻塞 - GD32 OTA在prepare阶段进行整分区预擦,提升后续写入吞吐量 - 优化OTA协调器状态管理,修复FINALIZING状态残留问题 fix(log): 改进UDP日志发送的堆内存管理 - 当堆内存不足时记录UDP发送跳过的告警信息到ring buffer - 降低UDP发送跳过的内存阈值从10KB到6KB - 降低日志排水最小内存阈值从8KB到4KB docs(readme): 更新OTA配置参数文档说明 移除已废弃的OTA_PACKET_DELAY_MS参数,更新包间节奏控制说明为 基于CREDIT背压机制的描述。
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* upload-esp32.js - ESP32 本机 OTA 上传流程 + 完成升级
|
||||
*
|
||||
* 从 app.js 的「上传固件」「ESP32 OTA 完成升级」段提取。
|
||||
* 包含:startUpload 入口(处理 esp32 + gd32 共用互斥/重读逻辑)、
|
||||
* XHR 上传、SHA 校验、verifyEsp32Firmware、restartEsp32。
|
||||
*
|
||||
@@ -10,7 +9,7 @@
|
||||
* window._esp32FileSha256 / window._gd32FileSha256 / window.activeXhr
|
||||
* window.isUploading / window.uploadBtnOrigText / window.activeUploadChip
|
||||
*
|
||||
* 未拆分的函数(通过 window.* 引用):
|
||||
* 复用的函数(通过 window.* 引用):
|
||||
* window.otaSessionActive / window.otaSessionTerminated / window.otaSessionReset
|
||||
* window.otaSessionTransition / window.OtaSessionState / window.otaSessionTarget
|
||||
* window.cleanupAfterOtaFailure / window.pausePollingForUpload
|
||||
@@ -31,6 +30,7 @@ import {
|
||||
startUploadWaitHeartbeat
|
||||
} from './session.js';
|
||||
import { updateEsp32StagesFromPhase, switchStageFlow, setActivePhase, setProgress, markPhaseDone, cleanupAfterOtaFailure } from './progress.js';
|
||||
import { ensureFileShaCached } from './file-handle.js';
|
||||
|
||||
function isOtaBusyError(status, detail) {
|
||||
var text = (detail || '').toLowerCase();
|
||||
@@ -104,9 +104,13 @@ export async function startUpload(chip) {
|
||||
var otaChannelName = ota.channel_name || (otaChannel === 1 ? 'BLE' : 'WIFI');
|
||||
// ESP32 RECEIVING(5) 可由同一 WiFi 通道的新上传接管;后台/休眠可能让 /ota/update 没发出,
|
||||
// 若这里提前拦截,后端 beginEsp32OtaWithRetry 的同通道清理分支永远没有机会执行。
|
||||
// ERROR(8) 也放行给后端清理底层 OTA 句柄;FINALIZING(6)/REBOOTING(7) 是不可逆忙态。
|
||||
if (ssc === 5 && otaChannel === 1 && _pi < 3) continue;
|
||||
var esp32Busy = (ssc === 6 || ssc === 7 || (ssc === 5 && otaChannel !== 0));
|
||||
// ERROR(8) 放行给后端清理;REBOOTING(7) 不可逆。
|
||||
// FINALIZING(6):真收尾时后端会 403;SHA 失败后可能假占用(core 已空),
|
||||
// status 自愈 / begin 残留清理需要请求触达设备,前端同通道不硬拦。
|
||||
if ((ssc === 5 || ssc === 6) && otaChannel === 1 && _pi < 3) continue;
|
||||
var esp32Busy = (ssc === 7 ||
|
||||
(ssc === 6 && otaChannel !== 0) ||
|
||||
(ssc === 5 && otaChannel !== 0));
|
||||
// GD32 STORING(1) 可由同一 WiFi 通道的新上传接管;RELAYING(3) 不可中断。
|
||||
var gd32Busy = (ssc === 3 || (ssc === 1 && otaChannel !== 0));
|
||||
if ((esp32Busy || gd32Busy)) {
|
||||
@@ -183,31 +187,10 @@ export async function startUpload(chip) {
|
||||
showToast('开始上传固件到 ' + chip.toUpperCase(), 'success');
|
||||
try {
|
||||
var arrayBuffer = await file.arrayBuffer();
|
||||
// 刷写时重新计算 SHA-256(ESP32 和 GD32 都算)
|
||||
if (chip === 'esp32') {
|
||||
try {
|
||||
var hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
|
||||
var hashArray = Array.from(new Uint8Array(hashBuffer));
|
||||
var hashHex = hashArray.map(function(b) { return b.toString(16).padStart(2, '0'); }).join('');
|
||||
window._esp32FileSha256 = hashHex.toUpperCase();
|
||||
var shaElem = document.getElementById('fileSha256Esp32');
|
||||
if (shaElem) shaElem.textContent = window._esp32FileSha256;
|
||||
addLog('I', '文件', '刷写前重新计算 SHA-256: ' + window._esp32FileSha256);
|
||||
} catch (e) {
|
||||
addLog('W', '文件', 'SHA-256 重新计算失败: ' + e.message);
|
||||
}
|
||||
} else if (chip === 'gd32') {
|
||||
try {
|
||||
var hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
|
||||
var hashArray = Array.from(new Uint8Array(hashBuffer));
|
||||
var hashHex = hashArray.map(function(b) { return b.toString(16).padStart(2, '0'); }).join('');
|
||||
window._gd32FileSha256 = hashHex.toUpperCase();
|
||||
addLog('I', '文件', 'GD32 SHA-256: ' + window._gd32FileSha256);
|
||||
} catch (e) {
|
||||
addLog('W', '文件', 'GD32 SHA-256 重新计算失败: ' + e.message);
|
||||
window._gd32FileSha256 = null;
|
||||
}
|
||||
}
|
||||
// SHA-256:processFile 阶段已算好并缓存;这里若 handle.getFile() 拿回的
|
||||
// fresh file meta 未变就直接复用(命中 30–100ms/MB 的计算);
|
||||
// 用户 rebuild 使 lastModified 变化时 ensureFileShaCached 自动 fallback 到重算。
|
||||
await ensureFileShaCached(file, chip, arrayBuffer);
|
||||
var fileSize = file.size;
|
||||
var body, url = window.getBaseUrl() + '/ota/upload';
|
||||
if (chip === 'esp32') {
|
||||
|
||||
Reference in New Issue
Block a user