同步移动端工程、设备控制改进与发布资料

This commit is contained in:
2026-09-26 19:21:12 +08:00
parent 912ba432cf
commit 98eadc5c8b
137 changed files with 9274 additions and 85 deletions
@@ -14,11 +14,13 @@ import sys
import time
import traceback
from typing import Any
import urllib.request
from app.ota.diagnostics import DiagnosticLog, clear_failure_log, persist_failure_log, sha256_file
from app.ota.package import extract_payload, inspect_package
from app.ota.state import read_json, utc_now, write_json, write_last_result
from app.ota.versioning import SoftwareVersion
from app.system.kernel_recovery import CANDIDATE_RELEASE, KernelRecovery
TARGET = Path("/opt/matrix-screen-controller")
RELEASES = Path("/opt/matrix-screen-controller.releases")
@@ -34,6 +36,32 @@ class UpdateFailed(RuntimeError):
pass
def verify_kernel_capability_retained(
before: dict[str, Any],
after: dict[str, Any],
performance_before: dict[str, Any],
performance_after: dict[str, Any],
) -> None:
previously_healthy = (
before.get("release") == CANDIDATE_RELEASE
and before.get("available") is True
and before.get("marker") is True
)
if not previously_healthy:
return
if (after["release"] != CANDIDATE_RELEASE or not after["available"] or not after["marker"]
or set(after["policies"]) != set(before["policies"])):
raise UpdateFailed("OTA lost the validated candidate kernel or cpufreq policy")
requested = performance_before.get("requested")
if (performance_after.get("available") is not True or performance_after.get("requested") is not requested
or (requested is True and performance_after.get("effective") is not True)):
raise UpdateFailed("OTA changed performance mode availability or effective state")
if requested is False and performance_after.get("current_governors") != {
name: value["current"] for name, value in before["policies"].items()
}:
raise UpdateFailed("OTA did not restore the original CPU governors")
def snapshot(root: Path, *, ignored: set[str] | None = None) -> dict[str, tuple[int, str]]:
records: dict[str, tuple[int, str]] = {}
ignored = ignored or set()
@@ -81,6 +109,9 @@ class Transaction:
f"job_id={self.job_id} current_version={self.current_version} "
f"target_version={self.target_version} packaged_at={request.get('packaged_at') or ''}"
)
self.kernel_recovery = KernelRecovery(DATA_ROOT)
self.kernel_before: dict[str, Any] | None = None
self.performance_before: dict[str, Any] | None = None
self.job = {
"id": self.job_id,
"target_version": str(self.target_version),
@@ -118,6 +149,13 @@ class Transaction:
)
def prepare(self) -> None:
self.kernel_before = self.kernel_recovery.capability()
with urllib.request.urlopen("http://127.0.0.1:8080/api/status", timeout=8) as response:
self.performance_before = json.loads(response.read().decode("utf-8"))["system"]["performance_mode"]
self.diagnostics.write(
f"kernel_before={self.kernel_before['release']} "
f"cpufreq_available={self.kernel_before['available']} marker={self.kernel_before['marker']}"
)
if any(path.exists() for path in (self.work_root, self.release, self.data_candidate, self.data_backup)):
raise UpdateFailed("OTA transaction paths already exist")
self.work_root.mkdir(parents=True, mode=0o700)
@@ -273,6 +311,15 @@ class Transaction:
raise UpdateFailed("new service reports the wrong hardware mapping")
if driver.get("oe_timing_backend") != "h618-pwm4" or driver.get("oe_pulse_faults") != 0:
raise UpdateFailed("new service did not pass the PWM4 OE health check")
before = self.kernel_before or {}
if (before.get("available") and before.get("marker")
and before.get("release") == CANDIDATE_RELEASE):
verify_kernel_capability_retained(
before, self.kernel_recovery.capability(), self.performance_before or {},
(status.get("system") or {}).get("performance_mode") or {},
)
else:
self.diagnostics.write("kernel_preexisting_degraded=true; application update may proceed")
self.diagnostics.write(
"health_summary="
f"version:{status.get('service', {}).get('software_version')} "