docs: 新增 .qoder Repowiki 知识库文档
This commit is contained in:
@@ -150,7 +150,8 @@ if (liveOk) {
|
||||
typeof m.rx_count === "number" && typeof m.seq === "number" &&
|
||||
typeof m.data === "string" && typeof m.config_data === "string";
|
||||
check("motors[] 每项字段名/类型与 WiFi 一致", shapeOk, JSON.stringify(m).slice(0, 150));
|
||||
check("data 是 64 字符 hex(32B STATUS)", m.data.length === 64, `len=${m.data.length}`);
|
||||
check("data 是 96 字符 hex(48B STATUS,v0.74 含 InitStatus)", m.data.length === 96,
|
||||
`len=${m.data.length}`);
|
||||
results.push(` \u2139 在线电机: ` + st.motors
|
||||
.map((x) => `N${x.node}@CH${x.ch}${x.online ? "" : "(离线)"}`).join(" "));
|
||||
} else {
|
||||
@@ -172,8 +173,8 @@ if (liveOk) {
|
||||
typeof ecat.takeNodeIdReport === "function" && ecat.takeNodeIdReport(7) === null);
|
||||
const dbg = ecat.debug();
|
||||
check("debug() 暴露 PDO 周期与槽位统计",
|
||||
dbg && typeof dbg.slots === "number" && dbg.slotsPerPdo === 7 &&
|
||||
dbg.slotCount === 27 && dbg.scanCycles === 4, JSON.stringify(dbg && {
|
||||
dbg && typeof dbg.slots === "number" && dbg.slotsPerPdo === 5 &&
|
||||
dbg.slotCount === 27 && dbg.scanCycles === 6, JSON.stringify(dbg && {
|
||||
periodMs: dbg.periodMs, slots: dbg.slots }));
|
||||
}
|
||||
|
||||
@@ -226,15 +227,15 @@ section("[8] 快照→契约翻译(构造快照,跑生产代码)");
|
||||
sent_seq: null, sent_frames: 0, queue_depth: 0,
|
||||
bridge: { valid: true, ack: 0, rx_count: 1000, tx_count: 5, drop: 2, tx_result: 0 },
|
||||
xrx: null, rx_batch: { count: 0, lost: 0, pending: 0, frames: [] },
|
||||
head: { active: motors.length, boot_active: false, win_base: 0, win_count: 7, global_seq: 42 },
|
||||
head: { active: motors.length, boot_active: false, win_base: 0, win_count: 5, global_seq: 42 },
|
||||
motors,
|
||||
}, extra || {});
|
||||
|
||||
const STATUS_HEX = "AB".repeat(32); /* 32B STATUS */
|
||||
const STATUS_HEX = "AB".repeat(48); /* 48B STATUS(v0.74) */
|
||||
const CONFIG_HEX = "5A".repeat(24); /* 24B CONFIG(0x0101) */
|
||||
|
||||
/* (a) 首次:seq 在动 → online=true,rx_count 从 1 起累 */
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 32, seq: 500, data: STATUS_HEX }]);
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 48, seq: 500, data: STATUS_HEX }]);
|
||||
let st = await tp.fetchMotorState();
|
||||
let m = st.motors[0];
|
||||
check("(a) 从站 online + seq 在动 → online=true", m.online === true);
|
||||
@@ -242,7 +243,7 @@ section("[8] 快照→契约翻译(构造快照,跑生产代码)");
|
||||
check("(a) active_count 取自 head.active", st.active_count === 1);
|
||||
|
||||
/* (b) seq 递增 → rx_count 继续累加 */
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 32, seq: 501, data: STATUS_HEX }]);
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 48, seq: 501, data: STATUS_HEX }]);
|
||||
st = await tp.fetchMotorState();
|
||||
check("(b) seq 递增 → rx_count 累加到 2", st.motors[0].rx_count === 2,
|
||||
`rx=${st.motors[0].rx_count}`);
|
||||
@@ -250,7 +251,7 @@ section("[8] 快照→契约翻译(构造快照,跑生产代码)");
|
||||
/* (c) CONFIG 从批量接收区累积 → 回填 motors[].config_data。
|
||||
* 这是 WiFi 有、EtherCAT 没有的字段,回填不了就等于 CONFIG 面板永久空白。 */
|
||||
rxFrames = [{ ch: 1, dlc: 24, valid: true, id: (3 << 16) | 0x0101, data: CONFIG_HEX }];
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 32, seq: 502, data: STATUS_HEX }],
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 48, seq: 502, data: STATUS_HEX }],
|
||||
{ rx_batch: { count: 1, lost: 0, pending: 0, frames: [] } });
|
||||
st = await tp.fetchMotorState();
|
||||
check("(c) CONFIG(0x0101) 回填到 config_data(48 hex)",
|
||||
@@ -260,13 +261,13 @@ section("[8] 快照→契约翻译(构造快照,跑生产代码)");
|
||||
/* (d) 版本(0x00FF):累积且 seq 递增 —— 上层靠 seq 增长判"这是本次查询的新应答" */
|
||||
const VER_HEX = "1A00" + "0200" + "0300" + "0400" + "00".repeat(16);
|
||||
rxFrames = [{ ch: 1, dlc: 24, valid: true, id: (3 << 16) | 0x00FF, data: VER_HEX }];
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 32, seq: 503, data: STATUS_HEX }],
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 48, seq: 503, data: STATUS_HEX }],
|
||||
{ rx_batch: { count: 1, lost: 0, pending: 0, frames: [] } });
|
||||
await tp.fetchMotorState();
|
||||
const v1 = await tp.fetchVersionNode(3);
|
||||
check("(d) 版本应答被累积", v1 && v1.node === 3 && v1.version === VER_HEX);
|
||||
rxFrames = [{ ch: 1, dlc: 24, valid: true, id: (3 << 16) | 0x00FF, data: VER_HEX }];
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 32, seq: 504, data: STATUS_HEX }],
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 48, seq: 504, data: STATUS_HEX }],
|
||||
{ rx_batch: { count: 1, lost: 0, pending: 0, frames: [] } });
|
||||
await tp.fetchMotorState();
|
||||
const v2 = await tp.fetchVersionNode(3);
|
||||
@@ -275,7 +276,7 @@ section("[8] 快照→契约翻译(构造快照,跑生产代码)");
|
||||
|
||||
/* (e) 节点 ID 应答:取走即清 */
|
||||
rxFrames = [{ ch: 1, dlc: 2, valid: true, id: (3 << 16) | 0x0401, data: "0700" }];
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 32, seq: 505, data: STATUS_HEX }],
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 48, seq: 505, data: STATUS_HEX }],
|
||||
{ rx_batch: { count: 1, lost: 0, pending: 0, frames: [] } });
|
||||
await tp.fetchMotorState();
|
||||
check("(e) 节点 ID 应答可取出", tp.takeNodeIdReport(3) === "0700");
|
||||
@@ -283,7 +284,7 @@ section("[8] 快照→契约翻译(构造快照,跑生产代码)");
|
||||
|
||||
/* (f) STATUS 已由状态窗口聚合,批量区里的 0x0100 必须跳过(否则重复解一遍) */
|
||||
rxFrames = [{ ch: 1, dlc: 32, valid: true, id: (3 << 16) | 0x0100, data: "FF".repeat(32) }];
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 32, seq: 506, data: STATUS_HEX }],
|
||||
snap = mkSnap([{ slot: 0, node: 3, ch: 1, online: true, dlc: 48, seq: 506, data: STATUS_HEX }],
|
||||
{ rx_batch: { count: 1, lost: 0, pending: 0, frames: [] } });
|
||||
st = await tp.fetchMotorState();
|
||||
check("(f) 批量区的 STATUS(0x0100) 不污染 config_data",
|
||||
@@ -292,7 +293,7 @@ section("[8] 快照→契约翻译(构造快照,跑生产代码)");
|
||||
|
||||
/* (g) xrx 长帧快照也要分派:CONFIG/版本是 DLC24,短快照会截断 */
|
||||
const CFG2 = "77".repeat(24);
|
||||
snap = mkSnap([{ slot: 1, node: 4, ch: 1, online: true, dlc: 32, seq: 10, data: STATUS_HEX }],
|
||||
snap = mkSnap([{ slot: 1, node: 4, ch: 1, online: true, dlc: 48, seq: 10, data: STATUS_HEX }],
|
||||
{ xrx: { ch: 1, dlc: 24, id: (4 << 16) | 0x0101, data: CFG2 } });
|
||||
st = await tp.fetchMotorState();
|
||||
check("(g) xrx 长帧快照的 CONFIG 也回填",
|
||||
@@ -301,7 +302,7 @@ section("[8] 快照→契约翻译(构造快照,跑生产代码)");
|
||||
/* (h) 关键:seq 停止不动超过阈值 → online 转 false。
|
||||
* 后端的快照**永不删除**见过的槽(电机拔了也还在),
|
||||
* 只认从站的 online 标志会让界面上永远挂着一台不存在的电机。 */
|
||||
const stale = { slot: 0, node: 3, ch: 1, online: true, dlc: 32, seq: 506, data: STATUS_HEX };
|
||||
const stale = { slot: 0, node: 3, ch: 1, online: true, dlc: 48, seq: 506, data: STATUS_HEX };
|
||||
snap = mkSnap([stale]);
|
||||
st = await tp.fetchMotorState();
|
||||
check("(h) seq 未变但仍在阈值内 → 暂时仍算在线",
|
||||
|
||||
Reference in New Issue
Block a user