feat: complete dual-seat whole-vehicle orchestration
This commit is contained in:
@@ -13,8 +13,12 @@ from cognitive_agent.application.self_repair.healing_model import (
|
||||
public_scenarios,
|
||||
)
|
||||
from cognitive_agent.application.self_repair.service import SelfRepairService
|
||||
from cognitive_agent.application.self_repair.can_runtime import (
|
||||
StandaloneCanRuntimeManager,
|
||||
)
|
||||
from cognitive_agent.application.self_repair.signal_diagnostic import SignalDiagnosticModel
|
||||
from cognitive_agent.application.self_repair.zcan_bridge import ZcanBridgeManager
|
||||
from cognitive_agent.infrastructure.can.gateway import ZlgChannelConfig
|
||||
|
||||
|
||||
def test_six_production_cases_complete_the_shadow_repair_loop() -> None:
|
||||
@@ -104,3 +108,179 @@ def test_bridge_manager_does_not_restart_a_live_busy_request(tmp_path: Path) ->
|
||||
|
||||
assert manager._heartbeat_online() is False
|
||||
assert manager._bridge_busy_within_grace() is True
|
||||
|
||||
|
||||
def test_standalone_runtime_opens_one_owner_for_seat_and_vehicle_channels(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
required_version = "runtime-test-v1"
|
||||
bridge_script = tmp_path / "seat_bridge.py"
|
||||
bridge_script.write_text(
|
||||
"BRIDGE_VERSION = 'runtime-test-v1'\n"
|
||||
"def run_bridge(transport=None, stop_event=None):\n"
|
||||
" stop_event.wait(2.0)\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
python_executable = tmp_path / "python.exe"
|
||||
zlg_bridge_script = tmp_path / "zlg_bridge.py"
|
||||
dll_path = tmp_path / "zlgcan.dll"
|
||||
for path in (python_executable, zlg_bridge_script, dll_path):
|
||||
path.write_bytes(b"test")
|
||||
|
||||
owners = []
|
||||
|
||||
class FakeBus:
|
||||
def __init__(self, channel, config):
|
||||
self.channel = channel
|
||||
self.config = config
|
||||
|
||||
def send(self, *args, **kwargs):
|
||||
return None
|
||||
|
||||
def receive(self, *args, **kwargs):
|
||||
return None
|
||||
|
||||
class FakeOwner:
|
||||
def __init__(self, **kwargs):
|
||||
self.kwargs = kwargs
|
||||
self.closed = False
|
||||
self.configs = {item.channel: item for item in kwargs["channels"]}
|
||||
owners.append(self)
|
||||
|
||||
def channel_bus(self, channel):
|
||||
return FakeBus(channel, self.configs[channel])
|
||||
|
||||
def close(self):
|
||||
self.closed = True
|
||||
|
||||
manager = StandaloneCanRuntimeManager(
|
||||
tmp_path / "status.json",
|
||||
bridge_script,
|
||||
required_version=required_version,
|
||||
python_executable=python_executable,
|
||||
zlg_bridge_script=zlg_bridge_script,
|
||||
dll_path=dll_path,
|
||||
device_type=41,
|
||||
device_index=0,
|
||||
channels=[
|
||||
ZlgChannelConfig(channel=0, frame_format="classic_can"),
|
||||
ZlgChannelConfig(channel=1, frame_format="can_fd"),
|
||||
],
|
||||
seat_channel=0,
|
||||
poll_s=0.01,
|
||||
process_factory=FakeOwner,
|
||||
)
|
||||
try:
|
||||
assert manager.configured is True
|
||||
assert manager.channel_bus(1, timeout_s=1.0).channel == 1
|
||||
manager.ensure_async()
|
||||
assert len(owners) == 1
|
||||
assert [
|
||||
item.frame_format for item in owners[0].kwargs["channels"]
|
||||
] == ["classic_can", "can_fd"]
|
||||
assert manager.snapshot()["backend"] == "standalone_zlg"
|
||||
finally:
|
||||
manager.stop()
|
||||
|
||||
assert owners[0].closed is True
|
||||
|
||||
|
||||
def _qualified_runtime_status(*, build_id: int = 16, frame_age_s: float = 0.0):
|
||||
now = time.time()
|
||||
frame_epoch = now - frame_age_s
|
||||
return {
|
||||
"bridge_version": "runtime-test-v1",
|
||||
"heartbeat_epoch": now,
|
||||
"current_request_id": None,
|
||||
"system": {"firmware_revision": 15},
|
||||
"firmware_identity": {
|
||||
"firmware_build_id": build_id,
|
||||
"qualified": build_id == 16,
|
||||
},
|
||||
"frame_epochs": {
|
||||
frame_id: frame_epoch
|
||||
for frame_id in (
|
||||
"0x301", "0x302", "0x303", "0x304", "0x305", "0x306", "0x307"
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_standalone_runtime_rejects_stale_protocol_and_old_firmware_build(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
manager = StandaloneCanRuntimeManager(
|
||||
tmp_path / "status.json",
|
||||
tmp_path / "seat_bridge.py",
|
||||
required_version="runtime-test-v1",
|
||||
python_executable=tmp_path / "python.exe",
|
||||
zlg_bridge_script=tmp_path / "zlg_bridge.py",
|
||||
dll_path=tmp_path / "zlgcan.dll",
|
||||
device_type=41,
|
||||
device_index=0,
|
||||
channels=[ZlgChannelConfig(channel=0, frame_format="classic_can")],
|
||||
seat_channel=0,
|
||||
expected_firmware_revision=15,
|
||||
expected_firmware_build_id=16,
|
||||
)
|
||||
|
||||
manager.status_path.write_text(
|
||||
json.dumps(_qualified_runtime_status(frame_age_s=5.0)), encoding="utf-8"
|
||||
)
|
||||
ready, reason, _ = manager._protocol_status()
|
||||
assert ready is False
|
||||
assert "过期" in reason
|
||||
|
||||
manager.status_path.write_text(
|
||||
json.dumps(_qualified_runtime_status(build_id=15)), encoding="utf-8"
|
||||
)
|
||||
ready, reason, _ = manager._protocol_status()
|
||||
assert ready is False
|
||||
assert "build" in reason
|
||||
|
||||
|
||||
def test_standalone_runtime_restores_repository_firmware_before_request(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
class FakeProgrammer:
|
||||
def __init__(self):
|
||||
self.calls = 0
|
||||
|
||||
def program(self):
|
||||
self.calls += 1
|
||||
return {"programmed": True, "image_sha256": "f" * 64}
|
||||
|
||||
programmer = FakeProgrammer()
|
||||
manager = StandaloneCanRuntimeManager(
|
||||
tmp_path / "status.json",
|
||||
tmp_path / "seat_bridge.py",
|
||||
required_version="runtime-test-v1",
|
||||
python_executable=tmp_path / "python.exe",
|
||||
zlg_bridge_script=tmp_path / "zlg_bridge.py",
|
||||
dll_path=tmp_path / "zlgcan.dll",
|
||||
device_type=41,
|
||||
device_index=0,
|
||||
channels=[ZlgChannelConfig(channel=0, frame_format="classic_can")],
|
||||
seat_channel=0,
|
||||
firmware_programmer=programmer,
|
||||
firmware_auto_flash=True,
|
||||
expected_firmware_revision=15,
|
||||
expected_firmware_build_id=16,
|
||||
firmware_boot_wait_s=0,
|
||||
protocol_probe_timeout_s=0.01,
|
||||
startup_timeout_s=0.2,
|
||||
)
|
||||
manager.status_path.write_text(
|
||||
json.dumps(_qualified_runtime_status(build_id=15)), encoding="utf-8"
|
||||
)
|
||||
manager.ensure_async = lambda: None
|
||||
manager._stop_runtime_owner = lambda: None
|
||||
manager._attempt_start = lambda: manager.status_path.write_text(
|
||||
json.dumps(_qualified_runtime_status()), encoding="utf-8"
|
||||
)
|
||||
|
||||
status = manager.ensure_seat_protocol()
|
||||
|
||||
assert status["firmware_identity"]["firmware_build_id"] == 16
|
||||
assert programmer.calls == 1
|
||||
assert manager.snapshot()["firmware_recovery_count"] == 1
|
||||
|
||||
Reference in New Issue
Block a user