feat: update CAN integration and T2G seat firmware

This commit is contained in:
vee
2026-08-01 15:55:21 +08:00
parent 8cda026ac2
commit 4aded5c21d
1443 changed files with 1007122 additions and 4978 deletions
@@ -2,4 +2,4 @@
公共 CAN Gateway 负责加载 DBC、编码 message/signal、选择总线、限速、发送、订阅回读和记录审计。默认实现使用虚拟总线;只有平台 manifest、部署环境和权限同时允许时才能启用硬件 backend。
T2G 座椅台架新增 `PythonCanBus` 和 `ZlgCanBridgeBus`。后者使用 ZCANPRO 自带的 32 位 Python 隔离加载 `zlgcan.dll`,主服务仍运行在 64 位 Python。硬件执行要求环境开关、请求授权令牌、固件 profile、适配器依赖、目标座椅和标定状态全部通过;详见 [T2G 台架接入](../../../docs/workflows/t2g-seat-hardware-bench.md)。
T2G 座椅台架新增 `PythonCanBus` 和 `ZlgCanBridgeBus`。后者使用 ZCANPRO 自带的 32 位 Python 隔离加载 `zlgcan.dll`,主服务仍运行在 64 位 Python。硬件执行要求环境开关、固件 profile、适配器依赖、目标座椅和标定状态全部通过;生产默认还要求请求授权令牌,本地受控调试可显式关闭令牌门禁。详见 [T2G 台架接入](../../../docs/workflows/t2g-seat-hardware-bench.md)。
@@ -10,7 +10,7 @@ from pathlib import Path
from typing import Any, Callable
from cognitive_agent.infrastructure.firmware import (
EXPECTED_IDENTITY_PAYLOAD,
FIRMWARE_PROTOCOL_SIGNATURE,
FirmwareProgrammingError,
T2GFirmwareGuard,
T2GSeatProgrammer,
@@ -21,17 +21,14 @@ from .gateway import CanBus, CanGatewayError, PythonCanBus, ZlgCanBridgeBus
TRUE_VALUES = {"1", "true", "yes", "on"}
VALID_PROFILES = {"combined"}
SHARED_PHYSICAL_TARGETS = {
("front_left", "driver"),
("front_right", "passenger"),
}
SUPPORTED_PHYSICAL_TARGET = ("front_right", "passenger")
CAPABILITY_PROFILE = {
"seat.set_heating": "combined",
"seat.set_ventilation": "combined",
"seat.set_longitudinal_position": "combined",
"seat.set_cushion_tilt": "combined",
"seat.set_backrest_recline": "combined",
"seat.set_legrest_height": "combined",
"seat.set_legrest_extension": "combined",
}
@@ -76,6 +73,7 @@ class SeatHardwareSettings:
repository_root: Path
enabled: bool
approval_required: bool
approval_token: str
profiles: frozenset[str]
backend: str
@@ -87,13 +85,13 @@ class SeatHardwareSettings:
zlg_python: Path
zlg_dll: Path
bridge_script: Path
sdk_root: Path
cspybat: Path
auto_flash_enabled: bool
auto_home_enabled: bool
home_timeout_s: int
firmware_probe_timeout_ms: int
firmware_boot_wait_ms: int
firmware_program_timeout_ms: int
allow_unverified_longitudinal: bool
thermal_timeout_ms: int
ack_timeout_ms: int
firmware_images: dict[str, Path]
@@ -102,12 +100,11 @@ class SeatHardwareSettings:
@classmethod
def from_env(cls, repository_root: Path) -> "SeatHardwareSettings":
sdk_root = Path(
os.getenv(
"COGNITIVE_AGENT_T2G_SDK_ROOT",
r"C:\Users\D30042075206\Desktop\T2G_Sample_Driver_Library_7.9.0",
)
).expanduser()
hardware_enabled = _env_bool("COGNITIVE_AGENT_HARDWARE_EXECUTION_ENABLED")
auto_flash_enabled = _env_bool(
"COGNITIVE_AGENT_SEAT_AUTO_FLASH_ENABLED",
default=hardware_enabled,
)
backend = os.getenv("COGNITIVE_AGENT_SEAT_CAN_BACKEND", "zlgcan").strip().lower()
if backend not in {"zlgcan", "python-can"}:
raise HardwareSafetyError(
@@ -135,7 +132,11 @@ class SeatHardwareSettings:
}
return cls(
repository_root=repository_root,
enabled=_env_bool("COGNITIVE_AGENT_HARDWARE_EXECUTION_ENABLED"),
enabled=hardware_enabled,
approval_required=_env_bool(
"COGNITIVE_AGENT_HARDWARE_APPROVAL_TOKEN_REQUIRED",
default=True,
),
approval_token=os.getenv("COGNITIVE_AGENT_HARDWARE_APPROVAL_TOKEN", ""),
profiles=_profiles_from_env(),
backend=backend,
@@ -162,16 +163,19 @@ class SeatHardwareSettings:
/ "infrastructure"
/ "can"
/ "zlgcan_bridge.py",
sdk_root=sdk_root,
cspybat=Path(
os.getenv(
"COGNITIVE_AGENT_IAR_CSPYBAT",
r"D:\Program Files\IAR Systems\Embedded Workbench 9.2\common\bin\cspybat.exe",
)
),
auto_flash_enabled=_env_bool("COGNITIVE_AGENT_SEAT_AUTO_FLASH_ENABLED"),
auto_flash_enabled=auto_flash_enabled,
auto_home_enabled=_env_bool("COGNITIVE_AGENT_SEAT_AUTO_HOME_ENABLED"),
home_timeout_s=_env_int(
"COGNITIVE_AGENT_SEAT_HOME_TIMEOUT_S", 120
),
firmware_probe_timeout_ms=_env_int(
"COGNITIVE_AGENT_SEAT_FIRMWARE_PROBE_TIMEOUT_MS", 750
"COGNITIVE_AGENT_SEAT_FIRMWARE_PROBE_TIMEOUT_MS", 1_200
),
firmware_boot_wait_ms=_env_int(
"COGNITIVE_AGENT_SEAT_FIRMWARE_BOOT_WAIT_MS", 1_500
@@ -179,9 +183,6 @@ class SeatHardwareSettings:
firmware_program_timeout_ms=_env_int(
"COGNITIVE_AGENT_SEAT_FIRMWARE_PROGRAM_TIMEOUT_MS", 120_000
),
allow_unverified_longitudinal=_env_bool(
"COGNITIVE_AGENT_ENABLE_UNVERIFIED_LONGITUDINAL"
),
thermal_timeout_ms=_env_int(
"COGNITIVE_AGENT_SEAT_THERMAL_TIMEOUT_MS", 2_000
),
@@ -207,7 +208,7 @@ class SeatHardwareSettings:
def configured(self) -> bool:
return bool(
self.enabled
and self.approval_token
and (not self.approval_required or self.approval_token)
and self.profiles
and self.adapter_configured
and all(self.firmware_images[item].is_file() for item in self.profiles)
@@ -228,20 +229,25 @@ class SeatHardwareSettings:
raise HardwareSafetyError(
"hardware execution is disabled; set COGNITIVE_AGENT_HARDWARE_EXECUTION_ENABLED=true"
)
if not self.approval_token:
raise HardwareSafetyError(
"COGNITIVE_AGENT_HARDWARE_APPROVAL_TOKEN is not configured"
)
if not supplied_token or not hmac.compare_digest(
supplied_token, self.approval_token
):
raise HardwareSafetyError("hardware approval token is invalid")
if self.approval_required:
if not self.approval_token:
raise HardwareSafetyError(
"COGNITIVE_AGENT_HARDWARE_APPROVAL_TOKEN is not configured"
)
if not supplied_token or not hmac.compare_digest(
supplied_token, self.approval_token
):
raise HardwareSafetyError("hardware approval token is invalid")
if not self.profiles:
raise HardwareSafetyError(
"the combined IAR seat-controller firmware profile is not selected"
)
if self.bitrate != 500_000:
raise HardwareSafetyError("the verified T2G seat protocol requires 500000 bit/s")
if not 10 <= self.home_timeout_s <= 600:
raise HardwareSafetyError(
"COGNITIVE_AGENT_SEAT_HOME_TIMEOUT_S must be between 10 and 600"
)
if not self.adapter_configured:
raise HardwareSafetyError("the configured CAN adapter runtime is not ready")
missing = [
@@ -268,10 +274,9 @@ class SeatHardwareSettings:
capability = str(operation.get("capability", ""))
target = operation.get("target", {})
target_key = (str(target.get("zone", "")), str(target.get("instance", "")))
if target_key not in SHARED_PHYSICAL_TARGETS:
if target_key != SUPPORTED_PHYSICAL_TARGET:
raise HardwareSafetyError(
"the T2G bench accepts driver/front_left and passenger/front_right; "
"both aliases operate the same physical seat actuators"
"the T2G bench accepts only passenger/front_right seat operations"
)
try:
profile = CAPABILITY_PROFILE[capability]
@@ -284,20 +289,7 @@ class SeatHardwareSettings:
f"{capability} requires the '{profile}' firmware profile"
)
parameters = dict(operation.get("parameters", {}))
previous = physical_requests.get(capability)
if previous is not None and previous != parameters:
raise HardwareSafetyError(
f"driver and passenger requests conflict for shared physical capability {capability}"
)
physical_requests[capability] = parameters
if (
capability == "seat.set_longitudinal_position"
and not self.allow_unverified_longitudinal
):
raise HardwareSafetyError(
"longitudinal Motor3 is unwired/unverified; explicitly set "
"COGNITIVE_AGENT_ENABLE_UNVERIFIED_LONGITUDINAL=true only after calibration"
)
def bus_factory(self) -> Callable[[], CanBus]:
if self.backend == "zlgcan":
@@ -362,7 +354,7 @@ class SeatHardwareSettings:
return {
"enabled": self.enabled,
"configured": self.configured,
"approval_required": True,
"approval_required": self.approval_required,
"approval_configured": bool(self.approval_token),
"platform": "t2g_seat_bench",
"backend": self.backend,
@@ -372,20 +364,23 @@ class SeatHardwareSettings:
"frame_format": "classic_can_11_bit",
"firmware_profiles": sorted(self.profiles),
"firmware_images": images,
"firmware_status": "identity_verified_before_each_hardware_request",
"firmware_identity": EXPECTED_IDENTITY_PAYLOAD.hex(" ").upper(),
"firmware_status": "passive_protocol_verified_before_each_hardware_request",
"firmware_protocol_signature": FIRMWARE_PROTOCOL_SIGNATURE,
"firmware_source": "repository",
"auto_flash_enabled": self.auto_flash_enabled,
"programmer_configured": self.programmer_configured,
"programmer_backend": "iar_cspy_cmsisdap",
"supported_targets": ["front_left", "front_right"],
"supported_targets": ["front_right"],
"supported_seat_instances": ["passenger"],
"physical_target": "t2g_primary_seat",
"target_aliasing": "driver_and_passenger_share_one_physical_actuator_group",
"unverified_longitudinal_enabled": self.allow_unverified_longitudinal,
"target_aliasing": "disabled_front_passenger_only",
"auto_home_enabled": self.auto_home_enabled,
"home_timeout_s": self.home_timeout_s,
"limitations": [
"hardware execution requires the unified Seat_Controller_demo image on one development board",
"driver and passenger commands are aliases for the same physical motor/heater/fan group",
"Motor3 longitudinal motion stays blocked until explicitly enabled after wiring and calibration",
"cushion tilt has no physical actuator in Seat_Move_demo",
"all seat capabilities are restricted to passenger/front_right",
"M0/M1/M2/M3 map to horizontal, seat-pan, backrest and legrest on one physical seat",
"known-range homing can move an invalid axis to its near endpoint and remains separately disabled by default",
"current diagnostics use conservative bench thresholds and do not replace external power limiting",
],
}
@@ -92,7 +92,9 @@ class Driver:
)
if not self.device:
raise RuntimeError(
"ZCAN_OpenDevice failed; connect the adapter and close ZCANPRO first"
f"ZCAN_OpenDevice failed for device_type={args.device_type}, "
f"device_index={args.device_index}; connect the adapter and close "
"ZCANPRO first"
)
self.channel = None
try: