feat: validate real-bench seat orchestration and self-repair
This commit is contained in:
+23
-8
@@ -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;
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user