mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-09 09:43:47 +08:00
The Final Countdown
This commit is contained in:
@@ -317,7 +317,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"DeveloperSidebarMetric7", {PERSISTENT, INT, "7", "0", 3}},
|
||||
{"DeveloperUI", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"GalaxyDeveloperMode", {PERSISTENT | DONT_LOG, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"GalaxyMobileDefault", {PERSISTENT | DONT_LOG, BOOL, "0", "0", 0, SETTINGS_ADVANCED}},
|
||||
{"GalaxyMobileDefault", {PERSISTENT | DONT_LOG, BOOL, "1", "1", 0, SETTINGS_SIMPLE}},
|
||||
{"DeveloperWidgets", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"DeviceManagement", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"DeviceShutdown", {PERSISTENT, INT, "6", "6", 1, SETTINGS_SIMPLE}},
|
||||
@@ -609,6 +609,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"RelaxedJerkDeceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"RelaxedJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"RelaxedJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"ReverseCruise", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"RivianAngleControl", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"RivianAngleSaturated", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"RivianToiRecoveryFailed", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
|
||||
+1
-11
@@ -72,11 +72,7 @@ class VCruiseHelper:
|
||||
return short_interval, long_interval
|
||||
|
||||
def _uses_software_cruise(self) -> bool:
|
||||
# Some cars, including Toyota TSS2, keep pcmCruise enabled while
|
||||
# openpilot owns longitudinal control. In that case the software cruise
|
||||
# target must be used so custom short/hold intervals are honored.
|
||||
return bool(self.gm_cc_only or self.redneck_non_pcm or not self.CP.pcmCruise or
|
||||
getattr(self.CP, "openpilotLongitudinalControl", False))
|
||||
return bool(self.gm_cc_only or self.redneck_non_pcm or not self.CP.pcmCruise)
|
||||
|
||||
@property
|
||||
def v_cruise_initialized(self):
|
||||
@@ -229,12 +225,6 @@ class VCruiseHelper:
|
||||
self.v_cruise_kph = float(np.clip(initialized_speed_limit_kph, V_CRUISE_MIN, V_CRUISE_MAX))
|
||||
elif self.redneck_non_pcm and CS.cruiseState.speedCluster > 0:
|
||||
self.v_cruise_kph = float(np.clip(CS.cruiseState.speedCluster * CV.MS_TO_KPH, V_CRUISE_MIN, V_CRUISE_MAX))
|
||||
elif self.CP.pcmCruise and CS.cruiseState.speed > 0:
|
||||
# Keep PCM/dash set speed as the starting target when software cruise
|
||||
# takes over, while allowing subsequent button presses to use custom
|
||||
# intervals. This preserves Toyota's stock engage behavior.
|
||||
pcm_speed_kph = CS.cruiseState.speed * CV.MS_TO_KPH
|
||||
self.v_cruise_kph = float(np.clip(pcm_speed_kph, V_CRUISE_MIN, V_CRUISE_MAX))
|
||||
else:
|
||||
self.v_cruise_kph = int(round(np.clip(CS.vEgo * CV.MS_TO_KPH, engage_floor_kph, V_CRUISE_MAX)))
|
||||
|
||||
|
||||
@@ -498,95 +498,34 @@ class TestVCruiseHelper:
|
||||
|
||||
assert self.v_cruise_helper.v_cruise_kph == pytest.approx(initial_v_cruise_kph + IMPERIAL_INCREMENT)
|
||||
|
||||
def test_pcm_cruise_uses_pcm_speed(self):
|
||||
CP = car.CarParams(pcmCruise=True, openpilotLongitudinalControl=False)
|
||||
@pytest.mark.parametrize("openpilot_longitudinal", [False, True])
|
||||
def test_pcm_cruise_always_tracks_pcm_speed(self, openpilot_longitudinal):
|
||||
CP = car.CarParams(pcmCruise=True, openpilotLongitudinalControl=openpilot_longitudinal)
|
||||
helper = VCruiseHelper(CP)
|
||||
toggles = SimpleNamespace(cruise_increase=5, cruise_increase_long=1, set_speed_limit=False)
|
||||
pcm_speed_kph = 72.0
|
||||
pcm_cluster_speed_kph = 71.0
|
||||
|
||||
helper.initialize_v_cruise(car.CarState(vEgo=40 * CV.KPH_TO_MS), False, False, toggles)
|
||||
assert not helper.v_cruise_initialized
|
||||
|
||||
cs = car.CarState(
|
||||
cruiseState={
|
||||
"available": True,
|
||||
"speed": pcm_speed_kph * CV.KPH_TO_MS,
|
||||
"speedCluster": pcm_cluster_speed_kph * CV.KPH_TO_MS,
|
||||
},
|
||||
samples = (
|
||||
(72.0, 71.0, None),
|
||||
(25.0, 25.0, {"type": ButtonType.decelCruise, "pressed": True}),
|
||||
(65.0, 65.0, {"type": ButtonType.decelCruise, "pressed": False}),
|
||||
(90.0, 90.0, {"type": ButtonType.accelCruise, "pressed": True}),
|
||||
(5.0, 5.0, {"type": ButtonType.accelCruise, "pressed": False}),
|
||||
)
|
||||
|
||||
helper.update_v_cruise(cs, True, True, False, toggles)
|
||||
assert helper.v_cruise_kph == pytest.approx(pcm_speed_kph)
|
||||
assert helper.v_cruise_cluster_kph == pytest.approx(pcm_cluster_speed_kph)
|
||||
|
||||
next_pcm_speed_kph = 74.0
|
||||
next_cs = car.CarState(
|
||||
cruiseState={
|
||||
"available": True,
|
||||
"speed": next_pcm_speed_kph * CV.KPH_TO_MS,
|
||||
"speedCluster": next_pcm_speed_kph * CV.KPH_TO_MS,
|
||||
},
|
||||
buttonEvents=[{"type": ButtonType.accelCruise, "pressed": False}],
|
||||
)
|
||||
helper.update_v_cruise(next_cs, True, True, False, toggles)
|
||||
assert helper.v_cruise_kph == pytest.approx(next_pcm_speed_kph)
|
||||
|
||||
def test_openpilot_longitudinal_pcm_cruise_uses_custom_intervals(self):
|
||||
CP = car.CarParams(pcmCruise=True, openpilotLongitudinalControl=True)
|
||||
helper = VCruiseHelper(CP)
|
||||
toggles = SimpleNamespace(cruise_increase=5, cruise_increase_long=1, set_speed_limit=False)
|
||||
initial_speed_kph = 40.0
|
||||
|
||||
helper.initialize_v_cruise(car.CarState(vEgo=initial_speed_kph * CV.KPH_TO_MS), False, False, toggles)
|
||||
assert helper.v_cruise_kph == pytest.approx(initial_speed_kph)
|
||||
|
||||
press_cs = car.CarState(
|
||||
cruiseState={"available": True},
|
||||
buttonEvents=[{"type": ButtonType.accelCruise, "pressed": True}],
|
||||
)
|
||||
helper.update_v_cruise(press_cs, True, True, False, toggles)
|
||||
|
||||
release_cs = car.CarState(
|
||||
cruiseState={"available": True},
|
||||
buttonEvents=[{"type": ButtonType.accelCruise, "pressed": False}],
|
||||
)
|
||||
helper.update_v_cruise(release_cs, True, True, False, toggles)
|
||||
assert helper.v_cruise_kph == pytest.approx(initial_speed_kph + 5)
|
||||
|
||||
helper.update_v_cruise(press_cs, True, True, False, toggles)
|
||||
for _ in range(50):
|
||||
helper.update_v_cruise(car.CarState(cruiseState={"available": True}), True, True, False, toggles)
|
||||
|
||||
assert helper.v_cruise_kph == pytest.approx(initial_speed_kph + 5 + 1)
|
||||
|
||||
def test_openpilot_longitudinal_pcm_cruise_starts_from_pcm_set_speed(self):
|
||||
CP = car.CarParams(pcmCruise=True, openpilotLongitudinalControl=True)
|
||||
helper = VCruiseHelper(CP)
|
||||
toggles = SimpleNamespace(cruise_increase=5, cruise_increase_long=1, set_speed_limit=False)
|
||||
pcm_speed_kph = 72.0
|
||||
|
||||
helper.initialize_v_cruise(
|
||||
car.CarState(
|
||||
vEgo=40 * CV.KPH_TO_MS,
|
||||
cruiseState={"available": True, "speed": pcm_speed_kph * CV.KPH_TO_MS},
|
||||
),
|
||||
False,
|
||||
False,
|
||||
toggles,
|
||||
)
|
||||
assert helper.v_cruise_kph == pytest.approx(pcm_speed_kph)
|
||||
|
||||
helper.update_v_cruise(
|
||||
car.CarState(
|
||||
cruiseState={"available": True, "speed": 74 * CV.KPH_TO_MS},
|
||||
),
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
toggles,
|
||||
)
|
||||
assert helper.v_cruise_kph == pytest.approx(pcm_speed_kph)
|
||||
for pcm_speed_kph, pcm_cluster_speed_kph, button_event in samples:
|
||||
cs = car.CarState(
|
||||
cruiseState={
|
||||
"available": True,
|
||||
"speed": pcm_speed_kph * CV.KPH_TO_MS,
|
||||
"speedCluster": pcm_cluster_speed_kph * CV.KPH_TO_MS,
|
||||
},
|
||||
buttonEvents=[] if button_event is None else [button_event],
|
||||
)
|
||||
helper.update_v_cruise(cs, True, True, False, toggles)
|
||||
assert helper.v_cruise_kph == pytest.approx(pcm_speed_kph)
|
||||
assert helper.v_cruise_cluster_kph == pytest.approx(pcm_cluster_speed_kph)
|
||||
|
||||
|
||||
class TestVCruiseHelperRedneck:
|
||||
|
||||
@@ -639,6 +639,9 @@ class LatControlTorque(LatControl):
|
||||
output_torque *= tucson_4th_gen_center_taper
|
||||
elif genesis_g70_active:
|
||||
output_torque *= genesis_g70_center_output_taper
|
||||
output_torque *= get_genesis_g70_high_speed_transition_scale(
|
||||
setpoint, desired_lateral_jerk, CS.vEgo,
|
||||
)
|
||||
output_torque *= get_genesis_g70_curve_unwind_output_scale(setpoint, desired_lateral_jerk, CS.vEgo)
|
||||
output_torque *= get_genesis_g70_high_speed_error_scale(
|
||||
setpoint, measurement, desired_lateral_jerk, CS.vEgo,
|
||||
|
||||
@@ -289,6 +289,13 @@ GENESIS_G70_CENTER_OUTPUT_TAPER_LAT = 0.30
|
||||
GENESIS_G70_CENTER_OUTPUT_TAPER_LAT_WIDTH = 0.10
|
||||
GENESIS_G70_CENTER_OUTPUT_TAPER_SPEED = 18.0
|
||||
GENESIS_G70_CENTER_OUTPUT_TAPER_SPEED_WIDTH = 3.0
|
||||
GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_MAX = 0.18
|
||||
GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_SPEED = 45.0 * CV.MPH_TO_MS
|
||||
GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_SPEED_WIDTH = 8.0 * CV.MPH_TO_MS
|
||||
GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_LAT = 0.45
|
||||
GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_LAT_WIDTH = 0.15
|
||||
GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_JERK = 0.35
|
||||
GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_JERK_WIDTH = 0.15
|
||||
GENESIS_G70_LOW_SPEED_CENTER_TAPER_MAX = 0.06
|
||||
GENESIS_G70_LOW_SPEED_CENTER_TAPER_LAT = 0.14
|
||||
GENESIS_G70_LOW_SPEED_CENTER_TAPER_LAT_WIDTH = 0.05
|
||||
@@ -3246,6 +3253,18 @@ def get_genesis_g70_center_output_scale(desired_lateral_accel: float, v_ego: flo
|
||||
return 1.0 - reduction
|
||||
|
||||
|
||||
def get_genesis_g70_high_speed_transition_scale(desired_lateral_accel: float,
|
||||
desired_lateral_jerk: float, v_ego: float) -> float:
|
||||
speed_weight = _sigmoid((v_ego - GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_SPEED) /
|
||||
GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_SPEED_WIDTH)
|
||||
center_weight = _sigmoid((GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_LAT - abs(desired_lateral_accel)) /
|
||||
GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_LAT_WIDTH)
|
||||
jerk_weight = _sigmoid((abs(desired_lateral_jerk) - GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_JERK) /
|
||||
GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_JERK_WIDTH)
|
||||
reduction = (GENESIS_G70_HIGH_SPEED_TRANSITION_DAMPING_MAX * speed_weight * center_weight * jerk_weight)
|
||||
return 1.0 - reduction
|
||||
|
||||
|
||||
def get_genesis_g70_low_speed_angle_damping(desired_angle_deg: float, actual_angle_deg: float,
|
||||
current_output_torque: float, v_ego: float) -> float:
|
||||
angle_error = desired_angle_deg - actual_angle_deg
|
||||
|
||||
@@ -54,6 +54,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_vehicle_tunes import (
|
||||
get_rav4_tss2_pid_output,
|
||||
get_subaru_impreza_pid_output_scale,
|
||||
get_genesis_gv70_low_speed_center_overshoot_scale,
|
||||
get_genesis_g70_high_speed_transition_scale,
|
||||
normalize_flm_overrides,
|
||||
set_flm_runtime_overrides,
|
||||
)
|
||||
@@ -960,6 +961,12 @@ class TestLatControl:
|
||||
assert get_genesis_g70_low_speed_output_limit(0.0, 2.0) < 0.30
|
||||
assert get_genesis_g70_low_speed_angle_damping(0.0, -20.0, 0.0, 2.0) < 0.0
|
||||
assert get_genesis_g70_low_speed_angle_damping(0.0, 20.0, 0.0, 2.0) > 0.0
|
||||
assert get_genesis_g70_high_speed_transition_scale(0.0, 0.8, 65.0 * 0.44704) < \
|
||||
get_genesis_g70_high_speed_transition_scale(0.0, 0.1, 65.0 * 0.44704)
|
||||
assert get_genesis_g70_high_speed_transition_scale(1.0, 0.8, 65.0 * 0.44704) > \
|
||||
get_genesis_g70_high_speed_transition_scale(0.0, 0.8, 65.0 * 0.44704)
|
||||
assert get_genesis_g70_high_speed_transition_scale(0.0, 0.8, 20.0 * 0.44704) > \
|
||||
get_genesis_g70_high_speed_transition_scale(0.0, 0.8, 65.0 * 0.44704)
|
||||
assert 0.90 < get_genesis_g70_curve_unwind_output_scale(0.7, -0.5, 25.0) < 1.0
|
||||
assert get_genesis_g70_curve_unwind_output_scale(0.7, 0.5, 25.0) == 1.0
|
||||
assert get_genesis_g70_angle_output_scale(55.0, 1.0) > get_genesis_g70_angle_output_scale(85.0, 1.0)
|
||||
|
||||
@@ -32,6 +32,7 @@ class DeveloperLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
self._params.put_bool("LongitudinalManeuverMode", False)
|
||||
|
||||
# Build items and keep references for callbacks/state updates
|
||||
self._adb_toggle = toggle_item(
|
||||
@@ -59,13 +60,6 @@ class DeveloperLayout(Widget):
|
||||
enabled=ui_state.is_offroad,
|
||||
)
|
||||
|
||||
self._long_maneuver_toggle = toggle_item(
|
||||
lambda: tr("Longitudinal Maneuver Mode"),
|
||||
description="",
|
||||
initial_state=self._params.get_bool("LongitudinalManeuverMode"),
|
||||
callback=self._on_long_maneuver_mode,
|
||||
)
|
||||
|
||||
self._alpha_long_toggle = toggle_item(
|
||||
lambda: tr("openpilot Longitudinal Control (Alpha)"),
|
||||
description=lambda: tr(DESCRIPTIONS["alpha_longitudinal"]),
|
||||
@@ -87,7 +81,6 @@ class DeveloperLayout(Widget):
|
||||
self._ssh_toggle,
|
||||
self._ssh_keys,
|
||||
self._joystick_toggle,
|
||||
self._long_maneuver_toggle,
|
||||
self._alpha_long_toggle,
|
||||
self._ui_debug_toggle,
|
||||
], line_separator=True, spacing=0)
|
||||
@@ -114,13 +107,7 @@ class DeveloperLayout(Widget):
|
||||
else:
|
||||
self._alpha_long_toggle.set_visible(True)
|
||||
|
||||
long_man_enabled = ui_state.has_longitudinal_control and ui_state.is_offroad()
|
||||
self._long_maneuver_toggle.action_item.set_enabled(long_man_enabled)
|
||||
if not long_man_enabled:
|
||||
self._long_maneuver_toggle.action_item.set_state(False)
|
||||
self._params.put_bool("LongitudinalManeuverMode", False)
|
||||
else:
|
||||
self._long_maneuver_toggle.action_item.set_enabled(False)
|
||||
self._alpha_long_toggle.set_visible(False)
|
||||
|
||||
# TODO: make a param control list item so we don't need to manage internal state as much here
|
||||
@@ -129,7 +116,6 @@ class DeveloperLayout(Widget):
|
||||
("AdbEnabled", self._adb_toggle),
|
||||
("SshEnabled", self._ssh_toggle),
|
||||
("JoystickDebugMode", self._joystick_toggle),
|
||||
("LongitudinalManeuverMode", self._long_maneuver_toggle),
|
||||
("AlphaLongitudinalEnabled", self._alpha_long_toggle),
|
||||
("ShowDebugInfo", self._ui_debug_toggle),
|
||||
):
|
||||
@@ -149,12 +135,6 @@ class DeveloperLayout(Widget):
|
||||
def _on_joystick_debug_mode(self, state: bool):
|
||||
self._params.put_bool("JoystickDebugMode", state)
|
||||
self._params.put_bool("LongitudinalManeuverMode", False)
|
||||
self._long_maneuver_toggle.action_item.set_state(False)
|
||||
|
||||
def _on_long_maneuver_mode(self, state: bool):
|
||||
self._params.put_bool("LongitudinalManeuverMode", state)
|
||||
self._params.put_bool("JoystickDebugMode", False)
|
||||
self._joystick_toggle.action_item.set_state(False)
|
||||
|
||||
def _on_alpha_long_enabled(self, state: bool):
|
||||
if state:
|
||||
|
||||
@@ -731,7 +731,7 @@ class StarPilotLongitudinalLayout(_SettingsPage):
|
||||
unit=self._speed_unit(),
|
||||
value_type="float",
|
||||
current_value=max(1, self._params.get_float("CustomCruise"))),
|
||||
visible=lambda: self._params.get_bool("QOLLongitudinal")),
|
||||
visible=lambda: self._params.get_bool("QOLLongitudinal") and not starpilot_state.car_state.isToyota),
|
||||
SettingRow("CustomCruiseLong", "value", tr_noop("Cruise Long"),
|
||||
subtitle="",
|
||||
get_value=lambda: f"{max(1, self._params.get_float('CustomCruiseLong')):g}{self._speed_unit()}",
|
||||
@@ -739,7 +739,12 @@ class StarPilotLongitudinalLayout(_SettingsPage):
|
||||
unit=self._speed_unit(),
|
||||
value_type="float",
|
||||
current_value=max(1, self._params.get_float("CustomCruiseLong"))),
|
||||
visible=lambda: self._params.get_bool("QOLLongitudinal")),
|
||||
visible=lambda: self._params.get_bool("QOLLongitudinal") and not starpilot_state.car_state.isToyota),
|
||||
SettingRow("ReverseCruise", "toggle", tr_noop("Reverse Cruise Increase"),
|
||||
subtitle=tr_noop("Swap Toyota/Lexus cruise increments: short press changes the dash set speed by 5; hold changes it by 1."),
|
||||
get_state=lambda: self._params.get_bool("ReverseCruise"),
|
||||
set_state=lambda s: self._params.put_bool("ReverseCruise", s),
|
||||
visible=lambda: self._params.get_bool("QOLLongitudinal") and starpilot_state.car_state.isToyota),
|
||||
SettingRow("ForceStops", "toggle", tr_noop("Force Stops"),
|
||||
subtitle="",
|
||||
get_state=lambda: self._params.get_bool("ForceStops"),
|
||||
|
||||
@@ -12,6 +12,7 @@ class DeveloperLayoutMici(NavScroller):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._ssh_fetcher = SshKeyFetcher(ui_state.params)
|
||||
ui_state.params.put_bool("LongitudinalManeuverMode", False)
|
||||
|
||||
def github_username_callback(username: str):
|
||||
if username:
|
||||
@@ -45,7 +46,6 @@ class DeveloperLayoutMici(NavScroller):
|
||||
self._ssh_keys_btn = BigButton("SSH keys", "Not set" if not github_username else github_username, icon=txt_ssh)
|
||||
self._ssh_keys_btn.set_click_callback(ssh_keys_callback)
|
||||
|
||||
# adb, ssh, ssh keys, debug mode, joystick debug mode, longitudinal maneuver mode, ip address
|
||||
# ******** Main Scroller ********
|
||||
self._adb_toggle = BigCircleParamControl(gui_app.texture("icons_mici/adb_short.png", 82, 82), "AdbEnabled", icon_offset=(0, 12))
|
||||
self._ssh_toggle = BigCircleParamControl(gui_app.texture("icons_mici/ssh_short.png", 82, 82), "SshEnabled", icon_offset=(0, 12))
|
||||
@@ -53,9 +53,6 @@ class DeveloperLayoutMici(NavScroller):
|
||||
self._joystick_toggle = BigToggle("joystick debug mode",
|
||||
initial_state=ui_state.params.get_bool("JoystickDebugMode"),
|
||||
toggle_callback=self._on_joystick_debug_mode)
|
||||
self._long_maneuver_toggle = BigToggle("longitudinal maneuver mode",
|
||||
initial_state=ui_state.params.get_bool("LongitudinalManeuverMode"),
|
||||
toggle_callback=self._on_long_maneuver_mode)
|
||||
self._alpha_long_toggle = BigToggle("alpha longitudinal",
|
||||
initial_state=ui_state.params.get_bool("AlphaLongitudinalEnabled"),
|
||||
toggle_callback=self._on_alpha_long_enabled)
|
||||
@@ -69,7 +66,6 @@ class DeveloperLayoutMici(NavScroller):
|
||||
self._ssh_keys_btn,
|
||||
self._disable_wide_road_toggle,
|
||||
self._joystick_toggle,
|
||||
self._long_maneuver_toggle,
|
||||
self._alpha_long_toggle,
|
||||
self._debug_mode_toggle,
|
||||
])
|
||||
@@ -80,7 +76,6 @@ class DeveloperLayoutMici(NavScroller):
|
||||
("SshEnabled", self._ssh_toggle),
|
||||
("DisableWideRoad", self._disable_wide_road_toggle),
|
||||
("JoystickDebugMode", self._joystick_toggle),
|
||||
("LongitudinalManeuverMode", self._long_maneuver_toggle),
|
||||
("AlphaLongitudinalEnabled", self._alpha_long_toggle),
|
||||
("ShowDebugInfo", self._debug_mode_toggle),
|
||||
)
|
||||
@@ -89,7 +84,7 @@ class DeveloperLayoutMici(NavScroller):
|
||||
self._disable_wide_road_toggle,
|
||||
self._joystick_toggle,
|
||||
)
|
||||
engaged_blocked_toggles = (self._long_maneuver_toggle, self._alpha_long_toggle)
|
||||
engaged_blocked_toggles = (self._alpha_long_toggle,)
|
||||
|
||||
# Disable toggles that require offroad
|
||||
for item in onroad_blocked_toggles:
|
||||
@@ -129,13 +124,7 @@ class DeveloperLayoutMici(NavScroller):
|
||||
else:
|
||||
self._alpha_long_toggle.set_visible(True)
|
||||
|
||||
long_man_enabled = ui_state.has_longitudinal_control and ui_state.is_offroad()
|
||||
self._long_maneuver_toggle.set_enabled(long_man_enabled)
|
||||
if not long_man_enabled:
|
||||
self._long_maneuver_toggle.set_checked(False)
|
||||
ui_state.params.put_bool("LongitudinalManeuverMode", False)
|
||||
else:
|
||||
self._long_maneuver_toggle.set_enabled(False)
|
||||
self._alpha_long_toggle.set_visible(False)
|
||||
|
||||
# Refresh toggles from params to mirror external changes
|
||||
@@ -145,16 +134,8 @@ class DeveloperLayoutMici(NavScroller):
|
||||
def _on_joystick_debug_mode(self, state: bool):
|
||||
ui_state.params.put_bool("JoystickDebugMode", state)
|
||||
ui_state.params.put_bool("LongitudinalManeuverMode", False)
|
||||
self._long_maneuver_toggle.set_checked(False)
|
||||
ui_state.params.put_bool("LateralManeuverMode", False)
|
||||
|
||||
def _on_long_maneuver_mode(self, state: bool):
|
||||
ui_state.params.put_bool("LongitudinalManeuverMode", state)
|
||||
ui_state.params.put_bool("JoystickDebugMode", False)
|
||||
self._joystick_toggle.set_checked(False)
|
||||
ui_state.params.put_bool("LateralManeuverMode", False)
|
||||
restart_needed_callback(state)
|
||||
|
||||
def _on_alpha_long_enabled(self, state: bool):
|
||||
# TODO: show confirmation dialog before enabling
|
||||
ui_state.params.put_bool("AlphaLongitudinalEnabled", state)
|
||||
|
||||
@@ -1704,6 +1704,10 @@
|
||||
"precision": 0,
|
||||
"unit_type": "vehicle_speed",
|
||||
"metric_max": 150.0,
|
||||
"excluded_vehicle_makes": [
|
||||
"Lexus",
|
||||
"Toyota"
|
||||
],
|
||||
"parent_key": "QOLLongitudinal",
|
||||
"settings_tier": "simple"
|
||||
},
|
||||
@@ -1719,6 +1723,24 @@
|
||||
"precision": 0,
|
||||
"unit_type": "vehicle_speed",
|
||||
"metric_max": 150.0,
|
||||
"excluded_vehicle_makes": [
|
||||
"Lexus",
|
||||
"Toyota"
|
||||
],
|
||||
"parent_key": "QOLLongitudinal",
|
||||
"settings_tier": "simple"
|
||||
},
|
||||
{
|
||||
"key": "ReverseCruise",
|
||||
"label": "Reverse Cruise Increase",
|
||||
"description": "Reverse Toyota/Lexus cruise-button behavior so a short press changes the dashboard set speed by 5 and a hold changes it by 1.",
|
||||
"picker_description": "Swaps Toyota/Lexus short-press and hold cruise increments.",
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle",
|
||||
"vehicle_makes": [
|
||||
"Lexus",
|
||||
"Toyota"
|
||||
],
|
||||
"parent_key": "QOLLongitudinal",
|
||||
"settings_tier": "simple"
|
||||
},
|
||||
@@ -4829,12 +4851,12 @@
|
||||
},
|
||||
{
|
||||
"key": "GalaxyMobileDefault",
|
||||
"label": "Try the Big Dipper Web UI",
|
||||
"description": "Open the Big Dipper at the top-level Galaxy link instead of the classic Galaxy. The classic UI remains available at /classic and Big Dipper at /mobile regardless of this toggle.",
|
||||
"picker_description": "Serve the Big Dipper as the default landing page.",
|
||||
"label": "Use Galaxy (new) by Default",
|
||||
"description": "Open Galaxy (new) at the top-level Galaxy link. Turn this off to use Galaxy (old) instead. Galaxy (old) remains available at /classic and Galaxy (new) at /mobile.",
|
||||
"picker_description": "Serve Galaxy (new) as the default landing page.",
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle",
|
||||
"settings_tier": "advanced"
|
||||
"settings_tier": "simple"
|
||||
},
|
||||
{
|
||||
"key": "AlphaLongitudinalEnabled",
|
||||
|
||||
@@ -390,6 +390,16 @@ def speed_limit_controller_available(openpilot_longitudinal: bool, redneck_cruis
|
||||
return openpilot_longitudinal or redneck_cruise
|
||||
|
||||
|
||||
def software_cruise_intervals_available(quality_of_life: bool, car_make: str, pcm_cruise: bool,
|
||||
openpilot_longitudinal: bool, pcm_cruise_speed: bool) -> bool:
|
||||
return bool(quality_of_life and not (car_make == "toyota" and pcm_cruise) and
|
||||
(openpilot_longitudinal or not pcm_cruise_speed))
|
||||
|
||||
|
||||
def reverse_cruise_available(quality_of_life: bool, car_make: str, pcm_cruise: bool) -> bool:
|
||||
return bool(quality_of_life and car_make == "toyota" and pcm_cruise)
|
||||
|
||||
|
||||
def migrate_cancel_button_controls(params: Params | None = None) -> bool:
|
||||
params = params or Params(return_defaults=True)
|
||||
if params.get_bool(CANCEL_BUTTON_MIGRATION_KEY) or not params.get_bool("RemapCancelToDistance"):
|
||||
@@ -1345,10 +1355,17 @@ class StarPilotVariables:
|
||||
toggle.pause_lateral_below_signal = self.get_value("PauseLateralOnSignal", condition=toggle.pause_lateral_below_speed != 0)
|
||||
toggle.pause_lateral_signal_delay = self.get_value("LateralResumeDelay", cast=float, condition=toggle.pause_lateral_below_signal, default=0.0, min=0.0, max=5.0)
|
||||
|
||||
quality_of_life_longitudinal = toggle.openpilot_longitudinal and self.get_value("QOLLongitudinal")
|
||||
quality_of_life_cruise = self.get_value("QOLLongitudinal") and (toggle.openpilot_longitudinal or not FPCP.pcmCruiseSpeed)
|
||||
quality_of_life = self.get_value("QOLLongitudinal")
|
||||
quality_of_life_longitudinal = toggle.openpilot_longitudinal and quality_of_life
|
||||
quality_of_life_cruise = software_cruise_intervals_available(
|
||||
quality_of_life, toggle.car_make, pcm_cruise, toggle.openpilot_longitudinal, FPCP.pcmCruiseSpeed,
|
||||
)
|
||||
toggle.cruise_increase = self.get_value("CustomCruise", cast=float, condition=quality_of_life_cruise, default=1.0)
|
||||
toggle.cruise_increase_long = self.get_value("CustomCruiseLong", cast=float, condition=quality_of_life_cruise, default=5.0)
|
||||
toggle.reverse_cruise_increase = self.get_value(
|
||||
"ReverseCruise",
|
||||
condition=reverse_cruise_available(quality_of_life, toggle.car_make, pcm_cruise),
|
||||
)
|
||||
toggle.force_stops = self.get_value("ForceStops", condition=quality_of_life_longitudinal)
|
||||
toggle.force_stop_distance_offset = self.get_value("ForceStopDistanceOffset", cast=int, condition=(quality_of_life_longitudinal and toggle.force_stops))
|
||||
toggle.force_standstill = self.get_value("ForceStandstill", condition=quality_of_life_longitudinal)
|
||||
|
||||
@@ -328,4 +328,14 @@ def test_set_speed_limit_unavailable_on_stock_pcm_without_helper():
|
||||
def test_speed_limit_controller_available_on_openpilot_longitudinal_or_redneck():
|
||||
assert spv.speed_limit_controller_available(openpilot_longitudinal=True, redneck_cruise=False) is True
|
||||
assert spv.speed_limit_controller_available(openpilot_longitudinal=False, redneck_cruise=True) is True
|
||||
|
||||
|
||||
def test_toyota_pcm_cruise_uses_hardware_reverse_instead_of_software_intervals():
|
||||
assert spv.software_cruise_intervals_available(True, "toyota", True, True, True) is False
|
||||
assert spv.reverse_cruise_available(True, "toyota", True) is True
|
||||
|
||||
|
||||
def test_non_toyota_software_cruise_keeps_custom_intervals():
|
||||
assert spv.software_cruise_intervals_available(True, "hyundai", False, True, True) is True
|
||||
assert spv.reverse_cruise_available(True, "hyundai", False) is False
|
||||
assert spv.speed_limit_controller_available(openpilot_longitudinal=False, redneck_cruise=False) is False
|
||||
|
||||
@@ -10,7 +10,6 @@ import { VehicleFeatures } from "/assets/components/tools/vehicle_features.js"
|
||||
import { TSKManager } from "/assets/components/tools/tsk_manager.js"
|
||||
import { GalaxyPairing } from "/assets/components/tools/galaxy.js"
|
||||
import { Home } from "/assets/components/home/home.js"
|
||||
import { LongitudinalManeuvers } from "/assets/components/tools/longitudinal_maneuvers.js"
|
||||
import { MapsManager } from "/assets/components/tools/maps.js"
|
||||
import { NavDestination } from "/assets/components/navigation/navigation_destination.js?v=nav-search-context-2"
|
||||
import { NavKeys } from "/assets/components/navigation/navigation_keys.js?v=app-keys-session-1"
|
||||
@@ -91,7 +90,6 @@ function Root() {
|
||||
createRoute("model_laboratory", "/model_laboratory", ModelLaboratory),
|
||||
createRoute("tuning", "/tuning", Tuning),
|
||||
createRoute("lateral_maneuvers", "/lateral_maneuvers", Tuning),
|
||||
createRoute("longitudinal_maneuvers", "/longitudinal_maneuvers", LongitudinalManeuvers),
|
||||
createRoute("maps", "/manage_maps", MapsManager),
|
||||
createRoute("plots", "/plots", LivePlots),
|
||||
createRoute("thememaker", "/theme_maker", ThemeMaker),
|
||||
|
||||
@@ -18,7 +18,6 @@ const MENU_ITEMS = {
|
||||
{ name: "Sentry Mode", link: "/sentry", icon: "bi-shield-exclamation" },
|
||||
{ name: "Controllers", link: "/wheel-controls", icon: "bi-controller" },
|
||||
{ name: "Lateral Tuning", link: "/tuning", icon: "bi-sign-turn-right" },
|
||||
{ name: "Long Maneuvers", link: "/longitudinal_maneuvers", icon: "bi-signpost-split" },
|
||||
{ name: "Maps", link: "/manage_maps", icon: "bi-map" },
|
||||
{ name: "Navigation", link: "/set_navigation_destination", icon: "bi-geo-alt-fill" },
|
||||
{ name: "App Keys", link: "/manage_navigation_keys", icon: "bi-key-fill" },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { html, reactive } from "/assets/vendor/arrow-core.js"
|
||||
import { formatNumericParamValue, resolveVehicleUnitParam, vehicleSpeedUnit } from "/assets/mobile/js/params.js"
|
||||
import { formatNumericParamValue, resolveVehicleUnitParam } from "/assets/mobile/js/params.js"
|
||||
|
||||
const endpointOptionsCache = {}
|
||||
const endpointOptionsInflight = {}
|
||||
@@ -102,9 +102,11 @@ function normalizeVehicleMake(value) {
|
||||
|
||||
function isVehicleSettingVisible(section, param) {
|
||||
const allowedMakes = param.vehicle_makes || (section.name === "Vehicle" ? VEHICLE_SETTING_MAKES[param.key] : null)
|
||||
if (!allowedMakes) return true
|
||||
const selectedMake = normalizeVehicleMake(state.values.CarMake)
|
||||
return allowedMakes.some(make => normalizeVehicleMake(make) === selectedMake)
|
||||
if (allowedMakes && !allowedMakes.some(make => normalizeVehicleMake(make) === selectedMake)) return false
|
||||
|
||||
const excludedMakes = param.excluded_vehicle_makes || []
|
||||
return !excludedMakes.some(make => normalizeVehicleMake(make) === selectedMake)
|
||||
}
|
||||
|
||||
function matchesSettingValueCondition(param) {
|
||||
@@ -1818,11 +1820,6 @@ export function DeviceSettings({ params }) {
|
||||
<div class="ds-wrapper">
|
||||
<h2>Toggles</h2>
|
||||
|
||||
<div class="ds-unit-note">
|
||||
<i class="bi bi-speedometer2"></i>
|
||||
<span>Vehicle-unit speed settings use <strong>${() => vehicleSpeedUnit(state.values)}</strong> and follow the comma's <em>Use Metric System</em> toggle. Each control shows its adjustment step.</span>
|
||||
</div>
|
||||
|
||||
<div class="ds-search-row">
|
||||
<input
|
||||
class="ds-search"
|
||||
|
||||
@@ -138,10 +138,13 @@
|
||||
|
||||
.dh-donut {
|
||||
--dh-value: 0;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 0 0 auto;
|
||||
gap: 3px;
|
||||
height: 116px;
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 116px;
|
||||
}
|
||||
|
||||
@@ -610,6 +610,48 @@ ul { list-style: none; margin: 0; padding: 0; }
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.gx-row.gx-diagnostic-row--changed {
|
||||
background: rgba(139, 108, 197, 0.18) !important;
|
||||
box-shadow: inset 3px 0 var(--primary);
|
||||
}
|
||||
|
||||
.gx-update-progress {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin-top: var(--sp-2);
|
||||
}
|
||||
|
||||
.gx-update-progress__track {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border-radius: var(--radius-full);
|
||||
height: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gx-update-progress__fill {
|
||||
background: linear-gradient(90deg, #5ec8c8 0%, #8b6cc5 100%);
|
||||
border-radius: inherit;
|
||||
height: 100%;
|
||||
transition: width 0.4s ease;
|
||||
}
|
||||
|
||||
.gx-update-progress__fill--error {
|
||||
background: linear-gradient(90deg, #b43a3a 0%, #de5656 100%);
|
||||
}
|
||||
|
||||
.gx-update-progress__meta {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-size: var(--fs-sm);
|
||||
gap: var(--sp-2);
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.gx-update-progress small {
|
||||
color: var(--text-muted);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
[data-theme="light"] .gx-card {
|
||||
border: 1px solid rgba(120, 73, 232, 0.22);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="Big Dipper">
|
||||
<meta name="apple-mobile-web-app-title" content="Galaxy">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="theme-color" content="#8b6cc5" />
|
||||
<link rel="manifest" href="/assets/mobile/manifest.json" crossorigin="use-credentials">
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<title>Big Dipper</title>
|
||||
<title>Galaxy</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -8,12 +8,12 @@ import { Logs } from "./views/Logs.js"
|
||||
import { Tuning } from "./views/Tuning.js"
|
||||
import { Navigation } from "./views/Navigation.js"
|
||||
import { Vehicle } from "./views/Vehicle.js"
|
||||
import { Bluetooth } from "./views/Bluetooth.js"
|
||||
import { SystemTools } from "./views/SystemTools.js"
|
||||
import { ToolEmbed } from "./views/ToolEmbed.js"
|
||||
import { Doors } from "./views/Doors.js"
|
||||
import { Galaxy } from "./views/Galaxy.js"
|
||||
import { Tsk } from "./views/Tsk.js"
|
||||
import { Sentry } from "./views/Sentry.js"
|
||||
import { ModelManager } from "./views/ModelManager.js"
|
||||
import { Plots } from "./views/Plots.js"
|
||||
import { TestingGround } from "./views/TestingGround.js"
|
||||
@@ -43,12 +43,13 @@ const VIEWS = {
|
||||
"/tuning": Tuning,
|
||||
"/navigation": Navigation,
|
||||
"/vehicle": Vehicle,
|
||||
"/bluetooth": Bluetooth,
|
||||
"/system": SystemTools,
|
||||
"/embed": ToolEmbed,
|
||||
"/manage_doors": Doors,
|
||||
"/galaxy": Galaxy,
|
||||
"/manage_tsk": Tsk,
|
||||
"/sentry": Sentry,
|
||||
"/sentry": Cameras,
|
||||
"/manage_models": ModelManager,
|
||||
"/plots": Plots,
|
||||
"/testing_ground": TestingGround,
|
||||
|
||||
@@ -7,12 +7,12 @@ const NAV = {
|
||||
{ name: "Recordings", link: "/recordings", icon: "bi-camera-reels" },
|
||||
],
|
||||
tools: [
|
||||
{ name: "Bluetooth", link: "/bluetooth", icon: "bi-bluetooth" },
|
||||
{ name: "Cameras & Monitoring", link: "/cameras", icon: "bi-camera-video" },
|
||||
{ name: "Galaxy", link: "/galaxy", icon: "bi-globe2" },
|
||||
{ name: "Logs & Diagnostics", link: "/logs", icon: "bi-exclamation-triangle" },
|
||||
{ name: "Model Manager", link: "/manage_models", icon: "bi-cpu" },
|
||||
{ name: "Navigation & Maps", link: "/navigation", icon: "bi-map" },
|
||||
{ name: "Sentry Mode", link: "/sentry", icon: "bi-shield-exclamation" },
|
||||
{ name: "System Tools", link: "/system", icon: "bi-arrow-repeat" },
|
||||
{ name: "Model Laboratory", link: "/model_laboratory", icon: "bi-bezier2" },
|
||||
{ name: "Plots", link: "/plots", icon: "bi-graph-up-arrow" },
|
||||
@@ -102,7 +102,7 @@ export const AppShell = {
|
||||
<button type="button" class="gx-icon-btn gx-menu-btn" aria-label="Menu" @click="store.drawerOpen = true">
|
||||
<i class="bi bi-list"></i>
|
||||
</button>
|
||||
<span class="gx-appbar__title">Big Dipper</span>
|
||||
<span class="gx-appbar__title">Galaxy</span>
|
||||
<div class="gx-searchwrap">
|
||||
<input ref="searchInput" class="gx-search gx-appbar__search" type="search" placeholder="Search toggles..."
|
||||
v-model="search" aria-label="Search toggles" />
|
||||
@@ -128,8 +128,8 @@ export const AppShell = {
|
||||
</transition>
|
||||
<aside class="gx-drawer" :class="{ open: store.drawerOpen }">
|
||||
<div class="gx-drawer__header">
|
||||
<img class="gx-logo" src="/assets/images/main_logo.png" alt="Big Dipper logo" />
|
||||
<span class="gx-drawer-title">Big Dipper</span>
|
||||
<img class="gx-logo" src="/assets/images/main_logo.png" alt="Galaxy logo" />
|
||||
<span class="gx-drawer-title">Galaxy</span>
|
||||
</div>
|
||||
<div class="gx-nav-section">
|
||||
<div class="gx-nav-section__title">Main</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ export const BluetoothPanel = {
|
||||
availableDevices() { return this.devices.filter((d) => !d.paired && !d.trusted && !d.connected) },
|
||||
},
|
||||
methods: {
|
||||
address,
|
||||
async refresh() {
|
||||
try {
|
||||
const p = await api.getBluetoothStatus()
|
||||
|
||||
@@ -216,6 +216,7 @@ export const TroubleshootPanel = {
|
||||
<div v-if="!itemsVisible(section).length" class="gx-empty">No settings are currently different from their defaults.</div>
|
||||
<div v-else style="display:grid; gap:8px; grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); padding:0 var(--sp-3) var(--sp-3);">
|
||||
<div v-for="item in itemsVisible(section)" :key="item.label" class="gx-row"
|
||||
:class="{ 'gx-diagnostic-row--changed': isChanged(item) }"
|
||||
style="border:none; background:var(--surface); border-radius:var(--radius-md); margin:0; padding:10px 12px; flex-direction:column; align-items:stretch; gap:8px;">
|
||||
<div style="display:flex; align-items:center; gap:6px; min-width:0;">
|
||||
<span class="gx-row__label" style="font-size:var(--fs-sm); overflow-wrap:anywhere;">{{ item.label }}</span>
|
||||
|
||||
@@ -91,7 +91,7 @@ export function goBack() {
|
||||
window.location.hash = prev
|
||||
}
|
||||
|
||||
const NATIVE_ROOTS = new Set(["/", "/settings", "/tools", "/recordings", "/logs", "/tuning", "/navigation", "/vehicle", "/system", "/embed", "/manage_doors", "/galaxy", "/manage_tsk", "/sentry", "/manage_models", "/plots", "/testing_ground", "/theme_maker", "/model_laboratory", "/cameras"])
|
||||
const NATIVE_ROOTS = new Set(["/", "/settings", "/tools", "/recordings", "/logs", "/tuning", "/navigation", "/vehicle", "/bluetooth", "/system", "/embed", "/manage_doors", "/galaxy", "/manage_tsk", "/sentry", "/manage_models", "/plots", "/testing_ground", "/theme_maker", "/model_laboratory", "/cameras"])
|
||||
|
||||
export function toolHref(link) {
|
||||
const path = link.split("?")[0]
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { BluetoothPanel } from "../components/BluetoothPanel.js"
|
||||
import { GalaxySection } from "../components/GalaxySection.js"
|
||||
|
||||
export const Bluetooth = {
|
||||
name: "Bluetooth",
|
||||
components: { BluetoothPanel, GalaxySection },
|
||||
template: `
|
||||
<div class="gx-view">
|
||||
<h2 style="margin-top:0;">Bluetooth</h2>
|
||||
<GalaxySection title="Bluetooth Devices" icon="bi-bluetooth" :collapsible="false">
|
||||
<BluetoothPanel />
|
||||
</GalaxySection>
|
||||
</div>
|
||||
`,
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
import { GalaxyTabs } from "../components/GalaxyTabs.js"
|
||||
import { useTabRouting } from "../composables.js"
|
||||
import { Sentry } from "./Sentry.js"
|
||||
import { Vasm } from "./Vasm.js"
|
||||
import { Pip } from "./Pip.js"
|
||||
|
||||
const TABS = {
|
||||
sentry: "Sentry Mode",
|
||||
vasm: "V-ASM Spot Monitor",
|
||||
pip: "PiP Side Camera",
|
||||
}
|
||||
|
||||
export const Cameras = {
|
||||
name: "Cameras",
|
||||
components: { Vasm, Pip, GalaxyTabs },
|
||||
components: { Sentry, Vasm, Pip, GalaxyTabs },
|
||||
setup() {
|
||||
return useTabRouting("/cameras", { vasm: "vasm", pip: "pip" })
|
||||
return useTabRouting("/cameras", { sentry: "sentry", vasm: "vasm", pip: "pip" })
|
||||
},
|
||||
data() { return { TABS } },
|
||||
template: `
|
||||
@@ -20,7 +22,11 @@ export const Cameras = {
|
||||
<h2 style="margin-top:0;">Cameras & Monitoring</h2>
|
||||
<GalaxyTabs :items="TABS" :active="tab" @select="selectTab" />
|
||||
|
||||
<template v-if="tab === 'vasm'">
|
||||
<template v-if="tab === 'sentry'">
|
||||
<Sentry />
|
||||
</template>
|
||||
|
||||
<template v-else-if="tab === 'vasm'">
|
||||
<Vasm :embedded="true" />
|
||||
</template>
|
||||
|
||||
|
||||
@@ -4,6 +4,13 @@ import { PwaInstallSection, isFirestarOrigin } from "../components/PwaInstallSec
|
||||
|
||||
const isTunnel = () => isFirestarOrigin()
|
||||
|
||||
function localDeviceUrl(ip) {
|
||||
const raw = String(ip || "").trim()
|
||||
if (!raw || raw === "unknown") return ""
|
||||
const host = raw.includes(":") && !raw.startsWith("[") ? `[${raw}]` : raw
|
||||
return `http://${host}:8082`
|
||||
}
|
||||
|
||||
export const Galaxy = {
|
||||
name: "Galaxy",
|
||||
components: { PwaInstallSection },
|
||||
@@ -14,10 +21,17 @@ export const Galaxy = {
|
||||
url: "",
|
||||
password: "",
|
||||
submitting: false,
|
||||
localUrl: "",
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (this.isTunnel) return
|
||||
if (this.isTunnel) {
|
||||
try {
|
||||
const status = await api.getDeviceStatus()
|
||||
this.localUrl = localDeviceUrl(status?.lanIp)
|
||||
} catch (e) {}
|
||||
return
|
||||
}
|
||||
try {
|
||||
const data = await api.getGalaxyStatus()
|
||||
this.paired = !!data?.paired
|
||||
@@ -76,7 +90,11 @@ export const Galaxy = {
|
||||
<i class="bi bi-satellite gx-alert__icon"></i>
|
||||
<div class="gx-alert__body">
|
||||
<strong>Galaxy Pairing Unavailable via Galaxy</strong>
|
||||
<span>Galaxy pairing requires a direct connection. Connect to your device's local network to use this feature.</span>
|
||||
<span>
|
||||
Galaxy pairing requires a direct connection. If you are on the same local network, connect here:
|
||||
<a v-if="localUrl" :href="localUrl" style="color:inherit; font-weight:var(--fw-bold); overflow-wrap:anywhere;">{{ localUrl }}</a>
|
||||
<span v-else>your device's local IP on port 8082.</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -253,7 +253,7 @@ export const Home = {
|
||||
name: m.name,
|
||||
label: `${toInt(m.drives)} ${toNum(m.drives) === 1 ? "drive" : "drives"} using this model`,
|
||||
}))
|
||||
return { hasModels: true, style: `background: conic-gradient(${segments.join(", ")})`, rows }
|
||||
return { hasModels: true, style: `conic-gradient(${segments.join(", ")})`, rows }
|
||||
},
|
||||
|
||||
storageView() {
|
||||
@@ -510,7 +510,7 @@ export const Home = {
|
||||
<section class="gx-card dh-card">
|
||||
<div class="dh-card__head"><i class="bi bi-stars"></i><span>Most used models</span></div>
|
||||
<div v-if="modelView.hasModels" class="dh-body dh-models">
|
||||
<div class="dh-chart-ring" :style="{ background: modelView.style }"></div>
|
||||
<div class="dh-chart-ring" :style="{ backgroundImage: modelView.style }" role="img" aria-label="Model usage share"></div>
|
||||
<div class="dh-models__list">
|
||||
<div v-for="m in modelView.rows" :key="m.name" class="dh-model">
|
||||
<span class="dh-swatch" :style="{ background: m.color }"></span>
|
||||
|
||||
@@ -5,9 +5,9 @@ import { TroubleshootPanel } from "../components/TroubleshootPanel.js"
|
||||
import { GalaxyTabs } from "../components/GalaxyTabs.js"
|
||||
|
||||
const TABS = {
|
||||
troubleshoot: "Troubleshoot",
|
||||
errors: "Error Logs",
|
||||
tmux: "Tmux Live Log",
|
||||
troubleshoot: "Troubleshoot",
|
||||
}
|
||||
|
||||
function parseLogDate(filename) {
|
||||
@@ -35,7 +35,7 @@ export const Logs = {
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
return useTabRouting("/logs", { errors: "errors", tmux: "tmux", troubleshoot: "troubleshoot" })
|
||||
return useTabRouting("/logs", { troubleshoot: "troubleshoot", errors: "errors", tmux: "tmux" })
|
||||
},
|
||||
created() {
|
||||
this.stream = useLogStream({ endpoint: "/api/tmux_log/live", snapshotFn: () => api.tmuxSnapshot(), interval: 2000 })
|
||||
|
||||
@@ -52,6 +52,13 @@ function normalizeRoute(r) {
|
||||
}
|
||||
}
|
||||
|
||||
function localDeviceUrl(ip) {
|
||||
const raw = String(ip || "").trim()
|
||||
if (!raw || raw === "unknown") return ""
|
||||
const host = raw.includes(":") && !raw.startsWith("[") ? `[${raw}]` : raw
|
||||
return `http://${host}:8082`
|
||||
}
|
||||
|
||||
export const Recordings = {
|
||||
name: "Recordings",
|
||||
components: { GalaxyTabs, GxNotice },
|
||||
@@ -75,6 +82,7 @@ export const Recordings = {
|
||||
logsRoute: null,
|
||||
logsData: null,
|
||||
onFirestar: isFirestarOrigin(),
|
||||
localUrl: "",
|
||||
// Screen recordings subtab
|
||||
screenLoading: false,
|
||||
screenError: "",
|
||||
@@ -335,7 +343,14 @@ export const Recordings = {
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
if (!this.onFirestar) await this.loadRoutes()
|
||||
if (this.onFirestar) {
|
||||
try {
|
||||
const status = await api.getDeviceStatus()
|
||||
this.localUrl = localDeviceUrl(status?.lanIp)
|
||||
} catch (e) {}
|
||||
return
|
||||
}
|
||||
await this.loadRoutes()
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.controller?.abort()
|
||||
@@ -503,8 +518,11 @@ export const Recordings = {
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<GxNotice v-else tone="info" icon="bi-satellite" title="Recordings Unavailable via Galaxy"
|
||||
text="Loading recordings requires a direct connection. Connect to your device's local network to use this feature." />
|
||||
<GxNotice v-else tone="info" icon="bi-satellite" title="Recordings unavailable via Galaxy">
|
||||
Recordings are unavailable via Galaxy for bandwidth reasons. If you are on the same local network, connect here:
|
||||
<a v-if="localUrl" :href="localUrl" style="color:inherit; font-weight:var(--fw-bold); overflow-wrap:anywhere;">{{ localUrl }}</a>
|
||||
<span v-else>your device's local IP on port 8082.</span>
|
||||
</GxNotice>
|
||||
</div>
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { api, showSnackbar } from "../api.js"
|
||||
import { navigate, store } from "../store.js"
|
||||
import {
|
||||
applyParamChange, countAdvancedHiddenByDeveloperMode, GALAXY_DEVELOPER_MODE_KEY, isSettingVisible,
|
||||
resolveVehicleUnitParam, slugifySectionName, vehicleSpeedUnit,
|
||||
resolveVehicleUnitParam, slugifySectionName,
|
||||
} from "../params.js"
|
||||
import { SettingTree } from "../components/SettingTree.js"
|
||||
import { GalaxyToggleCard } from "../components/GalaxyToggleCard.js"
|
||||
@@ -39,7 +39,6 @@ export const Settings = {
|
||||
return this.sections.find((s) => s.slug === this.activeSectionSlug) || this.sections[0]
|
||||
},
|
||||
hiddenAdvancedCount() { return countAdvancedHiddenByDeveloperMode(this.layout, this.values) },
|
||||
speedUnit() { return vehicleSpeedUnit(this.values) },
|
||||
searchActive() { return !!this.searchTerm },
|
||||
searchTerm: {
|
||||
get() { return store.search },
|
||||
@@ -120,11 +119,6 @@ export const Settings = {
|
||||
<div>
|
||||
<h2 style="margin-top:0;">Toggles</h2>
|
||||
|
||||
<div class="gx-unit-note">
|
||||
<i class="bi bi-speedometer2"></i>
|
||||
<span>Vehicle-unit speed settings use <strong>{{ speedUnit }}</strong> and follow the comma's <em>Use Metric System</em> toggle. Each control shows its adjustment step.</span>
|
||||
</div>
|
||||
|
||||
<DevModeBanner :hidden-count="hiddenAdvancedCount" :dev-mode-on="devModeOn" />
|
||||
|
||||
<div v-if="loading" class="gx-loading">Loading configuration...</div>
|
||||
|
||||
@@ -30,11 +30,17 @@ export const SystemTools = {
|
||||
profileBusy: "",
|
||||
}
|
||||
},
|
||||
created() { this.poll = usePolling(() => this.loadFastStatus(), { interval: 3000 }); this.poll.start() },
|
||||
created() {
|
||||
this.poll = usePolling(() => this.loadFastStatus(), {
|
||||
interval: 1000,
|
||||
enabled: () => !this.fastStatus || !!this.fastStatus.running,
|
||||
})
|
||||
this.poll.start()
|
||||
},
|
||||
mounted() { this.loadBranches(); this.loadProfiles() },
|
||||
beforeUnmount() { this.poll?.destroy() },
|
||||
computed: {
|
||||
updateAvailable() { return !!this.fastStatus?.updateAvailable && !this.fastStatus?.running },
|
||||
updateAvailable() { return this.checkedForUpdates && !!this.fastStatus?.updateAvailable && !this.fastStatus?.running },
|
||||
factoryResetStatus() {
|
||||
const s = this.fastStatus
|
||||
if (!s || String(s?.lastMode || "").trim() !== "factory-reset") return null
|
||||
@@ -53,6 +59,7 @@ export const SystemTools = {
|
||||
},
|
||||
methods: {
|
||||
shortCommit,
|
||||
toPercent,
|
||||
async loadBranches() {
|
||||
try {
|
||||
const data = await api.getUpdateBranches()
|
||||
@@ -65,8 +72,15 @@ export const SystemTools = {
|
||||
this.branchLoading = false
|
||||
}
|
||||
},
|
||||
async loadFastStatus() {
|
||||
try { this.fastStatus = await api.getUpdateFastStatus() } catch (e) { this.fastStatus = null }
|
||||
async loadFastStatus({ throwOnError = false } = {}) {
|
||||
try {
|
||||
const status = await api.getUpdateFastStatus()
|
||||
if (!status) throw new Error("Update status unavailable")
|
||||
this.fastStatus = status
|
||||
} catch (e) {
|
||||
this.fastStatus = null
|
||||
if (throwOnError) throw e
|
||||
}
|
||||
},
|
||||
async backupToggles() {
|
||||
try {
|
||||
@@ -160,6 +174,7 @@ export const SystemTools = {
|
||||
try {
|
||||
await api.setUpdateBranch(branch)
|
||||
showSnackbar(`Switching to ${branch}...`)
|
||||
await this.loadFastStatus()
|
||||
} catch (e) {
|
||||
showSnackbar(e?.message || "Switch failed.", "error")
|
||||
}
|
||||
@@ -168,7 +183,7 @@ export const SystemTools = {
|
||||
if (this.busy) return
|
||||
this.busy = "check"
|
||||
try {
|
||||
await this.loadFastStatus()
|
||||
await this.loadFastStatus({ throwOnError: true })
|
||||
this.checkedForUpdates = true
|
||||
const st = this.fastStatus
|
||||
if (st?.running) showSnackbar("An update is already running.")
|
||||
@@ -227,6 +242,7 @@ export const SystemTools = {
|
||||
try {
|
||||
await api.factoryReset()
|
||||
showSnackbar("SAVE ME initiated — factory resetting...")
|
||||
await this.loadFastStatus()
|
||||
} catch (e) {
|
||||
showSnackbar(e?.message || "Factory reset failed.", "error")
|
||||
}
|
||||
@@ -256,14 +272,27 @@ export const SystemTools = {
|
||||
<i class="bi bi-arrow-repeat"></i>
|
||||
<span class="gx-section__title">Update Status</span>
|
||||
<span v-if="fastStatus.running" class="gx-chip" style="background:var(--primary);color:var(--on-primary);">{{ fastStatus.progressPercent }}%</span>
|
||||
<span v-else-if="fastStatus.updateAvailable" class="gx-chip" style="background:var(--warning);color:var(--black);">Update available</span>
|
||||
<span v-else class="gx-chip">Up to date</span>
|
||||
<span v-else-if="updateAvailable" class="gx-chip" style="background:var(--warning);color:var(--black);">Update available</span>
|
||||
<span v-else-if="checkedForUpdates" class="gx-chip">Up to date</span>
|
||||
<span v-else class="gx-chip">Not checked</span>
|
||||
</div>
|
||||
<div style="padding: var(--sp-3); display:grid; gap:6px;">
|
||||
<div class="gx-row" style="border-top:none; min-height:0; padding:4px 0;"><span class="gx-row__label">Branch</span><span class="gx-row__value">{{ fastStatus.branch || currentBranch || '—' }}</span></div>
|
||||
<div v-if="fastStatus.running" class="gx-row" style="border-top:none; min-height:0; padding:4px 0;"><span class="gx-row__label">Stage</span><span class="gx-row__value">{{ fastStatus.stage }} · {{ fastStatus.progressLabel }}</span></div>
|
||||
<div class="gx-row" style="border-top:none; min-height:0; padding:4px 0;"><span class="gx-row__label">Local</span><span class="gx-row__value" style="font-family:monospace;">{{ shortCommit(fastStatus.localCommit) }}</span></div>
|
||||
<div class="gx-row" style="border-top:none; min-height:0; padding:4px 0;"><span class="gx-row__label">Remote</span><span class="gx-row__value" style="font-family:monospace;">{{ shortCommit(fastStatus.remoteCommit) }}</span></div>
|
||||
<div v-if="fastStatus.running" class="gx-update-progress" role="progressbar" aria-label="Update progress"
|
||||
:aria-valuenow="Math.round(fastStatus.progressPercent || 0)" aria-valuemin="0" aria-valuemax="100">
|
||||
<div class="gx-update-progress__track">
|
||||
<div class="gx-update-progress__fill" :class="{ 'gx-update-progress__fill--error': fastStatus.stage === 'error' }"
|
||||
:style="{ width: toPercent(fastStatus.progressPercent) + '%' }"></div>
|
||||
</div>
|
||||
<div class="gx-update-progress__meta">
|
||||
<span>Step {{ fastStatus.progressStep || 0 }}/{{ fastStatus.progressTotalSteps || 5 }}: {{ fastStatus.progressLabel || fastStatus.stage || 'Updating' }}</span>
|
||||
<strong>{{ Math.round(toPercent(fastStatus.progressPercent)) }}%</strong>
|
||||
</div>
|
||||
<small v-if="fastStatus.progressDetail">{{ fastStatus.progressDetail }}</small>
|
||||
</div>
|
||||
<div v-if="fastStatus.message" class="gx-note">{{ fastStatus.message }}</div>
|
||||
<div v-if="fastStatus.warning && (fastStatus.running || fastStatus.updateAvailable)" class="gx-note gx-note--danger">{{ fastStatus.warning }}</div>
|
||||
<div v-if="fastStatus.agnosUpdate?.available && fastStatus.agnosUpdate?.warnings?.length" style="margin-top:4px;">
|
||||
@@ -287,7 +316,7 @@ export const SystemTools = {
|
||||
<i v-if="busy === 'check'" class="bi bi-arrow-repeat gx-spin"></i>
|
||||
<i v-else class="bi bi-search"></i> {{ busy === 'check' ? 'Checking...' : 'Check for Updates' }}
|
||||
</button>
|
||||
<button type="button" class="gx-btn" :disabled="!updateAvailable || !!busy || isOnroad" @click="applyFastUpdate">
|
||||
<button v-if="updateAvailable" type="button" class="gx-btn" :disabled="!!busy || isOnroad" @click="applyFastUpdate">
|
||||
<i class="bi bi-arrow-up-circle"></i> {{ busy === 'fast' ? 'Updating...' : 'Update Now' }}
|
||||
</button>
|
||||
<button type="button" class="gx-btn gx-btn--tonal" :disabled="!!busy || isOnroad" @click="runUpdate('recover')">Recover</button>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { navigate, toolHref } from "../store.js"
|
||||
|
||||
const TOOLS = [
|
||||
{ name: "Cameras & Monitoring", link: "/cameras", icon: "bi-camera-video", desc: "PiP side camera & V-ASM spot monitor" },
|
||||
{ name: "Bluetooth", link: "/bluetooth", icon: "bi-bluetooth", desc: "Pair devices, controllers, & audio" },
|
||||
{ name: "Cameras & Monitoring", link: "/cameras", icon: "bi-camera-video", desc: "Sentry, PiP side camera, & V-ASM spot monitor" },
|
||||
{ name: "Galaxy & App Install", link: "/galaxy", icon: "bi-globe2", desc: "Remote access, pairing, & app install" },
|
||||
{ name: "Logs & Diagnostics", link: "/logs", icon: "bi-exclamation-triangle", desc: "Error logs, tmux, troubleshoot" },
|
||||
{ name: "Model Manager", link: "/manage_models", icon: "bi-cpu", desc: "Install/swap models" },
|
||||
{ name: "Model Laboratory", link: "/model_laboratory", icon: "bi-bezier2", desc: "Pair lateral and longitudinal models" },
|
||||
{ name: "Navigation & Maps", link: "/navigation", icon: "bi-map", desc: "Offline maps & destinations" },
|
||||
{ name: "Sentry Mode", link: "/sentry", icon: "bi-shield-exclamation", desc: "Sentry alerts & security" },
|
||||
{ name: "System Tools", link: "/system", icon: "bi-arrow-repeat", desc: "Backup, restore, updates" },
|
||||
{ name: "Theme Maker", link: "/theme_maker", icon: "bi-palette-fill", desc: "Customize the look" },
|
||||
{ name: "Tuning, Plots & Testing", link: "/tuning", icon: "bi-sign-turn-right", desc: "Steering & speed tuning, live plots, testing grounds" },
|
||||
{ name: "Vehicle Controls", link: "/vehicle", icon: "bi-car-front", desc: "Controllers, bluetooth, vehicle features" },
|
||||
{ name: "Vehicle Controls", link: "/vehicle", icon: "bi-car-front", desc: "Controllers & vehicle features" },
|
||||
].sort((a, b) => a.name.localeCompare(b.name))
|
||||
|
||||
export const Tools = {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { LateralTuningPanel } from "../components/LateralTuningPanel.js"
|
||||
import { LongitudinalManeuvers } from "../components/LongitudinalManeuvers.js"
|
||||
import { GalaxyTabs } from "../components/GalaxyTabs.js"
|
||||
import { Plots } from "./Plots.js"
|
||||
import { TestingGround } from "./TestingGround.js"
|
||||
@@ -7,16 +6,15 @@ import { useTabRouting } from "../composables.js"
|
||||
|
||||
const TABS = {
|
||||
lateral: "Lateral Tuning",
|
||||
long: "Long Maneuvers",
|
||||
plots: "Plots",
|
||||
testing: "Testing Ground",
|
||||
}
|
||||
|
||||
export const Tuning = {
|
||||
name: "Tuning",
|
||||
components: { LateralTuningPanel, LongitudinalManeuvers, Plots, TestingGround, GalaxyTabs },
|
||||
components: { LateralTuningPanel, Plots, TestingGround, GalaxyTabs },
|
||||
setup() {
|
||||
return useTabRouting("/tuning", { lateral: "lateral", long: "long", plots: "plots", testing: "testing" })
|
||||
return useTabRouting("/tuning", { lateral: "lateral", plots: "plots", testing: "testing" })
|
||||
},
|
||||
data() { return { TABS } },
|
||||
template: `
|
||||
@@ -29,10 +27,6 @@ export const Tuning = {
|
||||
<LateralTuningPanel />
|
||||
</template>
|
||||
|
||||
<template v-else-if="tab === 'long'">
|
||||
<LongitudinalManeuvers />
|
||||
</template>
|
||||
|
||||
<template v-else-if="tab === 'plots'">
|
||||
<Plots :embedded="true" />
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { api, showSnackbar } from "../api.js"
|
||||
import { navigate, toolHref } from "../store.js"
|
||||
import { WheelControls } from "../components/WheelControls.js"
|
||||
import { BluetoothPanel } from "../components/BluetoothPanel.js"
|
||||
import { GalaxySection } from "../components/GalaxySection.js"
|
||||
import { GalaxyTabs } from "../components/GalaxyTabs.js"
|
||||
import { useTabRouting } from "../composables.js"
|
||||
@@ -13,13 +12,12 @@ const FEATURES = [
|
||||
|
||||
const TABS = {
|
||||
controllers: "Controllers",
|
||||
bluetooth: "Bluetooth",
|
||||
features: "Vehicle Features",
|
||||
}
|
||||
|
||||
export const Vehicle = {
|
||||
name: "Vehicle",
|
||||
components: { WheelControls, BluetoothPanel, GalaxySection, GalaxyTabs },
|
||||
components: { WheelControls, GalaxySection, GalaxyTabs },
|
||||
data() {
|
||||
return {
|
||||
TABS,
|
||||
@@ -29,7 +27,7 @@ export const Vehicle = {
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
return useTabRouting("/vehicle", { controllers: "controllers", bluetooth: "bluetooth", features: "features" })
|
||||
return useTabRouting("/vehicle", { controllers: "controllers", features: "features" })
|
||||
},
|
||||
computed: {
|
||||
featureList() { return this.features },
|
||||
@@ -71,10 +69,6 @@ export const Vehicle = {
|
||||
<WheelControls />
|
||||
</template>
|
||||
|
||||
<template v-else-if="tab === 'bluetooth'">
|
||||
<BluetoothPanel />
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<GalaxySection title="Vehicle Features" icon="bi-check2-square">
|
||||
<div style="padding: var(--sp-3); display:grid; gap:8px;">
|
||||
|
||||
@@ -52,10 +52,15 @@ def test_device_settings_speed_units_follow_the_vehicle():
|
||||
assert 'from "/assets/mobile/js/params.js"' in source
|
||||
assert "resolveVehicleUnitParam" in source
|
||||
assert "formatNumericParamValue" in source
|
||||
assert "vehicleSpeedUnit(state.values)" in source
|
||||
assert "unit_search_terms" in source
|
||||
assert "Use Metric System" in source
|
||||
assert "per click" in source
|
||||
assert "ds-unit-note" not in source
|
||||
|
||||
|
||||
def test_device_settings_supports_vehicle_make_exclusions():
|
||||
source = _device_settings()
|
||||
|
||||
assert "excluded_vehicle_makes" in source
|
||||
|
||||
|
||||
def test_lane_center_offset_can_step_below_zero():
|
||||
|
||||
@@ -66,6 +66,15 @@ def test_galaxy_layout_contains_basic_mode_controls():
|
||||
assert {"AlphaLongitudinalEnabled", "ForceOffroad", "GalaxyDeveloperMode"} <= sections["Developer"].keys()
|
||||
|
||||
|
||||
def test_galaxy_new_ui_is_the_visible_default_choice():
|
||||
galaxy_default = _params_by_section(_layout())["Developer"]["GalaxyMobileDefault"]
|
||||
|
||||
assert _declared_default("GalaxyMobileDefault") == "1"
|
||||
assert galaxy_default["settings_tier"] == "simple"
|
||||
assert galaxy_default["label"] == "Use Galaxy (new) by Default"
|
||||
assert "Galaxy (old)" in galaxy_default["description"]
|
||||
|
||||
|
||||
def test_ford_lateral_controls_are_ford_only_and_galaxy_only():
|
||||
lateral = _params_by_section(_layout())["Lateral (Steering)"]
|
||||
ford_keys = {
|
||||
@@ -142,6 +151,15 @@ def test_speed_settings_follow_vehicle_units_with_one_unit_steps():
|
||||
assert params["PulseGlideSpeedDelta"]["imperial_max"] == 15
|
||||
|
||||
|
||||
def test_cruise_controls_are_split_between_toyota_and_software_cruise():
|
||||
longitudinal = _params_by_section(_layout())["Longitudinal (Speed & Following)"]
|
||||
|
||||
assert longitudinal["CustomCruise"]["excluded_vehicle_makes"] == ["Lexus", "Toyota"]
|
||||
assert longitudinal["CustomCruiseLong"]["excluded_vehicle_makes"] == ["Lexus", "Toyota"]
|
||||
assert longitudinal["ReverseCruise"]["vehicle_makes"] == ["Lexus", "Toyota"]
|
||||
assert _declared_default("ReverseCruise") == "0"
|
||||
|
||||
|
||||
def test_curve_speed_controller_no_lead_toggle_is_nested_under_csc():
|
||||
csc_no_lead = _params_by_section(_layout())["Longitudinal (Speed & Following)"]["CurveSpeedControllerNoLead"]
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ def test_ui_app_shell_files_exist():
|
||||
"js/views/Tuning.js",
|
||||
"js/views/Navigation.js",
|
||||
"js/views/Vehicle.js",
|
||||
"js/views/Bluetooth.js",
|
||||
"js/views/SystemTools.js",
|
||||
]
|
||||
for rel in required:
|
||||
@@ -55,6 +56,8 @@ def test_ui_index_wires_vue_and_mount_point():
|
||||
assert 'id="galaxy-app"' in index
|
||||
assert 'src="/assets/mobile/js/app.js"' in index
|
||||
assert '"vue": "/assets/vendor/vue/vue.esm-browser.js"' in index
|
||||
assert '<title>Galaxy</title>' in index
|
||||
assert 'apple-mobile-web-app-title" content="Galaxy"' in index
|
||||
|
||||
|
||||
def test_ui_uses_same_backend_endpoints():
|
||||
@@ -107,7 +110,7 @@ def test_ui_ports_all_tool_views():
|
||||
"js/views/Recordings.js": ["/api/routes", "getRoutesStream", "getRouteLogs"],
|
||||
"js/views/Logs.js": ["getErrorLogs", "tmuxSnapshot"],
|
||||
"js/components/TroubleshootPanel.js": ["getTroubleshoot", "resetTroubleshootSection", "GalaxyConfirm"],
|
||||
"js/views/Tuning.js": ["LateralTuningPanel", "LongitudinalManeuvers"],
|
||||
"js/views/Tuning.js": ["LateralTuningPanel"],
|
||||
"js/views/Navigation.js": ["getNavigation", "setNavigation", "MapsPanel", "NavigationKeysPanel"],
|
||||
"js/views/ToolEmbed.js": ["/manage_maps", "/manage_navigation_keys"],
|
||||
"js/views/SystemTools.js": [
|
||||
@@ -122,7 +125,9 @@ def test_ui_ports_all_tool_views():
|
||||
for ep in endpoints:
|
||||
assert ep in src, f"{rel} should use api.{ep}"
|
||||
vehicle = _read("js/views/Vehicle.js")
|
||||
assert "WheelControls" in vehicle and "BluetoothPanel" in vehicle and "carFeaturesCheck" in vehicle
|
||||
bluetooth = _read("js/views/Bluetooth.js")
|
||||
assert "WheelControls" in vehicle and "BluetoothPanel" not in vehicle and "carFeaturesCheck" in vehicle
|
||||
assert "BluetoothPanel" in bluetooth
|
||||
|
||||
|
||||
def test_ui_routes_ported_views_natively_no_classic_fallback():
|
||||
@@ -130,16 +135,16 @@ def test_ui_routes_ported_views_natively_no_classic_fallback():
|
||||
shell = _read("js/components/AppShell.js")
|
||||
tools = _read("js/views/Tools.js")
|
||||
|
||||
for view in ["Recordings", "Logs", "Tuning", "Navigation", "Vehicle", "SystemTools"]:
|
||||
for view in ["Recordings", "Logs", "Tuning", "Navigation", "Vehicle", "Bluetooth", "SystemTools"]:
|
||||
assert view in app, f"app.js should register {view}"
|
||||
|
||||
# Ported routes must resolve natively in the Vue app (zero /classic redirect).
|
||||
for route in ["/recordings", "/logs", "/tuning", "/navigation", "/vehicle", "/system"]:
|
||||
for route in ["/recordings", "/logs", "/tuning", "/navigation", "/vehicle", "/bluetooth", "/system"]:
|
||||
assert route in shell, f"AppShell should route {route} natively"
|
||||
assert route in app, f"app.js should resolve {route} natively"
|
||||
# Tools grid routes the native categories (Recordings lives in the bottom nav
|
||||
# and is intentionally absent from the Tools page).
|
||||
for tool in ["/tuning", "/logs", "/navigation", "/vehicle", "/system"]:
|
||||
for tool in ["/tuning", "/logs", "/navigation", "/vehicle", "/bluetooth", "/system"]:
|
||||
assert tool in tools, f"Tools grid should route {tool} natively"
|
||||
assert "/cameras" in tools, "Tools grid should route the camera hub natively"
|
||||
assert "/manage_v_asm" not in tools and "/manage_pip_sidecam" not in tools
|
||||
@@ -204,7 +209,7 @@ def test_ui_speed_units_follow_the_vehicle():
|
||||
assert "displayParam" in card and "formatNumericParamValue" in card
|
||||
assert "sliderStepDisplay" in card and "Step:" in card
|
||||
assert ':values="values"' in settings
|
||||
assert "Use Metric System" in settings
|
||||
assert "gx-unit-note" not in settings
|
||||
|
||||
|
||||
def test_ui_centralizes_api_and_uses_composables():
|
||||
@@ -226,10 +231,10 @@ def test_ui_centralizes_api_and_uses_composables():
|
||||
def test_ui_schema_driven_param_engine_reused():
|
||||
tuning = _read("js/views/Tuning.js")
|
||||
assert "GalaxyEmbed" not in tuning and 'src="/tuning"' not in tuning, "Tuning must be native, not a classic embed"
|
||||
assert "LateralTuningPanel" in tuning and "LongitudinalManeuvers" in tuning
|
||||
assert "LateralTuningPanel" in tuning and "LongitudinalManeuvers" not in tuning
|
||||
vehicle = _read("js/views/Vehicle.js")
|
||||
assert "ParamSections" not in vehicle, "Vehicle must not render redundant toggles"
|
||||
assert "WheelControls" in vehicle and "BluetoothPanel" in vehicle
|
||||
assert "WheelControls" in vehicle and "BluetoothPanel" not in vehicle
|
||||
assert "GalaxySection" in vehicle
|
||||
engine = _read("js/components/ParamSections.js")
|
||||
assert "SettingTree" in engine
|
||||
@@ -285,6 +290,7 @@ def test_ui_has_bottom_navigation_and_drawer():
|
||||
assert "gx-drawer" in shell
|
||||
assert "gx-appbar" in shell
|
||||
assert "Search toggles" in shell
|
||||
assert ">Galaxy</span>" in shell
|
||||
|
||||
|
||||
def test_ui_search_visible_on_mobile_and_content_full_width():
|
||||
@@ -342,8 +348,6 @@ def test_ui_galaxy_background_is_css_only_and_lightweight():
|
||||
|
||||
def test_galaxy_py_serves_classic_at_root_and_new_ui_at_mobile():
|
||||
source = GALAXY_PY.read_text(encoding="utf-8")
|
||||
# The classic Galaxy SPA is the default landing at / (original behaviour) unless
|
||||
# the "New Galaxy by Default" (GalaxyMobileDefault) toggle is enabled.
|
||||
assert '@app.route("/", methods=["GET"])' in source
|
||||
assert 'render_template("index.html")' in source
|
||||
assert 'params.get_bool("GalaxyMobileDefault")' in source
|
||||
@@ -358,7 +362,8 @@ def test_galaxy_py_serves_classic_at_root_and_new_ui_at_mobile():
|
||||
def test_ui_manifest_is_valid_pwa_manifest():
|
||||
manifest = json.loads((UI_ROOT / "manifest.json").read_text(encoding="utf-8"))
|
||||
assert manifest["display"] == "standalone"
|
||||
assert manifest["name"]
|
||||
assert manifest["name"] == "Galaxy"
|
||||
assert manifest["short_name"] == "Galaxy"
|
||||
assert manifest["icons"]
|
||||
assert "start_url" not in manifest
|
||||
|
||||
@@ -417,7 +422,6 @@ def test_ui_all_remaining_classic_tools_native_no_embed():
|
||||
|
||||
# Standalone native views + their routes.
|
||||
native = {
|
||||
"/sentry": "Sentry",
|
||||
"/manage_models": "ModelManager",
|
||||
"/plots": "Plots",
|
||||
"/testing_ground": "TestingGround",
|
||||
@@ -431,6 +435,10 @@ def test_ui_all_remaining_classic_tools_native_no_embed():
|
||||
assert src, f"missing view: {view}"
|
||||
assert "GalaxyEmbed" not in src and "fetch(" not in src, f"{view} should be native with no raw fetch"
|
||||
|
||||
assert '"/sentry": Cameras' in app
|
||||
sentry = _read("js/views/Sentry.js")
|
||||
assert "GalaxyEmbed" not in sentry and "fetch(" not in sentry
|
||||
|
||||
# Navigation maps + App Keys and Tuning lateral are native tabs now.
|
||||
nav = _read("js/views/Navigation.js")
|
||||
assert "GalaxyEmbed" not in nav and "MapsPanel" in nav and "NavigationKeysPanel" in nav
|
||||
@@ -466,8 +474,9 @@ def test_ui_cameras_hub_vasm_and_pip_native_no_embed():
|
||||
assert "/cameras" in app and "Cameras" in app, "app.js should register the camera hub"
|
||||
assert "/cameras" in store, "store NATIVE_ROOTS should include /cameras"
|
||||
assert "GalaxyEmbed" not in cameras and "fetch(" not in cameras
|
||||
assert "Vasm" in cameras and "Pip" in cameras, "camera hub should embed V-ASM and PiP"
|
||||
assert "Sentry" in cameras and "Vasm" in cameras and "Pip" in cameras, "camera hub should embed Sentry, V-ASM, and PiP"
|
||||
assert "GalaxyTabs" in cameras
|
||||
assert cameras.index('sentry: "Sentry Mode"') < cameras.index('vasm: "V-ASM Spot Monitor"')
|
||||
|
||||
# Removed standalone pages are no longer routed or listed as native roots.
|
||||
for route in ["/manage_v_asm", "/manage_pip_sidecam"]:
|
||||
@@ -487,6 +496,40 @@ def test_ui_cameras_hub_vasm_and_pip_native_no_embed():
|
||||
assert method in api, f"api.js should expose {method}"
|
||||
|
||||
|
||||
def test_ui_mobile_polish_regressions():
|
||||
system = _read("js/views/SystemTools.js")
|
||||
css = _read("css/material.css")
|
||||
assert 'button v-if="updateAvailable"' in system
|
||||
assert "checkedForUpdates && !!this.fastStatus?.updateAvailable" in system
|
||||
assert "gx-update-progress__fill" in system
|
||||
assert "linear-gradient(90deg, #5ec8c8 0%, #8b6cc5 100%)" in css
|
||||
|
||||
bluetooth = _read("js/components/BluetoothPanel.js")
|
||||
assert "methods: {\n address," in bluetooth
|
||||
|
||||
logs = _read("js/views/Logs.js")
|
||||
assert logs.index('troubleshoot: "Troubleshoot"') < logs.index('errors: "Error Logs"') < logs.index('tmux: "Tmux Live Log"')
|
||||
troubleshoot = _read("js/components/TroubleshootPanel.js")
|
||||
assert "gx-diagnostic-row--changed" in troubleshoot and ".gx-row.gx-diagnostic-row--changed" in css
|
||||
|
||||
recordings = _read("js/views/Recordings.js")
|
||||
galaxy = _read("js/views/Galaxy.js")
|
||||
assert "bandwidth reasons" in recordings and "status?.lanIp" in recordings
|
||||
assert "status?.lanIp" in galaxy
|
||||
assert ':href="localUrl"' in recordings and ':href="localUrl"' in galaxy
|
||||
|
||||
home = _read("js/views/Home.js")
|
||||
home_css = _read("css/home.css")
|
||||
assert "backgroundImage: modelView.style" in home
|
||||
assert "display: flex" in home_css and "flex-direction: column" in home_css
|
||||
|
||||
tuning = _read("js/views/Tuning.js")
|
||||
classic_sidebar = (REPO_ROOT / "starpilot/system/the_galaxy/assets/components/sidebar.js").read_text(encoding="utf-8")
|
||||
c4_developer = (REPO_ROOT / "selfdrive/ui/layouts/settings/developer.py").read_text(encoding="utf-8")
|
||||
assert "LongitudinalManeuvers" not in tuning and "Long Maneuvers" not in classic_sidebar
|
||||
assert 'tr("Longitudinal Maneuver Mode")' not in c4_developer
|
||||
|
||||
|
||||
def _node_exe():
|
||||
candidates = [
|
||||
shutil.which("node"),
|
||||
|
||||
@@ -5305,12 +5305,12 @@ def setup(app):
|
||||
def mobile_manifest():
|
||||
manifest_path = Path(app.static_folder) / "mobile" / "manifest.json"
|
||||
if not manifest_path.is_file():
|
||||
return jsonify({"error": "Big Dipper manifest not found"}), 404
|
||||
return jsonify({"error": "Galaxy manifest not found"}), 404
|
||||
|
||||
try:
|
||||
manifest_data = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
except (OSError, ValueError, json.JSONDecodeError):
|
||||
return jsonify({"error": "Big Dipper manifest is invalid"}), 500
|
||||
return jsonify({"error": "Galaxy manifest is invalid"}), 500
|
||||
|
||||
slug = _read_galaxy_text(_get_galaxy_dir() / "glxyslug")
|
||||
if re.fullmatch(r"[A-Za-z0-9]{16}", slug):
|
||||
|
||||
@@ -22,7 +22,7 @@ DEVELOPER_METRIC_DISPLAY_KEYS = (
|
||||
)
|
||||
DEVICE_SHUTDOWN_KEY = "DeviceShutdown"
|
||||
CAMERA_VIEW_KEY = "CameraView"
|
||||
REVERSE_CRUISE_KEY = "ReverseCruise"
|
||||
GALAXY_NEW_DEFAULT_KEY = "GalaxyMobileDefault"
|
||||
|
||||
DEFAULT_STEER_KP = 0.6
|
||||
LEGACY_STEER_KP = 0.7
|
||||
@@ -39,7 +39,8 @@ LANE_CHANGE_SMOOTHING_MIGRATION_MARKER = ".starpilot_lane_change_smoothing_defau
|
||||
SPEED_LIMIT_VISIBILITY_MIGRATION_MARKER = ".starpilot_speed_limit_visibility_v1"
|
||||
DEVICE_SHUTDOWN_HOURS_MIGRATION_MARKER = ".starpilot_device_shutdown_hours_v1"
|
||||
CAMERA_VIEW_DEFAULT_MIGRATION_MARKER = ".starpilot_camera_view_default_v1"
|
||||
REVERSE_CRUISE_REMOVAL_MIGRATION_MARKER = ".starpilot_remove_reverse_cruise_v1"
|
||||
REVERSE_CRUISE_RESTORE_MIGRATION_MARKER = ".starpilot_restore_reverse_cruise_v1"
|
||||
GALAXY_NEW_DEFAULT_MIGRATION_MARKER = ".starpilot_galaxy_new_default_v1"
|
||||
MARKER_DIRNAME = ".starpilot_param_migrations"
|
||||
|
||||
LATERAL_METHOD_PARAM_SUFFIXES = (
|
||||
@@ -147,8 +148,12 @@ def _camera_view_default_marker_path(params: ParamsLike) -> Path:
|
||||
return _marker_dir_path(params) / CAMERA_VIEW_DEFAULT_MIGRATION_MARKER
|
||||
|
||||
|
||||
def _reverse_cruise_removal_marker_path(params: ParamsLike) -> Path:
|
||||
return _marker_dir_path(params) / REVERSE_CRUISE_REMOVAL_MIGRATION_MARKER
|
||||
def _reverse_cruise_restore_marker_path(params: ParamsLike) -> Path:
|
||||
return _marker_dir_path(params) / REVERSE_CRUISE_RESTORE_MIGRATION_MARKER
|
||||
|
||||
|
||||
def _galaxy_new_default_marker_path(params: ParamsLike) -> Path:
|
||||
return _marker_dir_path(params) / GALAXY_NEW_DEFAULT_MIGRATION_MARKER
|
||||
|
||||
|
||||
def _marker_dir_path(params: ParamsLike) -> Path:
|
||||
@@ -332,12 +337,24 @@ def _apply_camera_view_default_migration(params: ParamsLike, marker: Path) -> No
|
||||
marker.touch()
|
||||
|
||||
|
||||
def _remove_reverse_cruise_param(params: ParamsLike, marker: Path) -> None:
|
||||
def _restore_reverse_cruise_param(params: ParamsLike, marker: Path) -> None:
|
||||
if marker.exists():
|
||||
return
|
||||
|
||||
marker.parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(params.get_param_path(REVERSE_CRUISE_KEY)).unlink(missing_ok=True)
|
||||
if (not _param_file_exists(params, "ReverseCruise") and
|
||||
_approx_equal(params.get_float("CustomCruise"), 5.0) and
|
||||
_approx_equal(params.get_float("CustomCruiseLong"), 1.0)):
|
||||
params.put_bool("ReverseCruise", True)
|
||||
marker.touch()
|
||||
|
||||
|
||||
def _enable_galaxy_new_default(params: ParamsLike, marker: Path) -> None:
|
||||
if marker.exists():
|
||||
return
|
||||
|
||||
marker.parent.mkdir(parents=True, exist_ok=True)
|
||||
params.put_bool(GALAXY_NEW_DEFAULT_KEY, True)
|
||||
marker.touch()
|
||||
|
||||
|
||||
@@ -352,7 +369,8 @@ def apply_launch_param_migrations(params: ParamsLike, marker_path: Path | None =
|
||||
speed_limit_visibility_marker_path: Path | None = None,
|
||||
device_shutdown_hours_marker_path: Path | None = None,
|
||||
camera_view_default_marker_path: Path | None = None,
|
||||
reverse_cruise_removal_marker_path: Path | None = None) -> None:
|
||||
reverse_cruise_restore_marker_path: Path | None = None,
|
||||
galaxy_new_default_marker_path: Path | None = None) -> None:
|
||||
_apply_legacy_launch_param_migrations(params, marker_path or _default_marker_path(params))
|
||||
# Keep branch-default rollout on its own marker so older installs that already
|
||||
# have the legacy marker still receive this one-time param reset.
|
||||
@@ -384,8 +402,11 @@ def apply_launch_param_migrations(params: ParamsLike, marker_path: Path | None =
|
||||
_apply_camera_view_default_migration(
|
||||
params, camera_view_default_marker_path or _camera_view_default_marker_path(params)
|
||||
)
|
||||
_remove_reverse_cruise_param(
|
||||
params, reverse_cruise_removal_marker_path or _reverse_cruise_removal_marker_path(params)
|
||||
_restore_reverse_cruise_param(
|
||||
params, reverse_cruise_restore_marker_path or _reverse_cruise_restore_marker_path(params)
|
||||
)
|
||||
_enable_galaxy_new_default(
|
||||
params, galaxy_new_default_marker_path or _galaxy_new_default_marker_path(params)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ STARPILOT_DEFAULT_MODEL_MIGRATION_FLAG = Path("/data") / "starpilot_default_mode
|
||||
STARPILOT_CE_MODEL_STOP_TIME_MIGRATION_FLAG = Path("/data") / "starpilot_ce_model_stop_time_v2"
|
||||
STARPILOT_LEGACY_CACHE_MARKER_KEYS = ("RemapCancelToDistance",)
|
||||
STARPILOT_REMOVED_PARAM_KEYS = (
|
||||
"CoastUpToLeads", "HumanAcceleration", "HumanFollowing", "PrioritizeSmoothFollowing", "ReverseCruise",
|
||||
"CoastUpToLeads", "HumanAcceleration", "HumanFollowing", "PrioritizeSmoothFollowing",
|
||||
)
|
||||
LEGACY_CARMODEL_MIGRATIONS = {
|
||||
"CHEVROLET_BOLT_CC_2019_2021": "CHEVROLET_BOLT_CC_2018_2021",
|
||||
|
||||
@@ -7,6 +7,7 @@ from openpilot.system.manager.launch_param_migrations import (
|
||||
DEFAULT_CAMERA_VIEW,
|
||||
DEVELOPER_METRIC_DISPLAY_KEYS,
|
||||
DEVELOPER_METRIC_DISPLAY_MIGRATION_MARKER,
|
||||
GALAXY_NEW_DEFAULT_MIGRATION_MARKER,
|
||||
DEFAULT_LANE_CHANGE_SMOOTHING,
|
||||
DEFAULT_STEER_KP,
|
||||
DEVICE_SHUTDOWN_HOURS_MIGRATION_MARKER,
|
||||
@@ -14,7 +15,7 @@ from openpilot.system.manager.launch_param_migrations import (
|
||||
LAUNCH_PARAM_MIGRATION_MARKER,
|
||||
LATERAL_METHOD_REBRAND_MIGRATION_MARKER,
|
||||
MARKER_DIRNAME,
|
||||
REVERSE_CRUISE_REMOVAL_MIGRATION_MARKER,
|
||||
REVERSE_CRUISE_RESTORE_MIGRATION_MARKER,
|
||||
STANDARD_ACCELERATION_PROFILE,
|
||||
SPEED_LIMIT_VISIBILITY_MIGRATION_MARKER,
|
||||
LEGACY_UI_SELECTION_MIGRATION_MARKER,
|
||||
@@ -178,14 +179,42 @@ def test_apply_launch_param_migrations_preserves_custom_camera_view(tmp_path):
|
||||
assert params.get_int("CameraView") == 0
|
||||
|
||||
|
||||
def test_apply_launch_param_migrations_removes_reverse_cruise_param(tmp_path):
|
||||
def test_apply_launch_param_migrations_restores_reverse_cruise_from_swapped_intervals(tmp_path):
|
||||
params = FileBackedFakeParams(tmp_path / "params")
|
||||
params.put_bool("ReverseCruise", True)
|
||||
params.put_float("CustomCruise", 5.0)
|
||||
params.put_float("CustomCruiseLong", 1.0)
|
||||
|
||||
apply_launch_param_migrations(params)
|
||||
|
||||
assert not Path(params.get_param_path("ReverseCruise")).exists()
|
||||
assert marker_path(tmp_path, REVERSE_CRUISE_REMOVAL_MIGRATION_MARKER).is_file()
|
||||
assert params.get_bool("ReverseCruise")
|
||||
assert marker_path(tmp_path, REVERSE_CRUISE_RESTORE_MIGRATION_MARKER).is_file()
|
||||
|
||||
|
||||
def test_apply_launch_param_migrations_preserves_explicit_reverse_cruise_choice(tmp_path):
|
||||
params = FileBackedFakeParams(tmp_path / "params")
|
||||
params.put_float("CustomCruise", 5.0)
|
||||
params.put_float("CustomCruiseLong", 1.0)
|
||||
params.put_bool("ReverseCruise", False)
|
||||
|
||||
apply_launch_param_migrations(params)
|
||||
|
||||
assert not params.get_bool("ReverseCruise")
|
||||
|
||||
|
||||
def test_apply_launch_param_migrations_enables_galaxy_new_default_once(tmp_path):
|
||||
params = FileBackedFakeParams(tmp_path / "params")
|
||||
params.put_bool("GalaxyMobileDefault", False)
|
||||
|
||||
apply_launch_param_migrations(params)
|
||||
|
||||
assert params.get_bool("GalaxyMobileDefault")
|
||||
marker = marker_path(tmp_path, GALAXY_NEW_DEFAULT_MIGRATION_MARKER)
|
||||
assert marker.is_file()
|
||||
|
||||
params.put_bool("GalaxyMobileDefault", False)
|
||||
apply_launch_param_migrations(params)
|
||||
|
||||
assert not params.get_bool("GalaxyMobileDefault")
|
||||
|
||||
|
||||
def test_apply_launch_param_migrations_applies_branch_defaults_for_existing_installs(tmp_path):
|
||||
|
||||
@@ -397,7 +397,6 @@ class TestManager:
|
||||
params_cache = FileBackedFakeParams(tmp_path / "cache", {
|
||||
"HumanFollowing": False,
|
||||
"PrioritizeSmoothFollowing": True,
|
||||
"ReverseCruise": True,
|
||||
})
|
||||
|
||||
manager.cleanup_removed_starpilot_params(params, params_cache)
|
||||
@@ -405,10 +404,9 @@ class TestManager:
|
||||
assert not Path(params.get_param_path("CoastUpToLeads")).exists()
|
||||
assert not Path(params.get_param_path("HumanAcceleration")).exists()
|
||||
assert not Path(params.get_param_path("HumanFollowing")).exists()
|
||||
assert not Path(params.get_param_path("ReverseCruise")).exists()
|
||||
assert params.get_bool("ReverseCruise")
|
||||
assert not Path(params_cache.get_param_path("HumanFollowing")).exists()
|
||||
assert not Path(params_cache.get_param_path("PrioritizeSmoothFollowing")).exists()
|
||||
assert not Path(params_cache.get_param_path("ReverseCruise")).exists()
|
||||
|
||||
def test_migrate_legacy_starpilot_params_cache_copies_marker_sources(self, tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(manager, "STARPILOT_PARAMS_CACHE_MIGRATION_FLAG", tmp_path / "starpilot_params_cache_v1")
|
||||
|
||||
Reference in New Issue
Block a user