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:
小龙虾
2026-07-17 02:02:36 +08:00
parent 6364eacfb1
commit 1b10f89d8c
39 changed files with 851 additions and 331 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
* 包含:共享 state/el、基础工具、DBC 解析、cssEscape、readDbcText。
*
* 本模块是 can 子系统的基础层,不依赖其他 can 子模块;state/el 在此导出,
* 供 messages/signals/send/feedback 共享(ES Module 的 live binding 保证引用一致)。
* 供 messages/signals/send 共享(ES Module 的 live binding 保证引用一致)。
*/
// ==================== 共享状态 ====================
-10
View File
@@ -1,10 +0,0 @@
/**
* feedback.js - 1Hz 反馈轮询(预留模块)
*
* 重新导出 common/connection.js 的连接管理函数,供 can 入口文件按需使用。
* 1Hz 轮询 /can/feedback 或 ID 快照功能可在本模块按需实现。
*/
import { updateConnectionUi, updateHealthUi, updateHeapAndTaskStatusUi, testConnection, refreshSystemStatusNow } from '../common/connection.js';
// 重新导出 common/connection.js 的连接管理函数,供 can 入口文件按需使用。
export { updateConnectionUi, updateHealthUi, updateHeapAndTaskStatusUi, testConnection, refreshSystemStatusNow };
+5 -6
View File
@@ -5,12 +5,12 @@
*
* 依赖:dbc.js(state/el/padHex/normalizeWhitespace/cssEscape)、
* common/utils.js(escapeHtml/formatBytes)、
* signals.js(renderInlineSignalGrid/ensureMessageDefaults/renderSelectedMessage/updateEncodedPreview)。
* signals.js(renderInlineSignalGrid/ensureMessageDefaults/updateEncodedPreview)。
*/
import { state, el, padHex, normalizeWhitespace, cssEscape } from './dbc.js?v=20260717v';
import { escapeHtml as sanitizeHtml, formatBytes } from '../common/utils.js?v=20260717v';
import { renderInlineSignalGrid, ensureMessageDefaults, renderSelectedMessage, updateEncodedPreview } from './signals.js?v=20260717v';
import { scheduleCanPersist } from './persist.js?v=20260717v';
import { state, el, padHex, normalizeWhitespace, cssEscape } from './dbc.js?v=20260717x';
import { escapeHtml as sanitizeHtml, formatBytes } from '../common/utils.js?v=20260717x';
import { renderInlineSignalGrid, ensureMessageDefaults, updateEncodedPreview } from './signals.js?v=20260717x';
import { scheduleCanPersist } from './persist.js?v=20260717x';
// ==================== DBC 文件信息 ====================
/** @description 更新 DBC 文件信息卡片(文件名/报文数/信号数/节点数/大小)。 */
@@ -395,7 +395,6 @@ export function selectMessage(key) {
state.selectedMessageKey = key;
ensureMessageDefaults(msg);
renderMessageList();
renderSelectedMessage();
updateEncodedPreview();
scheduleCanPersist();
}
+1 -12
View File
@@ -6,7 +6,7 @@
*
* 依赖:dbc.js(state/el/parseDbc/rebuildMergedDbc)
*/
import { state, el, parseDbc, rebuildMergedDbc } from './dbc.js?v=20260717v';
import { state, el, parseDbc, rebuildMergedDbc } from './dbc.js?v=20260717x';
var LS_KEY = 'dcu_can_session_v1';
var MAX_HISTORY = 200;
@@ -135,17 +135,6 @@ export function scheduleCanPersist() {
}, SAVE_DEBOUNCE_MS);
}
/** @description 清空持久化(全部清除 DBC 时)。 */
export function clearCanSessionPersist() {
if (saveTimer) {
clearTimeout(saveTimer);
saveTimer = null;
}
try {
localStorage.removeItem(LS_KEY);
} catch (_) {}
}
/**
* @description 从 localStorage 恢复会话到 state + 表单。
* @returns {{ restored: boolean, dbcCount: number, historyCount: number }}
+9 -9
View File
@@ -12,12 +12,12 @@
* 注意:formatTime 保留本地实现(HH:MM:SS,不含毫秒),与 common/utils.js
* 的 formatTime(含毫秒)不同,以保证发送历史时间戳输出格式一致。
*/
import { state, el, getBaseUrl, padHex, bytesToHex, parseNumericId } from './dbc.js?v=20260717w';
import { escapeHtml as sanitizeHtml } from '../common/utils.js?v=20260717w';
import { showToast } from '../common/toast.js?v=20260717w';
import { updateBatchUi } from './messages.js?v=20260717w';
import { ensureMessageDefaults, getSignalValueMap, getSelectedMessage, updateEncodedPreview, encodeMessageData } from './signals.js?v=20260717w';
import { scheduleCanPersist } from './persist.js?v=20260717w';
import { state, el, getBaseUrl, padHex, bytesToHex, parseNumericId } from './dbc.js?v=20260717x';
import { escapeHtml as sanitizeHtml } from '../common/utils.js?v=20260717x';
import { showToast } from '../common/toast.js?v=20260717x';
import { updateBatchUi } from './messages.js?v=20260717x';
import { ensureMessageDefaults, getSignalValueMap, getSelectedMessage, updateEncodedPreview, encodeMessageData } from './signals.js?v=20260717x';
import { scheduleCanPersist } from './persist.js?v=20260717x';
// ==================== 本地工具(保持与原 can.js 一致) ====================
function formatTime(date) {
@@ -109,7 +109,7 @@ async function sendCanBatchRequest(channel, frames) {
if (!json.results || !Array.isArray(json.results)) {
throw new Error('批量响应缺少 results');
}
/* 设备中途 fatal 会 skipped 剩余帧(少分配堆);前端补齐等长 results */
/* 设备可能返回 skipped;补齐与 frames 等长的 results */
var results = json.results.slice();
var need = frames.length - results.length;
if (need > 0) {
@@ -172,12 +172,12 @@ export function renderHistory() {
}
// ==================== 批量发送 ====================
/** 单批最多帧数(与设备 /can/send_batch 上限 128 对齐;一次 HTTP 连发,避免分块成“一阵一阵”) */
/** 单批最多帧数(与设备 /can/send_batch 上限一致) */
var BATCH_CHUNK = 128;
/**
* @description 批量发送所有勾选的报文。
* 优先一次 HTTP /can/send_batch(设备侧帧间 5ms);失败则回退单帧(无额外间隔)。
* 优先 POST /can/send_batch;不支持时回退逐帧 /can/send。
* 批量过程中不重绘历史,结束后统一刷新。
*/
export async function sendCheckedMessages() {
+4 -15
View File
@@ -1,14 +1,13 @@
/**
* signals.js - 信号编辑器渲染 + 信号编解码(raw ↔ physical)
*
* 包含:信号行渲染、信号值存取、编码写入、浮点/整数/Motorola/Intel 位操作、
* renderSelectedMessage shim。
* 包含:行内信号表格、信号值存取、编码写入、浮点/整数/Motorola/Intel 位操作。
*
* 依赖:dbc.js(state/el/padHex/bytesToHex/cssEscape/findSignal)、common/utils.js(escapeHtml)。
*/
import { state, el, padHex, bytesToHex, cssEscape, findSignal } from './dbc.js?v=20260717v';
import { escapeHtml as sanitizeHtml } from '../common/utils.js?v=20260717v';
import { scheduleCanPersist } from './persist.js?v=20260717v';
import { state, el, padHex, bytesToHex, cssEscape, findSignal } from './dbc.js?v=20260717x';
import { escapeHtml as sanitizeHtml } from '../common/utils.js?v=20260717x';
import { scheduleCanPersist } from './persist.js?v=20260717x';
// ==================== 信号值存取 ====================
/** @description 确保报文有 signalValues bag,并为每个信号填充默认值。返回 bag。 */
@@ -81,15 +80,6 @@ export function getActiveSignals(message) {
});
}
// ==================== 旧版 shim ====================
/**
* @description 旧版"顶部独立信号编辑面板"的兼容 shim。
* 该面板已移除,信号编辑现在通过报文行展开完成;
* 仍被 selectMessage、handleDbcFile、clearDbc、handleSignalInputEvent 调用,
* 保留空函数以避免修改所有调用方。
*/
export function renderSelectedMessage() {}
// ==================== 信号行渲染 ====================
/** @description 在报文展开行内渲染信号表格(inline grid)。 */
export function renderInlineSignalGrid(message, host) {
@@ -178,7 +168,6 @@ function handleSignalInputEvent(evt) {
setSignalValue(message, name, input.value);
var signal = findSignal(message, name);
if (signal && signal.multiplexRole === 'mux') {
if (key === state.selectedMessageKey) renderSelectedMessage();
var inlineGrid = document.querySelector('.message-row-expand[data-key="' + cssEscape(key) + '"] .signal-table-host');
if (inlineGrid) renderInlineSignalGrid(message, inlineGrid);
}