mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-20 07:43:48 +08:00
Here a we go
This commit is contained in:
+1
-1
@@ -21,7 +21,7 @@ fi
|
||||
export QCOM_PRIORITY=12
|
||||
|
||||
if [ -z "$AGNOS_VERSION" ]; then
|
||||
export AGNOS_VERSION="12.8.28"
|
||||
export AGNOS_VERSION="19.6.1"
|
||||
fi
|
||||
|
||||
if [ -z "$AGNOS_ACCEPTED_VERSIONS" ]; then
|
||||
|
||||
@@ -1 +1 @@
|
||||
DEV-230ed14e-DEBUG
|
||||
DEV-230ed14e-DEBUG
|
||||
|
||||
+5
-1
@@ -21,6 +21,10 @@ def copy(src, dest):
|
||||
shutil.copy2(src, dest, follow_symlinks=True)
|
||||
|
||||
|
||||
def ignore_broken_symlinks(src, names):
|
||||
return [name for name in names if Path(src, name).is_symlink() and not Path(src, name).exists()]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser(prog='pack.py', description="package script into a portable executable", epilog='comma.ai')
|
||||
parser.add_argument('-e', '--entrypoint', help="function to call in module, default is 'main'", default='main')
|
||||
@@ -43,7 +47,7 @@ if __name__ == '__main__':
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
for directory in DIRS:
|
||||
shutil.copytree(BASEDIR + '/' + directory, tmp + '/' + directory, symlinks=False, ignore_dangling_symlinks=True,
|
||||
shutil.copytree(BASEDIR + '/' + directory, tmp + '/' + directory, symlinks=False, ignore=ignore_broken_symlinks,
|
||||
dirs_exist_ok=True, copy_function=copy)
|
||||
entry = f'{args.module}:{args.entrypoint}'
|
||||
zipapp.create_archive(tmp, target=args.output, interpreter=INTERPRETER, main=entry)
|
||||
|
||||
@@ -384,8 +384,8 @@ class Controls:
|
||||
self.ecu_disable_failed = self.params.get_bool("EcuDisableFailed")
|
||||
self.ecu_disable_failed_checked = True
|
||||
if self.ecu_disable_failed and self.CP.carFingerprint == NISSAN_CAR.NISSAN_LEAF:
|
||||
self.CP.openpilotLongitudinalControl = False
|
||||
self.CP.pcmCruise = True
|
||||
self.CP = messaging.log_from_bytes(self.params.get("CarParams"), car.CarParams)
|
||||
self.FPCP = messaging.log_from_bytes(self.params.get("StarPilotCarParams"), custom.StarPilotCarParams)
|
||||
|
||||
def state_control(self):
|
||||
CS = self.sm['carState']
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import cereal.messaging as messaging
|
||||
|
||||
from cereal import car, custom
|
||||
from opendbc.car.nissan.values import CAR as NISSAN_CAR
|
||||
|
||||
from openpilot.selfdrive.car.cruise_state import should_cancel_stock_cruise
|
||||
from openpilot.selfdrive.controls.controlsd import Controls
|
||||
|
||||
|
||||
class FakeFallbackParams:
|
||||
def __init__(self, fallback_cp, fallback_fpcp):
|
||||
self.values = {
|
||||
"CarParams": fallback_cp.to_bytes(),
|
||||
"StarPilotCarParams": fallback_fpcp.to_bytes(),
|
||||
}
|
||||
|
||||
def get_bool(self, key):
|
||||
return key in ("ControlsReady", "EcuDisableFailed")
|
||||
|
||||
def get(self, key):
|
||||
return self.values[key]
|
||||
|
||||
|
||||
def test_leaf_ecu_disable_fallback_reloads_read_only_car_params():
|
||||
initial_cp = car.CarParams.new_message()
|
||||
initial_cp.carFingerprint = NISSAN_CAR.NISSAN_LEAF
|
||||
initial_cp.openpilotLongitudinalControl = True
|
||||
initial_cp.pcmCruise = False
|
||||
initial_fpcp = custom.StarPilotCarParams.new_message()
|
||||
initial_fpcp.safetyConfigs = [custom.StarPilotCarParams.SafetyConfig.new_message(safetyParam=2)]
|
||||
|
||||
fallback_cp = car.CarParams.new_message()
|
||||
fallback_cp.carFingerprint = NISSAN_CAR.NISSAN_LEAF
|
||||
fallback_cp.openpilotLongitudinalControl = False
|
||||
fallback_cp.pcmCruise = True
|
||||
fallback_fpcp = custom.StarPilotCarParams.new_message()
|
||||
fallback_fpcp.safetyConfigs = [custom.StarPilotCarParams.SafetyConfig.new_message(safetyParam=0)]
|
||||
|
||||
initial_cp_reader = messaging.log_from_bytes(initial_cp.to_bytes(), car.CarParams)
|
||||
controls = Controls.__new__(Controls)
|
||||
controls.CP = initial_cp_reader
|
||||
controls.FPCP = messaging.log_from_bytes(initial_fpcp.to_bytes(), custom.StarPilotCarParams)
|
||||
controls.params = FakeFallbackParams(fallback_cp, fallback_fpcp)
|
||||
controls.ecu_disable_failed = False
|
||||
controls.ecu_disable_failed_checked = False
|
||||
|
||||
controls.update_ecu_disable_failed()
|
||||
|
||||
assert controls.ecu_disable_failed
|
||||
assert controls.ecu_disable_failed_checked
|
||||
assert not controls.CP.openpilotLongitudinalControl
|
||||
assert controls.CP.pcmCruise
|
||||
assert controls.FPCP.safetyConfigs[0].safetyParam == 0
|
||||
assert initial_cp_reader.openpilotLongitudinalControl
|
||||
assert not initial_cp_reader.pcmCruise
|
||||
assert not should_cancel_stock_cruise(controls.CP, cruise_enabled=True, controls_enabled=True)
|
||||
@@ -297,11 +297,9 @@ class SelfdriveD:
|
||||
self.ecu_disable_failed = self.params.get_bool("EcuDisableFailed")
|
||||
self.ecu_disable_failed_checked = True
|
||||
if self.ecu_disable_failed:
|
||||
fallback_cp = messaging.log_from_bytes(self.params.get("CarParams"), car.CarParams)
|
||||
fallback_fpcp = messaging.log_from_bytes(self.params.get("StarPilotCarParams"), custom.StarPilotCarParams)
|
||||
self.CP.openpilotLongitudinalControl = fallback_cp.openpilotLongitudinalControl
|
||||
self.CP.pcmCruise = fallback_cp.pcmCruise
|
||||
self.FPCP = fallback_fpcp
|
||||
self.CP = messaging.log_from_bytes(self.params.get("CarParams"), car.CarParams)
|
||||
self.FPCP = messaging.log_from_bytes(self.params.get("StarPilotCarParams"), custom.StarPilotCarParams)
|
||||
self.car_events = CarSpecificEvents(self.CP)
|
||||
|
||||
def clear_longitudinal_excessive_actuation_alert(self):
|
||||
alert = self.params.get("Offroad_ExcessiveActuation")
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from cereal import car, custom
|
||||
import cereal.messaging as messaging
|
||||
|
||||
from cereal import car, custom, log
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR
|
||||
from opendbc.car.nissan.values import CAR as NISSAN_CAR
|
||||
|
||||
@@ -63,8 +65,9 @@ def test_ecu_disable_fallback_synchronizes_behavior_and_safety_params():
|
||||
fallback_fpcp.safetyConfigs = [custom.StarPilotCarParams.SafetyConfig.new_message(safetyParam=0)]
|
||||
|
||||
selfdrived = SelfdriveD.__new__(SelfdriveD)
|
||||
selfdrived.CP = initial_cp
|
||||
selfdrived.FPCP = initial_fpcp
|
||||
initial_cp_reader = messaging.log_from_bytes(initial_cp.to_bytes(), car.CarParams)
|
||||
selfdrived.CP = initial_cp_reader
|
||||
selfdrived.FPCP = messaging.log_from_bytes(initial_fpcp.to_bytes(), custom.StarPilotCarParams)
|
||||
selfdrived.params = FakeFallbackParams(True, True, fallback_cp, fallback_fpcp)
|
||||
selfdrived.ecu_disable_failed = False
|
||||
selfdrived.ecu_disable_failed_checked = False
|
||||
@@ -76,6 +79,16 @@ def test_ecu_disable_fallback_synchronizes_behavior_and_safety_params():
|
||||
assert not selfdrived.CP.openpilotLongitudinalControl
|
||||
assert selfdrived.CP.pcmCruise
|
||||
assert selfdrived.FPCP.safetyConfigs[0].safetyParam == 0
|
||||
assert initial_cp_reader.openpilotLongitudinalControl
|
||||
assert not initial_cp_reader.pcmCruise
|
||||
|
||||
CS = car.CarState.new_message()
|
||||
CS.gearShifter = car.CarState.GearShifter.drive
|
||||
CS.cruiseState.available = True
|
||||
CS.cruiseState.enabled = True
|
||||
CS_prev = car.CarState.new_message()
|
||||
events = selfdrived.car_events.update(CS, CS_prev, car.CarControl.new_message())
|
||||
assert log.OnroadEvent.EventName.pcmEnable in events.names
|
||||
|
||||
|
||||
def test_ecu_disable_fallback_does_not_change_other_cars():
|
||||
@@ -93,8 +106,10 @@ def test_ecu_disable_fallback_does_not_change_other_cars():
|
||||
fallback_fpcp.safetyConfigs = [custom.StarPilotCarParams.SafetyConfig.new_message(safetyParam=0)]
|
||||
|
||||
selfdrived = SelfdriveD.__new__(SelfdriveD)
|
||||
selfdrived.CP = initial_cp
|
||||
selfdrived.FPCP = initial_fpcp
|
||||
initial_cp_reader = messaging.log_from_bytes(initial_cp.to_bytes(), car.CarParams)
|
||||
initial_fpcp_reader = messaging.log_from_bytes(initial_fpcp.to_bytes(), custom.StarPilotCarParams)
|
||||
selfdrived.CP = initial_cp_reader
|
||||
selfdrived.FPCP = initial_fpcp_reader
|
||||
selfdrived.params = FakeFallbackParams(True, True, fallback_cp, fallback_fpcp)
|
||||
selfdrived.ecu_disable_failed = False
|
||||
selfdrived.ecu_disable_failed_checked = False
|
||||
@@ -105,3 +120,5 @@ def test_ecu_disable_fallback_does_not_change_other_cars():
|
||||
assert selfdrived.CP.openpilotLongitudinalControl
|
||||
assert not selfdrived.CP.pcmCruise
|
||||
assert selfdrived.FPCP.safetyConfigs[0].safetyParam == 4
|
||||
assert selfdrived.CP is initial_cp_reader
|
||||
assert selfdrived.FPCP is initial_fpcp_reader
|
||||
|
||||
Binary file not shown.
@@ -24,6 +24,8 @@
|
||||
#include "common/clutil.h"
|
||||
#include "common/params.h"
|
||||
#include "common/swaglog.h"
|
||||
#include "common/timing.h"
|
||||
#include "common/watchdog.h"
|
||||
|
||||
|
||||
ExitHandler do_exit;
|
||||
@@ -285,6 +287,7 @@ void camerad_thread() {
|
||||
// poll events
|
||||
LOG("-- Dequeueing Video events");
|
||||
while (!do_exit) {
|
||||
watchdog_kick(nanos_since_boot());
|
||||
struct pollfd fds[1] = {{.fd = m.video0_fd, .events = POLLPRI}};
|
||||
int ret = poll(fds, std::size(fds), 1000);
|
||||
if (ret < 0) {
|
||||
|
||||
@@ -276,7 +276,9 @@ int SpectraCamera::clear_req_queue() {
|
||||
.flush_type = CAM_FLUSH_TYPE_ALL,
|
||||
};
|
||||
int err = do_cam_control(m->icp_fd, CAM_FLUSH_REQ, &cmd, sizeof(cmd));
|
||||
assert(err == 0);
|
||||
if (err != 0) {
|
||||
LOGE("failed to flush BPS requests: %d", err);
|
||||
}
|
||||
LOGD("flushed bps: %d", err);
|
||||
}
|
||||
|
||||
@@ -1012,7 +1014,7 @@ void SpectraCamera::enqueue_frame(uint64_t request_id) {
|
||||
}
|
||||
|
||||
if (icp_dev_handle > 0) {
|
||||
ret = do_cam_control(m->cam_sync_fd, CAM_SYNC_CREATE, &sync_create, sizeof(sync_create));
|
||||
ret = do_sync_control(m->cam_sync_fd, CAM_SYNC_CREATE, &sync_create, sizeof(sync_create));
|
||||
if (ret != 0) {
|
||||
LOGE("failed to create fence: %d %d", ret, sync_create.sync_obj);
|
||||
} else {
|
||||
|
||||
@@ -56,23 +56,24 @@
|
||||
},
|
||||
{
|
||||
"name": "boot",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/boot-0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4.img.xz",
|
||||
"hash": "0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4",
|
||||
"hash_raw": "0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4",
|
||||
"size": 18515968,
|
||||
"url": "https://www.dropbox.com/scl/fi/9l9io42qfx2shr9er5jqx/boot9.img.xz?rlkey=lbtxz862kxbvn3jn98ejitj8p&st=vr35tgz7&dl=1",
|
||||
"hash": "ab2eba0f96b2f48efa376330c3eb509158361adf3ad9c20f269ec92457aa841f",
|
||||
"hash_raw": "ab2eba0f96b2f48efa376330c3eb509158361adf3ad9c20f269ec92457aa841f",
|
||||
"size": 48343040,
|
||||
"sparse": false,
|
||||
"full_check": true,
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "492ae27f569e8db457c79d0e358a7a6297d1a1c685c2b1ae6deba7315d3a6cb0"
|
||||
"ondevice_hash": "c12865a879012d6ad3da538c2ae8aff7d58c308c303fc5d8af309cb1707bc036"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"url": "https://www.dropbox.com/scl/fi/8fd3w7hbhsyq146kqqdab/system8.img.xz?rlkey=5zt15mtuehdlj8ncj6pwn7zjy&st=j6tr4d47&dl=1",
|
||||
"hash": "4c01245932068aedfceb41cb1aab1f7f044f6659aa2fe2de558f99e2d3aa5793",
|
||||
"hash_raw": "4c01245932068aedfceb41cb1aab1f7f044f6659aa2fe2de558f99e2d3aa5793",
|
||||
"size": 5368709120,
|
||||
"url": "https://www.dropbox.com/scl/fi/3gtw78kxn6xs6dil10fmc/system9.img.xz?rlkey=uwj6tss7lw3j1di09xvtdugcy&st=ghnm9teb&dl=1",
|
||||
"hash": "b6f1e5ade7baa0078990e881b9f392b6a29164347df8f54be20c81e9c9dbabfe",
|
||||
"hash_raw": "b6f1e5ade7baa0078990e881b9f392b6a29164347df8f54be20c81e9c9dbabfe",
|
||||
"size": 4718592000,
|
||||
"sparse": false,
|
||||
"full_check": false,
|
||||
"has_ab": true
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "b6f1e5ade7baa0078990e881b9f392b6a29164347df8f54be20c81e9c9dbabfe"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -522,6 +522,14 @@ class ManagerProcess(ABC):
|
||||
if dt > self.watchdog_max_dt and ENABLE_WATCHDOG:
|
||||
self.capture_watchdog_debug_dump_async(f"watchdog_timeout started={started}", dt)
|
||||
cloudlog.error(f"Watchdog timeout for {self.name} (exitcode {self.proc.exitcode}) restarting ({started=})")
|
||||
if isinstance(self, NativeProcess):
|
||||
sentry.capture_message(
|
||||
f"Native process watchdog timeout: {self.name}",
|
||||
level="fatal",
|
||||
tags={"process": self.name, "process_kind": "native", "failure": "watchdog"},
|
||||
extras={"pid": self.proc.pid, "watchdog_dt": dt, "started": started},
|
||||
flush_timeout=0.5,
|
||||
)
|
||||
self.restart()
|
||||
|
||||
def stop(self, retry: bool = True, block: bool = True, sig: signal.Signals = None) -> int | None:
|
||||
@@ -715,7 +723,16 @@ def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None
|
||||
for p in procs:
|
||||
# Reap crashed processes so they can be cleanly restarted below.
|
||||
if p.proc is not None and p.proc.exitcode is not None and not p.shutting_down:
|
||||
cloudlog.error(f"Process {p.name} crashed with exitcode {p.proc.exitcode}, restarting")
|
||||
exitcode = p.proc.exitcode
|
||||
cloudlog.error(f"Process {p.name} crashed with exitcode {exitcode}, restarting")
|
||||
if isinstance(p, NativeProcess):
|
||||
sentry.capture_message(
|
||||
f"Native process crashed: {p.name}",
|
||||
level="fatal",
|
||||
tags={"process": p.name, "process_kind": "native"},
|
||||
extras={"exitcode": exitcode, "started": started},
|
||||
flush_timeout=0.5,
|
||||
)
|
||||
p.stop(retry=False)
|
||||
|
||||
if p.enabled and p.name not in not_run and p.should_run(started, params, CP, starpilot_toggles):
|
||||
|
||||
@@ -12,6 +12,7 @@ from openpilot.system.manager.process import PythonProcess, NativeProcess, Daemo
|
||||
|
||||
WEBCAM = os.getenv("USE_WEBCAM") is not None
|
||||
UI_WATCHDOG_MAX_DT = int(os.getenv("UI_WATCHDOG_MAX_DT", "10"))
|
||||
CAMERAD_WATCHDOG_MAX_DT = int(os.getenv("CAMERAD_WATCHDOG_MAX_DT", "5"))
|
||||
|
||||
def driverview(started: bool, params: Params, CP: car.CarParams, starpilot_toggles: SimpleNamespace) -> bool:
|
||||
return started or params.get_bool("IsDriverViewEnabled")
|
||||
@@ -123,7 +124,8 @@ procs = [
|
||||
NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], or_(and_(livestream, not_(iscar)), notcar)),
|
||||
PythonProcess("logmessaged", "system.logmessaged", always_run),
|
||||
|
||||
NativeProcess("camerad", "system/camerad", ["./camerad"], or_(camera_run, livestream), enabled=not WEBCAM),
|
||||
NativeProcess("camerad", "system/camerad", ["./camerad"], or_(camera_run, livestream), enabled=not WEBCAM,
|
||||
watchdog_max_dt=CAMERAD_WATCHDOG_MAX_DT),
|
||||
PythonProcess("webcamerad", "tools.webcam.camerad", driverview, enabled=WEBCAM),
|
||||
PythonProcess("proclogd", "system.proclogd", and_(allow_logging, only_onroad), enabled=platform.system() != "Darwin"),
|
||||
PythonProcess("journald", "system.journald", and_(allow_logging, only_onroad), platform.system() != "Darwin"),
|
||||
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${1:-comma@192.168.3.110}"
|
||||
MANIFEST="${2:?usage: $0 [host] manifest.json boot.img.xz system.img.xz}"
|
||||
BOOT_IMAGE="${3:?missing boot image}"
|
||||
SYSTEM_IMAGE="${4:?missing system image}"
|
||||
EXPECTED_VERSION="${EXPECTED_VERSION:-19.6.1}"
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd -- "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null)
|
||||
if [[ -n "${SSH_KEY:-}" ]]; then
|
||||
SSH_OPTS=(-i "$SSH_KEY" "${SSH_OPTS[@]}")
|
||||
fi
|
||||
|
||||
for path in "$MANIFEST" "$BOOT_IMAGE" "$SYSTEM_IMAGE"; do
|
||||
if [[ ! -f "$path" ]]; then
|
||||
echo "missing file: $path" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
SESSION="local_agnos_pair_flash"
|
||||
REMOTE_DIR="/data/local_agnos_pair_flash"
|
||||
REMOTE_MANIFEST="${REMOTE_DIR}/agnos-local.json"
|
||||
REMOTE_RUNNER="${REMOTE_DIR}/run_flash.sh"
|
||||
REMOTE_AGNOS="${REMOTE_DIR}/agnos.py"
|
||||
PORT="8989"
|
||||
|
||||
LOCAL_DIR="$(mktemp -d "${TMPDIR:-/tmp}/agnos-pair.XXXXXX")"
|
||||
trap 'rm -rf "$LOCAL_DIR"' EXIT
|
||||
LOCAL_MANIFEST="${LOCAL_DIR}/agnos-local.json"
|
||||
LOCAL_AGNOS="${LOCAL_DIR}/agnos.py"
|
||||
|
||||
python3 - "$MANIFEST" "$LOCAL_MANIFEST" "$PORT" "$BOOT_IMAGE" "$SYSTEM_IMAGE" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
source, destination, port, boot_image, system_image = sys.argv[1:]
|
||||
images = {"boot": Path(boot_image), "system": Path(system_image)}
|
||||
manifest = json.loads(Path(source).read_text(encoding="utf-8"))
|
||||
|
||||
if len(manifest) != len(images) or {entry.get("name") for entry in manifest} != set(images):
|
||||
raise SystemExit("local test manifest must contain exactly boot and system entries")
|
||||
|
||||
for entry in manifest:
|
||||
name = entry["name"]
|
||||
if not entry.get("has_ab"):
|
||||
raise SystemExit(f"{name} must be an A/B partition")
|
||||
if entry.get("sparse"):
|
||||
raise SystemExit(f"{name} must use a raw, non-sparse payload for local flashing")
|
||||
entry["url"] = f"http://127.0.0.1:{port}/{images[name].name}"
|
||||
entry.pop("alt", None)
|
||||
entry.pop("casync_caibx", None)
|
||||
entry.pop("casync_store", None)
|
||||
|
||||
Path(destination).write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
PY
|
||||
|
||||
python3 - "$REPO_ROOT/system/hardware/tici/agnos.py" "$LOCAL_AGNOS" <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
src, dst = map(Path, sys.argv[1:])
|
||||
data = src.read_text(encoding="utf-8")
|
||||
needle = "import openpilot.system.updated.casync.casync as casync"
|
||||
if data.count(needle) != 1:
|
||||
raise SystemExit("could not isolate the casync dependency in agnos.py")
|
||||
data = data.replace(
|
||||
needle,
|
||||
'''class _UnusedCasync:
|
||||
ChunkReader = object
|
||||
ChunkDict = object
|
||||
|
||||
def __getattr__(self, name):
|
||||
raise RuntimeError("casync support is unavailable in local AGNOS flash runner")
|
||||
casync = _UnusedCasync()''',
|
||||
)
|
||||
dst.write_text(data, encoding="utf-8")
|
||||
PY
|
||||
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" "mkdir -p '$REMOTE_DIR'"
|
||||
scp "${SSH_OPTS[@]}" "$LOCAL_MANIFEST" "$HOST:$REMOTE_MANIFEST"
|
||||
scp "${SSH_OPTS[@]}" "$LOCAL_AGNOS" "$HOST:$REMOTE_AGNOS"
|
||||
scp "${SSH_OPTS[@]}" "$BOOT_IMAGE" "$SYSTEM_IMAGE" "$HOST:$REMOTE_DIR/"
|
||||
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" "cat > '$REMOTE_RUNNER' && chmod +x '$REMOTE_RUNNER'" <<'REMOTE_RUNNER'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
: "${REMOTE_DIR:?}"
|
||||
: "${REMOTE_MANIFEST:?}"
|
||||
: "${REMOTE_AGNOS:?}"
|
||||
: "${PORT:?}"
|
||||
: "${EXPECTED_VERSION:?}"
|
||||
|
||||
exec > >(tee -a "${REMOTE_DIR}/flash.log") 2>&1
|
||||
|
||||
echo "[STEP] Local AGNOS boot+system flash"
|
||||
echo "[CHECK] Device: $(tr -d '\0' </sys/firmware/devicetree/base/model)"
|
||||
echo "[CHECK] Installed AGNOS: $(cat /VERSION 2>/dev/null || echo unknown)"
|
||||
echo "[CHECK] Target AGNOS: ${EXPECTED_VERSION}"
|
||||
echo "[CHECK] Active slot before flash: $(abctl --boot_slot)"
|
||||
df -h /data
|
||||
|
||||
if [[ -x /usr/local/venv/bin/python3 ]]; then
|
||||
PYTHON_BIN="/usr/local/venv/bin/python3"
|
||||
else
|
||||
PYTHON_BIN="python3"
|
||||
fi
|
||||
|
||||
pkill -f "http.server ${PORT}.*${REMOTE_DIR}" >/dev/null 2>&1 || true
|
||||
"$PYTHON_BIN" -m http.server "$PORT" --bind 127.0.0.1 --directory "$REMOTE_DIR" >"${REMOTE_DIR}/http.log" 2>&1 &
|
||||
http_pid="$!"
|
||||
trap 'kill "$http_pid" >/dev/null 2>&1 || true' EXIT
|
||||
|
||||
http_ready=0
|
||||
for _ in $(seq 1 20); do
|
||||
if "$PYTHON_BIN" - "$REMOTE_MANIFEST" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
for entry in json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")):
|
||||
with urllib.request.urlopen(entry["url"], timeout=2) as response:
|
||||
response.read(1)
|
||||
PY
|
||||
then
|
||||
http_ready=1
|
||||
break
|
||||
fi
|
||||
sleep 0.25
|
||||
done
|
||||
|
||||
if [[ "$http_ready" != "1" ]]; then
|
||||
echo "[ERROR] Local image HTTP server did not become ready" >&2
|
||||
cat "${REMOTE_DIR}/http.log" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[FLASH] Writing boot and system to the inactive AGNOS slot"
|
||||
PYTHONPATH="$(dirname "$REMOTE_AGNOS")" "$PYTHON_BIN" "$REMOTE_AGNOS" --swap "$REMOTE_MANIFEST"
|
||||
|
||||
echo "[DONE] Both partitions verified and the inactive slot was selected"
|
||||
echo "[REBOOT] Rebooting now"
|
||||
sudo reboot
|
||||
REMOTE_RUNNER
|
||||
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" "tmux kill-session -t '$SESSION' >/dev/null 2>&1 || true"
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" "rm -f '$REMOTE_DIR/flash.log' '$REMOTE_DIR/http.log'"
|
||||
ssh "${SSH_OPTS[@]}" "$HOST" \
|
||||
"tmux new-session -d -s '$SESSION' \"REMOTE_DIR='$REMOTE_DIR' REMOTE_MANIFEST='$REMOTE_MANIFEST' REMOTE_AGNOS='$REMOTE_AGNOS' PORT='$PORT' EXPECTED_VERSION='$EXPECTED_VERSION' bash '$REMOTE_RUNNER'\""
|
||||
|
||||
echo "Started remote tmux session: $SESSION"
|
||||
echo "Watch it with: ssh $HOST 'tmux attach -t $SESSION'"
|
||||
@@ -263,6 +263,9 @@ def patch_setup_branding_script(original: bytes, entry_name: str) -> bytes:
|
||||
|
||||
if entry_name == MICI_SETUP_ENTRY_IN_SETUP_ZIPAPP:
|
||||
text = text.replace('LargerSlider("slide to use\\nopenpilot"', 'LargerSlider("slide to use\\nstarpilot"')
|
||||
text = text.replace('LargerSlider("slide to install\\nopenpilot"', 'LargerSlider("slide to install\\nstarpilot"')
|
||||
text = text.replace('BigPillButton("install openpilot"', 'BigPillButton("install StarPilot"')
|
||||
text = text.replace('set_text("install openpilot"', 'set_text("install StarPilot"')
|
||||
elif entry_name == TICI_SETUP_ENTRY_IN_SETUP_ZIPAPP:
|
||||
text = text.replace('ButtonRadio("openpilot"', 'ButtonRadio("StarPilot"')
|
||||
|
||||
@@ -810,6 +813,9 @@ def setup_zipapp_has_expected_content(data: bytes) -> bool:
|
||||
return False
|
||||
if b"installer.comma.ai/firestar5683/StarPilot" not in setup_script:
|
||||
return False
|
||||
mici_setup = z.read(MICI_SETUP_ENTRY_IN_SETUP_ZIPAPP)
|
||||
if b"install openpilot" in mici_setup or b"slide to install\\nopenpilot" in mici_setup:
|
||||
return False
|
||||
except KeyError:
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -10,6 +10,7 @@ from tools.agnos.patch_system_reset_image import (
|
||||
find_default_reference_manifest,
|
||||
format_debugfs_mode,
|
||||
patch_comma_sh_display_wait,
|
||||
patch_setup_branding_script,
|
||||
sha256_zstd_payload,
|
||||
)
|
||||
|
||||
@@ -50,6 +51,22 @@ def test_patch_comma_sh_display_wait_rejects_unknown_layout():
|
||||
patch_comma_sh_display_wait(b"#!/usr/bin/env bash\nexec /data/continue.sh\n")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("slider_text", ["slide to use", "slide to install"])
|
||||
def test_patch_mici_setup_branding_handles_old_and_new_labels(slider_text):
|
||||
original = f'''OPENPILOT_URL = "https://openpilot.comma.ai"
|
||||
self._openpilot_slider = LargerSlider("{slider_text}\\nopenpilot", callback)
|
||||
self._continue_button = BigPillButton("install openpilot", green=True)
|
||||
self._continue_button.set_text("install openpilot" if not custom_software else "choose software")
|
||||
'''.encode()
|
||||
|
||||
patched = patch_setup_branding_script(original, "openpilot/system/ui/mici_setup.py")
|
||||
|
||||
assert b"installer.comma.ai/firestar5683/StarPilot" in patched
|
||||
assert f"{slider_text}\\nstarpilot".encode() in patched
|
||||
assert b"install StarPilot" in patched
|
||||
assert b"install openpilot" not in patched
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("mode", "expected"), [
|
||||
("100775", "0100775"),
|
||||
("100644", "0100644"),
|
||||
|
||||
Reference in New Issue
Block a user