feat: refine vehicle console and self-repair workflow

This commit is contained in:
vee
2026-08-13 17:38:04 +08:00
parent fd3c219c39
commit 0ec43052f3
20 changed files with 3925 additions and 552 deletions
+118 -16
View File
@@ -177,28 +177,28 @@ operations:
def test_static_console_and_runtime_endpoint(self) -> None:
with self.opener.open(f"{self.base_url}/", timeout=3) as response:
html = response.read().decode("utf-8")
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-67", timeout=3) as response:
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-80", timeout=3) as response:
app_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/gesture-control.js?v=android-pad-7", timeout=3
) as response:
gesture_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/console-v2.js?v=desktop-refactor-28", timeout=3
f"{self.base_url}/console-v2.js?v=desktop-refactor-34", timeout=3
) as response:
console_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/console-v2.css?v=desktop-refactor-87", timeout=3
f"{self.base_url}/console-v2.css?v=desktop-refactor-106", timeout=3
) as response:
console_styles = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/self-extension.js?v=extension-18", timeout=3
f"{self.base_url}/self-extension.js?v=extension-20", timeout=3
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/self-extension.css?v=extension-12", timeout=3
f"{self.base_url}/self-extension.css?v=extension-18", timeout=3
) as response:
extension_styles = response.read().decode("utf-8")
extension_styles = response.read().decode("utf-8").replace("\r\n", "\n")
with self.opener.open(
f"{self.base_url}/api/extension/health", timeout=3
) as response:
@@ -219,22 +219,39 @@ operations:
self.assertIn("CMD 0x431 · FB 0x41A · CH1", html)
self.assertIn("CMD 0x431 · FB 0x388 · CH1", html)
self.assertNotIn("透明车体 · 实体设备", html)
self.assertIn('src="/app.js?v=dual-seat-67"', html)
self.assertIn('src="/app.js?v=dual-seat-80"', html)
self.assertIn('href="/styles.css?v=dual-seat-9"', html)
self.assertIn('src="/gesture-control.js?v=android-pad-7"', html)
self.assertNotIn('class="vehicle-system-hud"', html)
self.assertIn('src="/console-v2.js?v=desktop-refactor-28"', html)
self.assertIn('href="/console-v2.css?v=desktop-refactor-87"', html)
self.assertIn('src="/self-extension.js?v=extension-18"', html)
self.assertIn('href="/self-extension.css?v=extension-12"', html)
self.assertIn('src="/console-v2.js?v=desktop-refactor-34"', html)
self.assertIn('href="/console-v2.css?v=desktop-refactor-106"', html)
self.assertIn('src="/self-extension.js?v=extension-20"', html)
self.assertIn('href="/self-extension.css?v=extension-18"', html)
self.assertNotIn("滚动查看更多", html)
self.assertIn('aria-label="执行车舱控制指令"', html)
self.assertIn('sessionStorage.getItem("cognitive-agent.demo-role")', console_script)
self.assertIn('id="v2SidebarToggle"', console_script)
self.assertIn('id = "v2ModuleSwitcher"', console_script)
self.assertIn(
'panelHeading.classList.add("v2-operation-heading", "v2-module-heading")',
console_script,
)
self.assertIn('kicker.textContent = "SELF-ORCHESTRATION"', console_script)
self.assertIn(
'if (ui.activeModule === "self-extension" && ui.role !== "user")',
console_script,
)
self.assertIn(
'if (ui.activeModule === "self-extension" && role !== "user" && !allowModuleTransition)',
console_script,
)
self.assertIn('label: "自编排", available: true', console_script)
self.assertIn('id: "self-calibration"', console_script)
self.assertIn('label: "自标定"', console_script)
self.assertLess(
console_script.index('label: "自标定"'),
console_script.index('label: "自编排"'),
)
self.assertIn("adminOnly: true", console_script)
self.assertIn(
"$('.v2-module-tab[data-module=\"self-calibration\"]')",
@@ -245,6 +262,16 @@ operations:
self.assertIn('<span>Hi,</span><strong>user</strong>', console_script)
self.assertIn('id: "self-repair"', console_script)
self.assertIn('label: "自修复"', console_script)
self.assertIn('id: "self-evolution", label: "自进化", available: true', console_script)
self.assertIn(
'evolution.setAttribute("aria-label", "自进化能力日志")',
console_script,
)
self.assertIn('<span class="section-kicker">SELF-EVOLUTION</span>', console_script)
self.assertIn('v2-evolution-heading v2-module-heading', console_script)
self.assertIn('data-evolution-index', console_script)
self.assertIn('NEXT EVOLUTION · v0.6', console_script)
self.assertIn('["self-orchestration", "self-extension", "self-evolution"]', console_script)
self.assertIn('roleSelect.disabled = ui.activeModule === "self-repair"', console_script)
self.assertNotIn('label: "自诊断"', console_script)
self.assertIn('<small>手势模型</small>', console_script)
@@ -262,7 +289,37 @@ operations:
self.assertIn('grid-template-columns: clamp(245px, 18.5vw, 310px)', console_styles)
self.assertIn('.v2-control-switch {', console_styles)
self.assertIn('.v2-module-switcher {', console_styles)
self.assertIn('grid-template-columns: repeat(4, minmax(0, 1fr))', console_styles)
self.assertIn('grid-template-columns: repeat(5, minmax(0, 1fr))', console_styles)
self.assertIn('[data-module="self-evolution"] .v2-self-evolution', console_styles)
self.assertIn(
'.console-v2 .v2-module-heading.v2-module-heading h1',
console_styles,
)
self.assertIn('font-size: clamp(24px, 2vw, 31px)', console_styles)
self.assertIn('--v2-module-title-block: clamp(12px, 2vh, 22px)', console_styles)
self.assertIn('--v2-module-title-inline: clamp(20px, 2.3vw, 34px)', console_styles)
self.assertIn(
'grid-template-rows: 36px clamp(236px, 33dvh, 246px) minmax(0, 1fr)',
console_styles,
)
self.assertIn(
'[data-module="self-calibration"] .v2-self-calibration',
console_styles,
)
self.assertIn(
'[data-module="self-repair"] .v2-self-repair',
console_styles,
)
self.assertIn('.v2-evolution-version.active', console_styles)
self.assertIn('[data-evolution-scope="admin"]', console_styles)
self.assertIn(
'color-mix(in srgb, var(--v2-accent-soft) 86%, white)',
console_styles,
)
self.assertNotIn(
'.cabin-channel-legend [data-channel="ch0"]',
console_styles,
)
self.assertIn('margin-inline: calc(0px - var(--v2-main-pad))', console_styles)
self.assertIn('background: color-mix(in srgb, var(--v2-accent) 30%, var(--v2-accent-soft))', console_styles)
self.assertIn('width: clamp(48px, 45%, 72px)', console_styles)
@@ -333,8 +390,10 @@ operations:
self.assertIn('root.id = "v2SelfExtension"', extension_script)
self.assertIn('data-extension-mode="study"', extension_script)
self.assertIn('data-extension-mode="control"', extension_script)
self.assertIn('外设玩具能力一览与学习', extension_script)
self.assertIn('说一句,联动外设玩具与车端设备', extension_script)
self.assertIn('外设玩具能力学习', extension_script)
self.assertIn('外设玩具与车端联动', extension_script)
self.assertGreaterEqual(extension_script.count('class="v2-module-heading"'), 2)
self.assertGreaterEqual(extension_script.count('SELF-EXTENSION'), 2)
self.assertIn('id="extensionExecuteButton"', extension_script)
self.assertIn('data-lucide="play"', extension_script)
self.assertNotIn('id="extensionControlHealth"', extension_script)
@@ -363,6 +422,10 @@ operations:
self.assertIn('.v2-extension-twin-host > .twin-band {', extension_styles)
self.assertIn('.v2-extension-type-card[data-extension-connected="true"]', extension_styles)
self.assertIn('.v2-extension-type-card[data-extension-connected="false"]', extension_styles)
self.assertIn(
'.v2-extension-control\n.v2-extension-learned-action {\n --extension-card-color: var(--v2-accent);',
extension_styles,
)
self.assertIn('grid-template-columns: repeat(3, minmax(0, 1fr))', extension_styles)
self.assertTrue(extension_health["success"])
self.assertIn(extension_health["transport"], {"tcp", "mqtt", "simulation"})
@@ -444,6 +507,45 @@ operations:
self.assertIn('id="vehicleSpeed"', html)
self.assertNotIn('data-domain-panel="control_link"', html)
self.assertIn('id="cabinStateTitle">整车设备状态</h3>', html)
self.assertIn('id="cabinCarouselPrev"', app_script)
self.assertIn('id="cabinCarouselNext"', app_script)
self.assertIn('aria-label="左右浏览设备卡片"', app_script)
self.assertNotIn('class="cabin-category-tabs"', app_script)
self.assertIn('<span class="section-kicker">LIVE VEHICLE DIGITAL TWIN</span>', html)
self.assertIn('.joint-twin-shell > .twin-band {', console_styles)
self.assertIn('grid-template-rows: 62px minmax(0, 1fr)', console_styles)
self.assertIn('grid-template-columns: repeat(2, minmax(0, 1fr))', console_styles)
self.assertIn('--cabin-cards-per-view: 2', console_styles)
self.assertIn('const cabinCardsPerView = () => 2', app_script)
self.assertIn('scroll-snap-type: x mandatory', console_styles)
self.assertIn('data-device-power-value', app_script)
self.assertIn('查看${name}功率详情', app_script)
self.assertIn('name: "车内灯光", measurable: false', app_script)
self.assertIn('.device-power-entry.unavailable', console_styles)
self.assertIn('powerDrawer.id = "devicePowerDrawer"', app_script)
self.assertIn('window.updateDevicePowerTelemetry = updatePowerTelemetry', app_script)
self.assertIn('["longitudinal_motor", "水平电机"]', app_script)
self.assertIn('["cushion_motor", "坐盆电机"]', app_script)
self.assertIn('["backrest_motor", "座椅靠背电机"]', app_script)
self.assertIn('["legrest_motor", "腿托电机"]', app_script)
self.assertIn('["ventilation_control", "通风控制"]', app_script)
self.assertIn('["heating_control", "加热控制"]', app_script)
self.assertIn('["compressor", "压缩机"]', app_script)
self.assertIn('["electronic_fan", "电子风扇"]', app_script)
self.assertIn('["blower", "鼓风机"]', app_script)
self.assertIn('["thermal_management_module", "空调热管理集成模块"]', app_script)
self.assertIn('["compressor_controller", "压缩机电控"]', app_script)
self.assertIn('["electronic_cooling_pump", "电控冷却水泵"]', app_script)
self.assertIn('["front_left_window", "左前车窗"]', app_script)
self.assertIn('["front_right_window", "右前车窗"]', app_script)
self.assertIn('["rear_left_window", "左后车窗"]', app_script)
self.assertIn('["rear_right_window", "右后车窗"]', app_script)
self.assertIn('["front_left_combination_lamp", "左前组合灯"]', app_script)
self.assertIn('["front_right_combination_lamp", "右前组合灯"]', app_script)
self.assertIn('name: "主驾座椅", measurable: true, components: []', app_script)
self.assertIn('name: "遮阳帘", measurable: true, components: []', app_script)
self.assertIn("仅支持设备总功率", app_script)
self.assertIn('.device-power-drawer {', console_styles)
self.assertNotIn('class="roof-visual"', html)
self.assertNotIn('class="ambient-visual"', html)
self.assertIn('id="driverAirDirection"', html)
@@ -536,11 +638,11 @@ operations:
def test_self_extension_vehicle_success_contract_is_strict(self) -> None:
with self.opener.open(
f"{self.base_url}/self-extension.js?v=extension-18", timeout=3
f"{self.base_url}/self-extension.js?v=extension-20", timeout=3
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/app.js?v=dual-seat-67", timeout=3
f"{self.base_url}/app.js?v=dual-seat-80", timeout=3
) as response:
app_script = response.read().decode("utf-8")
+14 -8
View File
@@ -126,17 +126,17 @@ class TestSelfCalibrationApi:
with urllib.request.urlopen(f"{self.base_url}/", timeout=3) as response:
html = response.read().decode("utf-8")
with urllib.request.urlopen(
f"{self.base_url}/self-calibration.js?v=calibration-3",
f"{self.base_url}/self-calibration.js?v=calibration-6",
timeout=3,
) as response:
script = response.read().decode("utf-8")
with urllib.request.urlopen(
f"{self.base_url}/self-calibration.css?v=calibration-6",
f"{self.base_url}/self-calibration.css?v=calibration-10",
timeout=3,
) as response:
styles = response.read().decode("utf-8")
with urllib.request.urlopen(
f"{self.base_url}/console-v2.js?v=desktop-refactor-28",
f"{self.base_url}/console-v2.js?v=desktop-refactor-31",
timeout=3,
) as response:
console_script = response.read().decode("utf-8")
@@ -148,24 +148,30 @@ class TestSelfCalibrationApi:
content_type = response.headers["Content-Type"]
content_security_policy = response.headers["Content-Security-Policy"]
assert 'href="/self-calibration.css?v=calibration-6"' in html
assert 'src="/self-calibration.js?v=calibration-3"' in html
assert 'href="/self-calibration.css?v=calibration-10"' in html
assert 'src="/self-calibration.js?v=calibration-6"' in html
assert 'data-calibration-page="live"' in script
assert 'data-calibration-view="live"' in script
assert 'data-calibration-page="hall"' not in script
assert 'data-calibration-page="stereo"' not in script
assert "座椅—双目联合标定" in script
assert "座椅与双目联合标定" in script
assert 'class="v2-module-heading"' in script
assert 'SELF-CALIBRATION' in script
assert 'padding: var(--v2-module-title-block) 0 0' in styles
assert 'margin-inline: var(--v2-module-title-inline)' in styles
assert "实时座椅姿态" in script
assert "四轴实时控制" in script
assert "ZED 实时画面" in script
assert "双目位姿数据" in script
assert "双目位姿数据" not in script
assert 'class="v2-calibration-panel v2-calibration-mqtt-panel"' not in script
assert "grid-template-columns: minmax(420px, 1.06fr) minmax(380px, 0.94fr)" in styles
assert "标定仿真台架" in script
assert "X-Cognitive-Agent-Role" in script
assert "--v2-sidebar: #dae3f5" in styles
assert "--v2-sidebar-deep: #b6c7ea" in styles
assert "--v2-accent-strong: #1e386b" in styles
assert '["self-calibration", "self-repair"].includes(module)' in console_script
assert 'setRole("admin")' in console_script
assert 'setRole("admin", { allowModuleTransition: true })' in console_script
assert len(model) == 845_992
assert content_type.startswith("model/gltf-binary")
assert "img-src 'self' data: blob:;" in content_security_policy
+39 -12
View File
@@ -95,6 +95,17 @@ class TestSelfRepairApi:
assert len(state["scenarios"]) == 6
assert state["hardware"]["connector_enabled_count"] == 21
with urllib.request.urlopen(
self.request(
"/api/self-repair/runtime",
method="POST",
payload={"active": True, "execution_mode": "shadow"},
),
timeout=3,
) as response:
runtime = json.loads(response.read().decode("utf-8"))["runtime"]
assert runtime["active"] is True
with urllib.request.urlopen(
self.request(
"/api/self-repair/healing/start",
@@ -135,26 +146,37 @@ class TestSelfRepairApi:
with urllib.request.urlopen(f"{self.base_url}/", timeout=3) as response:
html = response.read().decode("utf-8")
with urllib.request.urlopen(
f"{self.base_url}/self-repair.js?v=repair-17", timeout=3
f"{self.base_url}/self-repair.js?v=repair-20", timeout=3
) as response:
script = response.read().decode("utf-8")
script = response.read().decode("utf-8").replace("\r\n", "\n")
with urllib.request.urlopen(
f"{self.base_url}/self-repair.css?v=repair-17", timeout=3
f"{self.base_url}/self-repair.css?v=repair-21", timeout=3
) as response:
styles = response.read().decode("utf-8")
with urllib.request.urlopen(
f"{self.base_url}/self-repair-seat3d.js?v=repair-17", timeout=3
f"{self.base_url}/self-repair-seat3d.js?v=repair-18", timeout=3
) as response:
seat3d = response.read().decode("utf-8")
with urllib.request.urlopen(
f"{self.base_url}/console-v2.js?v=desktop-refactor-28", timeout=3
f"{self.base_url}/console-v2.js?v=desktop-refactor-31", timeout=3
) as response:
console_script = response.read().decode("utf-8")
assert 'href="/self-repair.css?v=repair-17"' in html
assert 'src="/self-repair.js?v=repair-17"' in html
assert 'src="/self-repair-seat3d.js?v=repair-17"' in html
assert "量产故障用例" in script
assert 'href="/self-repair.css?v=repair-21"' in html
assert 'src="/self-repair.js?v=repair-20"' in html
assert 'class="v2-module-heading"' in script
assert 'SELF-REPAIR' in script
assert 'padding: var(--v2-module-title-block) 0 0' in styles
assert 'margin-inline: var(--v2-module-title-inline)' in styles
assert 'src="/self-repair-seat3d.js?v=repair-18"' in html
assert "量产故障注入" in script
assert "运行与自修复日志" in script
assert "启动座椅运行" in script
assert "开始正常任务" not in script
assert 'id="repairSeatCanvas"' in script
assert 'id="repairSeatViewer"' in script
assert "self-repair-seat-reset" in script
assert "seat-healing-state" in script
assert 'id="repairConfirmDialog"' in script
assert "confirmAction(" in script
assert "window.confirm" not in script
@@ -163,10 +185,15 @@ class TestSelfRepairApi:
assert "connectorRows" in script
assert "Polling refreshes data only" in script
assert "renderRecords();\n showView(activeView);" not in script
assert 'let latest = fallbackState();' in script
assert 'renderAll();\n window.lucide?.createIcons' in script
assert '"WDT复位后失能"' in script
assert "--repair-deep-blue" in styles
assert '.console-v2[data-module="self-repair"] .section-kicker' in styles
assert 'grid-template-areas: "twin cases timeline"' in styles
assert "background: color-mix(in srgb, var(--v2-accent-soft) 82%, white)" in styles
assert 'grid-template-areas: "twin cases logs"' in styles
assert ".v2-repair-event-stream" in styles
assert "background: rgba(255, 255, 255, 0.94)" in styles
assert "border-color: color-mix(in srgb, var(--v2-accent) 18%, white)" in styles
assert ".v2-repair-toolbar button.primary:hover:not(:disabled)" in styles
assert "color: #fff;" in styles
assert "seat-healing-state" in seat3d
@@ -175,7 +202,7 @@ class TestSelfRepairApi:
assert 'cssThemeColor("--v2-accent", "#315eaa")' in seat3d
assert 'viewer.addEventListener("pointerdown"' in seat3d
assert 'window.addEventListener("pointermove"' in seat3d
assert 'window.addEventListener("self-repair-seat-reset"' in seat3d
assert "window.addEventListener(resetEvent" in seat3d
assert '["self-calibration", "self-repair"].includes(module)' in console_script
assert 'roleSelect.disabled = ui.activeModule === "self-repair"' in console_script