From 6d768aaef48365c0dd10a922692b1980867f2d2b Mon Sep 17 00:00:00 2001 From: vee Date: Fri, 14 Aug 2026 11:17:17 +0800 Subject: [PATCH] update model --- apps/web-console/app.js | 250 +++++++++++++----- apps/web-console/index.html | 4 +- apps/web-console/self-repair-seat3d.js | 75 +++++- apps/web-console/service-worker.js | 8 +- artifacts/local-server/server.combined.log | Bin 0 -> 2735118 bytes artifacts/local-server/server.pid | 1 + artifacts/local-server/server.stderr.log | 0 artifacts/local-server/server.stdout.log | 0 .../vehicles/denza_n7/user/k-c78dff.yaml | 61 ----- .../denza_n7/user/mode-ceff921663.yaml | 46 ++++ src/cognitive_agent/server.py | 21 +- tests/e2e/test_seat_web_api.py | 27 +- tests/e2e/test_self_calibration_api.py | 16 ++ tests/e2e/test_self_repair_api.py | 9 +- .../bridge.json | 1 + .../python.exe | 1 + .../seat_bridge.py | 3 + .../zlg_bridge.py | 1 + .../zlgcan.dll | 1 + .../status.json | 1 + .../status.json | 1 + .../bridge.json | 1 + .../python.exe | 1 + .../seat_bridge.py | 3 + .../zlg_bridge.py | 1 + .../zlgcan.dll | 1 + .../status.json | 1 + .../status.json | 1 + .../bridge.json | 1 + .../python.exe | 1 + .../seat_bridge.py | 3 + .../zlg_bridge.py | 1 + .../zlgcan.dll | 1 + .../status.json | 1 + .../status.json | 1 + .../bridge.json | 1 + .../python.exe | 1 + .../seat_bridge.py | 3 + .../zlg_bridge.py | 1 + .../zlgcan.dll | 1 + .../status.json | 1 + .../status.json | 1 + .../bridge.json | 1 + .../python.exe | 1 + .../seat_bridge.py | 3 + .../zlg_bridge.py | 1 + .../zlgcan.dll | 1 + .../status.json | 1 + .../status.json | 1 + .../bridge.json | 1 + .../python.exe | 1 + .../seat_bridge.py | 3 + .../zlg_bridge.py | 1 + .../zlgcan.dll | 1 + .../status.json | 1 + .../status.json | 1 + 56 files changed, 424 insertions(+), 148 deletions(-) create mode 100644 artifacts/local-server/server.combined.log create mode 100644 artifacts/local-server/server.pid create mode 100644 artifacts/local-server/server.stderr.log create mode 100644 artifacts/local-server/server.stdout.log delete mode 100644 config/modes/vehicles/denza_n7/user/k-c78dff.yaml create mode 100644 config/modes/vehicles/denza_n7/user/mode-ceff921663.yaml create mode 100644 tmp/pytest-feature-dynamic-mode-push/test_bridge_manager_does_not_r0/bridge.json create mode 100644 tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/python.exe create mode 100644 tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/seat_bridge.py create mode 100644 tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/zlg_bridge.py create mode 100644 tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_opens_0/zlgcan.dll create mode 100644 tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_reject0/status.json create mode 100644 tmp/pytest-feature-dynamic-mode-push/test_standalone_runtime_restor0/status.json create mode 100644 tmp/pytest-rebase-resolution-2/test_bridge_manager_does_not_r0/bridge.json create mode 100644 tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/python.exe create mode 100644 tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/seat_bridge.py create mode 100644 tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/zlg_bridge.py create mode 100644 tmp/pytest-rebase-resolution-2/test_standalone_runtime_opens_0/zlgcan.dll create mode 100644 tmp/pytest-rebase-resolution-2/test_standalone_runtime_reject0/status.json create mode 100644 tmp/pytest-rebase-resolution-2/test_standalone_runtime_restor0/status.json create mode 100644 tmp/pytest-rebase-resolution/test_bridge_manager_does_not_r0/bridge.json create mode 100644 tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/python.exe create mode 100644 tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/seat_bridge.py create mode 100644 tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/zlg_bridge.py create mode 100644 tmp/pytest-rebase-resolution/test_standalone_runtime_opens_0/zlgcan.dll create mode 100644 tmp/pytest-rebase-resolution/test_standalone_runtime_reject0/status.json create mode 100644 tmp/pytest-rebase-resolution/test_standalone_runtime_restor0/status.json create mode 100644 tmp/pytest-self-orchestration-fix-20260813/test_bridge_manager_does_not_r0/bridge.json create mode 100644 tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/python.exe create mode 100644 tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/seat_bridge.py create mode 100644 tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/zlg_bridge.py create mode 100644 tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_opens_0/zlgcan.dll create mode 100644 tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_reject0/status.json create mode 100644 tmp/pytest-self-orchestration-fix-20260813/test_standalone_runtime_restor0/status.json create mode 100644 tmp/pytest-self-repair-redesign-final-2/test_bridge_manager_does_not_r0/bridge.json create mode 100644 tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/python.exe create mode 100644 tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/seat_bridge.py create mode 100644 tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/zlg_bridge.py create mode 100644 tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_opens_0/zlgcan.dll create mode 100644 tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_reject0/status.json create mode 100644 tmp/pytest-self-repair-redesign-final-2/test_standalone_runtime_restor0/status.json create mode 100644 tmp/pytest-self-repair-redesign/test_bridge_manager_does_not_r0/bridge.json create mode 100644 tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/python.exe create mode 100644 tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/seat_bridge.py create mode 100644 tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/zlg_bridge.py create mode 100644 tmp/pytest-self-repair-redesign/test_standalone_runtime_opens_0/zlgcan.dll create mode 100644 tmp/pytest-self-repair-redesign/test_standalone_runtime_reject0/status.json create mode 100644 tmp/pytest-self-repair-redesign/test_standalone_runtime_restor0/status.json 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 0000000000000000000000000000000000000000..2ff5136057253bddf23e410934e2d99129c5c29a GIT binary patch literal 2735118 zcmeFa-EL$@k|yYwO96TZLKiCaYzLF^#~(EkxjEIfh%uftWi3-@6WZt%PfpX1m#9E3^O#d8mT$KBrp zoSD6Ud>lT)r%%EuK6``fU*U6S;S!&hZ@R^qdz^XIojb?Tmv}GVdx_)s;Wger!DEc? zKfw3PZ^YmEJRIVkJmeRC7rw`5&%&GDx5+ojH{NyMcaGosAsplHo9?&%7yQN@e)%Q- zzwS^haP={M_XO9Rhj-oU0#|*8^RMvT-{HH?!(ZWue0KOc!Sx!yd5>%5{&w+sLAlf6 zS-+#5Fv2-`euMj+;;x?q+Y7vkY$stKXXN6-ql$p1^6yN3*4<1=TF$OWF#=H%RG(9}a4=(m`tFJ1eetA+q zh#LMa&b`Fr1Q^coTj}cf8}e7b&kwVV{lCaEe%UV{LQhkm|4VFgJHIUUbptDU1r04= zb8pdtcA?ulJf7jdr_g}-$M2x&SJ3we+E}2~o!}Zda*HEJ@CqN|Rkm{nCqVTYnE%v6 z99!H7M;2YX8lh*HJ7c_pzm+~sY(f0Jr^H0N3!u0Irql;scmJKjV$Y$|`_B5LXL)#J z)b$k;^enq*2dD4^R(CR4vm4R9$n^w1Mr8T~_aMmc@&8=~xTHT6L~lB)d4j*fEW~HN z#2J6N3EPNVO?ZxY0-rXT*KGX=J$3pTZT<(euq$|D@hie7o`WL@Z;f^!t?Ulh+~A5! zv_H}DRrs;9k;JLRn_qSQKk|rwdjBoQ;rFmF;cb75zi+!=k?-qYFaJp|u+#Mu>6&Bw z`VZY_pLT3~)MW_C^vpNbjTh_-|Sz3W=E#B#Uq zdBtXIt1;7duq|z57karzzadtxbNEv2eO*$1zby9iLuX5OYy5Pb?HxJFXtmEDp-qXk zOH3skyU$aj7n9>+_apc>@pHme{p4I9n_h2)QKId{EFXFY{Rn0#=9+e1(aR5r$Gk>g zBi{TWgItXs*Uw9jVHYp1%U;L6)WIg?;_>}#bBR2%y({#m{p{$fL;y>zW!>g#|J|y!n zMIYXm`RY}*_`W_W4Xvl7g^vq&k}Or^B_p(rcr3O8$yL6_6)zmEEg{;Ba*ozo1IiOf zQCjGxoS#H}$K6?Jzq^p;HF_35{}nU;GPV)3XS?ua3m|%U+z+9|?vlHZ%*PYROfoAA zJf46b$cUQ6y)p{<1ovv3b+x?H>?{S|JnbZ&uuS)(J`BWJN z6KqlTGvqrC-^;##wochg7Wf?aV;);_ZPJ>=R=>kjMq^|&J!NKN+e$4=a!kv!$FL8{ zEuW*cEpY!YyYY}(+So6X z?cFMt@(>B=b6QHsp2k` z8qb*o8m{$hq{m3ThR|aiT*ft}UC|@kdE_K>dgM4--{v^@3{f&I$94uuk~uA>Wy_I_ zZarK^L*vP1G+cFX84aNamzk5xAbV-+k(12nk=ypjq9kLt+Fh#hNaJh_a9 zs}3%sA@txfvYG%h<#u_QXgyKp|BkwBDW1++Xp=8X8Y7qv5K9%V-EaxQwiT z!RXR9x=b=BmuZ>HOvz=;xlEEdEvIG6(du7p*LD>yqoMKSG8(QrxQvF-gUh6v8rCt{ z)GK)8z+GaG9%xO7oT8^y9Ec=|*G52>EjSrU@#^udrh7sCwnS_U9Om@{6t1_oY zZrdZ1%h+2E^D-^xWupJn>R&ALGQ(&*xr`>II=GC6(1Xjcj!9O>wu&y3^>B>oF?eKh z8GFkimucT}wE7o&E~BCGKhOhahAxXchPe=akG z(3;CcJgj4~%#`w(mJqC|VE@RDQ(jHih0$Pp&wE7o&E~BCG&;1qur`5mMa~TbdCzsK1)xl*ngdSWb)zpy7*m0R8b8?xM zxlF35q2*Xw4#$|<&uD7(FZNtUL*vP1G+cFX84aNamq|4>^vHG|Imw({re!X}Iwt$k z*(7sXPRo{~)xX$t84Zmmm(g(5!DTdr9$Y5X)R4>AahW7@dgQh}GRK&#$Cy&>7%it& z%b8SF$25e-i^~k*^5-%`2(7tH#KSr!OOM=_IX!aQ9+_Om-f}p`)b<#Y_&=@w#WpW9 zgvOK0Xi}v>Z#zVP2+v%hBpz?757F#*@ovxa#0C8bS{) zlWJ<{k?lNkk~zniS{`F!9g}@VGs&Em)3W7g^)L2ZMnmJtWi(uMa2XAu2bW1THRLjO zTqen!9=UCg%rPeGF(%eAJ%%wRt^UQH%V=mkxr~OZ4lbi1^x!h7riNU`j>{yOlgqTs zWl~KIEyvPwIL6fW7}K<>I;J5sUR-7fmp_*oLTJrpA|BQ;Sw@$AnUl-3%4MP!nU}G* z9CDfVxr|o-Vw;x{JJ!&6av2R*9b863=)q-J$7DO|AFYRTe;wp?;Urv#`*4XXz6f9A@l|*N#<7d%Cpb32r}ywW3Ll4$aBLC|aBlDY zbKISgvpeA%oSovc&%>MW0U0Gueu}0 zcs&nKe>y+Hxr1;D7Cy%@;Y%ZYUf!MI-7Fl1LwJ}G{w_MMKEjcU?zk}j1>T+G)BE_G zeD>1^n9{GEf*R$gA)gx|Y^;0sWkHM{aTC2oJwII035H!2G8k z;y1u|4P(@ZSjM zPGE}?lRrE%LJN^7eFm9d;OzGm$km5CXcCV4egV0P-+lAmC)@Va!}z?pO+vW8=9iMi z)Q`|dr%_^@acD8)aJZE|BFWUJYGPpz^_7V)i4~_JqKXe@R4tQ4i={no9B$ z@Wzs3I)(3BX2ez{zxf);fYf{kOZn&U&#S;MnI-8HuR7k<@8kPtq^;K>?^RSO*EB_+ z-uL#aSJh&teN-A+Pf4G^|K;*jk|#dy&Wf$>B2#q@%k{HDi5gSMt%yz!+bRd~`8E?4*6O^1p;()1br4}YRrsdgVSn->#P0Bcq+^zKbwY0Kd zcAqk)d51fCc*p6uj3Y|SD*2ilT$|$7O?NNjk|k#-=CewxcBZw{zYG5^{KxS3@FtQE zs3GgLirrp&NwW_%80V9gon!i;8r-%NZlHg$^_OS^dW#k*Y;VqOEAm*j5s`<*$nG56 zL*`O()WvSpCCS`!&I?CfN{_nCG1^nZEX3&BG&G(w2{c^mnKO^sBpO1Gac~(!q(|OC z1ZU}ywHd{$^Y%CnUgJ3UxSJPFE@L-;SN33#x%gUz4=tzu86;7QwQ!jsG+tb02$w&X z8A52yWg;GO8GDaRF0-k|t(`f2VmUG<#3;Zn3h2w6d70JK-deMqnz+nZ$r5>T84Xt* zTt-9a!DVEoH$AeoM<$nPpUX(C2QA0ea+1u+Wm@Jk6NNq4*4+v}*3fuz84Xt*Tt-9a z!DVFiGJ0fdk4!GpK9`x2%U~{-t>q+{lgqTsWu!)0`kc*Lv|E`lj$3MIJh_a9s}3%s zA@txfQ*s#xk4!GpK9`x1%a~hElKJLdkn?5Qm@gx12iC)7G&G)EM#EJHm(dVsM{YT~Of@yEW3rhwFXI78=Cqu)EvF_fqgCU0av2R*9b863=)q-J$7H$3 zTg+-EnbRY;>X9eCni_H$8!pqAIW4Dc%c+IS#5R>jIWIbWvj z`7+`IQ%w!?GIn{HBy)0^mbr{p|6jmBb9&^KJ#wn4 zA(ye^GD+sNoX21}eJws*(|wF8r?M;ZOZ=g?AIl1=h-R9d1MSJ8WXH4I-0anz0iR%R z1=(Lhc8Zsk@U66%$5#i-(R5PEQ#3Av29M@}*)muZ>HOvq&%EQeg?F>sl6EpETE zN}|YQvwhEd7G9yk>kfafQQeqc@eKbxMJr$6{dcGWdlg>eid+1Dftl84 z-H}@y8DrdR&!9RicMr??@77Fkiu=yh(s?pL4VQO~pJ-9TR|ivV8=(i6mKC-+gVt^a zZM35(^S(!4dPDA(RsU{TQHztcUZjQ^zdW}4@I%KJ?$)@%8!&@wa00=90){X`oM6Ll zHdX&$f!hdw`5rhgf#xN!`JwUq|6;_Fua6m#t`%>?dY1`KETiM7~+BeGj?$iV><2YH0V8?~q>c3T>y4x_?|+x+l+B zO1qD0)js=Y>cqQbjZ<(CP1bf^WqmE8muL$&Xbl%=7kBU_yXZ?7@GK9HTeaPNM&0le zXXvF~by3Go_^0sO@H~7Oz6yT`zsI?Y&UQstABT@%sj@=jF1%xW3d0$_e}03r)9?hJ zeiL5cReb2AcP0lsTjBmPfFr??Uc1@D= z$b-({LzoA(^~g!)n{AnS&^Gd*GxW5y9Ba!VmuY`aL0Y|Lv;VX1ku@})Tt>rH2ba+h zdT^OJxePK9wjMdje6uZ+%e28|=HxOCmP0PnK9@_^>dl1MdoEJqkz85X*sQqF-0xrM^9`=N`}yQahV}p{#<4V zp$C^4lgpTMnIv<1ou^XM0ekaMCmeX<+uxTZXG~3obFQcLHh9q4DY;hNR@*KMWzX?jQPi*gx3x4}FT+G)BE_GeD+hUD7!$7 z_*J)F!y+8x-TC`9W^cleSXtv3Yik_fx=Xyj#W`80N>(m8?mlsacbgdd7*`#3&wY$0 zje$bO`R6z$7!U9%Id%l3CqN^2AS)B?;W@$gp5w{`ye^Qr_#%9b$5&YUVT|=5#^Dnj zo8wAZgK1P?{fMZ=xT**($I^1vS8`cqd{^7rlLxI-)}^^e%aPTuo}rz{N^jDRZ*hFN znpA4b>HG-iXgS-loWB3tY_qa*i>ygq+GhK|ym@W6I@iwb`*>+@R z2#qgW&3m)el_{UVOTFp(%qy%=mFwXwtR;EdTG({lk@ul3NIR<^p^r}IoNa&}#cHLo z=zpTjH?`7-44JH@UcSFR&LS%|-hxF+BqaN8q_vslXk7Wwir1`T^ZR9=^}3^_t;<1J ze_iXG=aAO9zIP7oDnqGt?d%--`WW+i%dKmD@0=tz$4|>ujcZvO`{&`WIsRL3Da)0E z*IDoC`oZhxt=9L>$zR95Y)6}r+pLZ~jC}Lj*Q=7>e2vY@WNVMiJZRhV9`?ayWJH#hV`n*inQ!g| zIq%^R^MvZ2F%-2}3zr!}xr~OZ4lbi1^x!fRav5_jlVnbh+_FcWlFOJ|PLeq-r)|s0XF}|` z%v51ko?J%5RR@>R5PEQ#DY=Y;M`m88?YztZ;yGElB$u1E^~i0Z@#-Ijq~zZ}3?a1c zANqK>8jj6sIDMIuvA4?DMT>J}{Oai+G&G(bS;JL_N7fK}cw}wHy*=;Xe0$q9^`mED z{Lps%aESha^X+Xdr!R9dt5%s+)M72U&>=Kl%xVaiKeHM_Xw9r59?rM7oHg8+IX!aA z9y!hZW|qh%OC*+)WKPRz+j45+GTN+hPcEb3s)NgD2tBwA=iA#ymr3UI$Sr&1H20fa z#*WJ*nbUIGww#){j5cfBlgnth>fkaOLJuyJ=6=&7TY6-UF||L&q|G+99V?cMg@(q{ zBWt+o@W>iM509+j@n~1f+gs=D)4Xvq_LdpD>;zfI95v}5G&G+5gNCb){y{_N(Lba$ zn#tJh7<-aAJ#xz)xt`gb;*m8po*r4lRfk8`5c+sza%5|coaRQ;Be&|2kKhA2PuF%N zwJ&p8PTQ7K%Xm<1Q+YIATxJNDKbILoXw79J9&#B=F4LDeJ#xz)In5bmUdARbBbJk7 zPRnWAa`NoKcEl^TsXQ7_E~CwYt%J*G2tBw=nlnm|Z0V6Xx25g5E#d>yoKaeio#iB% zlgqTsWwiO7_FP6orH2ba+hdT^OEznLD{(jzl3(|%rN#yvhvSCp4rSduxpOv_wmrtGR= z&t)_;o?J%5RR@>R5PEQ#8Ta^b@W|vc?Q@w_Q^P30E(%C8CzolN%V_m4_FP6oPcEb3s)NgD2tBwA z>zM4L%OrDp- zWs=NkIc-}`OCHjE2yI%dn2gp35Ye(<8U)k;klK3f6T@eVNm8+P0ip zxJ+zQc{E;JW(b!*ml;B6&1E7U)-l=b$27x^GE&XU`7&+Kmk}+dni}S1Z1OUFnUl-3 z%w@Ft7u)$Vu}#&7flXlOjSjE1WY zE~6pz;4-PEh923{Ba_Rt&t+0g4K2sca+1u+Wm@JkTK$VXm(kF8av2R*9b863=)q-D zO$|M=rAH>0X`9PTSjS{u$JCcOxlF5ECTg*kF{U9jUR-7fmp_*oLTJrpBA(c$KfL!n}h?L+k5{UcW316PWT3A zr}*si@Fu*#t9)`2_Hjmze;#J|PoGcj>5(-l)!~sfgdQF_;bC-X7hNWqlOwmvkq^2| z`C+#bdtZy&_rAGbJ`1nHO}N9~>#%?)e+xgo8_sa#E2z=jf@@`4Qe- zgwt>ckAH#ZKD_?|ebfx^NBApo%mnX_@P3ACM~F}^aQ?iDa^##`aftILSeNY62bj{Y zNxyg#e#AA$cs;;%5AWs}-+c{ia@{?yT4gO~_?0O>eTv6Lhw&6gX5BlPaeo9nBb<}# z1h>=-9pby@`0pH#1u!1rlbgMv%yXOKT#3j{v?vmh{bTy9Mf%oTNJq3K+~FOxaSNs( zM`Lfi8C%JyGKbCYm#?60(a!gf!6o$m5^cZ_jo8l*oh{w1@w#=6bmWNi1~R+vlq|jCRmZ&g zxcg_Mt=EWm*~;rjyUZv}!9n_M{2_KvuksRY;RdbY0_{Q=;4b>o1?=y{? zV?;aQpTci3d*MsWfcQiBJ$UIw$EKwB{N-~W#3Lt}_cLh8dLCvDnFp;k51Pi{If7s} zf{r7v+5-Z zNHVA8v~4*xaha*YtUS4lhN})PqapO*GHInvav3`=lVnbh+_FdJDwOtICdr(Z)3)W* z#ARlcav2R*9b863=)q;uN}J>|c3dXOoF2JlkIYpl?YT^nIW4Dc%c+UWNWD&O#LF`; zqv5K9%V-F#xy)f#v(5Q3mNOXpGN(sw)gwnOuCEBT{aMGkUoKVcGBK!I$lb$o&Y0!u zJrT{Uy8dhk*J=%%{YnBu`273Z_7VCp0@gIxGXmB$w7rr*^fd=-y@*t@d9Ngp!)C=+ z)}`XJl0Z%l^{ph3Lt5ub0y(t8=W1EKDu;2EMb)~JKn{IfD+%OKwilbMd#ug(u&nPL zUQ5nqHH4fFmKD|Oyu6l`1pYGYOC7A=*{G{c^6Tf8Qr}!> zYatA`oqiSjs@(f$tX2~Expa6rs~Wj%leDC5!-d%WWAEWkmNk_pxl|mcCJcn1I&ZAp9f9rL6FPXahW7@ za+#L7%mh`GxnA0y%S;r`;5ExjdI=3z9b863=)q;uIwSPRmL8d0rhP7x)`Ots*jY}J zIk`;BTt>#b>ftieO1X@Ns}3%sA@txfX`K;zWJ`}sF4I1jN$Ww-a_lT8$(&rKWiB(T zip$8}UO8svnU~RU)xl*ngdSWbtusQ8Z0V87W!mR5X*~#9j-BNsnUl-3%w^^Zd$1p4 z($IKv84Xt*Tt-9a!DZ4qBlO6Y9+_OGZ7y@fH8{E*-7G6^`!Xk&X_d=FE!Hx|6w%a| zml?w4&t--XT639*C-$r4GB#YMFLQe2mOV1-m~3awi{&Jl({kFjoSL|dRxjk0mx*mX z&#~&@GMeN(xD4x-Ws=NkIc-}`e)Pn4?yux!v}znrE~DY9gUe_LJ-7_(nC!Vsk~uwc z+a7s;t(J59t9?iByZ+0YSGhk6cX&JtZ^P3tyw1JMMwuF{Sf0BFpIyX;(D?Re>zYnd zTHXJ`eiyMJxz*C&uI~tuBv?N}A4U*&h-Hjs8KdQQ<-J6e#Vx$|yY6)np1_+g!ezJ$ zXW<&}WX=A2oV&;SXE^pM+;msm;W&=a^&GuGIxqX>T!d5X27G|mgRqb9S-}6# z@P359V~o8{@b0MN`e%4I#dUl5j*r7f-8s495a&WUS@&3*?_s&O&v0~Qvx`_x2h07e)>-fBo=WTIWoM;Bv$$|w z_Dk%W_o-UNzxf)Qy}14|=`UN+CfUopO23|ZSoTZwImuh>A~r1f{_b0=EvA3QY8SDe zi+3sI`q9?4i&#$5_3R=h2#xs+w;aX=M z*1L#B517}ZM;>$z&MWPT9@)|(GY{Hw^_{FU_&J_&-YqT1!g5w+PA=0Xmzg8SB9>Ff zEU(tk_%N$sDS0!iVT88KY84Or2b=z3*t2YBg!u#`zNheLbByuII{Y%iEbnAAd4`eW zL-@N9{w^?*Jj1&qj4;b_8DU=F-8nwJkI%_xw>82%#@O^>_uPjE9s|V~zbV{bFdhJh z96JKi6QGehkX-m4o)g^1Ij%gw>mrk(mA+YKRE@fOk`eQRu#02yIquHL*`4qWzITeT z{paCLc!5{R`c8mkr#mkBp{PZ!Zf3XM9E{tVbggD){oGK?=zEX&syv=sbnps zS<9yNLH%cxMo+y7n+>Dd^vfPIO7n84ZANJxX^nF-^JrHYN}cmx^XO}1%$w|}rWvJq zNwyY0ZIblndsxo%s$)iJUI)vWf$OYyb$;FYd7Bxfe<}8*4mKgTn2-4($T!cnT$TLh zYb@r%{*pUb%0GvH`!h;E1b=@f=BnKLXKZGa{(QVkDc6s-rWvJqN!K!?G$G_H4C|;{ z#⋙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 literal 0 HcmV?d00001 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