优化无线/有线CAN,以及控制指令解析

This commit is contained in:
minghanchen
2026-08-26 20:11:09 +08:00
parent 9ac8fdface
commit 4a0570706f
49 changed files with 8409 additions and 1289 deletions
+82 -9
View File
@@ -22,6 +22,7 @@ from cognitive_agent.application.self_extension import (
)
from cognitive_agent.infrastructure.llm.openai_responses import OpenAIIntentError
from cognitive_agent.infrastructure.can.access import HardwareAccessController
from cognitive_agent.infrastructure.can.gateway import CanGatewayError
from cognitive_agent.server import SeatDemoHandler
@@ -40,6 +41,7 @@ class SeatWebApiTests(unittest.TestCase):
{
"COGNITIVE_AGENT_HARDWARE_EXECUTION_ENABLED": "false",
"COGNITIVE_AGENT_VEHICLE_CAN_ENABLED": "false",
"COGNITIVE_AGENT_WIRED_PASSENGER_CAN_ENABLED": "false",
},
clear=False,
):
@@ -332,14 +334,14 @@ 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-103", timeout=3) as response:
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-107", 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-9", timeout=3
) as response:
gesture_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/console-v2.js?v=sync-layout-43", timeout=3
f"{self.base_url}/console-v2.js?v=task-outcome-47", timeout=3
) as response:
console_script = response.read().decode("utf-8")
with self.opener.open(
@@ -378,11 +380,11 @@ 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-103"', html)
self.assertIn('src="/app.js?v=dual-seat-107"', html)
self.assertIn('href="/styles.css?v=dual-seat-16"', html)
self.assertIn('src="/gesture-control.js?v=android-pad-9"', html)
self.assertNotIn('class="vehicle-system-hud"', html)
self.assertIn('src="/console-v2.js?v=sync-layout-43"', html)
self.assertIn('src="/console-v2.js?v=task-outcome-47"', html)
self.assertIn('href="/console-v2.css?v=sync-layout-41"', html)
self.assertIn('<body class="console-v2">', html)
self.assertIn('id="v2BootScreen"', html)
@@ -507,8 +509,10 @@ operations:
self.assertNotIn("授权并真实执行", app_script)
self.assertIn('const dynamicModeOrder = ["rocking_horse", "dynamic_cradle"]', app_script)
self.assertIn('divider.className = "mode-group-divider"', app_script)
self.assertIn(
'mode === "hardware" && !hardwareExecutionReady',
self.assertIn("function isHardwareExecutionReady()", app_script)
self.assertIn("function hardwareExecutionIssue(", app_script)
self.assertNotIn(
'if (mode === "hardware" && !hardwareExecutionReady) return false',
app_script,
)
self.assertIn('data-control-transport="wired_can"', html)
@@ -529,7 +533,15 @@ operations:
'availableTransports.find((item) => item.state_configured)?.id',
app_script,
)
self.assertIn("hardwareButton.disabled = !hardwareExecutionReady", app_script)
self.assertIn("hardwareButton.disabled = false", app_script)
self.assertIn(
'hardwareButton.dataset.executionState = hardwareExecutionReady ? "available" : "blocked"',
app_script,
)
self.assertNotIn(
'id="hardwareMode" data-execution-mode="hardware" disabled',
html,
)
self.assertIn('const seat = twin.selected === "driver" ? "driver" : "passenger"', app_script)
self.assertNotIn('data-mode-command="seat driver vent low"', app_script)
self.assertIn("CH0 副驾座椅与 CH1 主驾座椅独立收发、独立反馈", app_script)
@@ -610,9 +622,15 @@ operations:
self.assertIn("const stageHasLayout = () =>", app_script)
self.assertIn("window.requestAnimationFrame(initSeatTwin)", app_script)
self.assertIn('client_type: "web_console"', app_script)
self.assertIn("function executionVerificationStatus(result = {})", app_script)
self.assertIn('"submitted_unverified"', app_script)
self.assertIn("指令已提交,等待状态确认", app_script)
self.assertIn("指令已提交 · 等待状态确认", console_script)
self.assertIn("CAN 指令已提交,等待车辆状态确认", console_script)
self.assertIn('canvas.addEventListener("webglcontextlost"', app_script)
self.assertIn('"cognitive-agent:module-change"', app_script)
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v204"', service_worker)
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v210"', service_worker)
self.assertIn('"/app.js?v=dual-seat-107"', service_worker)
self.assertIn('"/assets/pets/self-extension-cat.png?v=3"', service_worker)
self.assertIn(".then(() => self.skipWaiting())", service_worker)
self.assertNotIn("const cached = await caches.match(request)", service_worker)
@@ -852,7 +870,7 @@ operations:
) as response:
extension_script = response.read().decode("utf-8")
with self.opener.open(
f"{self.base_url}/app.js?v=dual-seat-103", timeout=3
f"{self.base_url}/app.js?v=dual-seat-107", timeout=3
) as response:
app_script = response.read().decode("utf-8")
@@ -1442,6 +1460,61 @@ operations:
self.assertTrue(validated["command_plans"])
self.assertEqual(events[1]["payload"]["status"], "succeeded")
def test_stream_failure_uses_cli_outcome_and_updates_display_sync_terminal_state(
self,
) -> None:
request = urllib.request.Request(
f"{self.base_url}/api/requests",
method="POST",
headers={
"Content-Type": "application/json",
"Accept": "application/x-ndjson",
},
data=json.dumps(
{
"utterance": "打开副驾座椅通风",
"provider": "local",
"conversation_id": "conv-e2e-stream-can-failure",
"execution_mode": "hardware",
"client_type": "web_console",
},
ensure_ascii=False,
).encode("utf-8"),
)
with patch.object(
self.service,
"process",
side_effect=CanGatewayError("VCI_OpenDevice failed"),
):
with self.opener.open(request, timeout=3) as response:
events = [
json.loads(line)
for line in response.read().decode("utf-8").splitlines()
if line.strip()
]
with self.opener.open(
f"{self.base_url}/api/vehicle-display-sync", timeout=3
) as response:
snapshot = json.loads(response.read().decode("utf-8"))
self.assertEqual(events, [{
"type": "error",
"error": "can_transport_failed",
"message": "VCI_OpenDevice failed",
"outcome": "failed_or_blocked",
}])
self.assertEqual(snapshot["task"]["status"], "failed")
self.assertEqual(snapshot["task"]["utterance"], "打开副驾座椅通风")
self.assertEqual(
snapshot["task"]["error"],
{
"code": "can_transport_failed",
"message": "VCI_OpenDevice failed",
"outcome": "failed_or_blocked",
},
)
def test_frontend_suite_covers_every_physical_seat_capability(self) -> None:
suite_path = (
REPOSITORY_ROOT