feat(protocol): 更新SPI Link协议至v2版本并优化CAN通信
- 将SPI OTA帧格式升级为SPI Link v2,采用新的帧格式: | SOF0 0xA5 | SOF1 0x5A | VER 0x02 | FLAGS | SESSION 4B LE | TX_SEQ | ACK_SEQ | CREDIT | TYPE | LEN 2B LE | RSV 2B | PAYLOAD | CRC32 4B LE | - 新增V2_OVERHEAD=20字节开销,APP最大payload为236字节,BOOT为108字节 - 修改帧类型定义:ENTER_BOOT从0xB5合并至0xB0,新增CAN业务类型0xC0~C3 - 更新CAN协议文档,支持SPI Link v2-only模式,调整最大业务payload计算 - 扩展0xC1帧功能,支持双向通信和1Hz主动推送ID快照 - 添加0xC3完整ID快照帧类型,包含seq/rx_count等完整字段 - 更新协议文档中的错误码定义和反馈payload格式 refactor(main): 重构主程序入口并添加CAN反馈任务 - 在main.cpp中添加CAN反馈任务,更新启动流程注释 - 创建createCriticalTask辅助函数,统一关键任务创建逻辑 - 调整SPI初始化顺序,确保CAN反馈任务依赖正确 - 优化错误处理机制,关键任务失败时立即重启 - 将GD32版本探测移至ota_relay任务处理 - 更新版本号至0.3.48,调整编译时间和Git哈希 config: 更新SPI和BLE配置参数 - 添加SPI主机相关配置参数,包括CS GPIO、READY GPIO等引脚定义 - 配置SPI缓冲区大小:APP为256字节,BOOT为128字节 - 更新BLE滑动窗口参数,ACK窗口设为32,数据队列深度为72 - 调整SPI工作队列大小至5120字节,增加任务堆栈监控说明
This commit is contained in:
+91
-7
@@ -9,8 +9,9 @@
|
||||
|
||||
// ==================== 共享状态 ====================
|
||||
export var state = {
|
||||
dbcFile: null,
|
||||
dbcText: '',
|
||||
/** @type {{ id: string, name: string, size: number, dbc: object }[]} */
|
||||
dbcSources: [],
|
||||
/** 合并视图 { messages, nodeNames, messageMap };由 rebuildMergedDbc() 维护 */
|
||||
dbc: null,
|
||||
filteredMessages: [],
|
||||
selectedMessageKey: '',
|
||||
@@ -30,10 +31,32 @@ export var state = {
|
||||
export var el = {};
|
||||
|
||||
// ==================== 基础工具 ====================
|
||||
/** @description 拼接设备基础 URL(http://<ip>),ip 取自 el.toolIp,默认 192.168.31.127。 */
|
||||
var LS_ESP_IP = 'dcu_esp_ip';
|
||||
var DEFAULT_ESP_IP = '192.168.31.127';
|
||||
|
||||
/** @description 设备 IP:优先 localStorage(与 OTA 页共用),否则默认。 */
|
||||
export function getDeviceIp() {
|
||||
try {
|
||||
var saved = localStorage.getItem(LS_ESP_IP);
|
||||
if (saved && saved.trim()) return saved.trim();
|
||||
} catch (_) {}
|
||||
if (el.toolIp && el.toolIp.value && el.toolIp.value.trim()) {
|
||||
return el.toolIp.value.trim();
|
||||
}
|
||||
return DEFAULT_ESP_IP;
|
||||
}
|
||||
|
||||
/** @description 记住设备 IP(status 回报或其它页写入)。 */
|
||||
export function setDeviceIp(ip) {
|
||||
if (!ip || !String(ip).trim()) return;
|
||||
var v = String(ip).trim();
|
||||
try { localStorage.setItem(LS_ESP_IP, v); } catch (_) {}
|
||||
if (el.toolIp) el.toolIp.value = v;
|
||||
}
|
||||
|
||||
/** @description 拼接设备基础 URL(http://<ip>)。 */
|
||||
export function getBaseUrl() {
|
||||
var ip = (el.toolIp && el.toolIp.value ? el.toolIp.value.trim() : '') || '192.168.31.127';
|
||||
return 'http://' + ip;
|
||||
return 'http://' + getDeviceIp();
|
||||
}
|
||||
|
||||
/** @description 解析手动输入的 CAN ID(支持 0x/hex/十进制)。 */
|
||||
@@ -239,15 +262,76 @@ export function parseMultiplexToken(token) {
|
||||
return { role: null, value: null };
|
||||
}
|
||||
|
||||
/**
|
||||
* 将多个 DBC 源合并为统一 messages 列表。
|
||||
* 每条报文 key = sourceId:idRaw:name,避免不同文件同 ID/同名冲突。
|
||||
*/
|
||||
export function rebuildMergedDbc() {
|
||||
if (!state.dbcSources.length) {
|
||||
state.dbc = null;
|
||||
return null;
|
||||
}
|
||||
var messages = [];
|
||||
var nodeSet = Object.create(null);
|
||||
var nodeNames = [];
|
||||
var messageMap = {};
|
||||
for (var s = 0; s < state.dbcSources.length; s++) {
|
||||
var src = state.dbcSources[s];
|
||||
var dbc = src.dbc;
|
||||
if (!dbc || !dbc.messages) continue;
|
||||
for (var n = 0; n < (dbc.nodeNames || []).length; n++) {
|
||||
var nn = dbc.nodeNames[n];
|
||||
if (nn && !nodeSet[nn]) {
|
||||
nodeSet[nn] = 1;
|
||||
nodeNames.push(nn);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < dbc.messages.length; i++) {
|
||||
var msg = dbc.messages[i];
|
||||
var merged = {
|
||||
idRaw: msg.idRaw,
|
||||
name: msg.name,
|
||||
dlc: msg.dlc,
|
||||
transmitter: msg.transmitter,
|
||||
signals: msg.signals,
|
||||
comment: msg.comment,
|
||||
id: msg.id,
|
||||
isExtended: msg.isExtended,
|
||||
sourceId: src.id,
|
||||
sourceName: src.name,
|
||||
key: src.id + ':' + msg.idRaw + ':' + msg.name
|
||||
};
|
||||
messages.push(merged);
|
||||
messageMap[merged.key] = merged;
|
||||
}
|
||||
}
|
||||
state.dbc = { messages: messages, nodeNames: nodeNames, messageMap: messageMap };
|
||||
return state.dbc;
|
||||
}
|
||||
|
||||
/** @description 生成唯一 source id(同名文件可多次添加时追加 #2) */
|
||||
export function makeDbcSourceId(fileName) {
|
||||
var base = String(fileName || 'dbc').replace(/[^\w.\-]+/g, '_');
|
||||
var id = base;
|
||||
var n = 2;
|
||||
while (state.dbcSources.some(function (s) { return s.id === id; })) {
|
||||
id = base + '#' + n;
|
||||
n++;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/** @description 更新顶部 DBC / 报文数量摘要 pill。 */
|
||||
export function buildSummary() {
|
||||
if (!state.dbc) {
|
||||
if (!state.dbc || !state.dbcSources.length) {
|
||||
if (el.dbcSummaryPill) el.dbcSummaryPill.textContent = '未导入 DBC';
|
||||
if (el.messageSummaryPill) el.messageSummaryPill.textContent = '0 报文';
|
||||
return;
|
||||
}
|
||||
var nFiles = state.dbcSources.length;
|
||||
if (el.dbcSummaryPill) {
|
||||
el.dbcSummaryPill.textContent = (state.dbc.nodeNames.length || 0) + ' 节点';
|
||||
el.dbcSummaryPill.textContent =
|
||||
nFiles + ' 文件 · ' + (state.dbc.nodeNames.length || 0) + ' 节点';
|
||||
}
|
||||
if (el.messageSummaryPill) {
|
||||
el.messageSummaryPill.textContent = state.dbc.messages.length + ' 报文';
|
||||
|
||||
Reference in New Issue
Block a user