70 lines
3.0 KiB
Python
70 lines
3.0 KiB
Python
"""Translate the N7 passenger-seat preset contract into local seat operations."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from collections.abc import Mapping
|
||
from typing import Any
|
||
|
||
from cognitive_agent.application.vehicle_profile import VehicleProfile
|
||
|
||
|
||
# N7 targets_pulse uses the ECU's M0, M1, M2, M3 ordering.
|
||
N7_SEAT_PRESET_AXES = (
|
||
"longitudinal", "cushion_tilt", "backrest_recline", "legrest_height",
|
||
)
|
||
N7_SEAT_PRESET_CAPABILITIES = (
|
||
"seat.set_longitudinal_position", "seat.set_cushion_tilt",
|
||
"seat.set_backrest_recline", "seat.set_legrest_height",
|
||
)
|
||
|
||
|
||
def expand_n7_seat_preset(
|
||
action: Mapping[str, Any], vehicle: VehicleProfile,
|
||
) -> list[dict[str, Any]] | None:
|
||
"""Expand an immediate preset; retain unrelated/timed actions for N7 routing.
|
||
|
||
The existing seat contract accepts whole percentages. Retain the source
|
||
pulses in the stored card, and round only at this execution boundary.
|
||
Never round a nonzero pulse target into the ECU's explicit homing command.
|
||
"""
|
||
if action.get("capability") != "local_scene.seat_preset":
|
||
return None
|
||
if (
|
||
vehicle.id != "denza_n7"
|
||
or action.get("domain") != "local_vehicle"
|
||
or action.get("target_scope") not in {"vehicle", "passenger", "front_right"}
|
||
or action.get("operation") != "set"
|
||
):
|
||
raise ValueError("车端座椅预设仅支持腾势 N7 副驾的绝对位置设置。")
|
||
parameters = action.get("parameters")
|
||
if not isinstance(parameters, Mapping) or set(parameters) != {"targets_pulse"}:
|
||
raise ValueError("车端座椅预设必须且只能包含 targets_pulse 参数。")
|
||
targets = parameters["targets_pulse"]
|
||
if not isinstance(targets, list) or len(targets) != 4:
|
||
raise ValueError("车端座椅预设需要按 M0–M3 顺序提供四个脉冲目标。")
|
||
operations = []
|
||
for axis, capability, pulse in zip(
|
||
N7_SEAT_PRESET_AXES, N7_SEAT_PRESET_CAPABILITIES, targets, strict=True
|
||
):
|
||
motor = vehicle.seat_motors.get(axis)
|
||
maximum = motor.hall_total_pulses if motor is not None else 0
|
||
if maximum <= 0:
|
||
raise ValueError(f"车端座椅预设缺少 {axis} 的有效霍尔标定。")
|
||
if isinstance(pulse, bool) or not isinstance(pulse, int) or not 0 <= pulse <= maximum:
|
||
raise ValueError(f"车端座椅预设 {axis} 脉冲目标必须为 0–{maximum} 的整数。")
|
||
position_pct = round(pulse * 100 / maximum)
|
||
if 0 < pulse < maximum:
|
||
position_pct = max(1, min(99, position_pct))
|
||
operations.append({
|
||
"domain": "seat",
|
||
"capability": capability,
|
||
"target_scope": "passenger",
|
||
"operation": "set",
|
||
"parameters": {"position_pct": position_pct},
|
||
"reason": f"车端副驾预设 {axis}:{pulse}/{maximum} 脉冲转换为 {position_pct}%。",
|
||
})
|
||
# A delayed vehicle-owned action must not become an immediate PC movement.
|
||
if action.get("at_ms", 0) != 0:
|
||
return None
|
||
return operations
|