优化了副驾座椅控制
This commit is contained in:
@@ -57,6 +57,9 @@ from cognitive_agent.application.mqtt_ota_seat_executor import ( # noqa: E402
|
||||
MqttOtaSeatExecutor,
|
||||
)
|
||||
from cognitive_agent.application.native_can_intent import merge_native_can_intent # noqa: E402
|
||||
from cognitive_agent.application.direct_can_control import ( # noqa: E402
|
||||
build_direct_can_intent,
|
||||
)
|
||||
from cognitive_agent.application.native_can_relative import ( # noqa: E402
|
||||
NativeCanRelativeResolver,
|
||||
)
|
||||
@@ -1401,6 +1404,47 @@ class SeatDemoService:
|
||||
}
|
||||
return merged
|
||||
|
||||
def process_can_commands(
|
||||
self,
|
||||
commands: object,
|
||||
conversation_id: str | None = None,
|
||||
execution_mode: str = "dry_run",
|
||||
approval_token: str | None = None,
|
||||
vehicle_id: str | None = None,
|
||||
simulation_fault: str = "normal",
|
||||
*,
|
||||
hardware_client_id: str | None = None,
|
||||
hardware_access_bypass: bool = True,
|
||||
control_transport: str = WIRED_CAN_TRANSPORT,
|
||||
) -> dict[str, Any]:
|
||||
"""Compile and execute an exact UI command batch without intent parsing."""
|
||||
|
||||
resolved_conversation_id = conversation_id or f"conv-{uuid.uuid4().hex}"
|
||||
if not CONVERSATION_ID_PATTERN.fullmatch(resolved_conversation_id):
|
||||
raise ValueError(
|
||||
"conversation_id must start with conv- and contain only lowercase letters, numbers, or hyphens"
|
||||
)
|
||||
vehicle_profile = self.vehicle_profiles.get(vehicle_id)
|
||||
context = self._context_for_vehicle(
|
||||
self.conversations.snapshot(resolved_conversation_id),
|
||||
vehicle_profile,
|
||||
)
|
||||
direct_can_batch = build_direct_can_intent(commands, context)
|
||||
normalized_commands, _parsed = direct_can_batch
|
||||
return self.process(
|
||||
";".join(normalized_commands),
|
||||
provider="local",
|
||||
conversation_id=resolved_conversation_id,
|
||||
execution_mode=execution_mode,
|
||||
approval_token=approval_token,
|
||||
vehicle_id=vehicle_id,
|
||||
simulation_fault=simulation_fault,
|
||||
hardware_client_id=hardware_client_id,
|
||||
hardware_access_bypass=hardware_access_bypass,
|
||||
control_transport=control_transport,
|
||||
_direct_can_batch=direct_can_batch,
|
||||
)
|
||||
|
||||
def process(
|
||||
self,
|
||||
utterance: str,
|
||||
@@ -1420,6 +1464,7 @@ class SeatDemoService:
|
||||
_model_parse_source: str = "cloud_model",
|
||||
_allow_empty_control_result: bool = False,
|
||||
_on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
|
||||
_direct_can_batch: tuple[list[str], dict[str, Any]] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
control_transport = self._validate_control_transport(control_transport)
|
||||
# Creating a reusable scene is a configuration workflow. Synthesize
|
||||
@@ -1444,6 +1489,28 @@ class SeatDemoService:
|
||||
_model_parse_source,
|
||||
_allow_empty_control_result,
|
||||
_on_commands_validated,
|
||||
_direct_can_batch,
|
||||
)
|
||||
if control_transport == WIRELESS_MQTT_TRANSPORT:
|
||||
# The head unit owns both physical CAN channels in wireless mode.
|
||||
# Local T2G/ZLG hardware access, firmware profiles and the local
|
||||
# physical-bench lease are unrelated to MQTT relay execution.
|
||||
return self._process_once(
|
||||
utterance,
|
||||
provider,
|
||||
conversation_id,
|
||||
execution_mode,
|
||||
approval_token,
|
||||
vehicle_id,
|
||||
simulation_fault,
|
||||
reasoning_effort,
|
||||
control_transport,
|
||||
_local_preview,
|
||||
_excluded_control_signatures,
|
||||
_model_parse_source,
|
||||
_allow_empty_control_result,
|
||||
_on_commands_validated,
|
||||
_direct_can_batch,
|
||||
)
|
||||
if hardware_access_bypass or self._driver_only_vehicle_execution_active(
|
||||
control_transport
|
||||
@@ -1464,6 +1531,7 @@ class SeatDemoService:
|
||||
_model_parse_source,
|
||||
_allow_empty_control_result,
|
||||
_on_commands_validated,
|
||||
_direct_can_batch,
|
||||
)
|
||||
authorized_client_id = self.hardware_access.ensure_authorized(hardware_client_id)
|
||||
with self.hardware_execution.reserve():
|
||||
@@ -1483,6 +1551,7 @@ class SeatDemoService:
|
||||
_model_parse_source,
|
||||
_allow_empty_control_result,
|
||||
_on_commands_validated,
|
||||
_direct_can_batch,
|
||||
)
|
||||
|
||||
def _process_once(
|
||||
@@ -1501,6 +1570,7 @@ class SeatDemoService:
|
||||
model_parse_source: str = "cloud_model",
|
||||
allow_empty_control_result: bool = False,
|
||||
on_commands_validated: Callable[[dict[str, Any]], None] | None = None,
|
||||
direct_can_batch: tuple[list[str], dict[str, Any]] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
control_transport = self._validate_control_transport(control_transport)
|
||||
utterance = utterance.strip()
|
||||
@@ -1569,9 +1639,13 @@ class SeatDemoService:
|
||||
vehicle_profile,
|
||||
)
|
||||
context["mode_profiles"] = self.mode_profiles.public_modes(vehicle_profile.id)
|
||||
matched_modes = self.mode_profiles.match_text(
|
||||
utterance,
|
||||
vehicle_id=vehicle_profile.id,
|
||||
matched_modes = (
|
||||
[]
|
||||
if direct_can_batch is not None
|
||||
else self.mode_profiles.match_text(
|
||||
utterance,
|
||||
vehicle_id=vehicle_profile.id,
|
||||
)
|
||||
)
|
||||
traces: list[dict[str, Any]] = []
|
||||
result: dict[str, Any] = {
|
||||
@@ -1614,6 +1688,9 @@ class SeatDemoService:
|
||||
for item in context.get("presets", [])
|
||||
],
|
||||
"trace_events": traces,
|
||||
"can_commands": (
|
||||
list(direct_can_batch[0]) if direct_can_batch is not None else []
|
||||
),
|
||||
}
|
||||
|
||||
started = time.perf_counter()
|
||||
@@ -1652,40 +1729,45 @@ class SeatDemoService:
|
||||
result,
|
||||
traces,
|
||||
)
|
||||
parsed, provider_used, provider_notice = self._interpret(
|
||||
utterance,
|
||||
provider,
|
||||
context,
|
||||
vehicle_profile,
|
||||
reasoning_effort=reasoning_effort,
|
||||
local_preview=local_preview,
|
||||
model_parse_source=model_parse_source,
|
||||
)
|
||||
if self._is_dynamic_mode_stop_request(utterance):
|
||||
parsed = self._apply_dynamic_mode_stop_request(parsed, utterance)
|
||||
if direct_can_batch is not None:
|
||||
parsed = deepcopy(direct_can_batch[1])
|
||||
provider_used = "direct_can_control"
|
||||
provider_notice = None
|
||||
else:
|
||||
parsed = self._apply_mode_profiles(
|
||||
parsed, matched_modes, context, vehicle_profile
|
||||
parsed, provider_used, provider_notice = self._interpret(
|
||||
utterance,
|
||||
provider,
|
||||
context,
|
||||
vehicle_profile,
|
||||
reasoning_effort=reasoning_effort,
|
||||
local_preview=local_preview,
|
||||
model_parse_source=model_parse_source,
|
||||
)
|
||||
if self._is_dynamic_mode_stop_request(utterance):
|
||||
parsed = self._apply_dynamic_mode_stop_request(parsed, utterance)
|
||||
else:
|
||||
parsed = self._apply_mode_profiles(
|
||||
parsed, matched_modes, context, vehicle_profile
|
||||
)
|
||||
parsed = self._mark_control_parse_source(parsed, "local_rule")
|
||||
relative_resolution = (
|
||||
self.native_can_relative_resolver.resolve(utterance, context)
|
||||
if self.native_can_relative_resolver.matches(utterance)
|
||||
else None
|
||||
)
|
||||
parsed = merge_native_can_intent(
|
||||
parsed,
|
||||
utterance,
|
||||
relative_actions=(
|
||||
relative_resolution.actions if relative_resolution else None
|
||||
),
|
||||
relative_ambiguities=(
|
||||
relative_resolution.ambiguities if relative_resolution else None
|
||||
),
|
||||
relative_claimed_controls=(
|
||||
relative_resolution.claimed_controls if relative_resolution else None
|
||||
),
|
||||
)
|
||||
parsed = self._mark_control_parse_source(parsed, "local_rule")
|
||||
relative_resolution = (
|
||||
self.native_can_relative_resolver.resolve(utterance, context)
|
||||
if self.native_can_relative_resolver.matches(utterance)
|
||||
else None
|
||||
)
|
||||
parsed = merge_native_can_intent(
|
||||
parsed,
|
||||
utterance,
|
||||
relative_actions=(
|
||||
relative_resolution.actions if relative_resolution else None
|
||||
),
|
||||
relative_ambiguities=(
|
||||
relative_resolution.ambiguities if relative_resolution else None
|
||||
),
|
||||
relative_claimed_controls=(
|
||||
relative_resolution.claimed_controls if relative_resolution else None
|
||||
),
|
||||
)
|
||||
if local_preview:
|
||||
parsed["ambiguities"] = []
|
||||
parsed["unresolved_spans"] = []
|
||||
@@ -2136,7 +2218,8 @@ class SeatDemoService:
|
||||
compile_started = time.perf_counter()
|
||||
try:
|
||||
if hardware_request:
|
||||
self.hardware_settings.validate_function_config(function_config)
|
||||
if control_transport != WIRELESS_MQTT_TRANSPORT:
|
||||
self.hardware_settings.validate_function_config(function_config)
|
||||
command_plan = self.hardware_compiler.compile(
|
||||
function_config, context["devices"]
|
||||
)
|
||||
@@ -2199,7 +2282,11 @@ class SeatDemoService:
|
||||
"safety_check",
|
||||
"succeeded",
|
||||
(
|
||||
(
|
||||
"无线 MQTT CAN0 座椅语义白名单与 seat-ota 报文白名单已通过;"
|
||||
"本地 T2G 固件 profile、USB-CAN 与物理台架连接不参与校验。"
|
||||
if hardware_request
|
||||
and control_transport == WIRELESS_MQTT_TRANSPORT
|
||||
else (
|
||||
"硬件启用、授权令牌、固件 profile 与目标座椅配置门禁已通过;"
|
||||
if self.hardware_settings.approval_required
|
||||
else "硬件启用、调试免令牌、固件 profile 与目标座椅配置门禁已通过;"
|
||||
@@ -2220,15 +2307,26 @@ class SeatDemoService:
|
||||
simulation_fault if simulated_hardware_request else None
|
||||
),
|
||||
"position_calibration_gate": (
|
||||
"runtime_can_preflight" if hardware_request else "not_applicable"
|
||||
"remote_seat_ota_feedback"
|
||||
if hardware_request
|
||||
and control_transport == WIRELESS_MQTT_TRANSPORT
|
||||
else "runtime_can_preflight"
|
||||
if hardware_request
|
||||
else "not_applicable"
|
||||
),
|
||||
"mapping_status": (
|
||||
"protocol_hil_verified_combined_firmware_hil_pending"
|
||||
"mqtt_can0_seat_ota_whitelist"
|
||||
if hardware_request
|
||||
and control_transport == WIRELESS_MQTT_TRANSPORT
|
||||
else "protocol_hil_verified_combined_firmware_hil_pending"
|
||||
if hardware_request
|
||||
else "draft"
|
||||
),
|
||||
"firmware_profiles": (
|
||||
sorted(self.hardware_settings.profiles) if hardware_request else []
|
||||
sorted(self.hardware_settings.profiles)
|
||||
if hardware_request
|
||||
and control_transport != WIRELESS_MQTT_TRANSPORT
|
||||
else []
|
||||
),
|
||||
},
|
||||
)
|
||||
@@ -2249,9 +2347,6 @@ class SeatDemoService:
|
||||
command_plan, function_config, context["devices"]
|
||||
)
|
||||
if vehicle_safety_preflight is not None:
|
||||
execution_report["spec"]["vehicle_safety_preflight"] = (
|
||||
vehicle_safety_preflight
|
||||
)
|
||||
for item in execution_report["spec"].get("results", []):
|
||||
item.setdefault("telemetry", {})[
|
||||
"vehicle_safety_preflight"
|
||||
@@ -4045,10 +4140,15 @@ class SeatDemoService:
|
||||
|
||||
compile_started = time.perf_counter()
|
||||
plans: dict[str, dict[str, Any]] = {}
|
||||
wireless_hardware_request = (
|
||||
hardware_request
|
||||
and control_transport == WIRELESS_MQTT_TRANSPORT
|
||||
)
|
||||
for domain, config in configs.items():
|
||||
if domain == "seat":
|
||||
if hardware_request:
|
||||
self.hardware_settings.validate_function_config(config)
|
||||
if not wireless_hardware_request:
|
||||
self.hardware_settings.validate_function_config(config)
|
||||
plans[domain] = self.hardware_compiler.compile(
|
||||
config,
|
||||
context["devices"],
|
||||
@@ -4225,13 +4325,10 @@ class SeatDemoService:
|
||||
"labels": {
|
||||
"simulation": "mixed" if hardware_request else "true",
|
||||
"seat_feedback": (
|
||||
"wireless_mqtt_ch1"
|
||||
if (
|
||||
hardware_request
|
||||
and control_transport == WIRELESS_MQTT_TRANSPORT
|
||||
and native_config_keys
|
||||
and "seat" not in configs
|
||||
)
|
||||
"wireless_mqtt_ch0"
|
||||
if wireless_hardware_request and "seat" in configs
|
||||
else "wireless_mqtt_ch1"
|
||||
if wireless_hardware_request and native_config_keys
|
||||
else "physical_can_ch1"
|
||||
if hardware_request and native_config_keys and "seat" not in configs
|
||||
else "physical_can"
|
||||
@@ -4276,13 +4373,9 @@ class SeatDemoService:
|
||||
"execute",
|
||||
"succeeded" if execution_succeeded and not report_issues else "failed",
|
||||
(
|
||||
"无线 MQTT 白名单控制帧已提交;车辆实际状态以状态话题回读为准。"
|
||||
if (
|
||||
hardware_request
|
||||
and control_transport == WIRELESS_MQTT_TRANSPORT
|
||||
and native_config_keys
|
||||
and execution_succeeded
|
||||
)
|
||||
"无线 MQTT CAN0/CAN1 白名单控制帧已提交;"
|
||||
"车辆实际状态以状态话题回读为准。"
|
||||
if wireless_hardware_request and execution_succeeded
|
||||
else "CH1 车身 CAN 动作已按目标字段物理回读闭环执行。"
|
||||
if hardware_request and native_config_keys and execution_succeeded
|
||||
else "副驾座椅已按独立 T2G 物理 CAN 回读闭环执行。"
|
||||
|
||||
Reference in New Issue
Block a user