feat: update CAN integration and T2G seat firmware
This commit is contained in:
@@ -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)。
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user