mirror of
https://github.com/infiniteCable2/openpilot.git
synced 2026-08-06 16:55:54 +08:00
Merge branch 'upstream/openpilot/master' into sync-20250823
# Conflicts: # .github/workflows/release.yaml # README.md # RELEASES.md # common/params_keys.h # docs/CARS.md # opendbc_repo # panda # release/build_stripped.sh # selfdrive/controls/lib/longitudinal_planner.py # selfdrive/modeld/modeld.py # selfdrive/ui/feedback/feedbackd.py # selfdrive/ui/translations/main_ar.ts # selfdrive/ui/translations/main_de.ts # selfdrive/ui/translations/main_es.ts # selfdrive/ui/translations/main_fr.ts # selfdrive/ui/translations/main_ja.ts # selfdrive/ui/translations/main_ko.ts # selfdrive/ui/translations/main_pt-BR.ts # selfdrive/ui/translations/main_th.ts # selfdrive/ui/translations/main_tr.ts # selfdrive/ui/translations/main_zh-CHS.ts # selfdrive/ui/translations/main_zh-CHT.ts # system/version.py # uv.lock
This commit is contained in:
@@ -1,34 +0,0 @@
|
||||
#if SENSOR_ID == 1
|
||||
|
||||
#define VIGNETTE_PROFILE_8DT0MM
|
||||
|
||||
#define BIT_DEPTH 12
|
||||
#define PV_MAX 4096
|
||||
#define BLACK_LVL 168
|
||||
|
||||
float4 normalize_pv(int4 parsed, float vignette_factor) {
|
||||
float4 pv = (convert_float4(parsed) - BLACK_LVL) / (PV_MAX - BLACK_LVL);
|
||||
return clamp(pv*vignette_factor, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float3 color_correct(float3 rgb) {
|
||||
float3 corrected = rgb.x * (float3)(1.82717181, -0.31231438, 0.07307673);
|
||||
corrected += rgb.y * (float3)(-0.5743977, 1.36858544, -0.53183455);
|
||||
corrected += rgb.z * (float3)(-0.25277411, -0.05627105, 1.45875782);
|
||||
return corrected;
|
||||
}
|
||||
|
||||
float3 apply_gamma(float3 rgb, int expo_time) {
|
||||
// tone mapping params
|
||||
const float gamma_k = 0.75;
|
||||
const float gamma_b = 0.125;
|
||||
const float mp = 0.01; // ideally midpoint should be adaptive
|
||||
const float rk = 9 - 100*mp;
|
||||
|
||||
// poly approximation for s curve
|
||||
return (rgb > mp) ?
|
||||
((rk * (rgb-mp) * (1-(gamma_k*mp+gamma_b)) * (1+1/(rk*(1-mp))) / (1+rk*(rgb-mp))) + gamma_k*mp + gamma_b) :
|
||||
((rk * (rgb-mp) * (gamma_k*mp+gamma_b) * (1+1/(rk*mp)) / (1-rk*(rgb-mp))) + gamma_k*mp + gamma_b);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,58 +0,0 @@
|
||||
#if SENSOR_ID == 3
|
||||
|
||||
#define BGGR
|
||||
#define VIGNETTE_PROFILE_4DT6MM
|
||||
|
||||
#define BIT_DEPTH 12
|
||||
#define PV_MAX10 1023
|
||||
#define PV_MAX12 4095
|
||||
#define PV_MAX16 65536 // gamma curve is calibrated to 16bit
|
||||
#define BLACK_LVL 48
|
||||
|
||||
float combine_dual_pvs(float lv, float sv, int expo_time) {
|
||||
float svc = fmax(sv * expo_time, (float)(64 * (PV_MAX10 - BLACK_LVL)));
|
||||
float svd = sv * fmin(expo_time, 8.0) / 8;
|
||||
|
||||
if (expo_time > 64) {
|
||||
if (lv < PV_MAX10 - BLACK_LVL) {
|
||||
return lv / (PV_MAX16 - BLACK_LVL);
|
||||
} else {
|
||||
return (svc / 64) / (PV_MAX16 - BLACK_LVL);
|
||||
}
|
||||
} else {
|
||||
if (lv > 32) {
|
||||
return (lv * 64 / fmax(expo_time, 8.0)) / (PV_MAX16 - BLACK_LVL);
|
||||
} else {
|
||||
return svd / (PV_MAX16 - BLACK_LVL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float4 normalize_pv_hdr(int4 parsed, int4 short_parsed, float vignette_factor, int expo_time) {
|
||||
float4 pl = convert_float4(parsed - BLACK_LVL);
|
||||
float4 ps = convert_float4(short_parsed - BLACK_LVL);
|
||||
float4 pv;
|
||||
pv.s0 = combine_dual_pvs(pl.s0, ps.s0, expo_time);
|
||||
pv.s1 = combine_dual_pvs(pl.s1, ps.s1, expo_time);
|
||||
pv.s2 = combine_dual_pvs(pl.s2, ps.s2, expo_time);
|
||||
pv.s3 = combine_dual_pvs(pl.s3, ps.s3, expo_time);
|
||||
return clamp(pv*vignette_factor, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float4 normalize_pv(int4 parsed, float vignette_factor) {
|
||||
float4 pv = (convert_float4(parsed) - BLACK_LVL) / (PV_MAX12 - BLACK_LVL);
|
||||
return clamp(pv*vignette_factor, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float3 color_correct(float3 rgb) {
|
||||
float3 corrected = rgb.x * (float3)(1.55361989, -0.268894615, -0.000593219);
|
||||
corrected += rgb.y * (float3)(-0.421217301, 1.51883144, -0.69760146);
|
||||
corrected += rgb.z * (float3)(-0.132402589, -0.249936825, 1.69819468);
|
||||
return corrected;
|
||||
}
|
||||
|
||||
float3 apply_gamma(float3 rgb, int expo_time) {
|
||||
return (10 * rgb) / (1 + 9 * rgb);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,47 +0,0 @@
|
||||
#if SENSOR_ID == 2
|
||||
|
||||
#define VIGNETTE_PROFILE_8DT0MM
|
||||
|
||||
#define BIT_DEPTH 12
|
||||
#define BLACK_LVL 64
|
||||
|
||||
float ox_lut_func(int x) {
|
||||
if (x < 512) {
|
||||
return x * 5.94873e-8;
|
||||
} else if (512 <= x && x < 768) {
|
||||
return 3.0458e-05 + (x-512) * 1.19913e-7;
|
||||
} else if (768 <= x && x < 1536) {
|
||||
return 6.1154e-05 + (x-768) * 2.38493e-7;
|
||||
} else if (1536 <= x && x < 1792) {
|
||||
return 0.0002448 + (x-1536) * 9.56930e-7;
|
||||
} else if (1792 <= x && x < 2048) {
|
||||
return 0.00048977 + (x-1792) * 1.91441e-6;
|
||||
} else if (2048 <= x && x < 2304) {
|
||||
return 0.00097984 + (x-2048) * 3.82937e-6;
|
||||
} else if (2304 <= x && x < 2560) {
|
||||
return 0.0019601 + (x-2304) * 7.659055e-6;
|
||||
} else if (2560 <= x && x < 2816) {
|
||||
return 0.0039207 + (x-2560) * 1.525e-5;
|
||||
} else {
|
||||
return 0.0078421 + (exp((x-2816)/273.0) - 1) * 0.0092421;
|
||||
}
|
||||
}
|
||||
|
||||
float4 normalize_pv(int4 parsed, float vignette_factor) {
|
||||
// PWL
|
||||
float4 pv = {ox_lut_func(parsed.s0), ox_lut_func(parsed.s1), ox_lut_func(parsed.s2), ox_lut_func(parsed.s3)};
|
||||
return clamp(pv*vignette_factor*256.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float3 color_correct(float3 rgb) {
|
||||
float3 corrected = rgb.x * (float3)(1.5664815, -0.29808738, -0.03973474);
|
||||
corrected += rgb.y * (float3)(-0.48672447, 1.41914433, -0.40295248);
|
||||
corrected += rgb.z * (float3)(-0.07975703, -0.12105695, 1.44268722);
|
||||
return corrected;
|
||||
}
|
||||
|
||||
float3 apply_gamma(float3 rgb, int expo_time) {
|
||||
return -0.507089*exp(-12.54124638*rgb) + 0.9655*powr(rgb, 0.5) - 0.472597*rgb + 0.507089;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -21,16 +21,16 @@ class TiciFanController(BaseFanController):
|
||||
self.controller = PIDController(k_p=0, k_i=4e-3, k_f=1, rate=(1 / DT_HW))
|
||||
|
||||
def update(self, cur_temp: float, ignition: bool) -> int:
|
||||
self.controller.neg_limit = -(100 if ignition else 30)
|
||||
self.controller.pos_limit = -(30 if ignition else 0)
|
||||
self.controller.pos_limit = 100 if ignition else 30
|
||||
self.controller.neg_limit = 30 if ignition else 0
|
||||
|
||||
if ignition != self.last_ignition:
|
||||
self.controller.reset()
|
||||
|
||||
error = 75 - cur_temp
|
||||
fan_pwr_out = -int(self.controller.update(
|
||||
error = cur_temp - 75
|
||||
fan_pwr_out = int(self.controller.update(
|
||||
error=error,
|
||||
feedforward=np.interp(cur_temp, [60.0, 100.0], [0, -100])
|
||||
feedforward=np.interp(cur_temp, [60.0, 100.0], [0, 100])
|
||||
))
|
||||
|
||||
self.last_ignition = ignition
|
||||
|
||||
@@ -67,17 +67,17 @@
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img.xz",
|
||||
"hash": "49faee0e9b084abf0ea46f87722e3366bbd0435fb6b25cce189295c1ff368da1",
|
||||
"hash_raw": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img.xz",
|
||||
"hash": "1468d50b7ad0fda0f04074755d21e786e3b1b6ca5dd5b17eb2608202025e6126",
|
||||
"hash_raw": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087",
|
||||
"size": 5368709120,
|
||||
"sparse": true,
|
||||
"full_check": false,
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "db07761be0130e35a9d3ea6bec8df231260d3e767ae770850f18f10e14d0ab3f",
|
||||
"ondevice_hash": "242aa5adad1c04e1398e00e2440d1babf962022eb12b89adf2e60ee3068946e7",
|
||||
"alt": {
|
||||
"hash": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img",
|
||||
"hash": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img",
|
||||
"size": 5368709120
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,51 +350,51 @@
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img.xz",
|
||||
"hash": "49faee0e9b084abf0ea46f87722e3366bbd0435fb6b25cce189295c1ff368da1",
|
||||
"hash_raw": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img.xz",
|
||||
"hash": "1468d50b7ad0fda0f04074755d21e786e3b1b6ca5dd5b17eb2608202025e6126",
|
||||
"hash_raw": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087",
|
||||
"size": 5368709120,
|
||||
"sparse": true,
|
||||
"full_check": false,
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "db07761be0130e35a9d3ea6bec8df231260d3e767ae770850f18f10e14d0ab3f",
|
||||
"ondevice_hash": "242aa5adad1c04e1398e00e2440d1babf962022eb12b89adf2e60ee3068946e7",
|
||||
"alt": {
|
||||
"hash": "18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/system-18100d9065bb44a315262041b9fb6bfd9e59179981876e442200cc1284d43643.img",
|
||||
"hash": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img",
|
||||
"size": 5368709120
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "userdata_90",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/userdata_90-02f7abb4b667c04043c0c6950145aaebd704851261f32256d0f7e84a52059dda.img.xz",
|
||||
"hash": "1eda66d4e31222fc2e792a62ae8e7d322fc643f0b23785e7527bb51a9fee97c7",
|
||||
"hash_raw": "02f7abb4b667c04043c0c6950145aaebd704851261f32256d0f7e84a52059dda",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/userdata_90-602d5103cba97e1b07f76508d5febb47cfc4463a7e31bd20e461b55c801feb0a.img.xz",
|
||||
"hash": "6a11d448bac50467791809339051eed2894aae971c37bf6284b3b972a99ba3ac",
|
||||
"hash_raw": "602d5103cba97e1b07f76508d5febb47cfc4463a7e31bd20e461b55c801feb0a",
|
||||
"size": 96636764160,
|
||||
"sparse": true,
|
||||
"full_check": true,
|
||||
"has_ab": false,
|
||||
"ondevice_hash": "679b650ee04b7b1ef610b63fde9b43569fded39ceacf88789b564de99c221ea1"
|
||||
"ondevice_hash": "e014d92940a696bf8582807259820ab73948b950656ed83a45da738f26083705"
|
||||
},
|
||||
{
|
||||
"name": "userdata_89",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/userdata_89-bab8399bbe3968f3c496f7bc83c2541b33acc1f47814c4ad95801bf5cb7e7588.img.xz",
|
||||
"hash": "e63d3277285aae1f04fd7f4f48429ce35010f4843ab755f10d360c3aa788e484",
|
||||
"hash_raw": "bab8399bbe3968f3c496f7bc83c2541b33acc1f47814c4ad95801bf5cb7e7588",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/userdata_89-4d7f6d12a5557eb6e3cbff9a4cd595677456fdfddcc879eddcea96a43a9d8b48.img.xz",
|
||||
"hash": "748e31a5fc01fc256c012e359c3382d1f98cce98feafe8ecc0fca3e47caef116",
|
||||
"hash_raw": "4d7f6d12a5557eb6e3cbff9a4cd595677456fdfddcc879eddcea96a43a9d8b48",
|
||||
"size": 95563022336,
|
||||
"sparse": true,
|
||||
"full_check": true,
|
||||
"has_ab": false,
|
||||
"ondevice_hash": "2947374fc5980ffe3c5b94b61cc1c81bc55214f494153ed234164801731f5dc0"
|
||||
"ondevice_hash": "c181b93050787adcfef730c086bcb780f28508d84e6376d9b80d37e5dc02b55e"
|
||||
},
|
||||
{
|
||||
"name": "userdata_30",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/userdata_30-22c874b4b66bbc000f3219abede8d62cb307f5786fd526a8473c61422765dea0.img.xz",
|
||||
"hash": "12d9245711e8c49c51ff2c7b82d7301f2fcb1911edcddb35a105a80911859113",
|
||||
"hash_raw": "22c874b4b66bbc000f3219abede8d62cb307f5786fd526a8473c61422765dea0",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/userdata_30-80a76c8e56bbd7536fd5e87e8daa12984e2960db4edeb1f83229b2baeecc4668.img.xz",
|
||||
"hash": "09ff390e639e4373d772e1688d05a5ac77a573463ed1deeff86390686fa686f9",
|
||||
"hash_raw": "80a76c8e56bbd7536fd5e87e8daa12984e2960db4edeb1f83229b2baeecc4668",
|
||||
"size": 32212254720,
|
||||
"sparse": true,
|
||||
"full_check": true,
|
||||
"has_ab": false,
|
||||
"ondevice_hash": "03c8b65c945207f887ed6c52d38b53d53d71c8597dcb0b63dfbb11f7cfff8d2b"
|
||||
"ondevice_hash": "2c01ab470c02121c721ff6afc25582437e821686207f3afef659387afb69c507"
|
||||
}
|
||||
]
|
||||
@@ -44,7 +44,7 @@ def manager_init() -> None:
|
||||
# set unset params to their default value
|
||||
for k in params.all_keys():
|
||||
default_value = params.get_default_value(k)
|
||||
if default_value and params.get(k) is None:
|
||||
if default_value is not None and params.get(k) is None:
|
||||
params.put(k, default_value)
|
||||
|
||||
# Create folders needed for msgq
|
||||
|
||||
@@ -46,9 +46,10 @@ class TestManager:
|
||||
manager.main()
|
||||
for k in params.all_keys():
|
||||
default_value = params.get_default_value(k)
|
||||
if default_value:
|
||||
if default_value is not None:
|
||||
assert params.get(k) == default_value
|
||||
assert params.get("OpenpilotEnabledToggle")
|
||||
assert params.get("RouteCount") == 0
|
||||
|
||||
@pytest.mark.skip("this test is flaky the way it's currently written, should be moved to test_onroad")
|
||||
def test_clean_exit(self, subtests):
|
||||
|
||||
@@ -67,7 +67,8 @@ class MouseEvent(NamedTuple):
|
||||
|
||||
|
||||
class MouseState:
|
||||
def __init__(self):
|
||||
def __init__(self, scale: float = 1.0):
|
||||
self._scale = scale
|
||||
self._events: deque[MouseEvent] = deque(maxlen=MOUSE_THREAD_RATE) # bound event list
|
||||
self._prev_mouse_event: list[MouseEvent | None] = [None] * MAX_TOUCH_SLOTS
|
||||
|
||||
@@ -102,8 +103,10 @@ class MouseState:
|
||||
def _handle_mouse_event(self):
|
||||
for slot in range(MAX_TOUCH_SLOTS):
|
||||
mouse_pos = rl.get_touch_position(slot)
|
||||
x = mouse_pos.x / self._scale if self._scale != 1.0 else mouse_pos.x
|
||||
y = mouse_pos.y / self._scale if self._scale != 1.0 else mouse_pos.y
|
||||
ev = MouseEvent(
|
||||
MousePos(mouse_pos.x, mouse_pos.y),
|
||||
MousePos(x, y),
|
||||
slot,
|
||||
rl.is_mouse_button_pressed(slot),
|
||||
rl.is_mouse_button_released(slot),
|
||||
@@ -133,7 +136,7 @@ class GuiApplication:
|
||||
self._trace_log_callback = None
|
||||
self._modal_overlay = ModalOverlay()
|
||||
|
||||
self._mouse = MouseState()
|
||||
self._mouse = MouseState(self._scale)
|
||||
self._mouse_events: list[MouseEvent] = []
|
||||
|
||||
# Debug variables
|
||||
|
||||
@@ -39,6 +39,10 @@ vec4 getGradientColor(vec2 pos) {
|
||||
float t = clamp(dot(pos - gradientStart, normalizedDir) / gradientLength, 0.0, 1.0);
|
||||
|
||||
if (gradientColorCount <= 1) return gradientColors[0];
|
||||
|
||||
// handle t before first / after last stop
|
||||
if (t <= gradientStops[0]) return gradientColors[0];
|
||||
if (t >= gradientStops[gradientColorCount-1]) return gradientColors[gradientColorCount-1];
|
||||
for (int i = 0; i < gradientColorCount - 1; i++) {
|
||||
if (t >= gradientStops[i] && t <= gradientStops[i+1]) {
|
||||
float segmentT = (t - gradientStops[i]) / (gradientStops[i+1] - gradientStops[i]);
|
||||
|
||||
@@ -37,7 +37,7 @@ NM_DEVICE_IFACE = "org.freedesktop.NetworkManager.Device"
|
||||
NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8
|
||||
|
||||
TETHERING_IP_ADDRESS = "192.168.43.1"
|
||||
DEFAULT_TETHERING_PASSWORD = "12345678"
|
||||
DEFAULT_TETHERING_PASSWORD = "swagswagcomma"
|
||||
|
||||
|
||||
# NetworkManager device states
|
||||
|
||||
+75
-19
@@ -6,10 +6,14 @@ import time
|
||||
import urllib.request
|
||||
from urllib.parse import urlparse
|
||||
from enum import IntEnum
|
||||
import shutil
|
||||
|
||||
import pyray as rl
|
||||
|
||||
from cereal import log
|
||||
from openpilot.common.run import run_cmd
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.button import Button, ButtonStyle, ButtonRadio
|
||||
@@ -30,6 +34,19 @@ BUTTON_SPACING = 50
|
||||
OPENPILOT_URL = "https://openpilot.comma.ai"
|
||||
USER_AGENT = f"AGNOSSetup-{HARDWARE.get_os_version()}"
|
||||
|
||||
CONTINUE_PATH = "/data/continue.sh"
|
||||
TMP_CONTINUE_PATH = "/data/continue.sh.new"
|
||||
INSTALL_PATH = "/data/openpilot"
|
||||
VALID_CACHE_PATH = "/data/.openpilot_cache"
|
||||
INSTALLER_SOURCE_PATH = "/usr/comma/installer"
|
||||
INSTALLER_DESTINATION_PATH = "/tmp/installer"
|
||||
INSTALLER_URL_PATH = "/tmp/installer_url"
|
||||
|
||||
CONTINUE = """#!/usr/bin/env bash
|
||||
|
||||
cd /data/openpilot
|
||||
exec ./launch_openpilot.sh
|
||||
"""
|
||||
|
||||
class SetupState(IntEnum):
|
||||
LOW_VOLTAGE = 0
|
||||
@@ -93,14 +110,21 @@ class Setup(Widget):
|
||||
self._network_setup_continue_button.set_enabled(False)
|
||||
self._network_setup_title_label = Label("Connect to Wi-Fi", TITLE_FONT_SIZE, FontWeight.BOLD, TextAlignment.LEFT)
|
||||
|
||||
self._custom_software_warning_continue_button = Button("Continue", self._custom_software_warning_continue_button_callback)
|
||||
self._custom_software_warning_continue_button = Button("Scroll to continue", self._custom_software_warning_continue_button_callback,
|
||||
button_style=ButtonStyle.PRIMARY)
|
||||
self._custom_software_warning_continue_button.set_enabled(False)
|
||||
self._custom_software_warning_back_button = Button("Back", self._custom_software_warning_back_button_callback)
|
||||
self._custom_software_warning_title_label = Label("WARNING: Custom Software", 100, FontWeight.BOLD, TextAlignment.LEFT, text_color=rl.Color(255,89,79,255),
|
||||
text_padding=60)
|
||||
self._custom_software_warning_body_label = Label("Use caution when installing third-party software. Third-party software has not been tested by comma,"
|
||||
+ " and may cause damage to your device and/or vehicle.\n\nIf you'd like to proceed, use https://flash.comma.ai "
|
||||
self._custom_software_warning_body_label = Label("Use caution when installing third-party software.\n\n"
|
||||
+ "⚠️ It has not been tested by comma.\n\n"
|
||||
+ "⚠️ It may not comply with relevant safety standards.\n\n"
|
||||
+ "⚠️ It may cause damage to your device and/or vehicle.\n\n"
|
||||
+ "If you'd like to proceed, use https://flash.comma.ai "
|
||||
+ "to restore your device to a factory state later.",
|
||||
85, text_alignment=TextAlignment.LEFT, text_padding=60)
|
||||
self._custom_software_warning_body_scroll_panel = GuiScrollPanel()
|
||||
|
||||
self._downloading_body_label = Label("Downloading...", TITLE_FONT_SIZE, FontWeight.MEDIUM)
|
||||
|
||||
try:
|
||||
@@ -136,21 +160,19 @@ class Setup(Widget):
|
||||
self.state = SetupState.SOFTWARE_SELECTION
|
||||
|
||||
def _custom_software_warning_continue_button_callback(self):
|
||||
self.state = SetupState.CUSTOM_SOFTWARE
|
||||
self.state = SetupState.NETWORK_SETUP
|
||||
self.stop_network_check_thread.clear()
|
||||
self.start_network_check()
|
||||
|
||||
def _getting_started_button_callback(self):
|
||||
self.state = SetupState.NETWORK_SETUP
|
||||
self.stop_network_check_thread.clear()
|
||||
self.start_network_check()
|
||||
self.state = SetupState.SOFTWARE_SELECTION
|
||||
|
||||
def _software_selection_back_button_callback(self):
|
||||
self.state = SetupState.NETWORK_SETUP
|
||||
self.stop_network_check_thread.clear()
|
||||
self.start_network_check()
|
||||
self.state = SetupState.GETTING_STARTED
|
||||
|
||||
def _software_selection_continue_button_callback(self):
|
||||
if self._software_selection_openpilot_button.selected:
|
||||
self.download(OPENPILOT_URL)
|
||||
self.use_openpilot()
|
||||
else:
|
||||
self.state = SetupState.CUSTOM_SOFTWARE_WARNING
|
||||
|
||||
@@ -158,11 +180,14 @@ class Setup(Widget):
|
||||
self.state = SetupState.GETTING_STARTED
|
||||
|
||||
def _network_setup_back_button_callback(self):
|
||||
self.state = SetupState.GETTING_STARTED
|
||||
self.state = SetupState.SOFTWARE_SELECTION
|
||||
|
||||
def _network_setup_continue_button_callback(self):
|
||||
self.state = SetupState.SOFTWARE_SELECTION
|
||||
self.stop_network_check_thread.set()
|
||||
if self._software_selection_openpilot_button.selected:
|
||||
self.download(OPENPILOT_URL)
|
||||
else:
|
||||
self.state = SetupState.CUSTOM_SOFTWARE
|
||||
|
||||
def render_low_voltage(self, rect: rl.Rectangle):
|
||||
rl.draw_texture(self.warning, int(rect.x + 150), int(rect.y + 110), rl.WHITE)
|
||||
@@ -274,13 +299,23 @@ class Setup(Widget):
|
||||
self._download_failed_startover_button.render(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT))
|
||||
|
||||
def render_custom_software_warning(self, rect: rl.Rectangle):
|
||||
self._custom_software_warning_title_label.render(rl.Rectangle(rect.x + 50, rect.y + 150, rect.width - 265, TITLE_FONT_SIZE))
|
||||
self._custom_software_warning_body_label.render(rl.Rectangle(rect.x + 50, rect.y + 200 , rect.width - 50, BODY_FONT_SIZE * 3))
|
||||
warn_rect = rl.Rectangle(rect.x, rect.y, rect.width, 1500)
|
||||
offset = self._custom_software_warning_body_scroll_panel.handle_scroll(rect, warn_rect)
|
||||
|
||||
button_width = (rect.width - MARGIN * 3) / 2
|
||||
button_y = rect.height - MARGIN - BUTTON_HEIGHT
|
||||
|
||||
rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(button_y - BODY_FONT_SIZE))
|
||||
y_offset = rect.y + offset.y
|
||||
self._custom_software_warning_title_label.render(rl.Rectangle(rect.x + 50, y_offset + 150, rect.width - 265, TITLE_FONT_SIZE))
|
||||
self._custom_software_warning_body_label.render(rl.Rectangle(rect.x + 50, y_offset + 200 , rect.width - 50, BODY_FONT_SIZE * 3))
|
||||
rl.end_scissor_mode()
|
||||
|
||||
self._custom_software_warning_back_button.render(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT))
|
||||
self._custom_software_warning_continue_button.render(rl.Rectangle(rect.x + MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT))
|
||||
if offset.y < (rect.height - warn_rect.height):
|
||||
self._custom_software_warning_continue_button.set_enabled(True)
|
||||
self._custom_software_warning_continue_button.set_text("Continue")
|
||||
|
||||
def render_custom_software(self):
|
||||
def handle_keyboard_result(result):
|
||||
@@ -299,6 +334,23 @@ class Setup(Widget):
|
||||
self.keyboard.set_title("Enter URL", "for Custom Software")
|
||||
gui_app.set_modal_overlay(self.keyboard, callback=handle_keyboard_result)
|
||||
|
||||
def use_openpilot(self):
|
||||
if os.path.isdir(INSTALL_PATH) and os.path.isfile(VALID_CACHE_PATH):
|
||||
os.remove(VALID_CACHE_PATH)
|
||||
with open(TMP_CONTINUE_PATH, "w") as f:
|
||||
f.write(CONTINUE)
|
||||
run_cmd(["chmod", "+x", TMP_CONTINUE_PATH])
|
||||
shutil.move(TMP_CONTINUE_PATH, CONTINUE_PATH)
|
||||
shutil.copyfile(INSTALLER_SOURCE_PATH, INSTALLER_DESTINATION_PATH)
|
||||
|
||||
# give time for installer UI to take over
|
||||
time.sleep(1)
|
||||
gui_app.request_close()
|
||||
else:
|
||||
self.state = SetupState.NETWORK_SETUP
|
||||
self.stop_network_check_thread.clear()
|
||||
self.start_network_check()
|
||||
|
||||
def download(self, url: str):
|
||||
# autocomplete incomplete URLs
|
||||
if re.match("^([^/.]+)/([^/]+)$", url):
|
||||
@@ -316,7 +368,7 @@ class Setup(Widget):
|
||||
try:
|
||||
import tempfile
|
||||
|
||||
_, tmpfile = tempfile.mkstemp(prefix="installer_")
|
||||
fd, tmpfile = tempfile.mkstemp(prefix="installer_")
|
||||
|
||||
headers = {"User-Agent": USER_AGENT, "X-openpilot-serial": HARDWARE.get_serial()}
|
||||
req = urllib.request.Request(self.download_url, headers=headers)
|
||||
@@ -346,12 +398,16 @@ class Setup(Widget):
|
||||
self.download_failed(self.download_url, "No custom software found at this URL.")
|
||||
return
|
||||
|
||||
os.rename(tmpfile, "/tmp/installer")
|
||||
os.chmod("/tmp/installer", 0o755)
|
||||
# AGNOS might try to execute the installer before this process exits.
|
||||
# Therefore, important to close the fd before renaming the installer.
|
||||
os.close(fd)
|
||||
os.rename(tmpfile, INSTALLER_DESTINATION_PATH)
|
||||
|
||||
with open("/tmp/installer_url", "w") as f:
|
||||
with open(INSTALLER_URL_PATH, "w") as f:
|
||||
f.write(self.download_url)
|
||||
|
||||
# give time for installer UI to take over
|
||||
time.sleep(5)
|
||||
gui_app.request_close()
|
||||
|
||||
except Exception:
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import IntEnum
|
||||
from functools import partial
|
||||
from threading import Lock
|
||||
from typing import Literal
|
||||
from typing import cast
|
||||
|
||||
import pyray as rl
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
|
||||
from openpilot.system.ui.lib.wifi_manager import NetworkInfo, WifiManagerCallbacks, WifiManagerWrapper, SecurityType
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.button import ButtonStyle, Button, TextAlignment
|
||||
from openpilot.system.ui.widgets.button import ButtonStyle, Button
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
||||
from openpilot.system.ui.widgets.keyboard import Keyboard
|
||||
from openpilot.system.ui.widgets.label import gui_label
|
||||
from openpilot.system.ui.widgets.label import TextAlignment, gui_label
|
||||
|
||||
NM_DEVICE_STATE_NEED_AUTH = 60
|
||||
MIN_PASSWORD_LENGTH = 8
|
||||
@@ -27,43 +27,20 @@ STRENGTH_ICONS = [
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class StateIdle:
|
||||
action: Literal["idle"] = "idle"
|
||||
|
||||
|
||||
@dataclass
|
||||
class StateConnecting:
|
||||
network: NetworkInfo
|
||||
action: Literal["connecting"] = "connecting"
|
||||
|
||||
|
||||
@dataclass
|
||||
class StateNeedsAuth:
|
||||
network: NetworkInfo
|
||||
retry: bool
|
||||
action: Literal["needs_auth"] = "needs_auth"
|
||||
|
||||
|
||||
@dataclass
|
||||
class StateShowForgetConfirm:
|
||||
network: NetworkInfo
|
||||
action: Literal["show_forget_confirm"] = "show_forget_confirm"
|
||||
|
||||
|
||||
@dataclass
|
||||
class StateForgetting:
|
||||
network: NetworkInfo
|
||||
action: Literal["forgetting"] = "forgetting"
|
||||
|
||||
|
||||
UIState = StateIdle | StateConnecting | StateNeedsAuth | StateShowForgetConfirm | StateForgetting
|
||||
class UIState(IntEnum):
|
||||
IDLE = 0
|
||||
CONNECTING = 1
|
||||
NEEDS_AUTH = 2
|
||||
SHOW_FORGET_CONFIRM = 3
|
||||
FORGETTING = 4
|
||||
|
||||
|
||||
class WifiManagerUI(Widget):
|
||||
def __init__(self, wifi_manager: WifiManagerWrapper):
|
||||
super().__init__()
|
||||
self.state: UIState = StateIdle()
|
||||
self.state: UIState = UIState.IDLE
|
||||
self._state_network: NetworkInfo | None = None # for CONNECTING / NEEDS_AUTH / SHOW_FORGET_CONFIRM / FORGETTING
|
||||
self._password_retry: bool = False # for NEEDS_AUTH
|
||||
self.btn_width: int = 200
|
||||
self.scroll_panel = GuiScrollPanel()
|
||||
self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True)
|
||||
@@ -93,17 +70,16 @@ class WifiManagerUI(Widget):
|
||||
gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER)
|
||||
return
|
||||
|
||||
match self.state:
|
||||
case StateNeedsAuth(network, retry):
|
||||
self.keyboard.set_title("Wrong password" if retry else "Enter password", f"for {network.ssid}")
|
||||
self.keyboard.reset()
|
||||
gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(network, result))
|
||||
case StateShowForgetConfirm(network):
|
||||
self._confirm_dialog.set_text(f'Forget Wi-Fi Network "{network.ssid}"?')
|
||||
self._confirm_dialog.reset()
|
||||
gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(network, result))
|
||||
case _:
|
||||
self._draw_network_list(rect)
|
||||
if self.state == UIState.NEEDS_AUTH and self._state_network:
|
||||
self.keyboard.set_title("Wrong password" if self._password_retry else "Enter password", f"for {self._state_network.ssid}")
|
||||
self.keyboard.reset()
|
||||
gui_app.set_modal_overlay(self.keyboard, lambda result: self._on_password_entered(cast(NetworkInfo, self._state_network), result))
|
||||
elif self.state == UIState.SHOW_FORGET_CONFIRM and self._state_network:
|
||||
self._confirm_dialog.set_text(f'Forget Wi-Fi Network "{self._state_network.ssid}"?')
|
||||
self._confirm_dialog.reset()
|
||||
gui_app.set_modal_overlay(self._confirm_dialog, callback=lambda result: self.on_forgot_confirm_finished(self._state_network, result))
|
||||
else:
|
||||
self._draw_network_list(rect)
|
||||
|
||||
def _on_password_entered(self, network: NetworkInfo, result: int):
|
||||
if result == 1:
|
||||
@@ -113,13 +89,13 @@ class WifiManagerUI(Widget):
|
||||
if len(password) >= MIN_PASSWORD_LENGTH:
|
||||
self.connect_to_network(network, password)
|
||||
elif result == 0:
|
||||
self.state = StateIdle()
|
||||
self.state = UIState.IDLE
|
||||
|
||||
def on_forgot_confirm_finished(self, network, result: int):
|
||||
if result == 1:
|
||||
self.forget_network(network)
|
||||
elif result == 0:
|
||||
self.state = StateIdle()
|
||||
self.state = UIState.IDLE
|
||||
|
||||
def _draw_network_list(self, rect: rl.Rectangle):
|
||||
content_rect = rl.Rectangle(rect.x, rect.y, rect.width, len(self._networks) * ITEM_HEIGHT)
|
||||
@@ -147,17 +123,18 @@ class WifiManagerUI(Widget):
|
||||
security_icon_rect = rl.Rectangle(signal_icon_rect.x - spacing - ICON_SIZE, rect.y + (ITEM_HEIGHT - ICON_SIZE) / 2, ICON_SIZE, ICON_SIZE)
|
||||
|
||||
status_text = ""
|
||||
match self.state:
|
||||
case StateConnecting(network=connecting):
|
||||
if connecting.ssid == network.ssid:
|
||||
self._networks_buttons[network.ssid].set_enabled(False)
|
||||
status_text = "CONNECTING..."
|
||||
case StateForgetting(network=forgetting):
|
||||
if forgetting.ssid == network.ssid:
|
||||
self._networks_buttons[network.ssid].set_enabled(False)
|
||||
status_text = "FORGETTING..."
|
||||
case _:
|
||||
self._networks_buttons[network.ssid].set_enabled(True)
|
||||
if self.state == UIState.CONNECTING and self._state_network:
|
||||
if self._state_network.ssid == network.ssid:
|
||||
self._networks_buttons[network.ssid].set_enabled(False)
|
||||
status_text = "CONNECTING..."
|
||||
elif self.state == UIState.FORGETTING and self._state_network:
|
||||
if self._state_network.ssid == network.ssid:
|
||||
self._networks_buttons[network.ssid].set_enabled(False)
|
||||
status_text = "FORGETTING..."
|
||||
elif network.security_type == SecurityType.UNSUPPORTED:
|
||||
self._networks_buttons[network.ssid].set_enabled(False)
|
||||
else:
|
||||
self._networks_buttons[network.ssid].set_enabled(True)
|
||||
|
||||
self._networks_buttons[network.ssid].render(ssid_rect)
|
||||
|
||||
@@ -181,13 +158,16 @@ class WifiManagerUI(Widget):
|
||||
def _networks_buttons_callback(self, network):
|
||||
if self.scroll_panel.is_touch_valid():
|
||||
if not network.is_saved and network.security_type != SecurityType.OPEN:
|
||||
self.state = StateNeedsAuth(network, False)
|
||||
self.state = UIState.NEEDS_AUTH
|
||||
self._state_network = network
|
||||
self._password_retry = False
|
||||
elif not network.is_connected:
|
||||
self.connect_to_network(network)
|
||||
|
||||
def _forget_networks_buttons_callback(self, network):
|
||||
if self.scroll_panel.is_touch_valid():
|
||||
self.state = StateShowForgetConfirm(network)
|
||||
self.state = UIState.SHOW_FORGET_CONFIRM
|
||||
self._state_network = network
|
||||
|
||||
def _draw_status_icon(self, rect, network: NetworkInfo):
|
||||
"""Draw the status icon based on network's connection state"""
|
||||
@@ -212,14 +192,16 @@ class WifiManagerUI(Widget):
|
||||
rl.draw_texture_v(gui_app.texture(STRENGTH_ICONS[strength_level], ICON_SIZE, ICON_SIZE), rl.Vector2(rect.x, rect.y), rl.WHITE)
|
||||
|
||||
def connect_to_network(self, network: NetworkInfo, password=''):
|
||||
self.state = StateConnecting(network)
|
||||
self.state = UIState.CONNECTING
|
||||
self._state_network = network
|
||||
if network.is_saved and not password:
|
||||
self.wifi_manager.activate_connection(network.ssid)
|
||||
else:
|
||||
self.wifi_manager.connect_to_network(network.ssid, password)
|
||||
|
||||
def forget_network(self, network: NetworkInfo):
|
||||
self.state = StateForgetting(network)
|
||||
self.state = UIState.FORGETTING
|
||||
self._state_network = network
|
||||
network.is_saved = False
|
||||
self.wifi_manager.forget_connection(network.ssid)
|
||||
|
||||
@@ -236,22 +218,24 @@ class WifiManagerUI(Widget):
|
||||
with self._lock:
|
||||
network = next((n for n in self._networks if n.ssid == ssid), None)
|
||||
if network:
|
||||
self.state = StateNeedsAuth(network, True)
|
||||
self.state = UIState.NEEDS_AUTH
|
||||
self._state_network = network
|
||||
self._password_retry = True
|
||||
|
||||
def _on_activated(self):
|
||||
with self._lock:
|
||||
if isinstance(self.state, StateConnecting):
|
||||
self.state = StateIdle()
|
||||
if self.state == UIState.CONNECTING:
|
||||
self.state = UIState.IDLE
|
||||
|
||||
def _on_forgotten(self, ssid):
|
||||
with self._lock:
|
||||
if isinstance(self.state, StateForgetting):
|
||||
self.state = StateIdle()
|
||||
if self.state == UIState.FORGETTING:
|
||||
self.state = UIState.IDLE
|
||||
|
||||
def _on_connection_failed(self, ssid: str, error: str):
|
||||
with self._lock:
|
||||
if isinstance(self.state, StateConnecting):
|
||||
self.state = StateIdle()
|
||||
if self.state == UIState.CONNECTING:
|
||||
self.state = UIState.IDLE
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -242,6 +242,9 @@ class Updater:
|
||||
b: str | None = self.params.get("UpdaterTargetBranch")
|
||||
if b is None:
|
||||
b = self.get_branch(BASEDIR)
|
||||
b = {
|
||||
("tici", "release3"): "release-tici"
|
||||
}.get((HARDWARE.get_device_type(), b), b)
|
||||
return b
|
||||
|
||||
@property
|
||||
@@ -283,8 +286,8 @@ class Updater:
|
||||
self.params.put("LastUpdateUptimeOnroad", last_uptime_onroad)
|
||||
self.params.put("LastUpdateRouteCount", last_route_count)
|
||||
else:
|
||||
last_uptime_onroad = self.params.get("LastUpdateUptimeOnroad") or last_uptime_onroad
|
||||
last_route_count = self.params.get("LastUpdateRouteCount") or last_route_count
|
||||
last_uptime_onroad = self.params.get("LastUpdateUptimeOnroad", return_default=True)
|
||||
last_route_count = self.params.get("LastUpdateRouteCount", return_default=True)
|
||||
|
||||
if exception is None:
|
||||
self.params.remove("LastUpdateException")
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ from openpilot.common.git import get_commit, get_origin, get_branch, get_short_b
|
||||
RELEASE_SP_BRANCHES = ['release-c3']
|
||||
TESTED_SP_BRANCHES = ['staging-c3', 'staging-c3-new']
|
||||
MASTER_SP_BRANCHES = ['master']
|
||||
RELEASE_BRANCHES = ['release3-staging', 'release3', 'nightly'] + RELEASE_SP_BRANCHES
|
||||
RELEASE_BRANCHES = ['release3-staging', 'release3', 'release-tici', 'nightly'] + RELEASE_SP_BRANCHES
|
||||
TESTED_BRANCHES = RELEASE_BRANCHES + ['devel', 'devel-staging', 'nightly-dev'] + TESTED_SP_BRANCHES
|
||||
|
||||
BUILD_METADATA_FILENAME = "build.json"
|
||||
|
||||
Reference in New Issue
Block a user