完善模式卡片、NFC及多设备控制流程

This commit is contained in:
minghanchen
2026-09-01 16:18:57 +08:00
parent ca26259e63
commit cbf230182d
15 changed files with 1092 additions and 191 deletions
+220 -3
View File
@@ -795,15 +795,16 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
conversation_id: str | None,
execution_mode: str,
) -> dict[str, object]:
"""Execute already validated local-rule infrared capabilities."""
"""Execute already validated infrared capabilities."""
capabilities = [
{
**dict(entry["capability"]),
"parse_source": "local_rule",
**capability,
"parse_source": str(capability.get("parse_source") or "local_rule"),
}
for entry in entries
if isinstance(entry, dict) and isinstance(entry.get("capability"), dict)
for capability in [dict(entry["capability"])]
]
if not capabilities:
raise ExtensionIntegrationError("没有可执行的已学习红外能力。")
@@ -1454,6 +1455,14 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
)
def _handle_mode_profile_post(self, path: str) -> None:
execute_match = re.fullmatch(
r"/api/mode-profiles/(?P<mode_id>[a-z0-9][a-z0-9_-]{0,63})/execute",
path,
)
if execute_match is not None:
self._handle_mode_profile_execute(execute_match.group("mode_id"))
return
mode_id = path.removeprefix("/api/mode-profiles/").strip()
if not mode_id:
self._json(
@@ -1482,6 +1491,214 @@ class SeatDemoHandler(BaseHTTPRequestHandler):
HTTPStatus.BAD_REQUEST,
)
def _saved_mode_infrared_entries(
self,
profile: object,
) -> list[dict[str, object]]:
command_flow = getattr(profile, "command_flow", None)
if not isinstance(command_flow, dict):
return []
configurations = command_flow.get("function_configs") or {}
extension_config = None
if isinstance(configurations, dict):
extension_config = configurations.get("self_extension")
if not isinstance(extension_config, dict):
extension_config = next(
(
config
for config in configurations.values()
if isinstance(config, dict)
and str((config.get("spec") or {}).get("domain"))
== "self_extension"
),
None,
)
operations = (
(extension_config.get("spec") or {}).get("operations") or []
if isinstance(extension_config, dict)
else []
)
if not operations:
return []
snapshot = self.nfc.snapshot()
card = snapshot.get("card") if isinstance(snapshot, dict) else None
active_uid = re.sub(
r"[^0-9A-Fa-f]",
"",
str((card or {}).get("uid") or ""),
).upper()
if not active_uid or str((card or {}).get("action") or "present") != "present":
raise ExtensionIntegrationError(
"该模式包含 NFC 红外指令,请先放置生成此模式时绑定的 NFC 卡。"
)
catalog = self.extension.list_peripheral_capabilities()
entries: list[dict[str, object]] = []
for index, operation in enumerate(operations):
if not isinstance(operation, dict) or operation.get("capability") != "infrared.emit":
continue
parameters = operation.get("parameters") or {}
target = operation.get("target") or {}
try:
slot = int(parameters.get("slot"))
except (TypeError, ValueError) as exc:
raise ExtensionIntegrationError(
"已保存模式包含无效的红外槽位。"
) from exc
device_id = str(target.get("device") or "").strip()
capability = next(
(
item
for item in catalog
if item.get("status") == "learned"
and item.get("enabled", True) is True
and int(item.get("slot") or 0) == slot
and str(item.get("device_id") or "") == device_id
and re.sub(
r"[^0-9A-Fa-f]",
"",
str(item.get("card_uid") or ""),
).upper()
== active_uid
),
None,
)
if capability is None:
raise ExtensionIntegrationError(
f"当前 NFC 未授权模式中的红外命令:{device_id} / IR {slot}。"
)
entries.append(
{
"index": index,
"clause": str(parameters.get("action") or f"IR {slot}"),
"capability": {
**capability,
"parse_source": "saved_mode_command_flow",
},
}
)
return entries
def _handle_mode_profile_execute(self, mode_id: str) -> None:
utterance = ""
source = "web_console_mode_card"
try:
payload = self._read_json_body(max_bytes=16 * 1024)
vehicle_id = str(payload.get("vehicle_id", "")) or None
profile = self.service.get_mode_profile_card(mode_id, vehicle_id)
utterance = profile.name
source = str(payload.get("client_type") or source)
execution_mode = str(payload.get("execution_mode", "dry_run"))
control_transport = str(
payload.get("control_transport", "wired_can")
)
conversation_id = (
str(payload["conversation_id"])
if payload.get("conversation_id")
else None
)
hardware_client_id = (
str(payload["hardware_client_id"])
if payload.get("hardware_client_id")
else None
)
self.vehicle_display_sync.update_task(
status="running",
utterance=utterance,
source=source,
)
# Validate the complete saved flow, including the live NFC
# binding, before any transport is allowed to execute.
entries = self._saved_mode_infrared_entries(profile)
can_result = self.service.execute_mode_profile_card(
mode_id,
conversation_id=conversation_id,
execution_mode=execution_mode,
approval_token=os.getenv(
"COGNITIVE_AGENT_HARDWARE_APPROVAL_TOKEN"
),
vehicle_id=vehicle_id,
simulation_fault=str(payload.get("simulation_fault", "normal")),
hardware_client_id=hardware_client_id,
hardware_access_bypass=self._is_unproxied_loopback_request(),
control_transport=control_transport,
)
if entries:
infrared_result = self._execute_local_peripheral_actions(
utterance,
entries,
provider="saved_mode_command_flow",
conversation_id=conversation_id,
execution_mode=execution_mode,
)
result = self._merge_can_and_peripheral_results(
utterance,
can_result,
infrared_result,
entries,
)
result["provider_used"] = "saved_mode_command_flow"
result["orchestration_route"] = "saved_mode_command_flow"
result["mode_profile"] = profile.public_dict()
self.service.persist_composed_result(result)
else:
result = can_result
self.vehicle_display_sync.update_task(
status=str(result.get("status") or "succeeded"),
utterance=utterance,
source=source,
result=result,
)
self._json(result, HTTPStatus.OK)
except KeyError:
self._json(
{"error": "not_found", "message": "未找到需要执行的模式卡片。"},
HTTPStatus.NOT_FOUND,
)
except HardwareAccessRequiredError as exc:
self._json(
{
"error": "hardware_access_required",
"message": str(exc),
"access_state": exc.state,
},
HTTPStatus.FORBIDDEN,
)
except HardwareBusyError as exc:
self._json(
{
"error": "hardware_busy",
"message": str(exc),
"retry_after_seconds": exc.retry_after_seconds,
},
HTTPStatus.CONFLICT,
)
except (HardwareSafetyError, CanGatewayError, GatewayError) as exc:
if utterance:
self.vehicle_display_sync.update_task(
status="failed",
utterance=utterance,
source=source,
error={"code": "mode_execution_failed", "message": str(exc)},
)
self._json(
{"error": "mode_execution_failed", "message": str(exc)},
HTTPStatus.SERVICE_UNAVAILABLE,
)
except (ExtensionIntegrationError, ValueError, json.JSONDecodeError) as exc:
if utterance:
self.vehicle_display_sync.update_task(
status="failed",
utterance=utterance,
source=source,
error={"code": "invalid_mode_command_flow", "message": str(exc)},
)
self._json(
{"error": "invalid_mode_command_flow", "message": str(exc)},
HTTPStatus.BAD_REQUEST,
)
def _handle_extension_post(self, path: str) -> None:
action_map = {
"/api/extension/gscu/info": "info",