Skip to content
Merged

w #356

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions selfdrive/carrot/server/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
CARROT_TOOL_JOBS_STATE_PATH = os.path.join(CARROT_STATE_DIR, "tool_jobs.json")
CARROT_WEB_SETTINGS_PATH = os.path.join(CARROT_STATE_DIR, "web_settings.json")
CARROT_SETTING_FAVORITES_PATH = os.path.join(CARROT_STATE_DIR, "setting_favorites.json")
CARROT_SETTING_PROFILES_PATH = os.path.join(CARROT_STATE_DIR, "setting_profiles.json")

# Dashcam
DASHCAM_ROOT = "/data/media/0/realdata"
Expand Down
2 changes: 2 additions & 0 deletions selfdrive/carrot/server/features/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
screenrecord,
settings,
setting_favorites,
setting_profiles,
ssh_keys,
static,
stream,
Expand All @@ -25,6 +26,7 @@ def register_all(app: web.Application) -> None:
settings.register(app)
params.register(app)
setting_favorites.register(app)
setting_profiles.register(app)
web_settings.register(app)
ssh_keys.register(app)
cars.register(app)
Expand Down
103 changes: 103 additions & 0 deletions selfdrive/carrot/server/features/setting_profiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from aiohttp import web

from ..services.setting_profiles import (
apply_setting_profile,
create_setting_profile,
delete_setting_profile,
preview_setting_profile,
read_setting_profiles,
update_setting_profile,
)


async def get_setting_profiles(request: web.Request) -> web.Response:
return web.json_response({"ok": True, **read_setting_profiles()})


async def create_setting_profile_route(request: web.Request) -> web.Response:
try:
body = await request.json()
except Exception:
body = {}
try:
profile = create_setting_profile(body.get("name", ""))
return web.json_response({"ok": True, "profile": profile, **read_setting_profiles()})
except Exception as e:
return web.json_response({"ok": False, "error": str(e)}, status=400)


async def update_setting_profile_route(request: web.Request) -> web.Response:
try:
body = await request.json()
except Exception:
body = {}
profile_id = str(body.get("id") or "").strip()
if not profile_id:
return web.json_response({"ok": False, "error": "missing profile id"}, status=400)
try:
profile = update_setting_profile(profile_id, body)
return web.json_response({"ok": True, "profile": profile, **read_setting_profiles()})
except KeyError as e:
return web.json_response({"ok": False, "error": str(e)}, status=404)
except Exception as e:
return web.json_response({"ok": False, "error": str(e)}, status=400)


async def delete_setting_profile_route(request: web.Request) -> web.Response:
try:
body = await request.json()
except Exception:
body = {}
profile_id = str(body.get("id") or "").strip()
if not profile_id:
return web.json_response({"ok": False, "error": "missing profile id"}, status=400)
try:
delete_setting_profile(profile_id)
return web.json_response({"ok": True, **read_setting_profiles()})
except KeyError as e:
return web.json_response({"ok": False, "error": str(e)}, status=404)
except Exception as e:
return web.json_response({"ok": False, "error": str(e)}, status=400)


async def preview_setting_profile_route(request: web.Request) -> web.Response:
try:
body = await request.json()
except Exception:
body = {}
profile_id = str(body.get("id") or "").strip()
if not profile_id:
return web.json_response({"ok": False, "error": "missing profile id"}, status=400)
try:
preview = preview_setting_profile(profile_id, body.get("values") if isinstance(body, dict) else None)
return web.json_response({"ok": True, "preview": preview})
except KeyError as e:
return web.json_response({"ok": False, "error": str(e)}, status=404)
except Exception as e:
return web.json_response({"ok": False, "error": str(e)}, status=400)


async def apply_setting_profile_route(request: web.Request) -> web.Response:
try:
body = await request.json()
except Exception:
body = {}
profile_id = str(body.get("id") or "").strip()
if not profile_id:
return web.json_response({"ok": False, "error": "missing profile id"}, status=400)
try:
result = apply_setting_profile(profile_id, body.get("values") if isinstance(body, dict) else None)
return web.json_response({"ok": True, **result})
except KeyError as e:
return web.json_response({"ok": False, "error": str(e)}, status=404)
except Exception as e:
return web.json_response({"ok": False, "error": str(e)}, status=400)


def register(app: web.Application) -> None:
app.router.add_get("/api/setting_profiles", get_setting_profiles)
app.router.add_post("/api/setting_profiles", create_setting_profile_route)
app.router.add_post("/api/setting_profiles/update", update_setting_profile_route)
app.router.add_post("/api/setting_profiles/delete", delete_setting_profile_route)
app.router.add_post("/api/setting_profiles/preview", preview_setting_profile_route)
app.router.add_post("/api/setting_profiles/apply", apply_setting_profile_route)
90 changes: 87 additions & 3 deletions selfdrive/carrot/server/features/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from ..live_runtime.normalize import to_transport_safe
from ..config import OFFROAD_ASSETS_DIR
from ..services.device_info import get_calibration_status, get_device_network
from ..services.params import HAS_PARAMS, Params, set_param_value
from ..services.params import HAS_PARAMS, Params, restore_param_values_validated
from ..services.settings import get_settings_cached
from ..services.time_sync import TIME_SYNC_DEBUG_DEFAULT, sync_system_time_from_browser


Expand All @@ -25,6 +26,69 @@
"carrotMan",
)

_CARROT_DEFAULT_RESET_EXCLUDED_NAMES = {
# Vehicle identity / selection / fingerprinting.
"CarName",
"CarParams",
"CarParamsCache",
"CarParamsPersistent",
"CarParamsPrevRoute",
"CarModel",
"CarFingerprint",
"SupportedCars",
# Training, terms, calibration, and learned live parameters.
"CompletedTrainingVersion",
"TrainingVersion",
"TermsVersion",
"HasAcceptedTerms",
"CalibrationParams",
"LiveParameters",
"LiveTorqueParameters",
# Device/user/system identity and platform settings.
"DongleId",
"HardwareSerial",
"DeviceSerial",
"DeviceType",
"LanguageSetting",
"IsMetric",
"OpenpilotEnabledToggle",
"ExperimentalMode",
"ExperimentalModeConfirmed",
"SshEnabled",
"AdbEnabled",
"GithubUsername",
"GithubSshKeys",
# Recording/upload/update/git state should not be reset by Carrot tuning reset.
"RecordFront",
"RecordAudio",
"GitBranch",
"GitCommit",
"GitCommitDate",
"UpdaterState",
"UpdaterTargetBranch",
"UpdaterCurrentDescription",
}

_CARROT_DEFAULT_RESET_EXCLUDED_PREFIXES = (
"CarParams",
"Calibration",
"CompletedTraining",
"Git",
"Github",
"Updater",
"Dongle",
"Hardware",
"DeviceSerial",
)


def _is_carrot_default_reset_param(name: str, meta: Any) -> bool:
if not name or not isinstance(meta, dict) or "default" not in meta:
return False
if name in _CARROT_DEFAULT_RESET_EXCLUDED_NAMES:
return False
return not any(name.startswith(prefix) for prefix in _CARROT_DEFAULT_RESET_EXCLUDED_PREFIXES)


def _select_live_runtime_services(snapshot: dict[str, Any]) -> dict[str, Any]:
services = snapshot.get("services")
Expand Down Expand Up @@ -225,8 +289,28 @@ async def api_set_default(request: web.Request) -> web.Response:
if not HAS_PARAMS:
return web.json_response({"ok": False, "error": "params unavailable"}, status=500)
try:
Params().put_int("SoftRestartTriggered", 2)
return web.json_response({"ok": True})
_, _, by_name, _ = get_settings_cached()
values = {
name: meta.get("default", 0)
for name, meta in by_name.items()
if _is_carrot_default_reset_param(name, meta)
}
restored = restore_param_values_validated(values)
result = restored.get("result") or {}
ok = int(result.get("fail_cnt") or 0) == 0
applied_values = {
entry["key"]: entry["value"]
for entry in restored.get("preview", {}).get("entries", [])
if entry.get("apply")
}
status = 200 if ok else 500
return web.json_response({
"ok": ok,
"message": "설정 초기화 성공" if ok else "설정 초기화 실패",
"error": None if ok else "설정 초기화 실패",
"values": applied_values,
**restored,
}, status=status)
except Exception as e:
return web.json_response({"ok": False, "error": str(e)}, status=500)

Expand Down
Loading
Loading