Migration

Migration Banner
Auto Push to StarPilot / Update Fingerprints
This commit is contained in:
firestar5683
2026-02-11 13:48:33 -06:00
parent 17f38ea13e
commit c7751a283a
5 changed files with 214 additions and 0 deletions
+89
View File
@@ -18,6 +18,7 @@ from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.system.version import get_build_metadata
FRAME_FINGERPRINT = 100 # 1s
SOURCE_BRANCH_FILE = "/data/media/0/starpilot_source_branch"
EventName = car.CarEvent.EventName
FrogPilotEventName = custom.FrogPilotCarEvent.EventName
@@ -189,6 +190,64 @@ def get_car_interface(CP, FPCP):
CarInterface, CarController, CarState = interfaces[CP.carFingerprint]
return CarInterface(CP, FPCP, CarController, CarState)
def get_cached_car_fingerprint(params: Params) -> str | None:
for key in ("CarParamsPersistent", "CarParamsCache", "CarParams"):
cp_bytes = params.get(key)
if cp_bytes is None:
continue
try:
with car.CarParams.from_bytes(cp_bytes) as cached_cp:
if cached_cp.carFingerprint:
return cached_cp.carFingerprint
except Exception:
continue
return None
def clear_stale_car_params(params: Params, candidate: str) -> None:
cached_fingerprint = get_cached_car_fingerprint(params)
if cached_fingerprint is None or cached_fingerprint == candidate:
return
stale_keys = (
"CarParams",
"CarParamsCache",
"CarParamsPersistent",
"FrogPilotCarParams",
"FrogPilotCarParamsPersistent",
"CarModelName",
)
for key in stale_keys:
params.remove(key)
cloudlog.warning("cleared stale car params after fingerprint change: %s -> %s", cached_fingerprint, candidate)
def migrate_legacy_bolt_candidate(candidate: str) -> str:
source_branch = ""
try:
with open(SOURCE_BRANCH_FILE, encoding="utf-8") as f:
source_branch = f.read().strip()
except OSError:
pass
migration_branch = source_branch or get_build_metadata().channel
replacements = {}
if migration_branch in {"TorqueTune", "TorquePedal"}:
replacements = {
"CHEVROLET_BOLT_EUV": GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
"CHEVROLET_BOLT_CC": GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
}
elif migration_branch in {"TotallyTune", "StarPilot-2017", "StarPilot 2017"}:
replacements = {
"CHEVROLET_BOLT_CC": GM_CAR.CHEVROLET_BOLT_CC_2017,
}
elif migration_branch in {"StarPilot"}:
replacements = {
"CHEVROLET_BOLT_CC": GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
}
normalized_candidate = candidate[4:] if candidate.startswith("CAR.") else candidate
return replacements.get(normalized_candidate, normalized_candidate)
def get_car(logcan, sendcan, experimental_long_allowed, params, num_pandas=1, frogpilot_toggles=None):
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
@@ -203,9 +262,23 @@ def get_car(logcan, sendcan, experimental_long_allowed, params, num_pandas=1, fr
params.put_nonblocking("CarMake", candidate.split('_')[0].title())
params.put_nonblocking("CarModel", candidate)
# Branch migration can leave legacy Bolt candidate names active in params/cache.
# Remap the selected candidate itself so fingerprint selection and params stay in sync.
migrated_candidate = migrate_legacy_bolt_candidate(candidate)
if candidate != migrated_candidate:
cloudlog.warning("legacy Bolt candidate migration: %s -> %s", candidate, migrated_candidate)
candidate = migrated_candidate
params.put_nonblocking("CarMake", candidate.split('_')[0].title())
params.put_nonblocking("CarModel", candidate)
params.remove("CarModelName")
# VIN-based Bolt year mapping (selfdrive-only, bolt variants only)
if not frogpilot_toggles.force_fingerprint and is_valid_vin(vin):
bolt_variants = {
"CHEVROLET_BOLT_EUV",
"CHEVROLET_BOLT_CC",
"CAR.CHEVROLET_BOLT_EUV",
"CAR.CHEVROLET_BOLT_CC",
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
@@ -235,12 +308,28 @@ def get_car(logcan, sendcan, experimental_long_allowed, params, num_pandas=1, fr
candidate = vin_candidate
params.put_nonblocking("CarMake", candidate.split('_')[0].title())
params.put_nonblocking("CarModel", candidate)
params.remove("CarModelName")
cloudlog.warning("VIN Bolt override: %s -> %s", prev_candidate, candidate)
# Always prefer live fingerprint naming for Bolt variants to avoid stale manual labels.
if candidate in {
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
GM_CAR.CHEVROLET_BOLT_CC_2017,
"CHEVROLET_BOLT_EUV",
"CHEVROLET_BOLT_CC",
"CAR.CHEVROLET_BOLT_EUV",
"CAR.CHEVROLET_BOLT_CC",
}:
params.remove("CarModelName")
if frogpilot_toggles.block_user:
candidate = MOCK.MOCK
sentry.capture_block()
clear_stale_car_params(params, candidate)
CarInterface, _, _ = interfaces[candidate]
CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed, frogpilot_toggles, docs=False)
FPCP = CarInterface.get_frogpilot_params(candidate, fingerprints, car_fw, CP, frogpilot_toggles)
+28
View File
@@ -2,6 +2,7 @@
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QSet>
#include <QStackedWidget>
#include <QVBoxLayout>
@@ -172,6 +173,22 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
main_layout->addLayout(header_layout);
branch_merge_banner = new QLabel(this);
branch_merge_banner->setAlignment(Qt::AlignCenter);
branch_merge_banner->setWordWrap(true);
branch_merge_banner->setAttribute(Qt::WA_TransparentForMouseEvents, true);
branch_merge_banner->setVisible(false);
branch_merge_banner->setStyleSheet(R"(
background-color: #E22C2C;
color: white;
border-radius: 16px;
padding: 24px;
font-size: 44px;
font-weight: 700;
)");
branch_merge_banner->setText(tr("Branch Merge Notice: This branch is deprecated and has been merged into StarPilot. Please switch to the StarPilot branch."));
main_layout->addWidget(branch_merge_banner);
// main content
main_layout->addSpacing(25);
center_layout = new QStackedLayout();
@@ -268,10 +285,21 @@ void OffroadHome::hideEvent(QHideEvent *event) {
}
void OffroadHome::refresh() {
static const QSet<QString> deprecated_branches = {
"TorqueTune",
"TorquePedal",
"Kaofui",
"Red-Kao",
"TotallyTune",
"StarPilot-2017",
"TRX",
};
date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d"));
date->setVisible(util::system_time_valid());
version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + processModelName(frogpilotUIState()->frogpilot_toggles.value("model_name").toString()));
branch_merge_banner->setVisible(deprecated_branches.contains(QString::fromStdString(params.get("GitBranch"))));
bool updateAvailable = update_widget->refresh();
int alerts = alerts_widget->refresh();
+1
View File
@@ -41,6 +41,7 @@ private:
OffroadAlert* alerts_widget;
QPushButton* alert_notif;
QPushButton* update_notif;
QLabel* branch_merge_banner;
// FrogPilot variables
ElidedLabel* date;
+57
View File
@@ -92,6 +92,63 @@ def manager_init() -> None:
params.put_bool("IsTestedBranch", build_metadata.tested_channel)
params.put_bool("IsReleaseBranch", build_metadata.release_channel)
# Legacy Bolt fingerprint migration after branch consolidation
bolt_source_branch_file = "/data/media/0/starpilot_source_branch"
bolt_fingerprint_migration_flag_file = "/data/media/0/frogpilot_bolt_fingerprint_migrated.flag"
source_branch = ""
try:
if os.path.exists(bolt_source_branch_file):
with open(bolt_source_branch_file, encoding="utf-8") as f:
source_branch = f.read().strip()
except OSError:
cloudlog.exception("failed reading StarPilot source branch file")
migration_branch = source_branch or build_metadata.channel
replacements = {}
if migration_branch in {"TorqueTune", "TorquePedal"}:
replacements = {
"CHEVROLET_BOLT_EUV": "CHEVROLET_BOLT_ACC_2022_2023",
"CHEVROLET_BOLT_CC": "CHEVROLET_BOLT_CC_2022_2023",
}
elif migration_branch in {"TotallyTune", "StarPilot-2017", "StarPilot 2017"}:
replacements = {
"CHEVROLET_BOLT_CC": "CHEVROLET_BOLT_CC_2017",
}
elif migration_branch in {"StarPilot"}:
replacements = {
"CHEVROLET_BOLT_CC": "CHEVROLET_BOLT_CC_2019_2021",
}
migrated_values = []
if replacements:
for param_key in ("CarModel", "CarModelName"):
current_value = params.get(param_key, encoding='utf-8')
normalized_value = current_value[4:] if current_value is not None and current_value.startswith("CAR.") else current_value
if normalized_value in replacements:
new_value = replacements[normalized_value]
params.put(param_key, new_value)
params_cache.put(param_key, new_value)
migrated_values.append(f"{param_key}: {current_value} -> {new_value}")
if migrated_values:
cloudlog.info(f"migrated legacy bolt fingerprint values from branch '{migration_branch}' (source='{source_branch}'): {', '.join(migrated_values)}")
# Keep Bolt display label aligned with live fingerprint selection
bolt_models = {
"CHEVROLET_BOLT_EUV",
"CHEVROLET_BOLT_CC",
"CHEVROLET_BOLT_ACC_2022_2023",
"CHEVROLET_BOLT_CC_2022_2023",
"CHEVROLET_BOLT_CC_2019_2021",
"CHEVROLET_BOLT_CC_2017",
}
if (params.get("CarModel", encoding='utf-8') or "") in bolt_models:
params.remove("CarModelName")
params_cache.remove("CarModelName")
with open(bolt_fingerprint_migration_flag_file, "w") as f:
f.write(migration_branch or "unknown")
# One-time migration to align FrogPilot defaults after install
frogpilot_migration_flag_file = "/data/media/0/frogpilot_migrated.flag"
if not os.path.exists(frogpilot_migration_flag_file):
+39
View File
@@ -36,6 +36,19 @@ OVERLAY_INIT = Path(os.path.join(BASEDIR, ".overlay_init"))
DAYS_NO_CONNECTIVITY_MAX = 14 # do not allow to engage after this many days
DAYS_NO_CONNECTIVITY_PROMPT = 10 # send an offroad prompt after this many days
MIGRATED_TARGET_BRANCH = "StarPilot"
MIGRATION_DONE_FILE = "/data/starpilot_branch_migrated"
MIGRATION_SOURCE_BRANCH_FILE = "/data/media/0/starpilot_source_branch"
MIGRATION_EXCLUDED_BRANCHES = {"Dom"}
MIGRATION_SOURCE_BRANCHES = {
"TorqueTune",
"TorquePedal",
"Kaofui",
"Red-Kao",
"TotallyTune",
"StarPilot-2017",
"TRX",
}
class UserRequest:
NONE = 0
@@ -280,6 +293,7 @@ class Updater:
self.params = Params()
self.branches = defaultdict(str)
self._has_internet: bool = False
self._migrate_target_branch()
@property
def has_internet(self) -> bool:
@@ -292,6 +306,31 @@ class Updater:
b = self.get_branch(BASEDIR)
return b
def _migrate_target_branch(self) -> None:
target_branch: str | None = self.params.get("UpdaterTargetBranch", encoding='utf-8')
current_branch = self.get_branch(BASEDIR)
if current_branch in MIGRATION_EXCLUDED_BRANCHES or target_branch in MIGRATION_EXCLUDED_BRANCHES:
cloudlog.info(f"skipping StarPilot branch migration on excluded branch: current={current_branch}, target={target_branch}")
return
if current_branch not in MIGRATION_SOURCE_BRANCHES and target_branch not in MIGRATION_SOURCE_BRANCHES:
cloudlog.info(f"skipping StarPilot branch migration on unmanaged branch: current={current_branch}, target={target_branch}")
return
if current_branch != MIGRATED_TARGET_BRANCH:
try:
Path(MIGRATION_SOURCE_BRANCH_FILE).write_text(current_branch, encoding='utf-8')
except OSError:
cloudlog.exception(f"failed to persist source branch for migration: {MIGRATION_SOURCE_BRANCH_FILE}")
if target_branch != MIGRATED_TARGET_BRANCH or current_branch != MIGRATED_TARGET_BRANCH:
self.params.put("UpdaterTargetBranch", MIGRATED_TARGET_BRANCH)
cloudlog.info(f"migrated updater target branch to {MIGRATED_TARGET_BRANCH} from target={target_branch}, current={current_branch}")
try:
Path(MIGRATION_DONE_FILE).touch()
except OSError:
cloudlog.exception(f"failed to write migration flag file: {MIGRATION_DONE_FILE}")
@property
def update_ready(self) -> bool:
consistent_file = Path(os.path.join(FINALIZED, ".overlay_consistent"))