diff --git a/apps/web-console/app.js b/apps/web-console/app.js index e5bee40..af56591 100755 --- a/apps/web-console/app.js +++ b/apps/web-console/app.js @@ -2549,7 +2549,14 @@ function updateSeatTwin(actuatorState) { } const REFERENCE_VEHICLE_ROOT = "/assets/models/byd-tang/"; -const REFERENCE_VEHICLE_URL = `${REFERENCE_VEHICLE_ROOT}byd-tang-2024.fbx`; +const REFERENCE_VEHICLE_ASSET_VERSION = "77a0fae7"; +const REFERENCE_VEHICLE_LOAD_TIMEOUT_MS = 30000; + +function referenceVehicleAssetUrl(fileName) { + return `${REFERENCE_VEHICLE_ROOT}${encodeURIComponent(fileName)}?v=${REFERENCE_VEHICLE_ASSET_VERSION}`; +} + +const REFERENCE_VEHICLE_URL = referenceVehicleAssetUrl("byd-tang-2024.fbx"); function referenceMaterialDescriptor(material) { const textureNames = [ @@ -3643,73 +3650,92 @@ function loadReferenceVehicleModel(THREE, scene, vehicleSystems, fallback) { reject(new Error("FBXLoader is unavailable")); return; } + let settled = false; + const settle = (callback, value) => { + if (settled) return false; + settled = true; + window.clearTimeout(timeoutId); + callback(value); + return true; + }; + const timeoutId = window.setTimeout(() => { + settle( + reject, + new Error(`Reference vehicle load timed out after ${REFERENCE_VEHICLE_LOAD_TIMEOUT_MS} ms`), + ); + }, REFERENCE_VEHICLE_LOAD_TIMEOUT_MS); const manager = new THREE.LoadingManager(); manager.setURLModifier((url) => { - const cleanUrl = String(url).replaceAll("\\", "/"); + const cleanUrl = String(url).replaceAll("\\", "/").split(/[?#]/, 1)[0]; const fileName = decodeURIComponent(cleanUrl.split("/").pop() || ""); if (!/\.(png|jpe?g)$/i.test(fileName)) return url; - return `${REFERENCE_VEHICLE_ROOT}${encodeURIComponent(fileName)}`; + return referenceVehicleAssetUrl(fileName); }); const loader = new FBXLoaderClass(manager); loader.setResourcePath(REFERENCE_VEHICLE_ROOT); loader.load( REFERENCE_VEHICLE_URL, (vehicle) => { - const normalized = normalizeReferenceVehicle(THREE, vehicle); - const driverCushion = measureReferenceDriverCushion(THREE, vehicle); - const passengerCushion = measureReferencePassengerCushion(THREE, vehicle); - const removedFrontSeatTriangles = carveReferenceFrontSeats(THREE, vehicle); - const referenceWindowPanes = extractReferenceWindowPanes(THREE, vehicle); - const report = configureReferenceVehicleModel(THREE, vehicle); - styleReferencePassiveGlass(THREE, vehicle); - const passiveRearExterior = styleReferencePassiveRearExterior(THREE, vehicle); - const referenceFrontLampMeshes = collectReferenceFrontLampMeshes(vehicle); - emphasizeReferenceWindowPanes(THREE, referenceWindowPanes); - const referenceFrontLampMaterials = emphasizeReferenceFrontLampMeshes( - THREE, - referenceFrontLampMeshes, - ); - const referenceFrontLampHighlights = createReferenceFrontLampHighlights( - THREE, - referenceFrontLampMeshes, - ); - const referenceFrontLampLensOutlines = createReferenceFrontLampLensOutlines( - THREE, - vehicle, - ); - vehicleSystems.referenceWindowPanes = referenceWindowPanes; - vehicleSystems.referenceFrontLampMaterials = referenceFrontLampMaterials; - vehicleSystems.referenceFrontLampMeshes = referenceFrontLampMeshes; - vehicleSystems.referenceFrontLampHighlights = referenceFrontLampHighlights; - vehicleSystems.referenceFrontLampLensOutlines = referenceFrontLampLensOutlines; - applyTwinVehicleTheme(vehicleSystems); - vehicle.name = "REFERENCE_VEHICLE_OUTLINE"; - vehicle.userData.twinRole = "reference_vehicle"; - vehicle.traverse((object) => object.layers.set(1)); - scene.add(vehicle); - vehicleSystems.fallbackExteriorObjects.forEach((object) => { - object.visible = false; - }); - vehicleSystems.fallbackInteriorObjects.forEach((object) => { - object.visible = false; - }); - console.info("[vehicle-twin] reference vehicle model loaded", JSON.stringify({ - meshes: report.meshes, - size: normalized.size.toArray().map((value) => Number(value.toFixed(3))), - removedFrontSeatTriangles, - windowPanes: Object.fromEntries( - Object.entries(referenceWindowPanes).map(([key, value]) => [key, value.length]), - ), - frontLampMaterials: referenceFrontLampMaterials.length, - frontLampMeshes: referenceFrontLampMeshes.length, - frontLampHighlights: referenceFrontLampHighlights.length, - frontLampLensOutlines: referenceFrontLampLensOutlines.length, - rearLampMeshes: passiveRearExterior.rearLampMeshes.length, - rearBumperMeshes: passiveRearExterior.rearBumperMeshes.length, - driverCushion, - passengerCushion, - })); - resolve({ vehicle, report, normalized, driverCushion, passengerCushion }); + if (settled) return; + try { + const normalized = normalizeReferenceVehicle(THREE, vehicle); + const driverCushion = measureReferenceDriverCushion(THREE, vehicle); + const passengerCushion = measureReferencePassengerCushion(THREE, vehicle); + const removedFrontSeatTriangles = carveReferenceFrontSeats(THREE, vehicle); + const referenceWindowPanes = extractReferenceWindowPanes(THREE, vehicle); + const report = configureReferenceVehicleModel(THREE, vehicle); + styleReferencePassiveGlass(THREE, vehicle); + const passiveRearExterior = styleReferencePassiveRearExterior(THREE, vehicle); + const referenceFrontLampMeshes = collectReferenceFrontLampMeshes(vehicle); + emphasizeReferenceWindowPanes(THREE, referenceWindowPanes); + const referenceFrontLampMaterials = emphasizeReferenceFrontLampMeshes( + THREE, + referenceFrontLampMeshes, + ); + const referenceFrontLampHighlights = createReferenceFrontLampHighlights( + THREE, + referenceFrontLampMeshes, + ); + const referenceFrontLampLensOutlines = createReferenceFrontLampLensOutlines( + THREE, + vehicle, + ); + vehicleSystems.referenceWindowPanes = referenceWindowPanes; + vehicleSystems.referenceFrontLampMaterials = referenceFrontLampMaterials; + vehicleSystems.referenceFrontLampMeshes = referenceFrontLampMeshes; + vehicleSystems.referenceFrontLampHighlights = referenceFrontLampHighlights; + vehicleSystems.referenceFrontLampLensOutlines = referenceFrontLampLensOutlines; + applyTwinVehicleTheme(vehicleSystems); + vehicle.name = "REFERENCE_VEHICLE_OUTLINE"; + vehicle.userData.twinRole = "reference_vehicle"; + vehicle.traverse((object) => object.layers.set(1)); + scene.add(vehicle); + vehicleSystems.fallbackExteriorObjects.forEach((object) => { + object.visible = false; + }); + vehicleSystems.fallbackInteriorObjects.forEach((object) => { + object.visible = false; + }); + console.info("[vehicle-twin] reference vehicle model loaded", JSON.stringify({ + meshes: report.meshes, + size: normalized.size.toArray().map((value) => Number(value.toFixed(3))), + removedFrontSeatTriangles, + windowPanes: Object.fromEntries( + Object.entries(referenceWindowPanes).map(([key, value]) => [key, value.length]), + ), + frontLampMaterials: referenceFrontLampMaterials.length, + frontLampMeshes: referenceFrontLampMeshes.length, + frontLampHighlights: referenceFrontLampHighlights.length, + frontLampLensOutlines: referenceFrontLampLensOutlines.length, + rearLampMeshes: passiveRearExterior.rearLampMeshes.length, + rearBumperMeshes: passiveRearExterior.rearBumperMeshes.length, + driverCushion, + passengerCushion, + })); + settle(resolve, { vehicle, report, normalized, driverCushion, passengerCushion }); + } catch (error) { + settle(reject, error); + } }, (event) => { if (!fallback || !event.total) return; @@ -3717,7 +3743,7 @@ function loadReferenceVehicleModel(THREE, scene, vehicleSystems, fallback) { const label = fallback.querySelector("span"); if (label) label.textContent = `正在加载整车参考模型 · ${progress}%`; }, - reject, + (error) => settle(reject, error), ); }); } @@ -3726,7 +3752,33 @@ function initSeatTwin() { const canvas = $("#seatTwinCanvas"); const stage = $("#seatTwin"); const fallback = $("#twinFallback"); - if (!canvas || !stage) return; + if (!canvas || !stage || state.seatTwin) return; + const stageHasLayout = () => { + const bounds = stage.getBoundingClientRect(); + return stage.isConnected && bounds.width >= 2 && bounds.height >= 2; + }; + if (!stageHasLayout()) { + if (stage.dataset.twinInitPending === "true") return; + stage.dataset.twinInitPending = "true"; + let pendingObserver = null; + const retryInitialization = () => { + if (!stageHasLayout()) return; + pendingObserver?.disconnect(); + window.removeEventListener("resize", scheduleRetry); + window.removeEventListener("cognitive-agent:module-change", scheduleRetry); + delete stage.dataset.twinInitPending; + initSeatTwin(); + }; + const scheduleRetry = () => window.requestAnimationFrame(retryInitialization); + if (window.ResizeObserver) { + pendingObserver = new ResizeObserver(retryInitialization); + pendingObserver.observe(stage); + } + window.addEventListener("resize", scheduleRetry); + window.addEventListener("cognitive-agent:module-change", scheduleRetry); + scheduleRetry(); + return; + } if (!window.THREE) { fallback.classList.add("error"); fallback.innerHTML = '3D 引擎加载失败,精确状态仍可用'; @@ -3735,7 +3787,16 @@ function initSeatTwin() { } const THREE = window.THREE; - const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, powerPreference: "high-performance" }); + let renderer; + try { + renderer = new THREE.WebGLRenderer({ canvas, antialias: true, powerPreference: "high-performance" }); + } catch (error) { + console.error("[vehicle-twin] failed to initialize WebGL", error); + fallback.classList.add("error"); + fallback.innerHTML = '3D 渲染器初始化失败,状态数据仍可用'; + refreshIcons(); + return; + } renderer.autoClear = false; renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); if ("outputColorSpace" in renderer) renderer.outputColorSpace = THREE.SRGBColorSpace; @@ -3883,6 +3944,7 @@ function initSeatTwin() { applyTwinSceneTheme(state.seatTwin); }); + canvas.dataset.referenceModelState = "loading"; loadReferenceVehicleModel(THREE, scene, vehicleSystems, fallback) .then(({ vehicle, report, normalized, driverCushion, passengerCushion }) => { if (!state.seatTwin) return; @@ -3920,11 +3982,12 @@ function initSeatTwin() { .toArray() .map((value) => value.toFixed(2)) .join("x"); - canvas.dataset.referenceModelLoaded = "true"; + canvas.dataset.referenceModelState = "loaded"; fallback.hidden = true; }) .catch((error) => { console.error("[vehicle-twin] failed to load reference vehicle model", error); + canvas.dataset.referenceModelState = "failed"; fallback.classList.add("error"); fallback.innerHTML = '实车模型加载失败,已切换轻量回退模型'; refreshIcons(); @@ -3940,17 +4003,64 @@ function initSeatTwin() { }); } + let stageWasVisible = false; function resize() { - const width = Math.max(1, canvas.clientWidth); - const height = Math.max(1, canvas.clientHeight); + const width = Math.max(1, Math.round(stage.clientWidth)); + const height = Math.max(1, Math.round(stage.clientHeight)); renderer.setSize(width, height, false); camera.aspect = width / height; camera.updateProjectionMatrix(); state.seatTwin.narrowViewport = width < 600; } - new ResizeObserver(resize).observe(stage); - resize(); + function synchronizeTwinViewport({ reset = false } = {}) { + if (!stageHasLayout()) { + stageWasVisible = false; + return false; + } + resize(); + if (reset || !stageWasVisible) resetView(); + stageWasVisible = true; + return true; + } + + const scheduleViewportSync = () => { + window.requestAnimationFrame(() => window.requestAnimationFrame(() => { + synchronizeTwinViewport(); + })); + }; + if (window.ResizeObserver) { + const stageObserver = new ResizeObserver(() => synchronizeTwinViewport()); + stageObserver.observe(stage); + state.seatTwin.resizeObserver = stageObserver; + } + window.addEventListener("resize", scheduleViewportSync); + window.addEventListener("cognitive-agent:module-change", scheduleViewportSync); + canvas.addEventListener("webglcontextlost", (event) => { + event.preventDefault(); + canvas.dataset.contextLost = "true"; + fallback.hidden = false; + fallback.classList.add("error"); + fallback.innerHTML = '3D 上下文已中断,正在等待恢复'; + refreshIcons(); + }); + canvas.addEventListener("webglcontextrestored", () => { + delete canvas.dataset.contextLost; + synchronizeTwinViewport({ reset: true }); + const modelState = canvas.dataset.referenceModelState; + fallback.hidden = modelState === "loaded"; + if (modelState === "loading") { + fallback.classList.remove("error"); + fallback.innerHTML = '正在加载整车参考模型'; + refreshIcons(); + } else if (modelState === "failed") { + fallback.classList.add("error"); + fallback.innerHTML = '实车模型加载失败,已切换轻量回退模型'; + refreshIcons(); + window.setTimeout(() => { fallback.hidden = true; }, 3200); + } + }); + synchronizeTwinViewport({ reset: true }); stage.addEventListener("pointerdown", (event) => { state.seatTwin.dragging = true; state.seatTwin.pointer = { x: event.clientX, y: event.clientY }; @@ -4052,6 +4162,12 @@ function initSeatTwin() { function animate(timestamp = 0) { const twin = state.seatTwin; if (!twin) return; + if (!stageHasLayout() || canvas.dataset.contextLost === "true") { + stageWasVisible = false; + window.requestAnimationFrame(animate); + return; + } + if (!stageWasVisible) synchronizeTwinViewport({ reset: true }); Object.values(twin.models).forEach((model) => { Object.keys(model.positions).forEach((axis) => { const delta = model.targets[axis] - model.positions[axis]; @@ -6556,7 +6672,7 @@ document.addEventListener("DOMContentLoaded", async () => { initJointTwinLayout(); initResultWorkspace(); refreshIcons(); - initSeatTwin(); + window.requestAnimationFrame(initSeatTwin); $("#requestForm").addEventListener("submit", submitRequest); $("#utterance").addEventListener("keydown", (event) => { if (event.key === "Enter" && !event.shiftKey && !event.isComposing) { diff --git a/apps/web-console/index.html b/apps/web-console/index.html index ff3258e..e94fdd4 100755 --- a/apps/web-console/index.html +++ b/apps/web-console/index.html @@ -19,14 +19,14 @@ - + - +
diff --git a/apps/web-console/self-repair-seat3d.js b/apps/web-console/self-repair-seat3d.js index 7edfe88..4aa7950 100644 --- a/apps/web-console/self-repair-seat3d.js +++ b/apps/web-console/self-repair-seat3d.js @@ -1,6 +1,33 @@ (function () { "use strict"; + function viewerHasLayout(viewer) { + const bounds = viewer.getBoundingClientRect(); + return viewer.isConnected && bounds.width >= 2 && bounds.height >= 2; + } + + function deferSeat3dUntilVisible(viewer, options) { + if (viewer.dataset.seat3dPending === "true") return; + viewer.dataset.seat3dPending = "true"; + let observer = null; + const retry = () => { + if (!viewerHasLayout(viewer)) return; + observer?.disconnect(); + window.removeEventListener("resize", scheduleRetry); + window.removeEventListener("cognitive-agent:module-change", scheduleRetry); + delete viewer.dataset.seat3dPending; + initSeat3d(options); + }; + const scheduleRetry = () => window.requestAnimationFrame(retry); + if (window.ResizeObserver) { + observer = new ResizeObserver(retry); + observer.observe(viewer); + } + window.addEventListener("resize", scheduleRetry); + window.addEventListener("cognitive-agent:module-change", scheduleRetry); + scheduleRetry(); + } + function initSeat3d(options) { const { canvasId, @@ -15,6 +42,10 @@ const canvas = document.getElementById(canvasId); const viewer = document.getElementById(viewerId); if (!canvas || !viewer || viewer.dataset.seat3dInitialized === "true") return; + if (!viewerHasLayout(viewer)) { + deferSeat3dUntilVisible(viewer, options); + return; + } viewer.dataset.seat3dInitialized = "true"; const markReady = (fallback = false) => { @@ -387,6 +418,24 @@ camera.updateProjectionMatrix(); } + let viewerWasVisible = false; + function synchronizeViewport({ reset = false } = {}) { + if (!viewerHasLayout(viewer)) { + viewerWasVisible = false; + return false; + } + resize(); + if (reset || !viewerWasVisible) resetOrbit(); + viewerWasVisible = true; + return true; + } + + const scheduleViewportSync = () => { + window.requestAnimationFrame(() => window.requestAnimationFrame(() => { + synchronizeViewport(); + })); + }; + function onHealingState(event) { const detail = event.detail || {}; (detail.axes || []).forEach((axis) => { @@ -404,6 +453,11 @@ function animate() { window.requestAnimationFrame(animate); + if (!viewerHasLayout(viewer) || canvas.dataset.contextLost === "true") { + viewerWasVisible = false; + return; + } + if (!viewerWasVisible) synchronizeViewport({ reset: true }); for (let index = 0; index < current.axes.length; index += 1) { current.axes[index] += (desired.axes[index] - current.axes[index]) * 0.055; } @@ -486,10 +540,23 @@ window.addEventListener(stateEvent, onHealingState); window.addEventListener(resetEvent, resetOrbit); - if (window.ResizeObserver) new ResizeObserver(resize).observe(viewer); - window.addEventListener("resize", resize); - resize(); - resetOrbit(); + if (window.ResizeObserver) { + const resizeObserver = new ResizeObserver(() => synchronizeViewport()); + resizeObserver.observe(viewer); + } + window.addEventListener("resize", scheduleViewportSync); + window.addEventListener("cognitive-agent:module-change", scheduleViewportSync); + canvas.addEventListener("webglcontextlost", (event) => { + event.preventDefault(); + canvas.dataset.contextLost = "true"; + viewer.classList.add("context-lost"); + }); + canvas.addEventListener("webglcontextrestored", () => { + delete canvas.dataset.contextLost; + viewer.classList.remove("context-lost"); + synchronizeViewport({ reset: true }); + }); + synchronizeViewport({ reset: true }); animate(); window.requestAnimationFrame(() => markReady()); diff --git a/apps/web-console/service-worker.js b/apps/web-console/service-worker.js index 4d14852..cb09d95 100644 --- a/apps/web-console/service-worker.js +++ b/apps/web-console/service-worker.js @@ -1,4 +1,4 @@ -const CACHE_VERSION = "cognitive-agent-pad-v116"; +const CACHE_VERSION = "cognitive-agent-pad-v117"; const SHELL_CACHE = `${CACHE_VERSION}-shell`; const RUNTIME_CACHE = `${CACHE_VERSION}-runtime`; const APP_SHELL = [ @@ -6,7 +6,7 @@ const APP_SHELL = [ "/index.html", "/styles.css?v=dual-seat-9", "/network.js?v=android-pad-1", - "/app.js?v=dual-seat-80", + "/app.js?v=dual-seat-81", "/gesture-control.js?v=android-pad-7", "/pwa.js?v=android-pad-1", "/console-v2.css?v=desktop-refactor-106", @@ -17,8 +17,8 @@ const APP_SHELL = [ "/self-calibration.js?v=calibration-6", "/self-repair.css?v=repair-21", "/self-repair.js?v=repair-20", - "/self-repair-seat3d.js?v=repair-18", - "/assets/models/n7_seat.glb", + "/self-repair-seat3d.js?v=repair-19", + "/assets/models/n7_seat.glb?v=e7f50f03", "/manifest.webmanifest", "/icons/app-icon-192.png", "/icons/app-icon-512.png", diff --git a/artifacts/local-server/server.combined.log b/artifacts/local-server/server.combined.log new file mode 100644 index 0000000..2ff5136 Binary files /dev/null and b/artifacts/local-server/server.combined.log differ diff --git a/artifacts/local-server/server.pid b/artifacts/local-server/server.pid new file mode 100644 index 0000000..3a909e4 --- /dev/null +++ b/artifacts/local-server/server.pid @@ -0,0 +1 @@ +9136 diff --git a/artifacts/local-server/server.stderr.log b/artifacts/local-server/server.stderr.log new file mode 100644 index 0000000..e69de29 diff --git a/artifacts/local-server/server.stdout.log b/artifacts/local-server/server.stdout.log new file mode 100644 index 0000000..e69de29 diff --git a/config/modes/vehicles/denza_n7/user/k-c78dff.yaml b/config/modes/vehicles/denza_n7/user/k-c78dff.yaml deleted file mode 100644 index d0cfe7e..0000000 --- a/config/modes/vehicles/denza_n7/user/k-c78dff.yaml +++ /dev/null @@ -1,61 +0,0 @@ -id: k-c78dff -name: K歌模式 -aliases: -- K歌模式 -- K歌 -description: 用户固化的长期模式配置。 -icon: bookmark -scope: - vehicle_ids: - - denza_n7 -operations: -- domain: ambient_light - capability: ambient_light.set_power - target_scope: vehicle - operation: activate - parameters: - state: 'on' - reason: 开启氛围灯以营造K歌场景。 -- domain: ambient_light - capability: ambient_light.set_illumination - target_scope: vehicle - operation: set - parameters: - color_hex: '#B84DFF' - brightness_pct: 65 - reason: 使用明快紫色和中高亮度营造活跃舞台氛围。 -- domain: climate - capability: climate.set_power - target_scope: vehicle - operation: activate - parameters: - state: 'on' - reason: 空调当前关闭,需先开启以维持K歌时的座舱舒适度。 -- domain: climate - capability: climate.set_temperature - target_scope: driver - operation: set - parameters: - temperature_c: 24 - reason: 采用舒适且保守的座舱温度。 -- domain: climate - capability: climate.set_fan_level - target_scope: vehicle - operation: set - parameters: - level: 2 - reason: 采用低风量,兼顾通风并减少对歌声的干扰。 -- domain: climate - capability: climate.set_ac - target_scope: vehicle - operation: activate - parameters: - state: 'on' - reason: 启用制冷除湿以维持多人演唱时的舒适度。 -- domain: climate - capability: climate.set_cycle_mode - target_scope: vehicle - operation: set - parameters: - mode: internal - reason: 采用内循环以稳定座舱温度并减少外部噪声和气味。 diff --git a/config/modes/vehicles/denza_n7/user/mode-ceff921663.yaml b/config/modes/vehicles/denza_n7/user/mode-ceff921663.yaml new file mode 100644 index 0000000..4a8da8e --- /dev/null +++ b/config/modes/vehicles/denza_n7/user/mode-ceff921663.yaml @@ -0,0 +1,46 @@ +id: mode-ceff921663 +name: 躺平模式 +aliases: +- 躺平模式 +- 躺平 +description: 用户固化的长期模式配置。 +icon: bookmark +scope: + vehicle_ids: + - denza_n7 +operations: +- domain: seat + capability: seat.set_longitudinal_position + target_scope: passenger + operation: set + parameters: + position_pct: 90 + reason: 用户固化的座椅位置模式动作。 +- domain: seat + capability: seat.set_cushion_tilt + target_scope: passenger + operation: set + parameters: + position_pct: 40 + reason: 用户固化的座椅位置模式动作。 +- domain: seat + capability: seat.set_backrest_recline + target_scope: passenger + operation: set + parameters: + position_pct: 80 + reason: 用户固化的座椅位置模式动作。 +- domain: seat + capability: seat.set_legrest_height + target_scope: passenger + operation: set + parameters: + position_pct: 75 + reason: 用户固化的座椅位置模式动作。 +- domain: seat + capability: seat.set_legrest_extension + target_scope: passenger + operation: set + parameters: + position_pct: 80 + reason: 用户固化的座椅位置模式动作。 diff --git a/src/cognitive_agent/server.py b/src/cognitive_agent/server.py index 81c129d..45388b6 100755 --- a/src/cognitive_agent/server.py +++ b/src/cognitive_agent/server.py @@ -243,7 +243,7 @@ class SeatDemoHandler(BaseHTTPRequestHandler): if path.startswith("/api/requests/"): self._serve_request(path.removeprefix("/api/requests/")) return - self._serve_static(path) + self._serve_static(path, parsed_url.query) def do_POST(self) -> None: # noqa: N802 - stdlib handler API path = unquote(urlparse(self.path).path) @@ -942,17 +942,24 @@ class SeatDemoHandler(BaseHTTPRequestHandler): return self._json(json.loads(result_path.read_text(encoding="utf-8"))) - def _serve_static(self, path: str) -> None: + def _serve_static(self, path: str, query: str = "") -> None: relative_path = "index.html" if path in {"", "/"} else path.lstrip("/") - self._serve_static_file(relative_path) + versioned = bool(parse_qs(query).get("v")) + self._serve_static_file(relative_path, versioned=versioned) - def _serve_static_file(self, relative_path: str) -> None: + def _serve_static_file(self, relative_path: str, *, versioned: bool = False) -> None: candidate = (STATIC_ROOT / relative_path).resolve() try: candidate.relative_to(STATIC_ROOT.resolve()) except ValueError: self._json({"error": "not_found"}, HTTPStatus.NOT_FOUND) return + if not candidate.is_file() and ( + Path(relative_path).suffix + or relative_path.startswith(("assets/", "vendor/", "icons/")) + ): + self._json({"error": "not_found"}, HTTPStatus.NOT_FOUND) + return if not candidate.is_file(): candidate = STATIC_ROOT / "index.html" content_type, _ = mimetypes.guess_type(candidate.name) @@ -972,8 +979,12 @@ class SeatDemoHandler(BaseHTTPRequestHandler): ) self.send_header("Content-Length", str(len(content))) cache_control = "no-cache" - if relative_path.startswith(("vendor/", "assets/", "icons/")): + if relative_path.startswith(("vendor/", "icons/")) or ( + relative_path.startswith("assets/") and versioned + ): cache_control = "public, max-age=31536000, immutable" + elif relative_path.startswith("assets/"): + cache_control = "no-cache" elif candidate.name in {"index.html", "login.html", "service-worker.js"}: cache_control = "no-cache" self.send_header("Cache-Control", cache_control) diff --git a/tests/e2e/test_seat_web_api.py b/tests/e2e/test_seat_web_api.py index 4942e30..adcfea5 100755 --- a/tests/e2e/test_seat_web_api.py +++ b/tests/e2e/test_seat_web_api.py @@ -177,7 +177,7 @@ operations: def test_static_console_and_runtime_endpoint(self) -> None: with self.opener.open(f"{self.base_url}/", timeout=3) as response: html = response.read().decode("utf-8") - with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-80", timeout=3) as response: + with self.opener.open(f"{self.base_url}/app.js?v=dual-seat-81", timeout=3) as response: app_script = response.read().decode("utf-8") with self.opener.open( f"{self.base_url}/gesture-control.js?v=android-pad-7", timeout=3 @@ -199,6 +199,10 @@ operations: f"{self.base_url}/self-extension.css?v=extension-18", timeout=3 ) as response: extension_styles = response.read().decode("utf-8").replace("\r\n", "\n") + with self.opener.open( + f"{self.base_url}/service-worker.js", timeout=3 + ) as response: + service_worker = response.read().decode("utf-8") with self.opener.open( f"{self.base_url}/api/extension/health", timeout=3 ) as response: @@ -219,7 +223,7 @@ operations: self.assertIn("CMD 0x431 · FB 0x41A · CH1", html) self.assertIn("CMD 0x431 · FB 0x388 · CH1", html) self.assertNotIn("透明车体 · 实体设备", html) - self.assertIn('src="/app.js?v=dual-seat-80"', html) + self.assertIn('src="/app.js?v=dual-seat-81"', html) self.assertIn('href="/styles.css?v=dual-seat-9"', html) self.assertIn('src="/gesture-control.js?v=android-pad-7"', html) self.assertNotIn('class="vehicle-system-hud"', html) @@ -438,6 +442,23 @@ operations: self.assertIn("function createReferenceFrontLampHighlights", app_script) self.assertIn("referenceFrontLampMaterials", app_script) self.assertIn("targets.windowPositions", app_script) + self.assertIn( + 'const REFERENCE_VEHICLE_ASSET_VERSION = "77a0fae7"', app_script + ) + self.assertIn( + "const REFERENCE_VEHICLE_LOAD_TIMEOUT_MS = 30000", app_script + ) + self.assertIn( + 'referenceVehicleAssetUrl("byd-tang-2024.fbx")', app_script + ) + self.assertIn("const stageHasLayout = () =>", app_script) + self.assertIn("window.requestAnimationFrame(initSeatTwin)", app_script) + self.assertIn('canvas.addEventListener("webglcontextlost"', app_script) + self.assertIn('"cognitive-agent:module-change"', app_script) + self.assertIn('const CACHE_VERSION = "cognitive-agent-pad-v117"', service_worker) + self.assertIn( + '"/assets/models/n7_seat.glb?v=e7f50f03"', service_worker + ) self.assertIn("hardware_access_required", app_script) self.assertIn('button.setAttribute("aria-label", "关闭摄像头")', gesture_script) self.assertIn("function refreshCameraAvailability", gesture_script) @@ -647,7 +668,7 @@ operations: ) as response: extension_script = response.read().decode("utf-8") with self.opener.open( - f"{self.base_url}/app.js?v=dual-seat-80", timeout=3 + f"{self.base_url}/app.js?v=dual-seat-81", timeout=3 ) as response: app_script = response.read().decode("utf-8") diff --git a/tests/e2e/test_self_calibration_api.py b/tests/e2e/test_self_calibration_api.py index 4f9b7b0..f7b35fa 100644 --- a/tests/e2e/test_self_calibration_api.py +++ b/tests/e2e/test_self_calibration_api.py @@ -147,6 +147,18 @@ class TestSelfCalibrationApi: model = response.read() content_type = response.headers["Content-Type"] content_security_policy = response.headers["Content-Security-Policy"] + unversioned_cache_control = response.headers["Cache-Control"] + with urllib.request.urlopen( + f"{self.base_url}/assets/models/n7_seat.glb?v=e7f50f03", + timeout=3, + ) as response: + versioned_model_prefix = response.read(4) + versioned_cache_control = response.headers["Cache-Control"] + with pytest.raises(urllib.error.HTTPError) as missing_model: + urllib.request.urlopen( + f"{self.base_url}/assets/models/missing-seat.glb?v=missing", + timeout=3, + ) assert 'href="/self-calibration.css?v=calibration-10"' in html assert 'src="/self-calibration.js?v=calibration-6"' in html @@ -173,6 +185,10 @@ class TestSelfCalibrationApi: assert '["self-calibration", "self-repair"].includes(module)' in console_script assert 'setRole("admin", { allowModuleTransition: true })' in console_script assert len(model) == 845_992 + assert versioned_model_prefix == model[:4] assert content_type.startswith("model/gltf-binary") + assert unversioned_cache_control == "no-cache" + assert versioned_cache_control == "public, max-age=31536000, immutable" + assert missing_model.value.code == 404 assert "img-src 'self' data: blob:;" in content_security_policy assert "connect-src 'self' blob:;" in content_security_policy diff --git a/tests/e2e/test_self_repair_api.py b/tests/e2e/test_self_repair_api.py index 9bc020a..f599142 100644 --- a/tests/e2e/test_self_repair_api.py +++ b/tests/e2e/test_self_repair_api.py @@ -154,7 +154,7 @@ class TestSelfRepairApi: ) as response: styles = response.read().decode("utf-8") with urllib.request.urlopen( - f"{self.base_url}/self-repair-seat3d.js?v=repair-18", timeout=3 + f"{self.base_url}/self-repair-seat3d.js?v=repair-19", timeout=3 ) as response: seat3d = response.read().decode("utf-8") with urllib.request.urlopen( @@ -168,7 +168,7 @@ class TestSelfRepairApi: assert 'SELF-REPAIR' in script assert 'padding: var(--v2-module-title-block) 0 0' in styles assert 'margin-inline: var(--v2-module-title-inline)' in styles - assert 'src="/self-repair-seat3d.js?v=repair-18"' in html + assert 'src="/self-repair-seat3d.js?v=repair-19"' in html assert "量产故障注入" in script assert "运行与自修复日志" in script assert "启动座椅运行" in script @@ -203,6 +203,11 @@ class TestSelfRepairApi: assert 'viewer.addEventListener("pointerdown"' in seat3d assert 'window.addEventListener("pointermove"' in seat3d assert "window.addEventListener(resetEvent" in seat3d + assert "function viewerHasLayout(viewer)" in seat3d + assert "function deferSeat3dUntilVisible(viewer, options)" in seat3d + assert "if (!viewerHasLayout(viewer))" in seat3d + assert 'window.addEventListener("cognitive-agent:module-change"' in seat3d + assert 'canvas.addEventListener("webglcontextlost"' in seat3d assert '["self-calibration", "self-repair"].includes(module)' in console_script assert 'roleSelect.disabled = ui.activeModule === "self-repair"' in console_script diff --git a/tmp/pytest-feature-dynamic-mode-push/test_bridge_manager_does_not_r0/bridge.json b/tmp/pytest-feature-dynamic-mode-push/test_bridge_manager_does_not_r0/bridge.json new file mode 100644 index 0000000..6938bfd --- /dev/null +++ b/tmp/pytest-feature-dynamic-mode-push/test_bridge_manager_does_not_r0/bridge.json @@ -0,0 +1 @@ +{"heartbeat_epoch": 1786612896.954201, "bridge_state": "RUNNING", "current_request_id": "SELF_HEAL_BENCH_1"} \ No newline at end of file diff --git a/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/python.exe b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/python.exe new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/python.exe @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/seat_bridge.py b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/seat_bridge.py new file mode 100644 index 0000000..6c284f1 --- /dev/null +++ b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/seat_bridge.py @@ -0,0 +1,3 @@ +BRIDGE_VERSION = 'runtime-test-v1' +def run_bridge(transport=None, stop_event=None): + stop_event.wait(2.0) diff --git a/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/zlg_bridge.py b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/zlg_bridge.py new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/zlg_bridge.py @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/zlgcan.dll b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/zlgcan.dll new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/zlgcan.dll @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_reject0/status.json b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_reject0/status.json new file mode 100644 index 0000000..f4505df --- /dev/null +++ b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_reject0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786612902.0072305, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 15, "qualified": false}, "frame_epochs": {"0x301": 1786612902.0072305, "0x302": 1786612902.0072305, "0x303": 1786612902.0072305, "0x304": 1786612902.0072305, "0x305": 1786612902.0072305, "0x306": 1786612902.0072305, "0x307": 1786612902.0072305}} \ No newline at end of file diff --git a/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_restor0/status.json b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_restor0/status.json new file mode 100644 index 0000000..e654981 --- /dev/null +++ b/tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_restor0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786612902.1554296, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 16, "qualified": true}, "frame_epochs": {"0x301": 1786612902.1554296, "0x302": 1786612902.1554296, "0x303": 1786612902.1554296, "0x304": 1786612902.1554296, "0x305": 1786612902.1554296, "0x306": 1786612902.1554296, "0x307": 1786612902.1554296}} \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution-2/test_bridge_manager_does_not_r0/bridge.json b/tmp/pytest-rebase-resolution-2/test_bridge_manager_does_not_r0/bridge.json new file mode 100644 index 0000000..9c8db28 --- /dev/null +++ b/tmp/pytest-rebase-resolution-2/test_bridge_manager_does_not_r0/bridge.json @@ -0,0 +1 @@ +{"heartbeat_epoch": 1786614297.8563914, "bridge_state": "RUNNING", "current_request_id": "SELF_HEAL_BENCH_1"} \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/python.exe b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/python.exe new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/python.exe @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/seat_bridge.py b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/seat_bridge.py new file mode 100644 index 0000000..6c284f1 --- /dev/null +++ b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/seat_bridge.py @@ -0,0 +1,3 @@ +BRIDGE_VERSION = 'runtime-test-v1' +def run_bridge(transport=None, stop_event=None): + stop_event.wait(2.0) diff --git a/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/zlg_bridge.py b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/zlg_bridge.py new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/zlg_bridge.py @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/zlgcan.dll b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/zlgcan.dll new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/zlgcan.dll @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution-2/test_standalone_runtime_reject0/status.json b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_reject0/status.json new file mode 100644 index 0000000..b7499c2 --- /dev/null +++ b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_reject0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786614302.9093528, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 15, "qualified": false}, "frame_epochs": {"0x301": 1786614302.9093528, "0x302": 1786614302.9093528, "0x303": 1786614302.9093528, "0x304": 1786614302.9093528, "0x305": 1786614302.9093528, "0x306": 1786614302.9093528, "0x307": 1786614302.9093528}} \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution-2/test_standalone_runtime_restor0/status.json b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_restor0/status.json new file mode 100644 index 0000000..d811a48 --- /dev/null +++ b/tmp/pytest-rebase-resolution-2/test_standalone_runtime_restor0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786614303.057585, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 16, "qualified": true}, "frame_epochs": {"0x301": 1786614303.057585, "0x302": 1786614303.057585, "0x303": 1786614303.057585, "0x304": 1786614303.057585, "0x305": 1786614303.057585, "0x306": 1786614303.057585, "0x307": 1786614303.057585}} \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution/test_bridge_manager_does_not_r0/bridge.json b/tmp/pytest-rebase-resolution/test_bridge_manager_does_not_r0/bridge.json new file mode 100644 index 0000000..aee58ce --- /dev/null +++ b/tmp/pytest-rebase-resolution/test_bridge_manager_does_not_r0/bridge.json @@ -0,0 +1 @@ +{"heartbeat_epoch": 1786614211.5981486, "bridge_state": "RUNNING", "current_request_id": "SELF_HEAL_BENCH_1"} \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/python.exe b/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/python.exe new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/python.exe @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/seat_bridge.py b/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/seat_bridge.py new file mode 100644 index 0000000..6c284f1 --- /dev/null +++ b/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/seat_bridge.py @@ -0,0 +1,3 @@ +BRIDGE_VERSION = 'runtime-test-v1' +def run_bridge(transport=None, stop_event=None): + stop_event.wait(2.0) diff --git a/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/zlg_bridge.py b/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/zlg_bridge.py new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/zlg_bridge.py @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/zlgcan.dll b/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/zlgcan.dll new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/zlgcan.dll @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution/test_standalone_runtime_reject0/status.json b/tmp/pytest-rebase-resolution/test_standalone_runtime_reject0/status.json new file mode 100644 index 0000000..3243a72 --- /dev/null +++ b/tmp/pytest-rebase-resolution/test_standalone_runtime_reject0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786614216.6407876, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 15, "qualified": false}, "frame_epochs": {"0x301": 1786614216.6407876, "0x302": 1786614216.6407876, "0x303": 1786614216.6407876, "0x304": 1786614216.6407876, "0x305": 1786614216.6407876, "0x306": 1786614216.6407876, "0x307": 1786614216.6407876}} \ No newline at end of file diff --git a/tmp/pytest-rebase-resolution/test_standalone_runtime_restor0/status.json b/tmp/pytest-rebase-resolution/test_standalone_runtime_restor0/status.json new file mode 100644 index 0000000..c62eff2 --- /dev/null +++ b/tmp/pytest-rebase-resolution/test_standalone_runtime_restor0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786614216.7809346, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 16, "qualified": true}, "frame_epochs": {"0x301": 1786614216.7809346, "0x302": 1786614216.7809346, "0x303": 1786614216.7809346, "0x304": 1786614216.7809346, "0x305": 1786614216.7809346, "0x306": 1786614216.7809346, "0x307": 1786614216.7809346}} \ No newline at end of file diff --git a/tmp/pytest-self-orchestration-fix-20260813/test_bridge_manager_does_not_r0/bridge.json b/tmp/pytest-self-orchestration-fix-20260813/test_bridge_manager_does_not_r0/bridge.json new file mode 100644 index 0000000..9563725 --- /dev/null +++ b/tmp/pytest-self-orchestration-fix-20260813/test_bridge_manager_does_not_r0/bridge.json @@ -0,0 +1 @@ +{"heartbeat_epoch": 1786628979.608127, "bridge_state": "RUNNING", "current_request_id": "SELF_HEAL_BENCH_1"} \ No newline at end of file diff --git a/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/python.exe b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/python.exe new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/python.exe @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/seat_bridge.py b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/seat_bridge.py new file mode 100644 index 0000000..6c284f1 --- /dev/null +++ b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/seat_bridge.py @@ -0,0 +1,3 @@ +BRIDGE_VERSION = 'runtime-test-v1' +def run_bridge(transport=None, stop_event=None): + stop_event.wait(2.0) diff --git a/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/zlg_bridge.py b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/zlg_bridge.py new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/zlg_bridge.py @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/zlgcan.dll b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/zlgcan.dll new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/zlgcan.dll @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_reject0/status.json b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_reject0/status.json new file mode 100644 index 0000000..0620643 --- /dev/null +++ b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_reject0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786628984.6589825, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 15, "qualified": false}, "frame_epochs": {"0x301": 1786628984.6589825, "0x302": 1786628984.6589825, "0x303": 1786628984.6589825, "0x304": 1786628984.6589825, "0x305": 1786628984.6589825, "0x306": 1786628984.6589825, "0x307": 1786628984.6589825}} \ No newline at end of file diff --git a/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_restor0/status.json b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_restor0/status.json new file mode 100644 index 0000000..634e685 --- /dev/null +++ b/tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_restor0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786628984.8052018, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 16, "qualified": true}, "frame_epochs": {"0x301": 1786628984.8052018, "0x302": 1786628984.8052018, "0x303": 1786628984.8052018, "0x304": 1786628984.8052018, "0x305": 1786628984.8052018, "0x306": 1786628984.8052018, "0x307": 1786628984.8052018}} \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign-final-2/test_bridge_manager_does_not_r0/bridge.json b/tmp/pytest-self-repair-redesign-final-2/test_bridge_manager_does_not_r0/bridge.json new file mode 100644 index 0000000..b8abc4d --- /dev/null +++ b/tmp/pytest-self-repair-redesign-final-2/test_bridge_manager_does_not_r0/bridge.json @@ -0,0 +1 @@ +{"heartbeat_epoch": 1786608369.3573637, "bridge_state": "RUNNING", "current_request_id": "SELF_HEAL_BENCH_1"} \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/python.exe b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/python.exe new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/python.exe @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/seat_bridge.py b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/seat_bridge.py new file mode 100644 index 0000000..6c284f1 --- /dev/null +++ b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/seat_bridge.py @@ -0,0 +1,3 @@ +BRIDGE_VERSION = 'runtime-test-v1' +def run_bridge(transport=None, stop_event=None): + stop_event.wait(2.0) diff --git a/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/zlg_bridge.py b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/zlg_bridge.py new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/zlg_bridge.py @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/zlgcan.dll b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/zlgcan.dll new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/zlgcan.dll @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_reject0/status.json b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_reject0/status.json new file mode 100644 index 0000000..f88f41d --- /dev/null +++ b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_reject0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786608374.4077742, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 15, "qualified": false}, "frame_epochs": {"0x301": 1786608374.4077742, "0x302": 1786608374.4077742, "0x303": 1786608374.4077742, "0x304": 1786608374.4077742, "0x305": 1786608374.4077742, "0x306": 1786608374.4077742, "0x307": 1786608374.4077742}} \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_restor0/status.json b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_restor0/status.json new file mode 100644 index 0000000..8df1726 --- /dev/null +++ b/tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_restor0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786608374.5544136, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 16, "qualified": true}, "frame_epochs": {"0x301": 1786608374.5544136, "0x302": 1786608374.5544136, "0x303": 1786608374.5544136, "0x304": 1786608374.5544136, "0x305": 1786608374.5544136, "0x306": 1786608374.5544136, "0x307": 1786608374.5544136}} \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign/test_bridge_manager_does_not_r0/bridge.json b/tmp/pytest-self-repair-redesign/test_bridge_manager_does_not_r0/bridge.json new file mode 100644 index 0000000..bad4b5c --- /dev/null +++ b/tmp/pytest-self-repair-redesign/test_bridge_manager_does_not_r0/bridge.json @@ -0,0 +1 @@ +{"heartbeat_epoch": 1786607635.6544752, "bridge_state": "RUNNING", "current_request_id": "SELF_HEAL_BENCH_1"} \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/python.exe b/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/python.exe new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/python.exe @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/seat_bridge.py b/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/seat_bridge.py new file mode 100644 index 0000000..6c284f1 --- /dev/null +++ b/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/seat_bridge.py @@ -0,0 +1,3 @@ +BRIDGE_VERSION = 'runtime-test-v1' +def run_bridge(transport=None, stop_event=None): + stop_event.wait(2.0) diff --git a/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/zlg_bridge.py b/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/zlg_bridge.py new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/zlg_bridge.py @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/zlgcan.dll b/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/zlgcan.dll new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/zlgcan.dll @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign/test_standalone_runtime_reject0/status.json b/tmp/pytest-self-repair-redesign/test_standalone_runtime_reject0/status.json new file mode 100644 index 0000000..36ecd6f --- /dev/null +++ b/tmp/pytest-self-repair-redesign/test_standalone_runtime_reject0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786607640.7259998, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 15, "qualified": false}, "frame_epochs": {"0x301": 1786607640.7259998, "0x302": 1786607640.7259998, "0x303": 1786607640.7259998, "0x304": 1786607640.7259998, "0x305": 1786607640.7259998, "0x306": 1786607640.7259998, "0x307": 1786607640.7259998}} \ No newline at end of file diff --git a/tmp/pytest-self-repair-redesign/test_standalone_runtime_restor0/status.json b/tmp/pytest-self-repair-redesign/test_standalone_runtime_restor0/status.json new file mode 100644 index 0000000..f2d3488 --- /dev/null +++ b/tmp/pytest-self-repair-redesign/test_standalone_runtime_restor0/status.json @@ -0,0 +1 @@ +{"bridge_version": "runtime-test-v1", "heartbeat_epoch": 1786607640.8686898, "current_request_id": null, "system": {"firmware_revision": 15}, "firmware_identity": {"firmware_build_id": 16, "qualified": true}, "frame_epochs": {"0x301": 1786607640.8686898, "0x302": 1786607640.8686898, "0x303": 1786607640.8686898, "0x304": 1786607640.8686898, "0x305": 1786607640.8686898, "0x306": 1786607640.8686898, "0x307": 1786607640.8686898}} \ No newline at end of file