优化前端,优化前端指令,优化自定义控制
This commit is contained in:
+175
-36
@@ -175,6 +175,108 @@ operations:
|
||||
finally:
|
||||
self.service.mode_profiles = original_registry
|
||||
|
||||
def test_mode_card_editor_endpoint_persists_generated_can_flow(self) -> None:
|
||||
original_registry = self.service.mode_profiles
|
||||
original_artifact_root = self.service.artifact_root
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
base = Path(directory)
|
||||
preset_root = base / "presets"
|
||||
user_root = base / "user-modes"
|
||||
preset = preset_root / "vehicles" / "denza_n7" / "office.yaml"
|
||||
preset.parent.mkdir(parents=True)
|
||||
preset.write_text(
|
||||
"""
|
||||
id: office
|
||||
name: 办公模式
|
||||
scope:
|
||||
vehicle_ids: [denza_n7]
|
||||
operations:
|
||||
- domain: climate
|
||||
capability: climate.set_temperature
|
||||
target_scope: passenger
|
||||
parameters:
|
||||
temperature_c: 24
|
||||
""".strip()
|
||||
+ "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
request_id = "req-mode-editor-api-001"
|
||||
instruction = "副驾空调温度设置为24度"
|
||||
artifact_root = base / "runs"
|
||||
request_dir = artifact_root / request_id
|
||||
request_dir.mkdir(parents=True)
|
||||
result = {
|
||||
"request_id": request_id,
|
||||
"utterance": instruction,
|
||||
"execution_mode": "dry_run",
|
||||
"control_transport": "wireless_mqtt",
|
||||
"function_configs": {
|
||||
"native_climate": {
|
||||
"spec": {
|
||||
"domain": "climate",
|
||||
"operations": [
|
||||
{
|
||||
"id": "passenger_temperature",
|
||||
"capability": "climate.set_temperature",
|
||||
"target": {"zone": "passenger"},
|
||||
"parameters": {"temperature_c": 24},
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
"command_plans": {
|
||||
"native_climate": {
|
||||
"spec": {
|
||||
"commands": [
|
||||
{
|
||||
"message": "Native_0x1DE",
|
||||
"signals": {
|
||||
"frame_id": "0x1DE",
|
||||
"data_hex": "00 00 00 00 00 18 00 00",
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"execution_report": {"spec": {"results": []}},
|
||||
}
|
||||
(request_dir / "result.json").write_text(
|
||||
json.dumps(result, ensure_ascii=False), encoding="utf-8"
|
||||
)
|
||||
self.service.mode_profiles = ModeProfileRegistry(
|
||||
preset_root, user_root=user_root
|
||||
)
|
||||
self.service.artifact_root = artifact_root
|
||||
try:
|
||||
request = urllib.request.Request(
|
||||
f"{self.base_url}/api/mode-profiles/office",
|
||||
method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps(
|
||||
{
|
||||
"name": "专注办公模式",
|
||||
"instruction": instruction,
|
||||
"source_request_id": request_id,
|
||||
"vehicle_id": "denza_n7",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
).encode("utf-8"),
|
||||
)
|
||||
with self.opener.open(request, timeout=3) as response:
|
||||
payload = json.loads(response.read().decode("utf-8"))
|
||||
self.assertEqual(response.status, 200)
|
||||
self.assertEqual(payload["storage"], "persistent_yaml")
|
||||
self.assertEqual(payload["mode_profile"]["origin"], "customized")
|
||||
self.assertEqual(payload["mode_profile"]["instruction"], instruction)
|
||||
self.assertTrue(
|
||||
(user_root / "vehicles" / "denza_n7" / "user" / "office.yaml").is_file()
|
||||
)
|
||||
finally:
|
||||
self.service.mode_profiles = original_registry
|
||||
self.service.artifact_root = original_artifact_root
|
||||
|
||||
def test_head_unit_display_sync_tracks_tasks_and_usb_catalog(self) -> None:
|
||||
task_request = urllib.request.Request(
|
||||
f"{self.base_url}/api/requests",
|
||||
@@ -230,18 +332,18 @@ 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-90", timeout=3) as response:
|
||||
with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-101", 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-8", timeout=3
|
||||
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-27", timeout=3
|
||||
f"{self.base_url}/console-v2.js?v=sync-layout-41", timeout=3
|
||||
) as response:
|
||||
console_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/console-v2.css?v=sync-layout-27", timeout=3
|
||||
f"{self.base_url}/console-v2.css?v=sync-layout-40", timeout=3
|
||||
) as response:
|
||||
console_styles = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
@@ -263,7 +365,7 @@ operations:
|
||||
with self.opener.open(f"{self.base_url}/api/runtime", timeout=3) as response:
|
||||
runtime = json.loads(response.read().decode("utf-8"))
|
||||
|
||||
self.assertIn("Cognitive Agent", html)
|
||||
self.assertIn("511工程-智能座舱", html)
|
||||
self.assertIn('id="seatTwinCanvas"', html)
|
||||
self.assertIn('data-vehicle-model="byd-tang-2024-reference"', html)
|
||||
self.assertIn("整车孪生界面", html)
|
||||
@@ -276,12 +378,15 @@ 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-90"', html)
|
||||
self.assertIn('href="/styles.css?v=dual-seat-10"', html)
|
||||
self.assertIn('src="/gesture-control.js?v=android-pad-8"', html)
|
||||
self.assertIn('src="/app.js?v=dual-seat-101"', 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-27"', html)
|
||||
self.assertIn('href="/console-v2.css?v=sync-layout-27"', html)
|
||||
self.assertIn('src="/console-v2.js?v=sync-layout-41"', html)
|
||||
self.assertIn('href="/console-v2.css?v=sync-layout-40"', html)
|
||||
self.assertIn('<body class="console-v2">', html)
|
||||
self.assertIn('id="v2BootScreen"', html)
|
||||
self.assertIn('正在加载车机控制与实时状态', html)
|
||||
self.assertIn('src="/self-extension.js?v=sync-layout-26"', html)
|
||||
self.assertIn('href="/self-extension.css?v=sync-layout-26"', html)
|
||||
self.assertNotIn("self-calibration", html)
|
||||
@@ -291,7 +396,7 @@ operations:
|
||||
self.assertIn('aria-label="执行车舱控制指令"', html)
|
||||
self.assertNotIn('"admin"', console_script)
|
||||
self.assertNotIn("v2RoleSelect", console_script)
|
||||
self.assertIn('id="v2SidebarToggle"', console_script)
|
||||
self.assertIn('sidebarToggle.id = "v2SidebarToggle"', console_script)
|
||||
self.assertIn('id = "v2ModuleSwitcher"', console_script)
|
||||
self.assertIn(
|
||||
'panelHeading.classList.add("v2-operation-heading", "v2-module-heading")',
|
||||
@@ -316,28 +421,33 @@ operations:
|
||||
)
|
||||
self.assertIn('511工程-智能座舱', console_script)
|
||||
self.assertIn('网页控制链路', console_script)
|
||||
self.assertIn('<span>Hi,</span><strong>user</strong>', console_script)
|
||||
self.assertIn('["self-orchestration", "self-extension"]', console_script)
|
||||
self.assertNotIn('label: "自诊断"', console_script)
|
||||
self.assertIn('<small>手势模型</small>', console_script)
|
||||
self.assertIn('id="v2GestureModelValue">MediaPipe GR</strong>', console_script)
|
||||
self.assertIn('id="v2GestureModelNote">Tasks Vision · 0.10.14</small>', console_script)
|
||||
self.assertIn('<span class="v2-field-caption">配置信息</span>', console_script)
|
||||
self.assertIn('state.id = "v2VehicleState"', console_script)
|
||||
self.assertIn('fetch("/api/extension/health"', console_script)
|
||||
self.assertNotIn('runtimeControls.id = "v2RuntimeControls"', console_script)
|
||||
self.assertIn('function prepareRuntimeControls(originalForm)', console_script)
|
||||
self.assertIn('executionField.hidden = false', console_script)
|
||||
self.assertIn('insertVisibleControl(executionField, "v2-sync-execution-field")', console_script)
|
||||
self.assertIn('insertVisibleControl(simulationFaultField, "v2-sync-simulation-fault-field")', console_script)
|
||||
self.assertNotIn('class="v2-profile"', console_script)
|
||||
self.assertNotIn('class="v2-control-switch"', console_script)
|
||||
self.assertNotIn('class="v2-config-stack"', console_script)
|
||||
self.assertNotIn('id="v2GestureModelValue"', console_script)
|
||||
self.assertNotIn('id="v2VehicleState"', console_script)
|
||||
self.assertIn('actionRail.append(cameraToggle)', console_script)
|
||||
self.assertIn("function enhanceConfiguredDropdowns()", console_script)
|
||||
self.assertNotIn("function enhanceConfiguredDropdowns()", console_script)
|
||||
self.assertIn("function updateModeEdgeHints()", console_script)
|
||||
self.assertIn('classList.toggle("v2-twin-active", definition.index === 0)', console_script)
|
||||
self.assertIn('label: "执行结果与硬件回读"', console_script)
|
||||
self.assertIn("function requestUserSafeExecutionMode()", console_script)
|
||||
self.assertIn('const dryRunButton = $(\'[data-execution-mode="dry_run"]\')', console_script)
|
||||
self.assertIn('if (!dryRunButton.classList.contains("active")) dryRunButton.click()', console_script)
|
||||
self.assertIn('userTargetText.textContent = "执行目标:虚拟 CAN"', console_script)
|
||||
self.assertIn("user 固定使用虚拟 CAN 安全执行环境", console_script)
|
||||
self.assertNotIn("user 演示固定使用真实台架", console_script)
|
||||
self.assertIn("function keepRunButtonAvailable()", console_script)
|
||||
self.assertIn('window.setTimeout(() => $("#v2BootScreen")?.remove(), 220)', console_script)
|
||||
self.assertNotIn("function requestUserSafeExecutionMode()", console_script)
|
||||
self.assertNotIn('dryRunButton.click()', console_script)
|
||||
self.assertIn('const DEFAULT_EXECUTION_MODE = "hardware"', app_script)
|
||||
self.assertIn('preferredExecutionMode: DEFAULT_EXECUTION_MODE', app_script)
|
||||
self.assertIn('selectExecutionMode(modeAvailable ? preferredMode : "dry_run", { remember: false })', app_script)
|
||||
self.assertIn('class="mode-option active" id="hardwareMode"', html)
|
||||
self.assertIn('grid-template-columns: clamp(245px, 18.5vw, 310px)', console_styles)
|
||||
self.assertIn('.v2-control-switch {', console_styles)
|
||||
self.assertNotIn('.v2-control-switch {', console_styles)
|
||||
self.assertIn('.v2-module-switcher {', console_styles)
|
||||
self.assertIn('grid-template-columns: repeat(2, minmax(0, 1fr))', console_styles)
|
||||
self.assertIn(
|
||||
@@ -364,7 +474,7 @@ operations:
|
||||
self.assertIn('width: clamp(48px, 45%, 72px)', console_styles)
|
||||
self.assertIn('font-weight: 800', console_styles)
|
||||
self.assertIn('white-space: nowrap', console_styles)
|
||||
self.assertIn('.v2-gesture-model-field', console_styles)
|
||||
self.assertNotIn('.v2-gesture-model-field', console_styles)
|
||||
self.assertIn('.v2-inline-gesture .v2-camera-toggle', console_styles)
|
||||
self.assertIn('.v2-inline-gesture .v2-gesture-action-rail', console_styles)
|
||||
self.assertIn('grid-template-rows: 42px 10px 42px', console_styles)
|
||||
@@ -373,9 +483,8 @@ operations:
|
||||
self.assertIn('.v2-inline-gesture #gestureStopButton', console_styles)
|
||||
self.assertIn('.v2-inline-gesture .v2-gesture-result-stack', console_styles)
|
||||
self.assertIn('.v2-inline-gesture .v2-gesture-execution-slot', console_styles)
|
||||
self.assertIn('grid-template-columns: 1fr;', console_styles)
|
||||
self.assertIn('.v2-page-rail', console_styles)
|
||||
self.assertIn('.v2-dropdown-menu', console_styles)
|
||||
self.assertNotIn('.v2-dropdown-menu', console_styles)
|
||||
self.assertIn('.v2-edge-fade-left', console_styles)
|
||||
self.assertIn('.v2-edge-fade-right', console_styles)
|
||||
self.assertIn('.mode-group-divider', console_styles)
|
||||
@@ -385,8 +494,8 @@ operations:
|
||||
self.assertIn('.joint-seat-panel .thermal-states', console_styles)
|
||||
self.assertIn('.joint-twin-shell .domain-readouts', console_styles)
|
||||
self.assertIn('grid-template-columns: repeat(2, minmax(0, 1fr))', console_styles)
|
||||
self.assertIn('.v2-config-section > .v2-field-caption', console_styles)
|
||||
self.assertIn('.console-v2 .v2-config-card {', console_styles)
|
||||
self.assertNotIn('.v2-config-section', console_styles)
|
||||
self.assertNotIn('.console-v2 .v2-config-card {', console_styles)
|
||||
self.assertIn('.console-v2.v2-sidebar-collapsed .workspace', console_styles)
|
||||
self.assertNotIn('id="hardwareAccessField"', html)
|
||||
self.assertNotIn('id="hardwareClientLabel"', html)
|
||||
@@ -406,8 +515,14 @@ operations:
|
||||
self.assertIn('data-control-transport="wireless_mqtt"', html)
|
||||
self.assertIn('control_transport: state.controlTransport', app_script)
|
||||
self.assertIn('const transportField = $("#vehicleTransportField", originalForm)', console_script)
|
||||
self.assertIn('configStack.append(transportField)', console_script)
|
||||
self.assertNotIn('runtimeControls.append(transportField)', console_script)
|
||||
self.assertIn('insertVisibleControl(transportField, "v2-sync-transport-field")', console_script)
|
||||
self.assertIn('.v2-sync-execution-field .execution-mode-options', console_styles)
|
||||
self.assertIn('grid-template-columns: repeat(3, minmax(0, 1fr))', console_styles)
|
||||
self.assertNotIn('class="v2-sync-protocol"', console_script)
|
||||
self.assertIn('.console-v2 .v2-transport-field .vehicle-transport-options', console_styles)
|
||||
self.assertIn('.v2-sync-transport-field .mode-option', console_styles)
|
||||
self.assertIn('overflow-wrap: anywhere', console_styles)
|
||||
self.assertIn('button.disabled = false', app_script)
|
||||
self.assertIn('button.setAttribute("aria-pressed", String(selected))', app_script)
|
||||
self.assertNotIn(
|
||||
@@ -497,7 +612,7 @@ operations:
|
||||
self.assertIn('client_type: "web_console"', app_script)
|
||||
self.assertIn('canvas.addEventListener("webglcontextlost"', app_script)
|
||||
self.assertIn('"cognitive-agent:module-change"', app_script)
|
||||
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v179"', service_worker)
|
||||
self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v202"', 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)
|
||||
@@ -544,6 +659,13 @@ operations:
|
||||
self.assertIn('id="modeGallery"', html)
|
||||
self.assertIn('id="modeGalleryGroups"', html)
|
||||
self.assertIn("全部功能模式", html)
|
||||
self.assertIn('id="modeEditorBackdrop"', html)
|
||||
self.assertIn('id="modeEditorName"', html)
|
||||
self.assertIn('id="modeEditorInstruction"', html)
|
||||
self.assertIn('id="modeEditorExecute"', html)
|
||||
self.assertIn('id="modeEditorComplete"', html)
|
||||
self.assertIn('id="modeEditorFlowPreview"', html)
|
||||
self.assertIn('class="mode-editor-flow-preview empty"', html)
|
||||
self.assertIn('id="saveModeSource"', html)
|
||||
self.assertNotIn("主驾 24℃", html)
|
||||
populate_modes = app_script[
|
||||
@@ -568,6 +690,17 @@ operations:
|
||||
self.assertIn('deleteUserModeProfile(deleteButton.dataset.modeDelete)', app_script)
|
||||
self.assertIn('method: "DELETE"', app_script)
|
||||
self.assertIn('deleteButton.dataset.modeDelete = mode.id', app_script)
|
||||
self.assertIn('editButton.dataset.modeEdit = mode.id', app_script)
|
||||
self.assertIn("function openModeEditor", app_script)
|
||||
self.assertIn("function executeModeEditorInstruction", app_script)
|
||||
self.assertIn("function completeModeEditor", app_script)
|
||||
self.assertIn("function modeEditorReadableCommands", app_script)
|
||||
self.assertIn("function modeEditorMessagesForOperation", app_script)
|
||||
self.assertIn("function modeEditorAppendCommandCard", app_script)
|
||||
self.assertIn('title.textContent = flow?.control_transport === "wireless_mqtt" ? "MQTT 消息序列"', app_script)
|
||||
self.assertIn("message.payload", app_script)
|
||||
self.assertIn("source_request_id: editor.sourceRequestId", app_script)
|
||||
self.assertIn('method: "POST"', app_script)
|
||||
self.assertIn('data-domain-panel="climate"', html)
|
||||
self.assertIn('data-domain-panel="roof"', html)
|
||||
self.assertIn('data-domain-panel="ambient_light"', html)
|
||||
@@ -624,7 +757,10 @@ operations:
|
||||
self.assertNotIn('id="transmittanceValue"', html)
|
||||
self.assertNotIn('id="roofMotorState"', html)
|
||||
self.assertIn('id="ambientBrightness"', html)
|
||||
self.assertIn("坐垫脚托夹角133度", html)
|
||||
self.assertNotIn('class="example-group"', html)
|
||||
self.assertNotIn('id="safetyStrip"', html)
|
||||
self.assertNotIn('class="pipeline"', html)
|
||||
self.assertNotIn('class="topbar"', html)
|
||||
self.assertNotIn('data-axis="height"', html)
|
||||
self.assertEqual(runtime["domain"], "cabin")
|
||||
self.assertEqual(
|
||||
@@ -632,6 +768,7 @@ operations:
|
||||
(
|
||||
runtime["hardware"]["configured"]
|
||||
or runtime["vehicle_can"]["configured"]
|
||||
or runtime["vehicle_mqtt"]["configured"]
|
||||
),
|
||||
)
|
||||
self.assertEqual(runtime["hardware"]["supported_targets"], ["front_right"])
|
||||
@@ -686,11 +823,13 @@ operations:
|
||||
for item in runtime["capabilities"]
|
||||
if item["name"].startswith("seat.")
|
||||
and not item["name"].startswith("seat.set_driver_")
|
||||
and item["name"] != "seat.recall_driver_memory_native"
|
||||
]
|
||||
driver_seat_capabilities = [
|
||||
item
|
||||
for item in runtime["capabilities"]
|
||||
if item["name"].startswith("seat.set_driver_")
|
||||
or item["name"] == "seat.recall_driver_memory_native"
|
||||
]
|
||||
self.assertTrue(passenger_seat_capabilities)
|
||||
self.assertTrue(
|
||||
@@ -713,7 +852,7 @@ operations:
|
||||
) as response:
|
||||
extension_script = response.read().decode("utf-8")
|
||||
with self.opener.open(
|
||||
f"{self.base_url}/app.js?v=dual-seat-90", timeout=3
|
||||
f"{self.base_url}/app.js?v=dual-seat-101", timeout=3
|
||||
) as response:
|
||||
app_script = response.read().decode("utf-8")
|
||||
|
||||
@@ -1152,7 +1291,7 @@ operations:
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps(
|
||||
{
|
||||
"utterance": "打开副驾座椅通风",
|
||||
"utterance": "打开副驾座椅通风,同时播放音乐",
|
||||
"provider": "openai",
|
||||
"conversation_id": "conv-e2e-gpt-fallback",
|
||||
"execution_mode": "dry_run",
|
||||
|
||||
Reference in New Issue
Block a user