feat: synchronize CH1 vehicle control and feedback
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user