feat: complete dual-seat whole-vehicle orchestration
This commit is contained in:
@@ -124,14 +124,20 @@ class ZlgChannelConfig:
|
||||
"""Configuration for one channel on a shared ZLG CAN FD adapter."""
|
||||
|
||||
channel: int
|
||||
frame_format: str = "can_fd"
|
||||
arbitration_bitrate: int = 500_000
|
||||
data_bitrate: int = 2_000_000
|
||||
can_fd_iso: bool = True
|
||||
termination_enabled: bool = True
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.frame_format not in {"classic_can", "can_fd"}:
|
||||
raise ValueError("frame_format must be classic_can or can_fd")
|
||||
|
||||
def public(self) -> dict[str, Any]:
|
||||
return {
|
||||
"channel": self.channel,
|
||||
"frame_format": self.frame_format,
|
||||
"arbitration_bitrate": self.arbitration_bitrate,
|
||||
"data_bitrate": self.data_bitrate,
|
||||
"can_fd_iso": self.can_fd_iso,
|
||||
@@ -158,6 +164,7 @@ class ZlgCanBridgeProcess:
|
||||
if len(channel_ids) != len(set(channel_ids)):
|
||||
raise CanGatewayError("ZLG channel numbers must be unique")
|
||||
self._channels = frozenset(channel_ids)
|
||||
self._channel_configs = {item.channel: item for item in channels}
|
||||
command = [
|
||||
str(python_executable),
|
||||
str(bridge_script),
|
||||
@@ -193,7 +200,7 @@ class ZlgCanBridgeProcess:
|
||||
def channel_bus(self, channel: int) -> "ZlgCanChannelBus":
|
||||
if channel not in self._channels:
|
||||
raise CanGatewayError(f"ZLG channel {channel} was not initialized")
|
||||
return ZlgCanChannelBus(self, channel)
|
||||
return ZlgCanChannelBus(self, channel, self._channel_configs[channel])
|
||||
|
||||
def _read_response(self) -> dict[str, Any]:
|
||||
if self._process.stdout is None:
|
||||
@@ -246,9 +253,15 @@ class ZlgCanBridgeProcess:
|
||||
class ZlgCanChannelBus:
|
||||
"""Channel-scoped view over a shared :class:`ZlgCanBridgeProcess`."""
|
||||
|
||||
def __init__(self, bridge: ZlgCanBridgeProcess, channel: int):
|
||||
def __init__(
|
||||
self,
|
||||
bridge: ZlgCanBridgeProcess,
|
||||
channel: int,
|
||||
config: ZlgChannelConfig,
|
||||
):
|
||||
self._bridge = bridge
|
||||
self.channel = channel
|
||||
self.config = config
|
||||
|
||||
def send(
|
||||
self,
|
||||
@@ -258,6 +271,10 @@ class ZlgCanChannelBus:
|
||||
is_fd: bool = False,
|
||||
bitrate_switch: bool = False,
|
||||
) -> CanFrame:
|
||||
if is_fd and self.config.frame_format != "can_fd":
|
||||
raise CanGatewayError(
|
||||
f"ZLG channel {self.channel} is configured for classic CAN"
|
||||
)
|
||||
response = self._bridge._request(
|
||||
{
|
||||
"operation": "send",
|
||||
@@ -279,6 +296,10 @@ class ZlgCanChannelBus:
|
||||
def receive(
|
||||
self, timeout_ms: int, *, include_fd: bool = False
|
||||
) -> CanFrame | None:
|
||||
if include_fd and self.config.frame_format != "can_fd":
|
||||
raise CanGatewayError(
|
||||
f"ZLG channel {self.channel} is configured for classic CAN"
|
||||
)
|
||||
requested_ms = max(0, int(timeout_ms))
|
||||
deadline = time.monotonic() + requested_ms / 1000
|
||||
while True:
|
||||
@@ -334,6 +355,7 @@ class ZlgCanBridgeBus(ZlgCanChannelBus):
|
||||
channels=[
|
||||
ZlgChannelConfig(
|
||||
channel=channel,
|
||||
frame_format="classic_can",
|
||||
arbitration_bitrate=bitrate,
|
||||
termination_enabled=termination_enabled,
|
||||
)
|
||||
|
||||
@@ -641,6 +641,7 @@ class VehicleCanSettings:
|
||||
def channel_config(self) -> ZlgChannelConfig:
|
||||
return ZlgChannelConfig(
|
||||
channel=self.channel,
|
||||
frame_format="can_fd",
|
||||
arbitration_bitrate=self.arbitration_bitrate,
|
||||
data_bitrate=self.data_bitrate,
|
||||
can_fd_iso=True,
|
||||
@@ -667,6 +668,8 @@ class VehicleCanSettings:
|
||||
"required_channel": self.required_channel,
|
||||
"channel_is_allowed": self.channel_is_allowed,
|
||||
"target_policy": "ch1_body_can_driver_only",
|
||||
"channel_role": "driver_front_left",
|
||||
"coexists_with_seat_channel": True,
|
||||
"forbidden_paths": ["ch0", "t2g_seat_bench", "passenger", "front_right"],
|
||||
"mode": 0,
|
||||
"work_mode": "normal",
|
||||
|
||||
@@ -119,6 +119,7 @@ class Driver:
|
||||
)
|
||||
)
|
||||
self.channels = {}
|
||||
self.channel_configs = {}
|
||||
try:
|
||||
for config in self._channel_configs(args):
|
||||
self._initialize_channel(config)
|
||||
@@ -134,6 +135,7 @@ class Driver:
|
||||
configs = [
|
||||
{
|
||||
"channel": args.channel,
|
||||
"frame_format": "can_fd",
|
||||
"arbitration_bitrate": args.bitrate,
|
||||
"data_bitrate": args.data_bitrate,
|
||||
"can_fd_iso": True,
|
||||
@@ -145,6 +147,9 @@ class Driver:
|
||||
channel_ids = [int(item["channel"]) for item in configs]
|
||||
if len(channel_ids) != len(set(channel_ids)):
|
||||
raise ValueError("channel numbers must be unique")
|
||||
for item in configs:
|
||||
if item.get("frame_format", "can_fd") not in ("classic_can", "can_fd"):
|
||||
raise ValueError("frame_format must be classic_can or can_fd")
|
||||
return configs
|
||||
|
||||
def _set_value(self, path, value):
|
||||
@@ -156,6 +161,7 @@ class Driver:
|
||||
|
||||
def _initialize_channel(self, config):
|
||||
channel_id = int(config["channel"])
|
||||
frame_format = str(config.get("frame_format", "can_fd"))
|
||||
arbitration_bitrate = int(config.get("arbitration_bitrate", 500000))
|
||||
data_bitrate = int(config.get("data_bitrate", 2000000))
|
||||
can_fd_iso = bool(config.get("can_fd_iso", True))
|
||||
@@ -181,6 +187,7 @@ class Driver:
|
||||
if not handle:
|
||||
raise RuntimeError("ZCAN_InitCAN failed for channel {}".format(channel_id))
|
||||
self.channels[channel_id] = handle
|
||||
self.channel_configs[channel_id] = frame_format
|
||||
self._set_value(
|
||||
prefix + "/initenal_resistance",
|
||||
1 if bool(config.get("termination_enabled", True)) else 0,
|
||||
@@ -266,7 +273,12 @@ class Driver:
|
||||
if not 0 <= arbitration_id <= 0x7FF:
|
||||
raise ValueError("only 11-bit standard CAN identifiers are allowed")
|
||||
handle = self._handle(channel)
|
||||
frame_format = self.channel_configs[int(channel)]
|
||||
if is_fd:
|
||||
if frame_format != "can_fd":
|
||||
raise ValueError(
|
||||
"channel {} is configured for classic CAN".format(channel)
|
||||
)
|
||||
valid_lengths = set(range(9)) | {12, 16, 20, 24, 32, 48, 64}
|
||||
if len(data) not in valid_lengths:
|
||||
raise ValueError(
|
||||
@@ -320,6 +332,10 @@ class Driver:
|
||||
|
||||
def receive(self, channel, timeout_ms, include_fd=False):
|
||||
handle = self._handle(channel)
|
||||
if include_fd and self.channel_configs[int(channel)] != "can_fd":
|
||||
raise ValueError(
|
||||
"channel {} is configured for classic CAN".format(channel)
|
||||
)
|
||||
deadline = time.monotonic() + max(0, int(timeout_ms)) / 1000.0
|
||||
while True:
|
||||
classic = ZcanReceiveData()
|
||||
@@ -350,6 +366,7 @@ class Driver:
|
||||
for channel_id in sorted(self.channels, reverse=True):
|
||||
handle = self.channels.pop(channel_id)
|
||||
self.dll.ZCAN_ResetCAN(handle)
|
||||
self.channel_configs.clear()
|
||||
if self.device:
|
||||
self.dll.ZCAN_CloseDevice(self.device)
|
||||
self.device = None
|
||||
|
||||
Reference in New Issue
Block a user