feat(main): 添加启动模块自检与汇总功能
- 新增 SystemBoot 类用于跟踪各模块初始化状态,提供位图标记与报告功能 - 在 main.cpp 中重构初始化流程,按顺序初始化各模块并记录状态 - 添加 system_boot.cpp 模块文件到构建配置 - CAN反馈任务增加冷启动延迟,避免与GD32初始化窗口冲突 refactor(ota): 优化OTA协调器线程安全机制 - 修改 lock()/unlock() 方法返回take结果,确保mutex正确释放 - 修复多任务并发时mutex死锁问题,避免vTaskPriorityDisinherit断言 - 统一finalize失败后的状态复位逻辑,确保协调器回到IDLE状态 - 优化忙状态超时检测与自愈机制 perf(boot): 优化冷启动时序与资源管理 - 简化README中冷启动优化说明,聚焦核心逻辑 - 优化BOD阈值设置为LVL5,改进电源稳定性 - 增加BLE OTA工作线程优先级,减少丢包情况 - 优化堆告警节流机制,在OTA期间降低告警频率 fix(relay): 改进GD32应用存活检测逻辑 - 调整GD32 APP存活验证时序,增加会话建立验证 - 修复HELLO建立session后未验证role的问题 - 优化FINISH后等待APP启动的静默时间与重试机制 - 统一GD32探测逻辑到OTA中继任务 refactor(server): 优化HTTP服务端内存与性能 - 使用静态缓冲区替代动态分配,减少堆碎片化风险 - 优化CAN批量发送期间暂停反馈任务的机制 - 调整BLE连接参数请求逻辑,提升传输性能 - 改进错误处理流程,避免堆紧张时的额外分配 bump(version): 更新固件版本至0.3.50 - 更新版本号从0.3.49到0.3.50 - 更新编译时间戳与Git哈希值
This commit is contained in:
@@ -49,11 +49,105 @@ export function updateHealthUi(healthCode, freeHeap) {
|
||||
mh.textContent = health;
|
||||
mh.className = 'v ' + (healthCode === 0 ? 'ok' : healthCode === 1 ? 'warn' : 'err');
|
||||
}
|
||||
ensureHealthBootToggleBound();
|
||||
}
|
||||
/**
|
||||
* 每次 pollStatus 成功后刷新堆/栈/CPU(固定周期约 1000ms,中继期 400ms;
|
||||
* sample_ms 由设备两次采样墙钟差给出;WiFi 上传 ESP32 期间 pause 不刷)。
|
||||
*/
|
||||
|
||||
/** 健康状态 badge 点击展开/收起启动自检面板(只绑一次)。 */
|
||||
function ensureHealthBootToggleBound() {
|
||||
var badge = document.getElementById('healthBadge');
|
||||
var panel = document.getElementById('healthBootPanel');
|
||||
if (!badge || !panel || badge.dataset.bootToggleBound === '1') return;
|
||||
badge.dataset.bootToggleBound = '1';
|
||||
|
||||
function toggle() {
|
||||
var open = panel.classList.contains('collapsed') || panel.hasAttribute('hidden');
|
||||
if (open) {
|
||||
panel.hidden = false;
|
||||
panel.classList.remove('collapsed');
|
||||
badge.classList.add('is-open');
|
||||
badge.setAttribute('aria-expanded', 'true');
|
||||
} else {
|
||||
panel.hidden = true;
|
||||
panel.classList.add('collapsed');
|
||||
badge.classList.remove('is-open');
|
||||
badge.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
}
|
||||
badge.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
toggle();
|
||||
});
|
||||
badge.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
toggle();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 启动自检内容(/system/status.boot);挂在健康状态展开区,非常驻。 */
|
||||
function updateBootInitUi(data) {
|
||||
var boot = data && data.boot ? data.boot : null;
|
||||
var resultEl = document.getElementById('mBootResult');
|
||||
var failedEl = document.getElementById('mBootFailed');
|
||||
var listEl = document.getElementById('bootModuleList');
|
||||
if (!boot) {
|
||||
if (resultEl) {
|
||||
resultEl.textContent = '--';
|
||||
resultEl.className = 'health-boot-summary';
|
||||
}
|
||||
if (failedEl) {
|
||||
failedEl.textContent = '--';
|
||||
failedEl.className = '';
|
||||
}
|
||||
if (listEl) listEl.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
var criticalOk = !!boot.critical_ok;
|
||||
var allOk = !!boot.all_ok;
|
||||
var summary = boot.summary || (allOk ? 'OK (all modules)' : (criticalOk ? 'OK (critical)' : 'FAIL'));
|
||||
var failed = (boot.failed && String(boot.failed).trim()) ? String(boot.failed) : '';
|
||||
var tone = allOk ? 'ok' : (criticalOk ? 'warn' : 'err');
|
||||
|
||||
if (resultEl) {
|
||||
resultEl.textContent = summary;
|
||||
resultEl.className = 'health-boot-summary ' + tone;
|
||||
resultEl.title = 'mask=0x' + (Number(boot.mask) || 0).toString(16);
|
||||
}
|
||||
if (failedEl) {
|
||||
failedEl.textContent = failed || (allOk ? '无' : '--');
|
||||
failedEl.className = failed ? tone : 'ok';
|
||||
}
|
||||
|
||||
if (listEl) {
|
||||
var modules = Array.isArray(boot.modules) ? boot.modules : [];
|
||||
if (!modules.length) {
|
||||
listEl.innerHTML = '';
|
||||
} else {
|
||||
listEl.innerHTML = modules.map(function (m) {
|
||||
var ok = !!m.ok;
|
||||
var critical = !!m.critical;
|
||||
var cls = 'boot-mod ' + (ok ? 'ok' : (critical ? 'fail' : 'fail optional'));
|
||||
var tip = (m.name || '') + (ok ? ' 成功' : ' 失败') + (critical ? ' · 关键' : ' · 非关键');
|
||||
return '<span class="' + cls + '" title="' + escapeHtml(tip) + '">' +
|
||||
'<span class="boot-mod-dot"></span>' +
|
||||
escapeHtml(m.name || '?') +
|
||||
'</span>';
|
||||
}).join('');
|
||||
}
|
||||
}
|
||||
|
||||
if (!window._bootInitLogged) {
|
||||
window._bootInitLogged = true;
|
||||
var logLine = '启动自检: ' + summary + (failed ? ' · 失败/跳过: ' + failed : '');
|
||||
try {
|
||||
addLog(allOk ? 'I' : (criticalOk ? 'W' : 'E'), 'BOOT', logLine);
|
||||
} catch (_) {}
|
||||
}
|
||||
ensureHealthBootToggleBound();
|
||||
}
|
||||
|
||||
export function updateHeapAndTaskStatusUi(data) {
|
||||
if (!data) return;
|
||||
var ota = data.ota || {};
|
||||
@@ -62,6 +156,8 @@ export function updateHeapAndTaskStatusUi(data) {
|
||||
if (el) el.textContent = value;
|
||||
};
|
||||
|
||||
updateBootInitUi(data);
|
||||
|
||||
// ---- 堆概况:已用 / 总计 + 占用条 ----
|
||||
function setFill(id, pct) {
|
||||
var el = document.getElementById(id);
|
||||
|
||||
Reference in New Issue
Block a user