feat: synchronize CH1 vehicle control and feedback

This commit is contained in:
vee
2026-08-11 10:42:13 +08:00
parent 3701192a11
commit 32379a1573
50 changed files with 13636 additions and 736 deletions
File diff suppressed because it is too large Load Diff
+32 -177
View File
@@ -8,6 +8,7 @@ from unittest.mock import MagicMock, patch
from cognitive_agent.application.domain_runtime import ConfiguredDomainRuntime
from cognitive_agent.application.mode_profile import ModeProfileRegistry
from cognitive_agent.application.seat_service import REPOSITORY_ROOT, SeatDemoService
from cognitive_agent.infrastructure.can.hardware import HardwareSafetyError
class ConfiguredDomainRuntimeTests(unittest.TestCase):
@@ -311,7 +312,7 @@ class MultiDomainServiceTests(unittest.TestCase):
},
)
def test_hardware_orchestration_uses_physical_passenger_seat_and_virtual_roof(
def test_hardware_orchestration_rejects_ch0_seat_and_virtual_roof_before_send(
self,
) -> None:
model_output = {
@@ -338,112 +339,32 @@ class MultiDomainServiceTests(unittest.TestCase):
"decision_summary": ["执行副驾与天幕联合模式。"],
"confidence": 0.96,
}
physical_state = {
"position": {
"longitudinal": {"position_pct": 51, "position_valid": True},
"cushion_tilt": {"position_pct": 11, "position_valid": True},
"backrest_recline": {"position_pct": 54, "position_valid": True},
"legrest_height": {"position_pct": 0, "position_valid": True},
},
"ventilation": {"state": "off", "level": 0},
"heating": {"state": "off", "level": 0},
"captured_at": "2026-07-31T04:00:00Z",
"complete": True,
"missing": [],
}
def execute_hardware(plan, config, devices):
del plan, devices
operation = config["spec"]["operations"][0]
return {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "ExecutionReport",
"metadata": {
"id": "report-hardware-test",
"name": "T2G seat hardware execution report",
"version": "1.0.0",
"request_id": config["metadata"]["request_id"],
"labels": {"simulation": "false"},
},
"spec": {
"mode": "hil",
"status": "succeeded",
"started_at": "2026-07-31T04:00:00Z",
"finished_at": "2026-07-31T04:00:01Z",
"physical_state": physical_state,
"results": [
{
"ref": operation["id"],
"domain": "seat",
"status": "succeeded",
"message": "physical target reached",
"telemetry": {
"motion_axis": "backrest_recline",
"effective_position_pct": 54,
"position_valid": True,
"simulated": False,
},
}
],
},
}
hardware_executor = MagicMock()
hardware_executor.execute.side_effect = execute_hardware
initial_driver_backrest = self.service.conversations.snapshot(
"conv-mixed-hardware-success"
)["devices"]["driver"]["position"]["backrest_recline"]["position_pct"]
with (
patch.object(self.service.openai, "interpret", return_value=model_output),
patch.object(type(self.service.hardware_settings), "authorize"),
patch.object(
type(self.service.hardware_settings),
"validate_function_config",
),
patch.object(
self.service,
"_new_hardware_executor",
return_value=hardware_executor,
) as executor_factory,
):
result = self.service.process(
"副驾靠背调到55%,天幕透光率调到20%",
"openai",
"conv-mixed-hardware-success",
execution_mode="hardware",
approval_token="test-token",
vehicle_id="denza_n7",
)
with self.assertRaisesRegex(
HardwareSafetyError,
"hardware mode refuses virtual or unverified vehicle actions",
):
self.service.process(
"副驾靠背调到55%,天幕透光率调到20%",
"openai",
"conv-mixed-hardware-success",
execution_mode="hardware",
approval_token="test-token",
vehicle_id="denza_n7",
)
self.assertEqual(result["status"], "succeeded")
self.assertEqual(result["execution_report"]["spec"]["mode"], "hil")
self.assertEqual(
result["execution_report"]["metadata"]["labels"]["simulation"],
"mixed",
)
executor_factory.assert_called_once_with()
hardware_executor.execute.assert_called_once()
by_domain = {
item["domain"]: item
for item in result["execution_report"]["spec"]["results"]
}
self.assertFalse(by_domain["seat"]["telemetry"]["simulated"])
self.assertTrue(by_domain["roof"]["telemetry"]["simulated"])
self.assertEqual(
result["actuator_state"]["passenger"]["position"][
"backrest_recline"
]["position_pct"],
54,
)
self.assertEqual(
result["actuator_state"]["driver"]["position"]["backrest_recline"][
"position_pct"
],
initial_driver_backrest,
)
self.assertEqual(result["domain_state"]["roof"]["transmittance_pct"], 20)
executor_factory.assert_not_called()
hardware_executor.execute.assert_not_called()
def test_hardware_orchestration_failure_still_synchronizes_physical_seat(
def test_hardware_orchestration_rejects_ch0_motion_before_executor_creation(
self,
) -> None:
model_output = {
@@ -471,95 +392,29 @@ class MultiDomainServiceTests(unittest.TestCase):
"confidence": 0.96,
}
hardware_executor = MagicMock()
def execute_hardware(plan, config, devices):
del plan, devices
return {
"apiVersion": "cognitive-agent/v1alpha1",
"kind": "ExecutionReport",
"metadata": {
"id": "report-hardware-failure-test",
"name": "T2G seat hardware execution report",
"version": "1.0.0",
"request_id": config["metadata"]["request_id"],
"labels": {"simulation": "false"},
},
"spec": {
"mode": "hil",
"status": "failed",
"started_at": "2026-07-31T04:00:00Z",
"finished_at": "2026-07-31T04:00:01Z",
"physical_state": {
"position": {
"longitudinal": {
"position_pct": 63,
"position_valid": True,
}
},
"captured_at": "2026-07-31T04:00:01Z",
"complete": False,
"missing": ["cushion_tilt", "backrest_recline", "legrest_height"],
},
"results": [
{
"ref": config["spec"]["operations"][0]["id"],
"domain": "seat",
"status": "failed",
"message": "Motor0 TIMEOUT",
"telemetry": {"simulated": False},
}
],
},
}
hardware_executor.execute.side_effect = execute_hardware
initial_roof = self.service.conversations.snapshot(
"conv-mixed-hardware-failure"
)["domain_devices"]["roof"]["transmittance_pct"]
with (
patch.object(self.service.openai, "interpret", return_value=model_output),
patch.object(type(self.service.hardware_settings), "authorize"),
patch.object(
type(self.service.hardware_settings),
"validate_function_config",
),
patch.object(
self.service,
"_new_hardware_executor",
return_value=hardware_executor,
),
) as executor_factory,
):
result = self.service.process(
"副驾水平移到100%,天幕透光率调到20%",
"openai",
"conv-mixed-hardware-failure",
execution_mode="hardware",
approval_token="test-token",
vehicle_id="denza_n7",
)
with self.assertRaisesRegex(
HardwareSafetyError,
"hardware mode refuses virtual or unverified vehicle actions",
):
self.service.process(
"副驾水平移到100%,天幕透光率调到20%",
"openai",
"conv-mixed-hardware-failure",
execution_mode="hardware",
approval_token="test-token",
vehicle_id="denza_n7",
)
self.assertEqual(result["status"], "partially_succeeded")
self.assertEqual(result["execution_report"]["spec"]["status"], "failed")
self.assertEqual(
result["actuator_state"]["passenger"]["position"]["longitudinal"][
"position_pct"
],
63,
)
self.assertEqual(
result["actuator_state"]["passenger"]["position"]["longitudinal"][
"feedback_source"
],
"physical_can",
)
self.assertEqual(
result["domain_state"]["roof"]["transmittance_pct"],
20,
)
self.assertNotEqual(
result["domain_state"]["roof"]["transmittance_pct"],
initial_roof,
)
executor_factory.assert_not_called()
hardware_executor.execute.assert_not_called()
def test_reference_mode_library_modes_execute_for_denza(self) -> None:
model_output = {
@@ -8,6 +8,11 @@ from pathlib import Path
from unittest.mock import patch
from cognitive_agent.application.seat_service import REPOSITORY_ROOT, SeatDemoService
from cognitive_agent.infrastructure.can.hardware import (
HardwareSafetyError,
SeatHardwareSettings,
VehicleCanSettings,
)
SUITE_PATH = REPOSITORY_ROOT / "tests" / "fixtures" / "seat_frontend_hil_suite.json"
@@ -185,6 +190,35 @@ class SeatFrontendInputCoverageTests(unittest.TestCase):
self.assertEqual(result["function_configs"], {})
self.assertIsNone(result["execution_report"])
def test_mixed_ch1_driver_and_ch0_passenger_hardware_scope_fails_closed(self) -> None:
utterances = (
"打开主驾和副驾座椅通风",
"seat driver vent low, seat passenger vent low",
)
for index, utterance in enumerate(utterances):
with self.subTest(utterance=utterance):
with (
patch.object(self.service, "_interpret") as interpret,
patch.object(SeatHardwareSettings, "authorize") as seat_auth,
patch.object(VehicleCanSettings, "authorize") as vehicle_auth,
patch.object(VehicleCanSettings, "bridge") as bridge,
):
with self.assertRaisesRegex(
HardwareSafetyError, "mixed CH1.*CH0"
):
self.service.process(
utterance,
provider="local",
conversation_id=f"conv-mixed-scope-{index}",
execution_mode="hardware",
vehicle_id=self.suite["vehicle_id"],
)
interpret.assert_not_called()
seat_auth.assert_not_called()
vehicle_auth.assert_not_called()
bridge.assert_not_called()
if __name__ == "__main__":
unittest.main()