feat: integrate cabin CAN and verified dynamic seat modes

This commit is contained in:
vee
2026-08-04 17:51:38 +08:00
parent 155fbb59ba
commit dc7c91800e
99 changed files with 51897 additions and 3618 deletions
@@ -27,7 +27,14 @@ SEAT_MOTION_CAPABILITIES = {
SEAT_RELATIVE_PHYSICAL_PARAMETERS = {"distance_mm", "angle_delta_deg"}
SAVE_PRESET_CAPABILITY = "seat.save_posture_preset"
APPLY_PRESET_CAPABILITY = "seat.apply_posture_preset"
DYNAMIC_MODE_CAPABILITY = "seat.start_dynamic_mode"
DYNAMIC_MODE_STOP_CAPABILITY = "seat.stop_dynamic_mode"
DYNAMIC_MODE_MAX_RUNTIME_MS = 600_000
HEX_COLOR = re.compile(r"^#[0-9A-Fa-f]{6}$")
NATIVE_CAN_ONLY_CAPABILITIES = {
"climate.set_control_mode",
"roof.set_shade_motion",
}
class ModelPlanAdapter:
@@ -69,7 +76,7 @@ class ModelPlanAdapter:
)
continue
domain, availability, capability = registered
domain, domain_availability, capability = registered
parameters = {
str(item["name"]): item.get("value")
for item in request.get("parameters", [])
@@ -93,7 +100,26 @@ class ModelPlanAdapter:
)
continue
if availability != "executable":
availability = (
"native_can"
if capability_name in NATIVE_CAN_ONLY_CAPABILITIES
else domain_availability
)
native_only_request = (
domain == "seat"
and capability_name in SEAT_CAPABILITIES
and (
request.get("target_scope") in {"left_rear", "right_rear"}
or parameters.get("level") in {4, 5}
)
) or (
capability_name == "climate.set_outlet_mode"
and (
request.get("target_scope") == "vehicle"
or parameters.get("mode") not in {"face", "feet"}
)
)
if availability != "executable" or native_only_request:
values = [value for value in parameters.values() if value is not None]
deferred.append(
{
@@ -104,6 +130,11 @@ class ModelPlanAdapter:
"requested_value": values[0] if len(values) == 1 else None,
"parameters": parameters,
"reason": request["reason"],
"execution_backend": (
"native_can"
if availability == "native_can" or native_only_request
else availability
),
}
)
continue
@@ -182,6 +213,20 @@ class ModelPlanAdapter:
}
)
continue
if capability_name == DYNAMIC_MODE_STOP_CAPABILITY:
dynamic_action = self._dynamic_mode_stop_action(
index, request, parameters, ambiguities
)
if dynamic_action is not None:
actions.append(dynamic_action)
continue
if capability_name == DYNAMIC_MODE_CAPABILITY:
dynamic_action = self._dynamic_mode_action(
index, request, parameters, ambiguities
)
if dynamic_action is not None:
actions.append(dynamic_action)
continue
action = self._seat_action(
index, request, parameters, ambiguities, context, vehicle_profile
)
@@ -352,6 +397,79 @@ class ModelPlanAdapter:
return None
return {"target": target, "preset_name": preset_name.strip()}
@staticmethod
def _dynamic_mode_action(
index: int,
request: dict[str, Any],
parameters: dict[str, Any],
ambiguities: list[dict[str, str]],
) -> dict[str, Any] | None:
target = request.get("target_scope")
if target != "passenger":
ambiguities.append(
{
"field": f"requests[{index}].target_scope",
"question": "当前动态座椅模式仅控制副驾。",
}
)
return None
mode_id = parameters.get("mode_id")
if (
not isinstance(mode_id, (int, float))
or isinstance(mode_id, bool)
or not float(mode_id).is_integer()
or int(mode_id) not in {1, 2}
):
ambiguities.append(
{
"field": f"requests[{index}].parameters.mode_id",
"question": "动态座椅模式编号必须是 1(木马模式)或 2(动态摇篮)。",
}
)
return None
normalized_mode_id = int(mode_id)
return {
"action_type": "dynamic_mode",
"target": target,
"mode_id": normalized_mode_id,
"mode_name": (
"木马模式"
if normalized_mode_id == 1
else "动态摇篮模式"
),
"repeat": "until_stopped_or_timeout",
"max_runtime_ms": DYNAMIC_MODE_MAX_RUNTIME_MS,
}
@staticmethod
def _dynamic_mode_stop_action(
index: int,
request: dict[str, Any],
parameters: dict[str, Any],
ambiguities: list[dict[str, str]],
) -> dict[str, Any] | None:
target = request.get("target_scope")
if target != "passenger":
ambiguities.append(
{
"field": f"requests[{index}].target_scope",
"question": "当前动态座椅模式仅控制副驾。",
}
)
return None
if parameters:
ambiguities.append(
{
"field": f"requests[{index}].parameters",
"question": "停止动态座椅模式不接受额外参数。",
}
)
return None
return {
"action_type": "dynamic_mode_stop",
"target": target,
}
@staticmethod
def _find_preset(
presets: list[dict[str, Any]],