feat: validate real-bench seat orchestration and self-repair

This commit is contained in:
vee
2026-08-09 19:00:16 +08:00
parent 3bceb0d5a1
commit 3701192a11
41 changed files with 10403 additions and 3392 deletions
+23 -8
View File
@@ -3437,7 +3437,7 @@ function updateSeatFeedbackModeLabel() {
}
function selectExecutionMode(mode) {
if (mode === "hardware" && !(state.runtime?.hardware?.configured && state.runtime?.vehicle_can?.configured)) return;
if (mode === "hardware" && !state.runtime?.hardware_execution_allowed) return;
if (mode === "simulated_hardware" && !state.runtime?.simulated_hardware?.enabled) return;
state.executionMode = mode;
$$('[data-execution-mode]').forEach((button) => {
@@ -3446,6 +3446,7 @@ function selectExecutionMode(mode) {
const hardware = mode === "hardware";
const simulatedHardware = mode === "simulated_hardware";
const approvalRequired = Boolean(state.runtime?.hardware?.approval_required);
const vehicleCanReady = Boolean(state.runtime?.vehicle_can?.configured);
$("#approvalField").hidden = !hardware || !approvalRequired;
$("#simulationFaultField").hidden = !simulatedHardware;
$("#executionBadge").className = `status-badge ${hardware ? "" : simulatedHardware ? "warning" : "neutral"}`.trim();
@@ -3458,7 +3459,9 @@ function selectExecutionMode(mode) {
? "真实台架安全门禁"
: simulatedHardware ? "模拟台架隔离边界" : "虚拟执行边界";
$("#safetyText").textContent = hardware
? "同一 CAN 盒由单进程持有:通道 0 控制副驾座椅并校验板端反馈,通道 1 以 ISO CAN FD 500 kbit/s / 2 Mbit/s 控制空调等车身设备并校验状态帧。"
? vehicleCanReady
? "同一 CAN 盒由单进程持有:通道 0 控制副驾座椅并校验板端反馈,通道 1 以 ISO CAN FD 500 kbit/s / 2 Mbit/s 控制空调等车身设备并校验状态帧。"
: "当前仅通道 0 连接副驾座椅台架并校验板端反馈;车端 CAN 未启用,模式中的其他车舱领域保持虚拟执行。"
: simulatedHardware
? "故障只注入执行报告;物理 CAN、ZLG 适配器和 IAR 固件路径保持关闭。"
: "四域 CAN 均为 dry-run,真实发送被强制关闭。";
@@ -3716,6 +3719,8 @@ async function loadRuntime() {
const hardware = state.runtime.hardware || {};
const vehicleCan = state.runtime.vehicle_can || {};
const linkedHardwareReady = Boolean(hardware.configured && vehicleCan.configured);
const seatHardwareReady = Boolean(hardware.configured);
const hardwareExecutionReady = Boolean(state.runtime.hardware_execution_allowed);
const badge = $("#modelBadge");
const gptButton = $('[data-provider="openai"]');
if (state.runtime.openai_available) {
@@ -3734,7 +3739,7 @@ async function loadRuntime() {
}
$("#matrixCount").textContent = `${state.runtime.matrix.messages} / ${state.runtime.matrix.signals}`;
const hardwareButton = $("#hardwareMode");
hardwareButton.disabled = !linkedHardwareReady;
hardwareButton.disabled = !hardwareExecutionReady;
const simulation = state.runtime.simulated_hardware || {};
$("#simulationMode").disabled = !simulation.enabled;
$("#simulationMode").title = simulation.enabled
@@ -3751,21 +3756,31 @@ async function loadRuntime() {
: "";
$("#hardwareNote").textContent = linkedHardwareReady
? `${hardware.backend} · CH${hardware.channel} 座椅 ${hardware.bitrate / 1000} kbit/s · CH${vehicleCan.channel} 车身 CAN FD ${vehicleCan.arbitration.bitrate / 1000}k/${vehicleCan.data.bitrate / 1000000}M · ${profileText} · ${flashText} · ${approvalText}${accessText}${concurrencyText}`
: seatHardwareReady
? `${hardware.backend} · CH${hardware.channel} 座椅 ${hardware.bitrate / 1000} kbit/s · ${profileText} · ${flashText} · 车端 CAN 未启用,其他领域保持虚拟执行${accessText}${concurrencyText}`
: `联动真实执行已锁定:${profileText};请完成座椅固件、双通道 ZLG 适配器、车身 CAN FD${hardware.approval_required ? "和令牌" : ""}配置`;
$("#hardwareAliasNote").textContent = "CH0 → 副驾座椅控制器 · CH1 → 空调/车窗/门锁/灯光/遮阳帘(ISO CAN FD)";
$("#hardwareAliasNote").textContent = linkedHardwareReady
? "CH0 → 副驾座椅控制器 · CH1 → 车身 CAN FD"
: seatHardwareReady
? `CH${hardware.channel} → 副驾座椅控制器 · 车身控制暂用虚拟 CAN`
: "尚未配置可执行的物理 CAN 通道";
$("#autoFlashNote").textContent = hardware.auto_flash_enabled
? "未检测到匹配固件时,将在发送座椅控制帧前通过 IAR C-SPY 自动烧录并重新验证。"
: "仅接受周期反馈与四轴量程签名匹配的统一固件;需要自动烧录时请启用服务端开关。";
$("#hardwareBadge").className = `status-badge ${linkedHardwareReady ? "" : "danger"}`.trim();
$("#hardwareBadge").className = `status-badge ${hardwareExecutionReady ? "" : "danger"}`.trim();
$("#hardwareBadge").innerHTML = linkedHardwareReady
? '<i data-lucide="shield-check"></i>双通道已配置'
: seatHardwareReady
? '<i data-lucide="shield-check"></i>座椅台架已配置'
: '<i data-lucide="shield-off"></i>硬件锁定';
$("#hardwareCoverage").textContent = linkedHardwareReady
? `CH0 座椅 + CH${vehicleCan.channel} 车身 · ${approvalText}${accessText}${concurrencyText}`
: "已阻断";
$("#hardwareCoverage").className = linkedHardwareReady ? "available" : "blocked";
: seatHardwareReady
? `CH${hardware.channel} 座椅 · 物理反馈${accessText}${concurrencyText}`
: "已阻断";
$("#hardwareCoverage").className = hardwareExecutionReady ? "available" : "blocked";
const modeAvailable = state.executionMode === "hardware"
? linkedHardwareReady
? hardwareExecutionReady
: state.executionMode === "simulated_hardware"
? simulation.enabled
: true;
+4 -4
View File
@@ -13,20 +13,20 @@
<link rel="stylesheet" href="/console-v2.css?v=desktop-refactor-68" />
<link rel="stylesheet" href="/self-extension.css?v=extension-9" />
<link rel="stylesheet" href="/self-calibration.css?v=calibration-4" />
<link rel="stylesheet" href="/self-repair.css?v=repair-15" />
<link rel="stylesheet" href="/self-repair.css?v=repair-17" />
<script src="/vendor/lucide/0.468.0/lucide.min.js" defer></script>
<script src="/vendor/three/0.147.0/three.min.js" defer></script>
<script src="/vendor/three/0.147.0/fflate.min.js" defer></script>
<script src="/vendor/three/0.147.0/FBXLoader.js" defer></script>
<script src="/network.js?v=android-pad-1" defer></script>
<script src="/app.js?v=seat-hil-43" defer></script>
<script src="/app.js?v=seat-hil-44" defer></script>
<script type="module" src="/gesture-control.js?v=android-pad-6"></script>
<script src="/pwa.js?v=android-pad-1" defer></script>
<script src="/console-v2.js?v=desktop-refactor-28" defer></script>
<script src="/self-extension.js?v=extension-6" defer></script>
<script src="/self-calibration.js?v=calibration-2" defer></script>
<script src="/self-repair.js?v=repair-15" defer></script>
<script src="/self-repair-seat3d.js?v=repair-15" defer></script>
<script src="/self-repair.js?v=repair-17" defer></script>
<script src="/self-repair-seat3d.js?v=repair-17" defer></script>
</head>
<body>
<header class="topbar">
+4
View File
@@ -1270,6 +1270,10 @@
.v2-repair-dialog th,
.v2-repair-dialog td { padding: 7px; border-bottom: 1px solid var(--repair-line); text-align: left; }
.v2-repair-dialog th { color: var(--repair-muted); background: var(--repair-soft-strong); }
.v2-repair-confirm-dialog { width: min(460px, calc(100vw - 32px)); }
.v2-repair-confirm-dialog #repairConfirmMessage { margin: 0; color: var(--repair-ink); font-size: 13px; line-height: 1.7; }
.v2-repair-confirm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }
.v2-repair-confirm-actions button { min-width: 104px; }
.v2-repair-report-path { padding: 7px 9px; overflow-wrap: anywhere; color: var(--repair-muted); background: var(--repair-soft-strong); border-radius: 8px; font: 8px "IBM Plex Mono", Consolas, monospace; }
.v2-repair-event-list { display: grid; gap: 5px; margin-top: 10px; }
.v2-repair-event-list article { display: grid; gap: 2px; padding: 7px 9px; background: var(--repair-soft); border-radius: 8px; }
+69 -15
View File
@@ -50,6 +50,7 @@
: "shadow";
let polling = false;
let toastTimer = 0;
let confirmationResolve = null;
function isAdmin() {
return window.CognitiveAgentConsoleV2?.getRole() === "admin"
@@ -181,7 +182,11 @@
</section>
<dialog class="v2-repair-dialog" id="repairSoftwareDialog"><header><div><strong>软件自修复验证记录</strong><small>成功用例自动保存诊断快照</small></div><button type="button" data-repair-close="repairSoftwareDialog">×</button></header><div id="repairSoftwareRecordsBody"></div></dialog>
<dialog class="v2-repair-dialog" id="repairHardwareDialog"><header><div><strong>硬件自诊断记录</strong><small>支路状态变化与最终报告</small></div><button type="button" data-repair-close="repairHardwareDialog">×</button></header><div id="repairHardwareRecordsBody"></div></dialog>`;
<dialog class="v2-repair-dialog" id="repairHardwareDialog"><header><div><strong>硬件自诊断记录</strong><small>支路状态变化与最终报告</small></div><button type="button" data-repair-close="repairHardwareDialog">×</button></header><div id="repairHardwareRecordsBody"></div></dialog>
<dialog class="v2-repair-dialog v2-repair-confirm-dialog" id="repairConfirmDialog" aria-labelledby="repairConfirmTitle">
<header><div><strong id="repairConfirmTitle">确认台架安全条件</strong><small>确认后才会向实物链路提交请求</small></div></header>
<div><p id="repairConfirmMessage"></p><div class="v2-repair-confirm-actions"><button type="button" id="repairConfirmCancel">取消</button><button class="primary" type="button" id="repairConfirmAccept">确认执行</button></div></div>
</dialog>`;
moduleSwitcher.insertAdjacentElement("afterend", root);
const select = $("#repairExecutionMode");
@@ -402,9 +407,12 @@
const session = latest.hardware_session || {};
const bridge = latest.bridge || {};
const manager = latest.bridge_manager || {};
const adapterOnline = hardware.link?.adapter === "OK"
|| Boolean(bridge.bus?.opened)
|| (bridge.online && bridge.bus_traffic);
const layers = [
["1", "ZCANPRO桥接", bridge.online ? "正常" : "离线", bridge.online ? "OK" : "UNKNOWN"],
["2", "CAN设备 / 通道", bridge.bus?.opened ? "设备与通道已打开" : "未连接", bridge.bus?.opened ? "OK" : "UNKNOWN"],
["2", "CAN设备 / 通道", adapterOnline ? "设备与通道已打开" : "未连接", adapterOnline ? "OK" : "UNKNOWN"],
["3", "总线实时流量", bridge.bus_traffic ? "正常" : "未检测", bridge.bus_traffic ? "OK" : "WARN"],
["4", "座椅ECU协议", bridge.protocol_online ? "0x301~0x305在线" : "未检测", bridge.protocol_online ? "OK" : "UNKNOWN"],
];
@@ -431,13 +439,25 @@
const bridge = latest.bridge || {};
const manager = latest.bridge_manager || {};
const healing = latest.healing || {};
const hardwarePhase = latest.hardware_session?.progress?.phase;
const integration = latest.integration || {};
$("#repairCanStatus").textContent = bridge.online ? "在线" : "离线";
$("#repairCanDot").dataset.state = bridge.online ? "online" : manager.state === "STARTING" ? "working" : "offline";
$("#repairProtocolStatus").textContent = bridge.protocol_online ? "协议在线" : "未检测";
$("#repairProtocolDot").dataset.state = bridge.protocol_online ? "online" : "offline";
$("#repairAiStatus").textContent = healing.phase_text || "等待验证";
$("#repairAiDot").dataset.state = ["PASSED"].includes(healing.phase) ? "online" : ["IDLE"].includes(healing.phase) ? "offline" : "working";
const hardwareState = {
BASELINE: ["硬件检测中", "working"],
RUNNING: ["硬件检测中", "working"],
PASSED: ["21针通过", "online"],
FAILED: ["硬件异常", "offline"],
}[hardwarePhase];
const aiState = activeView === "hardware" && hardwareState
? hardwareState
: [healing.phase_text || "等待验证", healing.phase === "PASSED"
? "online"
: healing.phase === "IDLE" ? "offline" : "working"];
$("#repairAiStatus").textContent = aiState[0];
$("#repairAiDot").dataset.state = aiState[1];
$("#repairConnectionText").textContent = bridge.online ? "CAN 桥接在线" : manager.message || "CAN 桥接离线";
$(".v2-repair-footer").classList.toggle("offline", !bridge.online);
const benchOption = $('#repairExecutionMode option[value="bench"]');
@@ -504,6 +524,25 @@
}
}
function finishConfirmation(accepted) {
const dialog = $("#repairConfirmDialog");
const resolve = confirmationResolve;
confirmationResolve = null;
if (dialog?.open) dialog.close(accepted ? "confirmed" : "cancelled");
if (resolve) resolve(accepted);
}
function confirmAction(message, confirmLabel = "确认执行") {
const dialog = $("#repairConfirmDialog");
if (!dialog || confirmationResolve) return Promise.resolve(false);
$("#repairConfirmMessage").textContent = message;
$("#repairConfirmAccept").textContent = confirmLabel;
dialog.showModal();
return new Promise((resolve) => {
confirmationResolve = resolve;
});
}
function bindEvents() {
$$('[data-repair-view]').forEach((button) => button.addEventListener("click", () => showView(button.dataset.repairView)));
$("#repairExecutionMode").addEventListener("change", (event) => {
@@ -511,33 +550,48 @@
sessionStorage.setItem(MODE_KEY, executionMode);
if (latest) renderAll();
});
$("#repairStart").addEventListener("click", () => {
if (executionMode === "bench" && !window.confirm("即将向实物 ECU 写入受控故障并驱动座椅恢复。请确认运动区域无人、无遮挡且急停可用。")) return;
$("#repairStart").addEventListener("click", async () => {
if (executionMode === "bench" && !(await confirmAction("即将向实物 ECU 写入受控故障并驱动座椅恢复。请确认运动区域无人、无遮挡且急停可用。"))) return;
runAction(() => apiPost("/healing/start", { case_id: selectedCaseId, execution_mode: executionMode }), executionMode === "bench" ? "实物自修复用例已启动" : "影子故障已注入,开始自修复");
});
$("#repairRunAll").addEventListener("click", () => runAction(() => apiPost("/healing/run-all", { execution_mode: executionMode }), "六项自动验证已启动"));
$("#repairReset").addEventListener("click", () => {
if (executionMode === "bench" && !window.confirm("四轴将依次回到机械零位。请确认运动区域无人、无遮挡且急停可用。")) return;
$("#repairRunAll").addEventListener("click", async () => {
if (executionMode === "bench" && !(await confirmAction(
"将依次执行6个实物故障用例;每项开始前会自动停止输出并完成四轴归零。请确认运动区域无人、无遮挡且急停可用。",
"确认实物批量验证",
))) return;
runAction(() => apiPost("/healing/run-all", { execution_mode: executionMode }), "六项自动验证已启动");
});
$("#repairReset").addEventListener("click", async () => {
if (executionMode === "bench" && !(await confirmAction("四轴将依次回到机械零位。请确认运动区域无人、无遮挡且急停可用。", "确认归零"))) return;
runAction(() => apiPost("/healing/reset", { execution_mode: executionMode }), executionMode === "bench" ? "四轴归零请求已提交" : "自修复验证已重置");
});
$("#repairFirmwareFlash").addEventListener("click", () => {
if (!window.confirm("即将构建并烧录台架测试固件。请确认座椅运动区域无人、急停可用且 CMSIS-DAP 已连接。")) return;
$("#repairFirmwareFlash").addEventListener("click", async () => {
if (!(await confirmAction("即将构建并烧录台架测试固件。请确认座椅运动区域无人、急停可用且 CMSIS-DAP 已连接。", "确认构建/烧录"))) return;
runAction(() => apiPost("/firmware/build-flash", { confirmed_bench_clear: true }), "固件构建/烧录流程已完成");
});
$("#repairHardwareToggle").addEventListener("click", () => {
$("#repairHardwareToggle").addEventListener("click", async () => {
if (!latest) return;
if (latest.hardware_session?.active) {
runAction(() => apiPost("/hardware/stop"), "硬件诊断已停止并保存报告");
return;
}
if (!window.confirm("硬件诊断可能自动烧录测试固件,并短时驱动四路电机、风扇与低档加热。请确认台架区域安全且急停可用。")) return;
if (!(await confirmAction("硬件诊断可能自动烧录测试固件,并短时驱动四路电机、风扇与低档加热。请确认台架区域安全且急停可用。", "确认启动诊断"))) return;
runAction(() => apiPost("/hardware/start", { confirmed_bench_clear: true }), "21 针主动硬件诊断已启动");
});
$("#repairHardwareSelfTest").addEventListener("click", () => runAction(() => apiPost("/hardware/self-test"), "72 针映射和诊断规则自检完成"));
$("#repairEmergencyStop").addEventListener("click", () => {
if (!window.confirm("确认提交 STOP ALL 和热管理关闭请求?")) return;
$("#repairEmergencyStop").addEventListener("click", async () => {
if (!(await confirmAction("确认提交 STOP ALL 和热管理关闭请求?", "确认紧急停止"))) return;
runAction(() => apiPost("/emergency-stop"), "紧急停止请求已提交");
});
$("#repairConfirmCancel").addEventListener("click", () => finishConfirmation(false));
$("#repairConfirmAccept").addEventListener("click", () => finishConfirmation(true));
$("#repairConfirmDialog").addEventListener("cancel", (event) => {
event.preventDefault();
finishConfirmation(false);
});
$("#repairConfirmDialog").addEventListener("close", () => {
if (confirmationResolve) finishConfirmation(false);
});
$("#repairSoftwareRecords").addEventListener("click", () => $("#repairSoftwareDialog").showModal());
$("#repairHardwareRecords").addEventListener("click", () => $("#repairHardwareDialog").showModal());
$$('[data-repair-close]').forEach((button) => button.addEventListener("click", () => $(`#${button.dataset.repairClose}`).close()));
+5 -5
View File
@@ -1,4 +1,4 @@
const CACHE_VERSION = "cognitive-agent-pad-v39";
const CACHE_VERSION = "cognitive-agent-pad-v40";
const SHELL_CACHE = `${CACHE_VERSION}-shell`;
const RUNTIME_CACHE = `${CACHE_VERSION}-runtime`;
const APP_SHELL = [
@@ -6,7 +6,7 @@ const APP_SHELL = [
"/index.html",
"/styles.css?v=android-pad-4",
"/network.js?v=android-pad-1",
"/app.js?v=seat-hil-43",
"/app.js?v=seat-hil-44",
"/gesture-control.js?v=android-pad-6",
"/pwa.js?v=android-pad-1",
"/console-v2.css?v=desktop-refactor-68",
@@ -15,9 +15,9 @@ const APP_SHELL = [
"/self-extension.js?v=extension-6",
"/self-calibration.css?v=calibration-4",
"/self-calibration.js?v=calibration-2",
"/self-repair.css?v=repair-15",
"/self-repair.js?v=repair-15",
"/self-repair-seat3d.js?v=repair-15",
"/self-repair.css?v=repair-17",
"/self-repair.js?v=repair-17",
"/self-repair-seat3d.js?v=repair-17",
"/assets/self-calibration-model.bundle.js",
"/assets/models/n7_seat.glb",
"/manifest.webmanifest",